共计 8840 个字符,预计需要花费 23 分钟才能阅读完成。
这篇文章次要分上面几点来开展解说:
1)Android 最新 Camera 整体框架;
2)Android Camera2 和 HAL3 的根本理解;
3)Camera2 介绍;
(本文所写的内容基于 Android 9.0)
一、Android 最新 Camera 整体框架
Android Camera 整体框架次要包含三个过程:app 过程、camera server 过程、hal 过程(provider 过程)。
过程之间的通信都是通过 binder 实现,其中 app 和 camera server 通信应用 AIDL(Android Interface Definition Language),camera server 和 hal(provider 过程)通信应用HIDL(HAL interface definition language)。
Android 下面的框架分级,根本都是相似的,应用层 -> framework 层 ->Hal 层,咱们 ps 看下设施上理论的过程状况,如下图所示,能够看到有 cameraserver 和 provider 过程。cameraservice 是负责 app 和 framework 层的通信,而 provider 过程则是负责 framework 和 hal 层之间的通信。
(附:Android 8.0 从新设计了 Android 操作系统框架(在一个名为 “Treble” 的我的项目中),以便让制造商可能以更低的老本更轻松、更疾速地将设施更新到新版 Android 零碎。
Android O 之后应用 Treble 的架构,为了解决 Android 零碎的碎片化问题和进步零碎更新的效率,缩小了 framework 和 HAL 的耦合性,进而引出了 HIDL 的概念。
HIDL 全称为 HAL interface definition language(发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL)。
HIDL 的指标是,框架能够在无需从新构建 HAL 的状况下进行替换。HAL 将由供应商或 SOC 制造商构建,搁置在设施的 /vendor 分区中,这样一来,框架就能够在其本人的分区中通过 OTA 进行替换,而无需从新编译 HAL,这也是 Project Treble 框架设计而诞生的。)
如下图所示,展现了 Android Camera 的最新框架,咱们先大略看下图片流程,对整体框架有个根本理解。
二、Android Camera2 和 HAL3 的根本理解
1) Camera2 接口什么时候开始引入的?
从 Android 5.0 开始,Google 引入了一套全新的相机框架 Camera2(android.hardware.camera2)并且废除了旧的相机框架 Camera1(android.hardware.Camera)
不理解的同学,可能会有疑难,为啥要废除 Camera1 接口?根本起因是,camera1 接口过于简略,没法满足更加简单的相机利用场景。为了给应用层提供更多的相机管制权限,从而构建出更高质量的相机应用程序,Google 才推出了 Camera2 接口。上面能够看下和 Camera1 比拟,Camera2 有哪些高级个性。
2)一些只有 Camera2 才反对的高级个性
- 在开启相机之前查看相机信息
出于某些起因,你可能须要先查看相机信息再决定是否开启相机,例如查看闪光灯是否可用。在 Caemra1 上,你无奈在开机相机之前查看具体的相机信息,因为这些信息都是通过一个曾经开启的相机实例提供的。在 Camera2 上,咱们有了和相机实例齐全剥离的 CameraCharacteristics 实例专门提供相机信息,所以咱们能够在不开启相机的前提下查看简直所有的相机信息。 - 在不开启预览的状况下拍照
在 Camera1 上,开启预览是一个很重要的环节,因为只有在开启预览之后能力进行拍照,因而即便显示预览画面与理论业务需要相违反的时候,你也不得不开启预览。而 Camera2 则不强制要求你必须先开启预览能力拍照。 - 一次拍摄多张不同格局和尺寸的图片
在 Camera1 上,一次只能拍摄一张图片,更不同谈多张不同格局和尺寸的图片了。而 Camera2 则反对一次拍摄多张图片,甚至是多张格局和尺寸都不同的图片。例如你能够同时拍摄一张 1440×1080 的 JPEG 图片和一张全尺寸的 RAW 图片。 - 管制曝光工夫
在暗环境下拍照的时候,如果可能适当缩短曝光工夫,就能够让图像画面的亮度失去进步。在 Camera2 上,你能够在规定的曝光时长范畴内配置拍照的曝光工夫,从而实现拍摄长曝光图片,你甚至能够缩短每一帧预览画面的曝光工夫让整个预览画面在暗环境下也能保障肯定的亮度。而在 Camera1 上你只能 YY 一下。 - 连拍
连拍 30 张图片这样的性能在 Camera2 呈现之前恐怕只有零碎相机能力做到了(通过 OpenGL 截取预览画面的做法除外),也可能是出于这个起因,市面上的第三方相机无一例外都不反对连拍。有了 Camera2,你齐全能够让你的相机应用程序反对连拍性能,甚至是间断拍 30 张应用不同曝光工夫的图片。 - 灵便的 3A 管制
3A(AF、AE、AWB)的管制在 Camera2 上失去了最大化的放权,应用层能够依据业务需要灵便配置 3A 流程并且实时获取 3A 状态,而 Camera1 在 3A 的管制和监控方面提供的接口则要少了很多。例如你能够在拍照前进行 AE 操作,并且监听本这次拍照是否点亮闪光灯。
3)何为 HAL3?
为了配合 Camera2 的应用,Android Hal 层 Camera 框架也做了绝对应的改变,也就是 HAL3。Camera1 接口对应的是调用的 HAL1 框架。
4)一些概念
对于 Camera2 和 Hal3,有些基本概念咱们得理解下~~
咱们先来看下 Camera2 API 波及到哪些类,上面会对各个类的应用进行解说~~
1) Pipeline
Camera2 的 API 模型被设计成一个 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 操作可能仅仅是为了让预览画面更清晰而进行对焦而已。如果你相熟 Camera1,那你可能会问 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。
咱们举个例子来进一步阐明下面三种模式,假如咱们的相机应用程序开启了预览,所以会提交一个反复模式的 Capture 用于一直获取预览画面,而后咱们提交一个单次模式的 Capture,接着咱们又提交了一组间断三次的屡次模式的 Capture,这些不同模式的 Capture 会依照下图所示被执行:
上面是几个重要的注意事项:
- 无论 Capture 以何种模式被提交,它们都是按程序串行执行的,不存在并行执行的状况。
- 反复模式是一个比拟非凡的模式,因为它会保留咱们提交的 CaptureRequest 对象用于一直反复执行 Capture 操作,所以大多数状况下反复模式的 CaptureRequest 和其余模式的 CaptureRequest 是独立的,这就会导致反复模式的参数和其余模式的参数会有肯定的差别,例如反复模式不会配置
CaptureRequest.AF_TRIGGER_START
,因为这会导致相机一直触发对焦的操作。 - 如果某一次的 Capture 没有配置预览的 Surface,例如拍照的时候,就会导致本次 Capture 不会将画面输入到预览的 Surface 上,进而导致预览画面卡顿的状况,所以大部分状况下咱们都会将预览的 Surface 增加到所有的 CaptureRequest 里。
4) CameraManager
CameraManager 是一个负责查问和建设相机连贯的零碎服务,它的性能不多,这里列出几个 CameraManager 的要害性能:
- 将相机信息封装到 CameraCharacteristics 中,并提获取 CameraCharacteristics 实例的形式。
- 依据指定的相机 ID 连贯相机设施。
- 提供将闪光灯设置成手电筒模式的快捷方式。
5 )CameraCharacteristics
CameraCharacteristics 是一个只读的相机信息提供者,其外部携带大量的相机信息,包含代表相机朝向的 LENS_FACING
;判断闪光灯是否可用的 FLASH_INFO_AVAILABLE
;获取所有可用 AE 模式的 CONTROL_AE_AVAILABLE_MODES
等等。如果你对 Camera1 比拟相熟,那么 CameraCharacteristics 有点像 Camera1 的 Camera.CameraInfo
或者 Camera.Parameters
。
6 ) CameraDevice
CameraDevice 代表以后连贯的相机设施,它的职责有以下四个:
- 依据指定的参数创立 CameraCaptureSession。
- 依据指定的模板创立 CaptureRequest。
- 敞开相机设施。
- 监听相机设施的状态,例如断开连接、开启胜利和开启失败等。
相熟 Camera1 的人可能会说 CameraDevice 就是 Camera1 的 Camera 类,实则不是,Camera 类简直负责了所有相机的操作,而 CameraDevice 的性能则非常的繁多,就是只负责建设相机连贯的事务,而更加细化的相机操作则交给了稍后会介绍的 CameraCaptureSession。
7) Surface
Surface 是一块用于填充图像数据的内存空间,例如你能够应用 SurfaceView 的 Surface 接管每一帧预览数据用于显示预览画面,也能够应用 ImageReader 的 Surface 接管 JPEG 或 YUV 数据。每一个 Surface 都能够有本人的尺寸和数据格式,你能够从 CameraCharacteristics 获取某一个数据格式反对的尺寸列表。
8) CameraCaptureSession
CameraCaptureSession 实际上就是配置了指标 Surface 的 Pipeline 实例,咱们在应用相机性能之前必须先创立 CameraCaptureSession 实例。一个 CameraDevice 一次只能开启一个 CameraCaptureSession,绝大部分的相机操作都是通过向 CameraCaptureSession 提交一个 Capture 申请实现的,例如拍照、连拍、设置闪光灯模式、触摸对焦、显示预览画面等等。
9 ) CaptureRequest
CaptureRequest 是向 CameraCaptureSession 提交 Capture 申请时的信息载体,其外部包含了本次 Capture 的参数配置和接管图像数据的 Surface。CaptureRequest 能够配置的信息十分多,包含图像格式、图像分辨率、传感器管制、闪光灯管制、3A 管制等等,能够说绝大部分的相机参数都是通过 CaptureRequest 配置的。值得注意的是每一个 CaptureRequest 示意一帧画面的操作,这意味着你能够准确管制每一帧的 Capture 操作。
10) CaptureResult
CaptureResult 是每一次 Capture 操作的后果,外面包含了很多状态信息,包含闪光灯状态、对焦状态、工夫戳等等。例如你能够在拍照实现的时候,通过 CaptureResult 获取本次拍照时的对焦状态和工夫戳。须要留神的是,CaptureResult 并不蕴含任何图像数据,后面咱们在介绍 Surface 的时候说了,图像数据都是从 Surface 获取的。
11) Request 的整体解决流程
三、代码实战:如何拍摄单张照片
拍摄单张照片是最简略的拍照模式,它应用的就是 单次模式 的 Capture,咱们会应用 ImageReader 创立一个接管照片的 Surface,并且把它增加到 CaptureRequest 里提交给相机进行拍照,最初通过 ImageReader 的回调获取 Image 对象,进而获取 JPEG 图像数据进行保留。
1) 定义回调接口
当拍照实现的时候咱们会失去两个数据对象,一个是通过 onImageAvailable()
回调给咱们的存储图像数据的 Image,一个是通过 onCaptureCompleted()
回调给咱们的存储拍照信息的 CaptureResult,它们是一一对应的,所以咱们定义了如下两个回调接口:
private val captureResults: BlockingQueue<CaptureResult> = LinkedBlockingDeque()
private inner class CaptureImageStateCallback : CameraCaptureSession.CaptureCallback() {
@MainThread
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {super.onCaptureCompleted(session, request, result)
captureResults.put(result)
}
}
private inner class OnJpegImageAvailableListener : ImageReader.OnImageAvailableListener {
@WorkerThread
override fun onImageAvailable(imageReader: ImageReader) {val image = imageReader.acquireNextImage()
val captureResult = captureResults.take()
if (image != null && captureResult != null) {// Save image into sdcard.}
}
}
2) 创立 ImageReader
创立 ImageReader 须要咱们指定照片的大小,所以首先咱们要获取反对的照片尺寸列表,并且从中筛选出适合的尺寸,假如咱们要求照片的尺寸最大不能超过 4032×3024,并且比例必须是 4:3,所以会有如下筛选尺寸的代码片段:
@WorkerThread
private fun getOptimalSize(cameraCharacteristics: CameraCharacteristics, clazz: Class<*>, maxWidth: Int, maxHeight: Int): Size? {val streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val supportedSizes = streamConfigurationMap?.getOutputSizes(clazz)
return getOptimalSize(supportedSizes, maxWidth, maxHeight)
}
@AnyThread
private fun getOptimalSize(supportedSizes: Array<Size>?, maxWidth: Int, maxHeight: Int): Size? {val aspectRatio = maxWidth.toFloat() / maxHeight
if (supportedSizes != null) {for (size in supportedSizes) {if (size.width.toFloat() / size.height == aspectRatio && size.height <= maxHeight && size.width <= maxWidth) {return size}
}
}
return null
}
接着咱们就能够筛选出适合的尺寸,而后创立一个图像格式是 JPEG 的 ImageReader 对象,并且获取它的 Surface:
val imageSize = getOptimalSize(cameraCharacteristics, ImageReader::class.java, maxWidth, maxHeight)!!
jpegImageReader = ImageReader.newInstance(imageSize.width, imageSize.height, ImageFormat.JPEG, 5)
jpegImageReader?.setOnImageAvailableListener(OnJpegImageAvailableListener(), cameraHandler)
jpegSurface = jpegImageReader?.surface
3) 创立 CaptureRequest
接下来咱们应用 TEMPLATE_STILL_CAPTURE
模板创立一个用于拍照的 CaptureRequest.Builder 对象,并且增加拍照的 Surface 和预览的 Surface 到其中:
captureImageRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
captureImageRequestBuilder.addTarget(previewDataSurface)
captureImageRequestBuilder.addTarget(jpegSurface)
附:
- Google Android Camera Demo 代码: https://github.com/android/ca…
- Google 文档,Android Camera 版本介绍 https://source.android.google…
- Google 开发者文档,Camera2 API 介绍
https://developer.android.goo…
自己从事 Android Camera 相干开发已有 5 年
目前在深圳下班
欢送大家关注我的微信公众号“小驰笔记”
大家一起学习交换
——– 2021.03.11 深圳 22:03