1. HAL层Camera架构概述
各位同学好,我是老张。今天咱们开始聊Camera HAL层的架构。说实话,这个主题我讲了不下二十遍,但每次都有新感悟。Android Camera HAL的演进,说白了就是一部「从简单粗暴到精细控制」的发展史。
1.1 Android Camera HAL架构演进
Android Camera HAL从诞生到现在,经历了三个阶段。我个人习惯把它们叫做「石器时代」、「青铜时代」和「铁器时代」。
- 第一阶段:HAL1(Android 4.0之前)—— 那时候Camera功能简单,能拍照能录像就行。HAL层就是个薄薄的封装,把底层驱动往上透传一下。
- 第二阶段:HAL1改进版(Android 4.0~5.0)—— 开始支持预览、拍照、录像的并行操作,但架构上还是「一个设备一个流」的思维。
- 第三阶段:HAL3(Android 5.0至今)—— 彻底重构,引入了Pipeline模型。嗯,这里要注意,HAL3不是HAL1的升级版,而是完全重写的。
我在项目中遇到过一家芯片厂商,他们的HAL1代码从Android 2.3一直维护到Android 7.0,最后实在撑不住了。为什么?因为HAL1的架构限制了多帧合成、HDR、夜景模式这些新功能。你想想看,一个连「请求队列」都没有的架构,怎么去做多帧处理?
1.2 HAL3 vs HAL1 核心区别
咱们直接上干货,看看HAL3到底比HAL1强在哪。
| 对比维度 | HAL1 | HAL3 |
|---|---|---|
| 控制模型 | 同步调用,操作完才返回 | 异步Pipeline,请求-响应模式 |
| 数据流 | 单路输出,要么预览要么拍照 | 多路输出,可同时输出不同格式 |
| 元数据 | 几乎没有,全靠参数传递 | 完整的metadata系统,支持3A、HDR等 |
| Buffer管理 | HAL自己管理,容易内存泄漏 | 框架统一管理,Gralloc分配 |
| 多帧处理 | 不支持 | 原生支持,burst capture |
| 厂商扩展 | 改HAL代码,容易碎片化 | 通过vendor tag扩展,标准化 |
我刚开始接触HAL3时,最不适应的是它的异步模型。HAL1时代,你调用startPreview(),它阻塞到预览启动才返回。HAL3呢?你发一个请求,它告诉你「收到,处理中」,然后通过回调告诉你结果。这种思维转变,说实话我适应了两周。
核心差异一句话总结:HAL1是「你叫我做事,我做完了告诉你」;HAL3是「你告诉我你想做什么,我做好了通知你」。前者是函数调用,后者是消息驱动。
1.3 HAL层在Camera系统中的位置与作用
咱们先画个简单的层次图(别笑,我手绘的):
┌─────────────────────────────────┐
│ App / Framework │ ← 应用层
├─────────────────────────────────┤
│ CameraService (C++) │ ← 框架层
├─────────────────────────────────┤
│ Camera HAL (HAL1 / HAL3) │ ← 你在这里
├─────────────────────────────────┤
│ Kernel Driver (V4L2) │ ← 内核层
├─────────────────────────────────┤
│ Camera Sensor │ ← 硬件层
└─────────────────────────────────┘
HAL层的位置,说白了就是「翻译官」。上面Framework说「我要拍一张HDR照片」,HAL层得把它翻译成「打开长曝光、多帧合成、ToneMapping」这些硬件能听懂的命令。下面Sensor返回原始数据,HAL层得包装成Framework能理解的格式。
它的作用我总结为三点:
- 抽象硬件差异 —— 不同厂商的ISP、Sensor、马达,在HAL层统一成标准接口。我见过最夸张的,同一家厂商的不同芯片,ISP寄存器配置完全不一样,全靠HAL层抹平。
- 性能优化 —— 比如Buffer复用、Pipeline调度、3A算法集成。这些在Framework层做不了,因为不知道硬件特性。
- 功能扩展 —— 厂商想加个「超级夜景」功能,在HAL层实现最灵活。Framework只需要下发一个capture request,剩下的HAL搞定。
我的经验之谈:调试HAL层问题时,我习惯先确认「这个功能是Framework要求的,还是HAL自己加的?」。很多诡异bug,都是因为Framework和HAL对某个功能的理解不一致导致的。比如Framework以为HAL支持了YUV输出,实际上HAL只支持NV12——这种坑我踩过不止一次。
1.4 避坑指南
我曾经接手过一个项目,HAL1迁移到HAL3,厂商说「改改接口就行」。结果呢?
- HAL1的同步思维改不过来,导致Pipeline死锁
- metadata不会用,直接把参数塞到自定义结构体里
- Buffer管理混乱,预览和拍照互相覆盖
所以我的建议是:别把HAL3当HAL1用。HAL3的Pipeline模型、metadata系统、异步回调,每一个都是独立的设计哲学。你如果只是把HAL1的代码包一层HAL3的壳,那还不如不改。
特别注意:HAL3的process_capture_request函数是核心入口,但它不是线程安全的。多个请求同时进来时,你得自己做好同步。我见过有人在这个函数里加全局锁,结果性能直接腰斩。正确的做法是用无锁队列或者per-stream锁。
好了,这一章咱们把HAL层的架构脉络理清了。下一章我会详细拆解HAL3的Pipeline模型,包括request怎么流转、buffer怎么管理、metadata怎么传递。到时候我会拿一个实际项目的代码片段来分析,保证让你看完就能上手。