Android Camera HAL3 - 框架流程预览

前面说了 HAL3 是一个总线型的设计结构,本文就先对 HAL3 的控制流进行一个提纲挈领式的概述,主要理解整个 HAL3 的主干框架,以便对后续深入各个细节。

主干流程

以下全部都是摘抄 Google 官方网站上面介绍的 Camera 使用流程。

  • 枚举、打开相机设备并创建有效的会话(session)
    1. 在初始化完成之后(这个初始化我觉得是 framework 层的服务初始化完成),framework 就会监听多有的 camera provider,camera provider 是在 ICameraProvider 接口实现的(AIDL 类型的接口),如果发现系统里面有的话就会建立一个连接。
    2. framework 通过 ICameraProvider::getCameraIdList 接口来进行设备的枚举,其实这里并没有调用到具体设备相关的 HAL3 内部,因为在 provider 初始化的时候就已经调用过了,这里只是把 provider 里面保存下来的 camera list 返回给上层的 framework。
    3. framework 实例化一个新的 ICameraDevice,实例化过程是 ICameraProvider::getCameraDeviceInterface_VX_X() 来完成的。
    4. framework 调用 ICameraDevice::open() 接口来打开并且创建一个新的 session 来完成后续的控制过程。
  • 使用刚刚创建好的 session 进行具体的业务逻辑处理
    1. framework 通过 ICameraDeviceSession::configureStreams() 接口来向设备相关的 HAL 层传递用户所需的 stream 配置,同时 HAL 内部会根据传递下来的 stream 来选择具体的业务进行配置。
    2. framework 通过 ICameraDeviceSession::constructDefaultRequestSettings() 接口来获取一个默认的具体业务场景配置,这个操作可以在 ICameraDevice::open() 之后的任何时刻执行,后面说下这个 constructDefaultRequestSettings() 接口的内容。
    3. framework 使用上面构建的默认 setting,再佐以自己需要的特殊配置,然后发送第一个 request 请求到 HAL 里面去。这个 request 里面必须包含至少一个之前配置好的 stream(没错,它们的地址必须相同,不能够使用 copy 的方式重新新建一个 stream,并且这个 stream 地址与使用由 framework&app 去维护),因为要输出 buffer 给 framework 那边。上面的 request 使用 ICameraDeviceSession::processCaptureRequest() 进行传递,HAL 内部需要阻塞本次 request 请求,直到准备好接受下一个 request 的时候才返回。
    4. framework 持续调用 ICameraDeviceSession::processCaptureRequest() 进行 request 的下发,如果需要切换其它场景的话就再次调用 ICameraDeviceSession::constructDefaultRequestSettings() 进行具体场景的默认 setting 构建。
    5. 当本次 request 开始被 HAL 正式执行的时候,也就是 HAL 底下的硬件 sensor 开始曝光本帧的时候(sensor starts exposing for capture),HAL 需要调用 framework 实现的 ICameraDeviceCallback::notify() 函数进行 SHUTTER 消息回调,这个消息里面包含了时间戳数据和 framenumber。这个是 HAL 层主动发起的回调,但是不需要再第一个 request 下发下去之前就开始回调,并且一个 request 的 notify 完成之前,不要返回任何 result 给到 framework。
    6. 经过几个 pipeline 延迟之后,HAL 开始向 framework 返回已经完成的结果,返回过程通过 ICameraDeviceCallback::processCaptureResult() 函数完成。返回的顺序得是按照提交的 request 请求的顺序来,request 也可以一次性下发多个,数量取决于 request 的队列深度,一般 6~8 个吧,也取决于具体的平台和其代码实现。
  • 上面的过程会不断地进行循环,等到一定时刻,会发生下面的事件
    1. framework 停止下发 request 请求,然后等待所有的 buffer 被填充完毕并且所有的 result 全部返回,这个时候再次调用 ICameraDeviceSession::configureStreams() 接口重新配置业务流,这在 HAL 内部会导致整个的硬件被重置一遍,然后用新的 stream 重新配置,接下来继续上面的因果循环。这说白了就是场景的切换,比如从拍照切换到录像(不关闭 camera app)。
    2. framework 可能会调用 ICameraDeviceSession::close() 接口来结束整个会话,该函数可以在任何时候进行调用,但是 framework 在调用该函数的时候不可以继续下发 request,并且所有其它的函数调用都不能够进行下去,在 close 函数返回之后,HAL 层不得再回调 framework 的函数。其实在 close 之前,通常会有 flush 函数调用,这个函数的调用要求与 close 差不多,close 可以认为就是个收尾动作,flush 会等待所有的 request 完成并且所有的 result 全部返回到 framework。
    3. 当发生错误或者其它异步事件的时候,HAL 必须调用 ICameraDeviceCallback::notify() 函数来返回错误消息给 framework。如果 HAL 内部发生了严重的设备错误,HAL 内部在回复完错误消息之后需要按照 close 函数被调用时候的动作来清理案发现场。同时,在调用 notify 之前,也应该停止所有的尚在队列中的 request 请求响应,严重错误传递给 framework 的时候,HAL 不再返回任何结果给到 framework,并且在 framework 尝试调用 close 的时候需要返回 -ENODEV 或者 NULL。

上面就是整体上站在 framework 角度上来描述一个 camera 业务逻辑的生命周期完整的过程,从上面可以知道比较关键的就是下面几个:

  1. 设备枚举
  2. session 创建
  3. stream 配置,request 构建与下发
  4. result 返回和 notify 事件回调
  5. 现场清理

下面会从这些关键点来稍微概述下它们的工作以及基本的实现原理。依然是从大体上进行一次概述,先基本了解下整个的 Camera 的工作流程是什么样子的。

设备枚举

设备的枚举有两个层面意义上的枚举,第一个是在 provider 初始化的时候向 HAL 层内部的枚举,另一个是 framework 向 provider 索要的设备枚举过程,两者是不太一样的,至少其发生的时间是不一样的。

在 provider 创建的时候,也就是 CameraProviderImpl,这里有一个点,就是旧的 Android P 的代码,只有一个 Provider 的实现,但是新的代码里面扩展了两个,这个以后再说。创建的时候会去获取 HAL 内部定义的 camera_module_t 结构体,然后创建一个新的 Google HAL 层的 CameraModule 抽象,以后的很多配置获取相关的操作都是使用这个 CameraModule 进行中转。

在 CameraModule 创建之后,Provider 就会继续获取设备上面枚举出来的 Camera 数量,然后再遍历所有的 Camera,调用其 get_camera_info 回调函数把 camera 的信息给保存起来,注意这个时候 Camera 设备名的枚举还没有送往 framework 层,而是保存在了 Provider 的一个 map 数组里面,这里只是检查下时候 Camera 设备是否兼容,把兼容的设备放在 map 数组里面去。

然后在 framework 想去获取设备列表的时候,就调用 Provider 的 getCameraIdList 进行设备名列表的获取,这个调用过程依然是通过 HIDL 完成的,媒介就是 Binder 这个跨进程通信工具。

在枚举过程完成之后,就会调用 getCameraDeviceInterface_VX_X 进行设备的创建,这个函数的实现也是在 CameraProviderImpl 里面实现的,之后这个设备的构造函数被调用,正式创建一个与 Camera device id 相关联的 CameraDevice 实体类,最后 framework 会调用这个类的 open 接口,在 open 函数里面会调用到 HAL 底层实现的 open,并且会创建一个 Google HAL 层的 CameraDeviceSession。

然后呢,这个 Session 就是后续进行各种骚操作的基础类了,非常重要,等于说是 framework 通过 Provider 搭建了一个平台,然后平台之上催生了 CameraDeviceSession 这个交通枢纽,所有后期与业务逻辑紧密相关的调用操作都是通过这个交通枢纽来完成的,比如 configure_streams,processCaptureRequest,flush,close 等等。

session 的创建

session 附属于 device 下面,很多重要的操作全部都是使用 session 来完成的。

struct camera3_device_ops_t {
  1. initialize
  2. configure_streams
  3. construct_default_request_settings
  4. process_capture_request
  5. dump
  6. flush  
}

另外在 session 里面还附加了一些回调函数,比如 notify、processCaptureResult 这样的回调,这些回调函数的实现是从 HAL 内层往 Google HAL 以至于 framework 层进行回调的。session 里面还简单的对 request 进行了队列的创建与管理,还对 stream 与 buffer 进行了简单的构建与管理。

总之 session 的角色就是重要交通枢纽,我个人觉得,很多的重要操作以及数据资源都需要从它这里经过。

另外,上面讨论的 Provider 是 2.4 版本,CameraModule 是 1.0 版本,CameraDevice、CameraDeviceSession 都是基于 3.2 这个版本进行的,其它的版本是有不少改变的,尤其是 3.4 版本的 Device 和 3.2 就有很多的不同。这个在之后的文章里面也会稍稍进行说明。

constructDefaultRequestSettings

这个函数我觉得有必要在这里拎出来说下,这个函数的主要功通过我缜密的推理与验证,结论是它就是创建默认的 request 模板之用的(废话,名字都能看出来了)。

说,在 Camera 的世界里面,它有很多种模式,这个在 Google 的官方文档里面有去定义,比如下面的几种类型:

  1. TEMPLATE_MANUAL
  2. TEMPLATE_PREVIEW
  3. TEMPLATE_RECORD

以及其它的多种模板枚举定义,从它们的名字就可以看出来其基本的使用场景了,这里就说下手机上面的经典使用模式吧,比如普通录像模式就对应的是 TEMPLATE_RECORD,还有一种模式是在录像的时候还可以实时地进行拍照操作,这个在最新的手机上面大多都是有这样的功能的,就算是我的小米6上面也都有这个功能。TEMPLATE_ZERO_SHUTTER_LAG 就是所谓的零延时拍照,这个就是说你按下拍照键之后就立刻给你返回了一个照片,而不是等待那么一会儿延迟,可以知道,这个时候的照片其实并不是你按下拍照键那一刻的照片,而是稍前面一点的,所谓零延时拍照。

底层要构建好这样一个以 CameraMetadata 为载体的默认设置集,该 CameraMetadata 由 CameraDeviceSession 和 HAL 底层各执一份,当然源头还是在 HAL 底层,上面的都是其拷贝体,里面包含的内容就是 Google 规定的 CameraMetadata 的内容项了,当然,更加具体的还是取决于 HAL 底层的实现以及部分 Google 规定好的套路化的东西。

本操作在第一次真正的 request 下发之前就已经应该执行完了,在具体的业务逻辑场景开始的时候,framework 使用默认的 CameraMetadata 进行每一帧的 request 具体参数的构建操作,这样就省去了很多的麻烦并且也能够适配好底层 HAL 的实现。

End

本文主要就是说下对于 HAL 来说,在一个具体的业务逻辑下大致的流程,应该先要怎么样,后来怎么样,然后结果怎样返回等等,主要是对整体流程有一个概念性的了解,之后的文章会尽量的从上到下对 Camera 的初始化和使用进行说明,当然也不一定,有可能会穿插着底层 HAL 的实现来说明。

另外,我觉得此处以及那处以及这里那里都应该加上一张图,一图胜千言,但是无奈画图太费劲了,我这么懒,画一张图半个周就过去了,后面有缘的话就画点图,没有就是正常的。


想做的事情就去做吧
发布了128 篇原创文章 · 获赞 263 · 访问量 27万+

猜你喜欢

转载自blog.csdn.net/u013904227/article/details/101151155
今日推荐