阅读 405

Camera2+MediaCodec+MediaMuxer录制视频(音视频合成)及其YUV数据提取(一)

相机预览


简介

Camera2 是最新的 Android 相机框架 API,它取代了已弃用的相机框架库。 Camera2 为复杂的用例提供深入的控制,但需要您管理特定于设备的配置。


使用

步骤(一)获取管理器

获取Camera2 Manager管理类,初始化子线程

private CameraManager mCameraManager; private Handler mBgHandler; this.mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); HandlerThread hThread = new HandlerThread("camera2"); this.mBgHandler = new Handler(hThread.getLooper()) 复制代码

CameraManager 是一个负责查询和建立相机连接的系统服务

步骤(二)获取相机信息

通过CameraManager获取当前设备的camera list列表,并用cameraID获取相应相机信息CameraCharacteristics

private String mRearCameraID, mFrontCameraID, mCurrentCameraId; private CameraCharacteristics mRearCameraCs, mFrontCameraCs; private void getCameraListCameraCharacteristics() {     try {         //获取当前手机的所有摄像头id         String[] cameraList = this.mCameraManager.getCameraIdList();         for (String id : cameraList) {             //通过id获取对应相机信息结构             CameraCharacteristics ccs = this.mCameraManager.getCameraCharacteristics(id);             //获取前置/后置摄像头             Integer facing = ccs.get(CameraCharacteristics.LENS_FACING);             if (facing != null) {                 if (facing == CameraCharacteristics.LENS_FACING_BACK) {                     //后置                     this.mRearCameraCs = ccs;                     this.mRearCameraID = id;                     //设置默认后置                     this.mCurrentCameraId = this.mRearCameraID;                 } else if (facing == CameraCharacteristics.LENS_FACING_FRONT) {                     //前置                     this.mFrontCameraCs = ccs;                     this.mFrontCameraID = id;                 }             }         }     } catch (CameraAccessException e) {         e.printStackTrace();     } } 复制代码

CameraCharacteristics 相机信息提供类,类内部有很多相机信息常量,其中有代表相机方向的 LENS_FACING;判断闪光灯是否可用的 FLASH_INFO_AVAILABLE;获取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES 等。

这里主要去拿到前后置摄像头并且设置当前cameraId(mCurrentCareamId)默认为后置

步骤(三)寻找最佳预览尺寸

寻找最佳预览尺寸BestPreviewSize

private Size previewSize; //判断是前置or后置拿到对应的相机信息 CameraCharacteristics tmpCss = this.mCurrentCameraId.equals(this.mRearCameraID) ? this.mRearCameraCs : this.mFrontCameraCs; //获取摄像头支持的所有输出格式和尺寸 StreamConfigurationMap map = tmpCss.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map != null) {     //根据SurfaceTexture获取输出大小尺寸列表     Size[] sizes = map.getOutputSizes(SurfaceTexture.class);     //寻找最佳尺寸     this.previewSize = findSuitablePreviewSize(Arrays.asList(sizes));     if (this.previewSizeCallback != null) {         //预览尺寸回调         this.previewSizeCallback.onBestSize(this.previewSize.getWidth(), this.previewSize.getHeight());     } } 复制代码

StreamConfigurationMap 这是所有输出格式的权威列表(和大小分别 * 适用于该格式)相机设备支持。 这还包含每个格式/大小的最小帧持续时间和停顿持续时间

步骤(四)初始化ImageReader类

设置输出格式回调主要设置格式是YUV_420_888I420

private ImageReader mImageReader; private ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() {         @Override         public void onImageAvailable(ImageReader reader) {             Image image = reader.acquireNextImage();             //...后续主要代码             //close必须调用             image.close();         }     }; //初始化ImageReader传入预览尺寸宽高,设置相机输出图像格式I420 this.mImageReader = ImageReader.newInstance(this.previewSize.getWidth(), this.previewSize.getHeight(), ImageFormat.YUV_420_888, 1); //设置相机数据回调监听 this.mImageReader.setOnImageAvailableListener(this.imageAvailableListener, this.mBgHandler); 复制代码

Image对象的data被存储在Image类里面,构造参数maxImages控制了最多缓存几帧,新的images通过ImageReader的surface发送给ImageReader,类似一个队列,需要通过acquireLatestImage()或者acquireNextImage()方法取出Image。

需要在不使用完毕后将image.close()进行关闭,否则无法展示下一帧的图像以及数据会看到界面卡主现象

步骤(五)打开相机

相机的准备操作

private CameraDevice mCameraDevice;//相机设备驱动 private CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() {         @Override         public void onOpened(@NonNull CameraDevice camera) {             mCameraDevice = camera;         }         @Override         public void onDisconnected(@NonNull CameraDevice camera) {         }         @Override         public void onError(@NonNull CameraDevice camera, int error) {             camera.close();         }     }; try {     //设置需要打开的摄像头id,设置相机状态回调     this.mCameraManager.openCamera(this.mCurrentCameraId, this.cameraStateCallback, this.mBgHandler); } catch (CameraAccessException e) {     e.printStackTrace(); } 复制代码

CameraDevice.StateCallback 用于接收有关摄像机设备状态的更新的回调对象。必须提供此回调实例才能打开摄像机设备。 这些状态更新包括有关设备完成启动(允许调用CameraDevice.createCaptureSession(SessionConfiguration)),设备断开或关闭以及有关意外设备错误的通知。

步骤(六)相机预览

在此之前需要再xml中创建一个TextureView作为预览View进行输出图像,保证在onSurfaceTextureAvailable调用TextureView的draw方法时,如果还没有初始化SurfaceTexture。那么就会初始化它。初始化好时,就会回调这个接口。SurfaceTexture初始化好时,就表示可以接收外界的绘制指令。

private TextureView mAutoFitTextureView; //相机请求构建者 private CaptureRequest.Builder mCaptureRequestBuilder; this.mAutoFitTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {     @Override     public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {         //ready         startPreview();     }     @Override     public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {     }     @Override     public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {         return true;     }     @Override     public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {     } }); private void startPreview() {     //获取TextureView中的SurfaceView     SurfaceTexture surfaceTexture = this.mAutoFitTextureView.getSurfaceTexture();     //设置预览数据中的缓冲区大小,按照最佳预览尺寸设置     surfaceTexture.setDefaultBufferSize(this.previewSize.getWidth(), this.previewSize.getHeight());     //创建一个新的surface用于添加target list     Surface surface = new Surface(surfaceTexture);     try {         //创建预览请求 TEMPLATE_PREVIEW         this.mCaptureRequestBuilder = this.mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);         this.mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);         //add布局中的Surface 用于预览图像         this.mCaptureRequestBuilder.addTarget(surface);         //将回调预览数据data给ImageReader的Surface对象,此时就会回调设置的ImageReader监听器         this.mCaptureRequestBuilder.addTarget(mImageReader.getSurface());         //相机设备驱动创建session 第一个参数传入设置的target<Surface>list 上面加了下面list也要加入,第二个参数session状态回调,第三handler线程         mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), this.cameraSessionStateCallback, this.mBgHandler);     } catch (CameraAccessException e) {         e.printStackTrace();     } } private CameraCaptureSession mCameraCaptureSession; private CameraCaptureSession.StateCallback cameraSessionStateCallback = new CameraCaptureSession.StateCallback() {     @Override     public void onConfigured(@NonNull CameraCaptureSession session) {         mCameraCaptureSession = session;         try {            //利用session#setRepeatingRequest发送无限重复的指令(预览类型指令),第一个参数即传 请求构建者.build()该对象上面已经设置为预览(TEMPLATE_PREVIEW)类型            mCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {             }, mBgHandler);         } catch (CameraAccessException e) {             e.printStackTrace();         }     }     @Override     public void onConfigureFailed(@NonNull CameraCaptureSession session) {     } }; 复制代码

CaptureRequest 是向 CameraCaptureSession 提交 Capture 请求时的信息build,内部包括了本次 Capture 的参数配置和接收图像数据的 Surface,CaptureRequest 可配置的信息,包括图像格式、图像分辨率、传感器控制、闪光灯控制、3A 控制等等,绝大部分的相机参数都是通过 CaptureRequest 配置的。值得注意的是每一个 CaptureRequest 表示一帧画面的操作,这意味着你可以精确控制每一帧的 Capture 操作。

CameraCaptureSession 是配置了目标 SurfacePipeline 实例,在使用相机功能之前必须先创建 CameraCaptureSession 实例。一个 CameraDevice 一次只能开启一个 CameraCaptureSession,大部分的相机操作都是通过向 CameraCaptureSession 提交一个 Capture 请求实现的,例如拍照、连拍、设置闪光灯模式、触摸对焦、显示预览画面等等。

当设置完setRepeatingRequest的时候再界面中就已经能看到相机的预览效果,并且也在不断的回调ImageReader监听

结尾

由于本内容动较多,加上camer2步骤还是比较繁琐的,下一篇会说一下再YUV_420_888的预览数据输出格式下如何提取YUV420I420NV21NV12 都属于YUV420范畴)的数据进行图像操作


作者:z1zzhyluojin
链接:https://juejin.cn/post/7021736151522213919


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐