1、Camera HAL架构全景解析:HAL3 vs HAL1核心差异、Pipeline模型、Camera Provider进程模型
各位同学,咱们今天聊聊Camera HAL架构。说实话,HAL这块儿是驱动开发和ISP调优的桥梁,搞不懂它,后面所有工作都像在黑暗中摸索。我当年刚入行时,就被HAL1和HAL3的差异坑过好几次,后来才慢慢摸清门道。
1.1 HAL1 vs HAL3:核心差异在哪里?
先说说HAL1。它其实是个很古老的接口了,Android 4.0时代就有了。它的设计思路很简单——你调用一次拍照,它给你一张图。但问题在于,它不支持多路流,也不支持逐帧控制。
我举个例子你就明白了。用HAL1做预览,你只能拿到预览流。想同时做拍照?得先停掉预览,再切到拍照模式。这在现在的双摄、三摄手机上根本没法用。
HAL3就不一样了。它从Android 5.0开始引入,核心思想是「把控制权交给上层」。说白了,HAL3把Camera Sensor变成了一个可编程的设备,每一帧的参数你都能单独设置。
核心差异总结:
- 流模型:HAL1只支持单流,HAL3支持多路并发流(预览、拍照、视频同时跑)
- 控制粒度:HAL1是粗粒度(设置一次全局参数),HAL3是逐帧控制(每帧可以调曝光、增益、白平衡)
- Buffer管理:HAL1用gralloc分配,HAL3用BufferQueue + 生产者消费者模式
- 元数据:HAL1几乎没有元数据,HAL3有完整的Camera Metadata系统
嗯,这里要注意。HAL3虽然强大,但实现复杂度也高了一个数量级。我见过不少团队从HAL1迁移到HAL3时,光Pipeline的调试就花了两个月。
1.2 Pipeline模型:数据是怎么流的?
咱们来看看HAL3的Pipeline模型。它本质上是一条流水线,每一帧数据从Sensor出来,经过ISP处理,最后送到不同的目标端。
我个人习惯把Pipeline分成三个阶段:
- Capture阶段:Sensor曝光,输出RAW图
- Processing阶段:ISP做Bayer处理、去噪、色彩校正、Gamma校正等
- Output阶段:把处理好的YUV或JPEG送到Preview、Video或Snapshot流
你想想看,这三个阶段是流水线并行的。Sensor在曝第N+2帧时,ISP可能在处理第N+1帧,而第N帧已经送到显示层了。这就是为什么HAL3能做到低延迟。
避坑指南:我曾经在调试一个项目时,发现预览延迟特别大。查了半天,原来是Pipeline的Buffer数量配置不对。默认是4个Buffer,但我们的ISP处理速度跟不上,导致Pipeline经常被堵住。后来我把Buffer数调到6个,问题就解决了。所以,Pipeline的深度要根据实际处理耗时来调,不是越大越好,也不是越小越好。
1.3 Camera Provider进程模型
Android 8.0之后,HAL被移到了独立的进程中运行,这就是Camera Provider。为什么要这么做?说白了,为了稳定性和安全性。
以前HAL跑在CameraServer进程里,一旦HAL崩溃,整个Camera服务就挂了。现在HAL跑在独立的Provider进程里,崩溃了还能重启,不影响系统其他服务。
Provider进程模型有几个关键点:
| 组件 | 角色 | 进程 |
|---|---|---|
| CameraService | 框架层服务,管理Camera生命周期 | system_server |
| CameraProvider | HAL实现,操作硬件 | 独立进程(cameraserver或vendor进程) |
| CameraDevice | 代表一个Camera设备 | Provider进程内 |
| CameraSession | 管理一次Capture会话 | Provider进程内 |
我建议你重点关注Provider进程的启动流程。它通过HIDL(Android 8-10)或AIDL(Android 11+)与CameraService通信。每次打开Camera时,Service会通过hwservicemanager找到对应的Provider,然后创建CameraDevice。
注意:Provider进程的权限控制很严格。它通常运行在vendor分区,有独立的SELinux策略。我曾经遇到过一个坑——Provider进程无法访问某个GPIO节点,导致闪光灯一直打不开。查了两天才发现是SELinux策略没配好。所以,调试时别忘了看avc log。
1.4 实战中的协同要点
讲完了理论,咱们聊聊实战。HAL和ISP的协同,说白了就是两件事:参数传递和Buffer流转。
参数传递这块,HAL3通过CaptureRequest和CaptureResult来沟通。上层下发Request,里面包含AE、AWB、AF等参数。HAL解析后传给ISP驱动,ISP处理完再把结果通过Result返回。
Buffer流转更关键。HAL从ISP拿到RAW图后,要决定是直接送显示,还是先做后处理。我个人的经验是:预览流尽量走零拷贝路径,拍照流可以走完整ISP Pipeline。
// 伪代码示例:HAL3 Pipeline配置
camera3_stream_configuration_t config;
config.num_streams = 3;
config.streams = streams;
// 预览流:直接走ISP硬件输出YUV
streams[0]->stream_type = CAMERA3_STREAM_OUTPUT;
streams[0]->format = HAL_PIXEL_FORMAT_YCbCr_420_888;
// 拍照流:走完整Pipeline输出JPEG
streams[1]->stream_type = CAMERA3_STREAM_OUTPUT;
streams[1]->format = HAL_PIXEL_FORMAT_BLOB;
// 回调流:用于算法处理
streams[2]->stream_type = CAMERA3_STREAM_OUTPUT;
streams[2]->format = HAL_PIXEL_FORMAT_RAW10;
嗯,这里有个小技巧。如果你发现预览和拍照的帧率不匹配,可以试试调整Stream的usage flag。比如预览流加上GRALLOC_USAGE_HW_COMPOSER,让硬件合成器直接处理,能省不少CPU开销。
好了,这一章就讲到这里。HAL架构是Camera系统的骨架,理解透了,后面ISP调优才能有的放矢。下一章咱们深入Pipeline的细节,聊聊每一帧数据到底经历了什么。