视频捕获: 基于Camera 2实现的Android平台

优采云 发布时间: 2020-08-06 15:09

  前言

  本文简要介绍了如何使用Camera2相关API在移动Android系统下进行视频捕获.

  Camera2是Google添加到Android 5.0中的全新API,以代替Camera1来操作相机.

  按照惯例,源代码AndroidVideo是第一位.

  Camera2调用相机捕获视频的核心实现是在Camera2Capture.java中.

  权限配置

  要使用Android平台提供的*敏*感*词*,必须首先在配置文件中添加以下权限配置:

  获取相机信息

  打开相机管理器

  CameraManager是用于检测,连接和描述相机设备的系统服务. 您可以通过调用Context.getSystemService(java.lang.String)方法获取CameraManager的实例:

  CameraManager mManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);

  获取相机列表信息

  通过调用CameraManager.getCameraIdList()方法,可以获得摄像机ID的列表:

  String[] cameraIds = mCameraManager.getCameraIdList();

for (String id : cameraIds) {

//TODO

}

  可以通过相应的ID从CameraManager获取相应摄像机的属性集CameraCharacteristics.

  在CameraCharacteristics中,您可以获得诸如前后条件,支持的输出尺寸,支持的输出格式等信息.

  for (String id : cameraIds) {

//传入*敏*感*词*id,获取对应*敏*感*词*的参数集

CameraCharacteristics characteristics = mManager.getCameraCharacteristics(id);

//获取*敏*感*词*的支持等级

Integer level = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

//如果是LEGACY等级,不建议使用该*敏*感*词*

if (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY)

{

continue;

}

//获取*敏*感*词*的朝向

Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);

//筛选出前置*敏*感*词*

if (facing != CameraCharacteristics.LENS_FACING_FRONT) {

continue;

}

//StreamConfigurationMap包含了该*敏*感*词*支持的size、format等信息

StreamConfigurationMap map = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

//获取输出格式为YUV_420_888时兼容的size

Size[] size = map.getOutputSizes(ImageFormat.YUV_420_888);

//获取输出View为SurfaceView时兼容的size

//Size[] size = map.getOutputSizes(SurfaceHolder.class);

//TODO 其他的参数,例如输出格式、输出帧率上下限等

}

  PS: 对于Camera2采集系统,每个摄像机都有一个支持级别:

  PS: 通常,如果摄像机级别为LEVEL_3和LEVEL_FULL,建议使用Camera2进行采集,否则建议使用具有更好兼容性的Camera1进行视频采集.

  打开相机

  通过摄像机信息,我们可以找到所需的CameraId,然后使用该ID来获取我们的摄像机设备CameraDevice.

  函数原型是public void openCamera(String cameraId,final CameraDevice.StateCallback回调,Handler handler),

  cameraId是需要打开的摄像机的ID. 为了监视摄像机的情况,您需要传递一个回调,这是第二个参数CameraDevice.StateCallback. 当然,如果我们不希望打开操作占用UI线程时间,

  我们可以使用Looper构造HandlerThread子线程,然后传入Handler.

  //打开*敏*感*词*,正常打开会回调到CameraDeviceStateCallback的onOpened方法

mManager.openCamera(mCameraId, new CameraDevice.StateCallback() {

@Override

public void onOpened(@NonNull CameraDevice camera) {

//*敏*感*词*成功连接

//camera也就是我们需要获取的*敏*感*词*设备

mCameraDevice = camera;

}

@Override

public void onDisconnected(@NonNull CameraDevice camera) {

//*敏*感*词*断开连接

}

@Override

public void onError(@NonNull CameraDevice camera, int error) {

//打开错误

}

}, mHandler);

  创建一个采集会话

  成功打开相机并获取相应的CameraDevice之后,我们需要创建一个采集会话以提供程序与相机之间的通信.

  函数原型是公共抽象void createCaptureSession(列表输出,CameraCaptureSession.StateCallback回调,Handler处理程序)引发CameraAccessException.

  传入的第一个参数是需要采集的Surface. 为了监视会话的创建,我们需要传递CameraCaptureSession.StateCallback回调. 当然,第三个参数是允许在相应的Handler所在的线程中执行操作.

  //获取一个采集Session会话,正常流程回回调到CameraCaptureSessionStateCallback的onConfigured方法

mCameraDevice.createCaptureSession(Arrays.asList(mSurfaceView.getHolder().getSurface()), new CameraCaptureSession.StateCallback() {

@Override

public void onConfigured(@NonNull CameraCaptureSession session) {

//会话创建成功

//mCameraCaptureSession也就是新创建的会话

mCameraCaptureSession = session;

}

@Override

public void onConfigureFailed(@NonNull CameraCaptureSession session) {

//会话创建失败

}

}, mHandler);

  PS: 对于某些业务需求,有必要提高捕获帧速率(120fps及以上). 这个createConstrainedHighSpeedCaptureSession()会话可以很好地支持此功能.

  发送采集请求

  当您需要开始采集时,需要构造一个采集请求,然后将该请求发送到采集会话.

  //创建一个基于录制的请求

mRequest = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

//将需要的目标Surface加入Target列表

mRequest.addTarget(surface);

//重复发送这个请求,进行持续的采集

mCameraCaptureSession.setRepeatingRequest(mRequest.build(), NULL, mHandler);

  原创数据回调

  在获取Camera1时,通常通过设置setPreviewCallbackWithBuffer()和addCallbackBuffer()来获取采集的原创数据,那么该功能如何在Camera2中实现?

  我们可以使用ImageReader类:

  //ImageReader是一个数据回调模块,类似于Camera1的setPreviewCallbackWithBuffer

mReader = ImageReader.newInstance(mConfig.mWidth, mConfig.mHeight, mConfig.mFormat, 2);

mReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {

@Override

public void onImageAvailable(ImageReader reader) {

Image image = reader.acquireNextImage();

//数据处理

image.close();

}

}, mHandler);

  我们需要在createCaptureSession()的第一个参数中传递ImageReader的Surface:

  //通过ImageReader.getSurface()获取一个Surface并将其传给Session中

mCameraDevice.createCaptureSession(Arrays.asList(mReader.getSurface())//....);

  然后将这个目标添加到CaptureRequest中:

  //当然,构造请求时,需要将该Surface同时加入到Request的Target列表中

mRequest.addTarget(mReader.getSurface());

  参考资料

  对于Camera2相关的项目,我们通常可以参考以下项目:

  googlesamples / android-Camera2Basic

  google / cameraview

  结论

  本文简要介绍了基于Camera2 API的Android平台的相机采集功能.

  尽管Camera2是Google推荐的当前采集框架,但是由于制造商的兼容性问题,Camera2的API功能相对不稳定;

  因此,作者仍然建议开发以Camera1作为主要集合,而Camera2作为辅助集合的体系结构.

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线