一、HAL3 核心设计理念:从“黑盒”到“管道”的进化
说实话,我第一次接触 HAL3 的时候,第一反应是——这玩意儿比 HAL1 复杂太多了。但用久了你会发现,它的设计思路其实非常优雅。
HAL1 时代,相机是个“黑盒”。你调用 open(),它给你返回一个 preview 流;你调用 takePicture(),它给你一张 JPEG。至于中间发生了什么,你基本控制不了。说白了,HAL1 是为功能机设计的,它假设你只需要“拍照”和“预览”这两件事。
HAL3 完全推翻了这种思路。它的核心理念是:把相机拆解成一系列可配置的处理阶段,让上层应用能精确控制每一帧的采集、处理和输出。
我个人习惯把 HAL3 比作一个“相机工厂”。你告诉工厂:我要 3 条流水线,一条输出 1080p 预览,一条输出 4K 视频,一条输出 RAW 照片。工厂会根据你的需求,调度内部资源,并行生产。
核心设计理念总结:
- 显式控制:应用明确告诉 HAL 要做什么,而不是 HAL 猜测应用要什么
- 流水线并行:多个请求可以同时在管道中处理,提高吞吐量
- 资源按需分配:只创建实际需要的流,避免浪费
- 元数据驱动:通过 CaptureRequest 和 CaptureResult 传递控制参数和结果信息
二、Pipeline 模型:相机内部的“高速公路”
Pipeline 是 HAL3 最核心的抽象。你可以把它想象成一条高速公路——数据从传感器出发,经过 ISP、3A 算法、后处理等“收费站”,最终到达应用层。
每条 Pipeline 处理一个特定的输出流。比如你同时要预览和拍照,系统就会创建两条 Pipeline。它们共享传感器数据,但处理路径不同。
我记得在项目中遇到过一个问题:某款手机在 4K 视频录制时,预览流会卡顿。排查后发现,预览 Pipeline 和视频 Pipeline 在 ISP 阶段发生了资源争抢。解决方案是给预览 Pipeline 分配更高的优先级。
避坑指南:我曾经在调试时发现,Pipeline 的数量不是越多越好。每个 Pipeline 都会占用 ISP 带宽和内存带宽。如果同时开启 4 条以上 Pipeline,很容易出现帧率下降。建议根据实际需求,控制 Pipeline 数量在 2-3 条。
Pipeline 的工作流程
- 请求入队:应用提交 CaptureRequest,包含目标流、3A 参数、处理模式等
- 传感器采集:HAL 驱动传感器曝光,生成 RAW 数据
- ISP 处理:RAW 数据经过 ISP 管线,输出 YUV 或 JPEG
- 后处理:应用自定义的处理(如美颜、滤镜)
- 结果返回:HAL 返回 CaptureResult,包含元数据和输出 buffer
嗯,这里要注意:Pipeline 是异步的。应用提交请求后,不会阻塞等待结果。HAL 会按顺序处理请求,但不同 Pipeline 之间是并行的。
三、CaptureRequest / CaptureResult:相机控制的“遥控器”和“仪表盘”
如果你用过 DSLR 相机,应该知道 M 档(全手动模式)和 Auto 档的区别。HAL1 相当于 Auto 档,你只能按快门。HAL3 给了你 M 档——CaptureRequest 就是你的遥控器,CaptureResult 就是你的仪表盘。
CaptureRequest:告诉 HAL 你要什么
一个 CaptureRequest 包含以下关键信息:
| 字段 | 说明 | 示例 |
|---|---|---|
| targets | 输出目标流列表 | [preview_stream, photo_stream] |
| settings | 3A 参数、处理模式等 | AE_MODE_ON, AF_MODE_AUTO |
| metadata | 厂商自定义参数 | HDR_MODE_ON, BEAUTY_LEVEL_3 |
我建议你在调试时,先打印出默认的 CaptureRequest 内容。你会发现很多参数 HAL 已经帮你设好了,比如曝光补偿默认是 0,白平衡默认是 AUTO。你只需要修改你想控制的参数即可。
CaptureResult:HAL 告诉你发生了什么
CaptureResult 是 HAL 处理完一帧后返回的“报告”。它包含:
- 实际使用的参数:比如最终曝光时间、ISO、焦距
- 3A 状态:AE 是否收敛、AF 是否锁定、AWB 是否稳定
- 统计信息:直方图、人脸检测结果、场景模式
- 输出 buffer:处理后的图像数据
注意:CaptureResult 中的参数不一定和 CaptureRequest 完全一致。比如你请求了 AE_MODE_ON,但 HAL 发现场景太暗,可能会自动切换到 AE_MODE_ON_AUTO_FLASH。所以一定要检查 Result 中的实际参数,不要假设 Request 中的参数一定会被采纳。
四、Stream 配置:告诉 HAL 你要输出什么
在开始拍照之前,你需要先配置 Stream。Stream 定义了输出的格式、分辨率和用途。
我刚开始做 HAL3 开发时,犯过一个低级错误:配置了 3 个 Stream,但忘记设置 Stream 的 usage flag。结果 HAL 不知道每个 Stream 是用来预览还是录制,导致 buffer 分配错误。
Stream 配置的关键参数
| 参数 | 说明 | 常见值 |
|---|---|---|
| format | 像素格式 | YUV_420_888, JPEG, RAW10 |
| width/height | 分辨率 | 1920x1080, 3840x2160 |
| usage | 用途标记 | GRALLOC_USAGE_HW_TEXTURE, GRALLOC_USAGE_HW_VIDEO_ENCODER |
| dataSpace | 色彩空间 | HAL_DATASPACE_STANDARD_BT709, HAL_DATASPACE_V0_JFIF |
你想想看,为什么 HAL3 要求你提前配置 Stream?因为硬件资源是有限的。ISP 的带宽、内存的带宽、DMA 通道的数量,都需要提前规划。如果应用突然要求输出一个 4K 的 RAW 流,而 ISP 没有预留资源,那就只能丢帧了。
五、HAL3 的优势:为什么我们要从 HAL1 迁移到 HAL3?
说实话,HAL3 的迁移成本不低。驱动要重写,框架要适配,应用层也要改。但为什么 Google 和各大厂商还是坚持推 HAL3?
我用一个实际案例来说明:某款手机在 HAL1 下,拍照延迟是 300ms。迁移到 HAL3 后,通过 Pipeline 并行和零拷贝技术,延迟降到了 100ms 以内。用户感知到的就是“按下快门立刻出片”。
HAL3 的核心优势
- 低延迟:Pipeline 并行处理,请求和结果异步返回
- 高吞吐量:支持同时输出多个流,且互不干扰
- 精细控制:应用可以控制每一帧的 3A 参数、处理模式
- 可扩展性:厂商可以添加自定义的 metadata 和处理阶段
- 资源效率:按需分配 buffer,避免内存浪费
一句话总结:HAL3 把相机的控制权从 HAL 手里交还给了应用层。你不再是“用户”,而是“导演”。你可以决定每一帧怎么拍、怎么处理、怎么输出。
六、HAL3 核心架构图
下面这张图展示了 HAL3 的整体架构和数据流。我建议你把它打印出来贴在工位上,调试时对照着看,思路会清晰很多。
从这张图你可以看到,HAL3 的核心就是 Pipeline。每个 Pipeline 独立处理一个输出流,互不干扰。Framework 通过 CaptureRequest 下发指令,HAL 通过 CaptureResult 返回结果。整个流程是异步的、并行的。
调试技巧:我在项目中经常用 dumpsys media.camera 来查看当前活跃的 Pipeline 数量和状态。如果发现某个 Pipeline 长时间没有返回结果,多半是硬件卡住了,需要 reset。
好了,这一章的内容就到这里。HAL3 的设计确实比 HAL1 复杂,但它的灵活性和性能优势是显而易见的。下一章我们会深入 Pipeline 的内部实现,看看每一帧数据到底是怎么流过 ISP 的。
公众号:蓝海资料掘金营,微信deep3321