Android Camera 模型及 API 接口演变
要了解 Android Camear 相机模型的演变,首先还是得了解硬件抽象层 HAL 相关的知识内容。
可以通过这篇文章了解相关知识 Android 硬件抽象层调用流程小结,包括底层驱动、HAL 硬件抽象层接口、应用层到 HAL 的调用流程。
基本上 HAL 的调用流程都是相似的,对于 Camera 也是这样。
应用进程通过 Binder 通信得到一个系统服务,这个系统服务就是用来访问硬件的。
系统服务最后都是通过 HAL 的接口来访问硬件的驱动程序,从而最终访问硬件设备。
而 HAL 接口的实现方式则是由不同厂商去完成的,只需要按照接口定义的规范实现就好。
正是由于 Android Camera 的硬件抽象层发生了变化,访问硬件的方式有所改变,才导致相机模型发生变化,上层 API 接口也就随之变化了。
当了解了这些变化之后,再回过头去看 Camera 的 API 调用就显得清晰多了。
Android Camera 1.0 的相机模型
Android 5.0 之前的 Camera 版本,功能比较单一,不像 5.0 之后那样支持很多特性,这也是由于它的 HAL 所决定的。
用的是 android.hardware.Camera
包下的内容,回顾一下 Camera 1.0 的调用流程:
- 创建预览类 ( SurfaceView )
- 打开相机( Camera.open )
- 设置预览 ( setPreviewDisplay )
- 开始预览( startPreview )
- 对焦( autoFocus )
- 拍摄( takePicture )
- 拍照监听器处理( PictureCallback )
HAL 接口被设计成了三种运行模式:
- 预览
- 静态拍摄
- 视频录制
其中,预览对应于代码中 Camera 类的 startPreview
函数,而静态拍摄对应于 Camera 类的 takePicture
函数,视频录制则是在 MediaRecorder
类的 setCamera
函数中传入 Camera 对象实例。
三种模式具有略有不同又相互重叠的功能。这样就难以实现新类型的功能(例如连拍模式),因为新类型的功能会介于其中两种模式之间。
从图中可以看到,应用层 Camera 会发出一个请求队列到 HAL ,请求队列中的每个请求都对应三种运行模式中的一种。
当你想要在预览时拍照,然后再返回预览模式,那么就得在拍照前发送请求切换到静态拍摄模式,拍完后再发送请求切换到预览模式。
Android Camera 2.0 的相机模型
在 Android 5.0 之后,相机 API 就有了较大的变化,用的是 android.hardware.camera2
包下的内容了。
回顾一下 Camera 2.0 的调用流程:
- 创建预览类( SurfaceView 或者 TextureView 都行)
- 打开相机( CameraManager.openCamera )
- 相机回调( CemeraDevice.StateCallback )
- 创建 CameraCaptureSession 会话( createCaptureSession )
- 创建一个进行预览的请求( CaptureRequest.Builder )
- 预览请求中设置输出的 Surface( addTarget )
- 在会话中发出预览的请求( setRepeatingRequest )
- 在会话的回调类中处理发出请求的结果( CameraCaptureSession.CaptureCallback )
- 拍照时,创建一个拍照的请求( CaptureRequest )
- 在会话中发出拍照的请求( capture )
- 在会话的回调类中处理发出请求的结果( CameraCaptureSession.CaptureCallback )
以上就是 Camera 2.0 相关的调用流程和对应的重要函数,大致就是每做一次操作都要在会话中发出一次请求。
更具体的如下极客学院流程图所示:
Camera 2.0 的架构将多个运行模式整合为一个统一的视图,可以使用这种视图实现之前的任何模式以及一些其他模式,如连拍模式。这样一来,便可以提高用户对聚焦、曝光以及更多后期处理(例如降噪、对比度和锐化)效果的控制能力。此外,这种简化的视图还能够使应用开发者更轻松地使用相机的各种功能。
Camera 2.0 将相机系统塑造为一个管道,该管道可按照 1:1 的基准将传入的帧捕获请求转化为帧。这些请求会封装有关帧的捕获和处理的所有配置信息,其中包括分辨率和像素格式;手动传感器、镜头和闪光灯控件;3A 运行模式;RAW->YUV 处理控件;统计信息生成等等。
简单来说,应用框架从相机系统请求帧,然后相机系统将结果返回到输出流。
可以将 Camera 2.0 看作是 Camera 1.0 的单向流管道。它会将每个捕获请求转化为传感器捕获的一张图像,这张图像将被处理成:
- 包含有关捕获的元数据的结果对象。
- 图像数据的 1 到 N 个缓冲区,每个缓冲区会进入自己的目的地 Surface。也就是我们创建 CaptureRequest 时的 addTarget 添加的 Surface。
可能的输出 Surface 组经过预配置:
- 每个 Surface 都是一个固定分辨率的图像缓冲区流的目标位置
- 一次只能将少量的 Surface 配置为输出(约 3 个)
一个请求中包含所需的全部捕获设置,以及要针对该请求将图像缓冲区推送到其中的输出 Surface 的列表。请求可以只发生一次(使用 capture ),也可以无限重复(使用 setRepeatingRequest )。捕获的优先级高于重复请求的优先级。
如上图所示,一个 capture 请求,左边就是要输出的目的 Surface 列表。而 CameraDevice 框内的就是请求的队列。相机的硬件设备会处理每个请求,将图像数据的缓冲区输出到设置的目的 Surface 中,同时在回调的 onCaptureComplete 方法中处理请求的结果 CaptureResult。
就是这样一个队列模型,相机系统不断地处理队列中的请求,并且一次可以发起多个请求,而且提交的请求不会出现阻塞的情况,请求始终按照接收的顺序处理。
同样的,如果想要实现连拍功能,只要不断发送捕获的请求 capture 就好了,而不需要像之前一样每次拍完照还得设置回预览模式。
相机模型
这是一个更全面的相机模型图:
在模型图里把一些主要函数和流程图都绘制包含进去了。我们调用的流程基本也是顺着紫色的 API 接口来的。
可以看到,相机可以设置六种类型的 Surface 输出:
- MediaRecorder
- SurfaceView
- RenderScriptAllocation
- ImageReader
- MediaCodec
- SurfaceTexture
而在 CameraCaptureSession 会话类中,发出的请求方法有如下:
- capture()
- captureBurst()
- setStreamingRequest()
- setStreamingBurst()
- stopRepeating()
发出请求后,交由相机硬件去处理,处理后的会先将图像数据输出到缓冲区,然后再从缓冲区输出到设置的目的 Surface。
同时,在会话中发出请求,在请求的回调中还会返回 CaptureResult 这样的请求结果,相当于是一个请求有两个返回的来源了。
经过这样的理解安卓相机模型之后,再去看 API 的调用就不会那么困惑了。
关于 Android Camera 的相关代码,可以参考我的 Github 工程:https://github.com/glumes/Camera2Sample。
参考
1、https://source.android.com/devices/camera/