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怎么传递。到时候我会拿一个实际项目的代码片段来分析,保证让你看完就能上手。