Camera HAL3架构精讲:接口定义与核心关系
大家好,我是你们的嵌入式架构师老K。今天咱们来啃一块硬骨头——HAL3架构。说实话,我刚接触HAL3的时候也被它绕晕过,什么CameraDevice、CameraStream、CameraSession,一堆新概念砸过来。但搞懂了之后你会发现,这套设计其实挺优雅的。
咱们先看一张整体架构图,后面再逐个拆解。
一、HAL3接口定义——从HAL1到HAL3的进化
先说说为什么要有HAL3。HAL1时代,Camera操作是同步的,你拍一张照片,整个系统都得等着。这在单摄时代还行,但到了多摄时代就彻底崩了。我当年在项目里调试双摄同步,HAL1的代码改得我头皮发麻——每个流都要自己管理buffer,同步全靠spinlock,死锁问题排查了整整两周。
HAL3的核心接口定义在 hardware/libhardware/include/hardware/camera3.h 里。说白了,它定义了一套异步回调机制。关键接口就这几个:
// HAL3 设备接口
typedef struct camera3_device {
hw_device_t common;
// 初始化设备
int (*initialize)(const struct camera3_device *,
const camera3_callback_ops_t *callback_ops);
// 配置Stream
int (*configure_streams)(const struct camera3_device *,
camera3_stream_configuration_t *stream_list);
// 注册Buffer
int (*register_stream_buffers)(const struct camera3_device *,
const camera3_stream_buffer_set_t *buffer_set);
// 提交Capture请求
int (*process_capture_request)(const struct camera3_device *,
camera3_capture_request_t *request);
// 刷新
int (*flush)(const struct camera3_device *);
} camera3_device_t;
嗯,这里要注意 configure_streams 和 process_capture_request 这两个函数。前者决定了你能开多少路流,后者决定了每一帧怎么处理。我在项目中见过有人把这两个搞混,结果stream配置好了但request一直提交失败,debug了一整天才发现是stream配置和request里的stream ID对不上。
二、CameraDevice、CameraStream、CameraSession的关系
这三个概念是HAL3的基石。我习惯用一个比喻来理解:
- CameraDevice —— 就是相机硬件本身。一个物理摄像头对应一个CameraDevice。多摄系统里,你有几个摄像头就有几个Device。
- CameraStream —— 是数据流的抽象。比如你要预览,这是一个Stream;你要拍照,这是另一个Stream;你要做视频录制,可能又是第三个Stream。每个Stream有自己的分辨率、格式、帧率。
- CameraSession —— 是Stream的集合。它把多个Stream绑定到一个逻辑会话里,统一管理它们的生命周期。
它们的关系可以用一张表说清楚:
| 概念 | 对应实体 | 生命周期 | 典型数量 |
|---|---|---|---|
| CameraDevice | 物理摄像头 | 从open到close | 1个/摄像头 |
| CameraStream | 数据流通道 | 从configure到delete | 1~N个/Device |
| CameraSession | Stream集合 | 从创建到销毁 | 1个/应用 |
三、Pipeline模型详解——数据流的核心
Pipeline模型是HAL3最精妙的设计。你想想看,Camera的数据处理链路其实是一条流水线:应用层提交一个Request,HAL层处理,然后返回Result。但HAL3把这个过程拆成了更细的粒度。
每个Stream对应一个独立的Pipeline。Pipeline内部包含三个阶段:
- Request阶段:应用层通过
process_capture_request提交请求。请求里包含了要处理的Stream ID、需要的3A参数、输出buffer等。 - Capture阶段:HAL层拿到请求后,驱动ISP开始干活。这个阶段是异步的,HAL层可以同时处理多个请求。
- Result阶段:处理完成后,通过回调函数
notify和process_capture_result把结果返回给Framework。
我画了个Pipeline的流程图,方便你理解:
这里有个关键点——Pipeline的深度。HAL3允许同时有多个Request在Pipeline里排队。比如你设置了pipelineMaxDepth为4,那么你可以连续提交4个Request,HAL层会并行处理。这在多路Camera场景下特别重要,因为每个Stream的Pipeline是独立的,你可以让预览Stream的Pipeline深度大一些,拍照Stream的深度小一些。
camera3_stream_configuration 的 streams 数组里设置。
四、多路Camera场景下的Pipeline调度
好了,前面都是基础。现在咱们聊聊多路Camera时Pipeline怎么调度。这是HAL3最难的地方,也是面试常考的点。
假设你有两个摄像头:主摄和广角。每个摄像头都有自己的CameraDevice,每个Device又有自己的Stream。但注意,这些Stream的Pipeline是独立运行的。Framework层通过 process_capture_request 分别向两个Device提交Request,HAL层各自处理,然后分别回调Result。
这里有个坑——同步问题。两个摄像头的帧率可能不一样,主摄30fps,广角可能只有15fps。如果你要做双摄融合,就必须等两个Pipeline都完成才能处理。我当年在项目里实现双摄同步,用了两种方案:
- 方案一:Frame Sync —— 在HAL层维护一个同步计数器,等两个Stream都到达同一帧号再一起返回。优点是精度高,缺点是增加了延迟。
- 方案二:Timestamp对齐 —— 每个Result都带上硬件时间戳,Framework层根据时间戳做对齐。优点是延迟低,缺点是对时间戳精度要求高。
最后说一句,HAL3的Pipeline模型虽然复杂,但它给了我们极大的灵活性。你可以为每个Stream独立配置分辨率、格式、帧率,甚至可以为不同的Stream分配不同的ISP资源。这在多路Camera场景下是刚需。
嗯,今天就先聊到这儿。HAL3的接口定义和核心关系搞清楚了,后面咱们才能继续深入多路Camera的实战配置。
可可精品资料库