Android Camera系列(五):Camera2

Life was like a box of chocolates, you never know what you’re gonna get.
生命就像一盒巧克力,你永远无法知道下一个是什么味道的。

本系列主要讲述Android开发中Camera的相关操作、预览方式、视频录制等。项目结构简单、代码耦合性低,旨在帮助大家能从中有所收获(方便copy :) ),对于个人来说也是一个总结的好机会。

Alt
Android5.0开始Google推荐使用Camera2替代android.hardware.Camera,于是便有了这篇文章。这篇文章我们基于Android Camera系列(一):SurfaceView+Camera中定义好的ICameraManager接口来封装Camera2。

一.概述

Camera2 API是Android 5.0(Lollipop)之后引入的新版相机API。与早期的Camera API相比,Camera2提供了更多的功能和对摄像头硬件的更深入的控制。这使得开发者可以实现更复杂、更高级的摄像头功能,如实时预览、拍照、录像、对焦、闪光灯控制等。

camera2对于camera来说是一套全新的API,这就需要我们先了解下Camera2的整体架构,如下图:
请添加图片描述

Google重新设计Android Camera API 的目的在于大幅提高应用对于 Android 设备上的相机子系统的控制能力,同时重新组织 API,提高其效率和可维护性。
在CaptureRequest中设置不同的Surface用于接收不同的图片数据,最后从不同的Surface中获取到图片数据和包含拍照相关信息的CaptureResult。

Camera2的核心类和开发步骤请看下图:
请添加图片描述

二. 相关类

1. CameraManager

CameraManager 是一个负责查询和建立相机连接的系统服务,功能如下:

  1. 获取当前设备中可用的相机列表getCameraIdList
  2. 根据摄像头id返回该摄像头的相关信息getCameraCharacteristics(String cameraId)
  3. 根据指定的相机 ID 连接相机设备
  4. 提供将闪光灯设置成手电筒模式的快捷方式

2. CameraDevice

描述系统摄像头,类似于android.hardware.Camera

  1. 根据指定的参数创建 CameraCaptureSession。
  2. 根据指定的模板创建 CaptureRequest。
  3. 关闭相机设备。
  4. 监听相机设备的状态,例如断开连接、开启成功和开启失败等。

Camera1和CameraDevice很类似,但又不同。Camera 类几乎负责了所有相机的操作,而 CameraDevice 的功能则十分的单一,就是只负责建立相机连接的事务,而更加细化的相机操作则交给了稍后会介绍的 CameraCaptureSession。

3. CameraCaptureSession

当需要拍照、预览等功能时,需要先创建该类的实例,然后通过该实例里的方法进行控制。

一个 CameraDevice 一次只能开启一个 CameraCaptureSession,绝大部分的相机操作都是通过向 CameraCaptureSession 提交一个 Capture 请求实现的,例如拍照、连拍、设置闪光灯模式、触摸对焦、显示预览画面等等。

4. CameraCharacteristics

CameraCharacteristics 是一个只读的相机信息提供者,其内部携带大量的相机信息,包括代表相机朝向的 LENS_FACING;判断闪光灯是否可用的 FLASH_INFO_AVAILABLE;获取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES 等等。CameraCharacteristics 有点像 Camera1 的 Camera.CameraInfo 或者 Camera.Parameters

5. CaptureRequest

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

6. CaptureResult

CaptureResult 是每一次 Capture 操作的结果,里面包括了很多状态信息,包括闪光灯状态、对焦状态、时间戳等等。例如你可以在拍照完成的时候,通过 CaptureResult 获取本次拍照时的对焦状态和时间戳。需要注意的是,CaptureResult 并不包含任何图像数据,前面我们在介绍 Surface 的时候说了,图像数据都是从 Surface 获取的。

7. TotalCaptureResult

TotalCaptureResult继承自CaptureResult,功能类似

8. StreamConfigurationMap

获取输出流配置,如:可支持的预览尺寸

9. Image

一个完整的图片缓存,可从该对象中获取YUV、JPEG、RGBA等数据

10. ImageReader

用于从相机打开的通道中读取需要的格式的原始图像数据,可以设置多个ImageReader。

三. 开发步骤

我们按照ICameraManager定义的接口实现Camera2的API,我们这里只讲关键步骤

1. 打开摄像头

  • 获取想要打开的CameraId
  • 配置Camera参数,如:预览尺寸、拍照尺寸
  • 打开Camera
public void openCamera() {
   
   
    Log.v(TAG, "openCamera");
    if (mCameraDevice != null) {
   
   
        return;
    }
    mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
    // 相机ID
    String cameraId = setUpCameraOutputs(mCameraManager);
    if (cameraId == null) return;
    startBackgroundThread(); // 对应 releaseCamera() 方法中的 stopBackgroundThread()
    mOrientationEventListener.enable();
    try {
   
   
        mCameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
        // 每次切换摄像头计算一次就行,结果缓存到成员变量中
        initDisplayRotation();
        initZoomParameter();
        mFacing = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
        Logs.i(TAG, "facing:" + mFacing);
        if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
   
   
            return;
        }
        // 打开摄像头
        mCameraManager.openCamera(cameraId, mStateCallback, mBackgroundHandler);
    } catch (CameraAccessException e) {
   
   
        e.printStackTrace();
    }
}

选择想要打开的CameraId

private String setUpCameraOutputs(CameraManager cameraManager) {
   
   
    String cameraId = null;
    try {
   
   
        // 获取相机ID列表
        String[] cameraIdList = cameraManager.getCameraIdList();
        for (String id : cameraIdList) {
   
   
            // 获取相机特征
            CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(id);
            int facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
            if (mCameraId == 0 && facing == CameraCharacteristics.LENS_FACING_BACK) {
   
   
                cameraId = id;
                break;
            } else if (mCameraId == 1 && facing == CameraCharacteristics.LENS_FACING_FRONT) {
   
   
                cameraId = id;
                break;
            }
        }

        if (cameraId == null) {
   
   
            onOpenError(CAMERA_ERROR_NO_ID, "Camera id:" + mCameraId + " not found.");
            return null;
        }

        if (!configCameraParams(cameraManager, cameraId)) {
   
   
            onOpenError(CAMERA_ERROR_OPEN, "Config camera error.");
            return null;
        }
    } catch (CameraAccessException e) {
   
   
        onOpenError(CAMERA_ERROR_OPEN, e.getMessage());
        return null;
    } catch (NullPointerException e) {
   
   
        onOpenError(CAMERA_ERROR_OPEN, e.getMessage());
        return null;
    }
    return cameraId;
}

配置预览和拍照尺寸

private boolean configCameraParams(CameraManager manager, String cameraId) throws CameraAccessException {
   
   
    CameraCharacteristics characteristics
            = manager.getCameraCharacteristics(cameraId);

    StreamConfigurationMap map = characteristics.get(
            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    if (map == null) {
   
   
        return false;
    }
    Size previewSize = getSuitableSize(new ArrayList<>(Arrays.asList(map.getOutputSizes(SurfaceTexture.class))));
    Logs.i(TAG, "previewSize: " + previewSize);
    mPreviewSize = previewSize;
    mPreviewWidth = mPreviewSize.getWidth();
    mPreviewHeight = mPreviewSize.getHeight();

    Size[] supportPictureSizes = map.getOutputSizes(<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值