Camera HAL 架构深度解析:HAL3 架构、Pipeline 模型、Stream 配置、Session 管理、关键接口
大家好,我是你们的老朋友。今天咱们来啃一块硬骨头——Camera HAL 架构。说实话,HAL 层是手机影像系统的“腰杆子”,腰杆子硬不硬,直接决定了上层算法能不能发挥出来。我见过太多项目,算法团队和驱动团队互相甩锅,最后发现是 HAL 层的 pipeline 没搭对。
这一章,我会把 HAL3 的核心架构掰开揉碎了讲。你不需要背代码,但得理解它为什么这么设计。咱们从 Pipeline 模型开始。
2.1 HAL3 与 Pipeline 模型
HAL3 相比 HAL1,最大的变化就是引入了 Pipeline(流水线) 的概念。说白了,HAL1 是“单线程”的,你拍一张,我处理一张,处理完才能拍下一张。HAL3 不一样,它允许你同时处理多帧数据,就像工厂里的流水线,每个工位各干各的。
核心思想:Pipeline 将图像处理拆分为多个阶段(Stage),每个阶段可以独立调度、并行执行。这为多帧合成、HDR、夜景模式提供了底层支持。
我个人习惯把 Pipeline 想象成一条“数据高速公路”。Sensor 是起点,ISP 是加工厂,App 是终点。数据从起点出发,经过各个加工节点,最终到达 App 手里。每个节点都可以对数据做“手脚”——降噪、锐化、色彩校正,全在这里。
下面这张图是我手绘的 HAL3 Pipeline 核心结构,你感受一下:
我的经验:调试 Pipeline 时,最怕的是“数据打架”。比如预览流和拍照流同时抢 ISP 资源,导致预览掉帧。我建议你在设计初期就明确每个流的优先级,用 setStreamUseCase 给 HAL 层“打招呼”。
2.2 Stream 配置与 Session 管理
Stream 配置,说白了就是告诉 HAL 层:“我要开几条路,每条路跑什么数据。” 每条路就是一个 Stream,它有分辨率、格式、帧率、方向等属性。
Session 管理更关键。一个 Session 就是一次完整的“相机会话”——从打开相机到关闭相机。Session 内部可以动态添加或移除 Stream,但不能在 Session 运行中修改已有 Stream 的参数。嗯,这里要注意,很多新手会踩这个坑。
| 概念 | 说明 | 常见坑点 |
|---|---|---|
| Stream | 一条数据通道,包含格式、分辨率、方向 | 分辨率不匹配会导致 configure_streams 失败 |
| Session | 相机会话生命周期,管理一组 Stream | Session 内不能修改已有 Stream 参数 |
| Pipeline | Stream 内部的数据处理流水线 | Pipeline 深度影响延迟,太深会卡顿 |
我曾经在一个项目里,App 端想动态切换预览分辨率,结果直接调了 configure_streams,导致 Session 崩溃。后来才发现,必须先 close 旧 Session,再 open 新 Session。这个教训让我记住了:Stream 配置是静态的,Session 是动态的,但动态有限度。
2.3 关键接口:process_capture_request 与 process_capture_result
这两个接口是 HAL3 的“心脏”。所有数据流都通过它们传递。
- process_capture_request:App 层下发拍照/预览请求,HAL 层接收并开始处理。
- process_capture_result:HAL 层处理完成后,把结果返回给 App 层。
说白了,这就是一个“请求-响应”模型。但难点在于,HAL3 支持 异步 和 乱序返回。什么意思?你发了请求 A、B、C,结果可能先返回 C,再返回 A,最后返回 B。为什么?因为 Pipeline 里每个节点的处理时间不一样。
避坑指南:我曾经遇到一个 bug,App 层假设请求按顺序返回,结果预览画面一直闪烁。查了两天才发现是 HAL 层返回结果的 frame_number 和请求的 frame_number 没对齐。记住:永远用 frame_number 做匹配,不要依赖顺序。
下面是一个简化的代码示例,展示这两个接口的协作方式:
// HAL 层接收请求
void process_capture_request(const camera3_capture_request_t *request) {
uint32_t frame_number = request->frame_number;
// 解析 request 中的 settings、input_buffer、output_buffers
// 将请求分发到对应的 Pipeline 节点
// 异步处理...
}
// HAL 层返回结果
void process_capture_result(const camera3_capture_result_t *result) {
uint32_t frame_number = result->frame_number;
// 检查 result 中的 status、output_buffers、metadata
// 通知 App 层数据已就绪
}
关键点:process_capture_result 可以多次调用同一个 frame_number。第一次返回部分 metadata,第二次返回 buffer,第三次返回最终结果。这叫“分步返回”,用于降低延迟。
2.4 实战:如何调试 HAL3 性能问题
调试 HAL3 性能问题,我有一套自己的“三板斧”:
- 抓 log:打开
persist.vendor.camera.log相关开关,看每个 Pipeline 节点的耗时。 - 看帧率:用
dumpsys media.camera查看每个 Stream 的实际帧率,对比预期值。 - 查 buffer 队列:如果预览卡顿,多半是 buffer 队列满了。检查
dequeue_buffer和queue_buffer的调用频率。
我记得有一次,客户反馈夜景模式拍照慢。我抓了 log 一看,发现 RAW 节点处理时间从 30ms 飙到了 120ms。原因是有个降噪算法没走硬件加速,跑在了 CPU 上。后来改成 GPU 加速,直接降到 25ms。你看,问题往往出在 Pipeline 的某个节点上。
我的建议:在 HAL 层加一个“性能探针”,在每个 Pipeline 节点前后打时间戳。这样你就能一眼看出瓶颈在哪。别信“我觉得”,要信数据。
好了,这一章的内容就到这里。HAL3 架构的核心就是 Pipeline 模型、Stream 配置、Session 管理,以及那两个关键接口。你把这些搞懂了,后面调优的时候就不会抓瞎。