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 核心结构,你感受一下:

HAL3 Pipeline 核心结构 Sensor ISP RAW ISP YUV JPEG Enc 每个节点可以独立配置参数,比如 RAW 节点做黑电平校正,YUV 节点做降噪 并行分支示例 预览流 拍照流 视频流 深度流

我的经验:调试 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 性能问题,我有一套自己的“三板斧”:

  1. 抓 log:打开 persist.vendor.camera.log 相关开关,看每个 Pipeline 节点的耗时。
  2. 看帧率:用 dumpsys media.camera 查看每个 Stream 的实际帧率,对比预期值。
  3. 查 buffer 队列:如果预览卡顿,多半是 buffer 队列满了。检查 dequeue_buffer 和 queue_buffer 的调用频率。

我记得有一次,客户反馈夜景模式拍照慢。我抓了 log 一看,发现 RAW 节点处理时间从 30ms 飙到了 120ms。原因是有个降噪算法没走硬件加速,跑在了 CPU 上。后来改成 GPU 加速,直接降到 25ms。你看,问题往往出在 Pipeline 的某个节点上。

我的建议:在 HAL 层加一个“性能探针”,在每个 Pipeline 节点前后打时间戳。这样你就能一眼看出瓶颈在哪。别信“我觉得”,要信数据。

好了,这一章的内容就到这里。HAL3 架构的核心就是 Pipeline 模型、Stream 配置、Session 管理,以及那两个关键接口。你把这些搞懂了,后面调优的时候就不会抓瞎。