转载请标明出处:https://blog.csdn.net/ZhijunHong/article/details/115730693,谢谢~
写在前面
Google从Android 5.0 L(API 21) 版本,开始引入Camera2(android.hardware.camera2)
以取代Camera1(android.hardware.Camera)
相机框架。
Camera2相比于之前的Camera1架构完全不同,使用起来比较复杂,与此同时功能也变得非常强大。
此篇博客,能够帮助你快速构建并理解自定义Camera2相机的关键步骤。
完整代码,请移步:https://github.com/zhijunhong/custom_camera/tree/master/camera2
使用Camera2的优点
通过设计框架的改造和优化,Camera2具备了以下优点:
- 改进了新硬件的性能,使用更先进的API架构;
- 可以获取更多的帧(预览/拍照)信息以及手动控制每一帧的参数;
- 对Camera的控制更加完全(比如支持调整focus distance, 剪裁预览/拍照图片);
- 支持更多图片格式(yuv/raw)以及高速连拍;
- …
自定义Camera2相机
一些概念
1. Pipeline
Camera2的API模型被设计成一个 Pipeline(管道),它按顺序处理每一帧的请求并返回请求结果给客户端。下面这张来自官方的图展示了Pipeline的工作流程,我们会通过一个简单的例子详细解释这张图。
Pipeline示意图
为了解释上面的示意图,假设我们想要同时拍摄两张不同尺寸的图片,并且在拍摄的过程中闪光灯必须亮起来。整个拍摄流程如下:
- 创建一个用于从Pipeline获取图片的CaptureRequest;
- 修改CaptureRequest的闪光灯配置,让闪光灯在拍照过程中亮起来;
- 创建两个不同尺寸的Surface用于接收图片数据,并且将它们添加到CaptureRequest中;
- 发送配置好的CaptureRequest到Pipeline中等待它返回拍照结果。
一个新的CaptureRequest会被放入一个被称作Pending Request Queue的队列中等待被执行,当In-Flight Capture Queue队列空闲的时候就会从Pending Request Queue获取若干个待处理的CaptureRequest,并且根据每一个CaptureRequest 的配置进行Capture操作。最后我们从不同尺寸的Surface中获取图片数据并且还会得到一个包含了很多与本次拍照相关的信息的CaptureResult,流程结束。
2. Supported Hardware Level
相机功能的强大与否和硬件息息相关,不同厂商对 Camera2 的支持程度也不同,所以Camera2定义了一个叫做Supported Hardware Level的重要概念。其作用是将不同设备上的Camera2根据功能的支持情况划分成多个不同级别以便开发者能够大概了解当前设备上Camera2的支持情况。截止到Android P为止,从低到高一共有LEGACY、LIMITED、FULL 和 LEVEL_3四个级别:
- LEGACY:向后兼容的级别,处于该级别的设备意味着它只支持Camera1的功能,不具备任何Camera2高级特性;
- LIMITED:除了支持Camera1的基础功能之外,还支持部分Camera2高级特性的级别;
- FULL:支持所有Camera2的高级特性;
- LEVEL_3:新增更多Camera2高级特性,例如YUV数据的后处理等。
3. Capture
相机的所有操作和参数配置最终都是服务于图像捕获,例如对焦是为了让某一个区域的图像更加清晰,调节曝光补偿是为了调节图像的亮度等。因此,在Camera2 里面所有的相机操作和参数配置都被抽象成Capture(捕获),所以不要简单的把Capture直接理解成是拍照,因为Capture操作可能仅仅是为了让预览画面更清晰而进行对焦而已。如果你熟悉Camera,那你可能会问 setFlashMode()
在哪?setFocusMode()
在哪?takePicture()
在哪?告诉你,它们都是通过Capture 来实现的。
Capture从执行方式上又被细分为【单次模式】、【多次模式】和【重复模式】三种,我们来一一解释下:
- 单次模式(One-shot):指的是只执行一次的Capture操作,例如设置闪光灯模式、对焦模式和拍一张照片等。多个一次性模式的Capture会进入队列按顺序执行。
- 多次模式(Burst):指的是连续多次执行指定的Capture操作,该模式和多次执行单次模式的最大区别是连续多次Capture期间不允许插入其他任何Capture 操作,例如连续拍摄100张照片,在拍摄这100张照片期间任何新的Capture请求都会排队等待,直到拍完100张照片。多组多次模式的Capture会进入队列按顺序执行。
- 重复模式(Repeating):指的是不断重复执行指定的Capture操作,当有其他模式的Capture提交时会暂停该模式,转而执行其他被模式的Capture,当其他模式的 Capture 执行完毕后又会自动恢复继续执行该模式的Capture,例如显示预览画面就是不断 Capture 获取每一帧画面。该模式的 Capture 是全局唯一的,也就是新提交的重复模式Capture会覆盖旧的重复模式Capture。
关键API
CameraManager | CameraManager是一个负责查询和建立相机连接的系统服务,它的功能不多,这里列出几个CameraManager的关键功能: 1.将相机信息封装到CameraCharacteristics中,并提获取CameraCharacteristics实例的方式; 2.根据指定的相机ID连接相机设备; 3.提供将闪光灯设置成手电筒模式的快捷方式。 |
---|---|
CameraCharacteristics | CameraCharacteristics 是一个只读的相机信息提供者,其内部携带大量的相机信息,包括代表相机朝向的 LENS_FACING ;判断闪光灯是否可用的 FLASH_INFO_AVAILABLE ;获取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES 等。如果你对Camera1比较熟悉,那么CameraCharacteristics有点像Camera1的 Camera.CameraInfo 或者 Camera.Parameters 。 |
CameraDevice | CameraDevice 代表当前连接的相机设备,它的职责有以下四个: 1.根据指定的参数创建 CameraCaptureSession; 2.根据指定的模板创建 CaptureRequest; 3.关闭相机设备; 4.监听相机设备的状态,例如断开连接、开启成功和开启失败等。 熟悉Camera1的人可能会说CameraDevice就是Camera1的 Camera 类,实则不是,Camera 类几乎负责了所有相机的操作,而 CameraDevice 的功能则十分的单一,就是只负责建立相机连接的事务,而更加细化的相机操作则交给了稍后会介绍的CameraCaptureSession。 |
Surface | Surface 是一块用于填充图像数据的内存空间,例如你可以使用 SurfaceView 的 Surface 接收每一帧预览数据用于显示预览画面,也可以使用 ImageReader 的 Surface 接收 JPEG 或 YUV 数据。每一个 Surface 都可以有自己的尺寸和数据格式,你可以从 CameraCharacteristics 获取某一个数据格式支持的尺寸列表。 |
CameraCaptureSession | CameraCaptureSession 实际上就是配置了目标 Surface 的 Pipeline 实例,我们在使用相机功能之前必须先创建 CameraCaptureSession 实例。一个 CameraDevice 一次只能开启一个 CameraCaptureSession,绝大部分的相机操作都是通过向 CameraCaptureSession 提交一个 Capture 请求实现的,例如拍照、连拍、设置闪光灯模式、触摸对焦、显示预览画面等。 |
CaptureRequest | CaptureRequest 是向 CameraCaptureSession 提交 Capture 请求时的信息载体,其内部包括了本次 Capture 的参数配置和接收图像数据的 Surface。CaptureRequest 可以配置的信息非常多,包括图像格式、图像分辨率、传感器控制、闪光灯控制、3A 控制等,可以说绝大部分的相机参数都是通过 CaptureRequest 配置的。值得注意的是每一个 CaptureRequest 表示一帧画面的操作,这意味着你可以精确控制每一帧的 Capture 操作。 |
CaptureResult | CaptureResult 是每一次 Capture 操作的结果,里面包括了很多状态信息,包括闪光灯状态、对焦状态、时间戳等等。例如你可以在拍照完成的时候,通过 CaptureResult 获取本次拍照时的对焦状态和时间戳。需要注意的是,CaptureResult 并不包含任何图像数据,前面我们在介绍 Surface 的时候说了,图像数据都是从 Surface 获取的。 |
ImageReader | 用于从相机打开的通道中读取需要的格式的原始图像数据,可以设置多个ImageReader。 |
开发流程
1.获取CameraManager
private val cameraManager: CameraManager by lazy {
getSystemService(CameraManager::class.java) }
2.获取相机信息
val cameraIdList = cameraManager.cameraIdList
cameraIdList.forEach {
cameraId ->
val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
if (cameraCharacteristics.isHardwareLevelSupported(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
if (cameraCharacteristics[CameraCharacteristics.LENS_FACING] == CameraCharacteristics.LENS_FACING_FRONT) {
frontCameraId = cameraId
frontCameraCharacteristics = cameraCharacteristics
} else if (cameraCharacteristics[CameraCharacteristics.LENS_FACING] == CameraCharacteristics.LENS_FACING_BACK) {
backCameraId = cameraId
backCameraCharacteristics = cameraCharacteristics