第10章 相机HAL架构概览
做相机驱动这么多年,我经常被问到同一个问题:「Android相机到底是怎么工作的?」
说实话,刚入行那会儿,我也被这套架构搞得晕头转向。后来慢慢摸透了,才发现它其实没那么复杂。今天咱们就来聊聊相机HAL的整体架构,以及HAL3和HAL1的区别,还有Camera Provider是怎么启动的。
10.1 Android相机HAL整体架构
先看一张宏观图(嗯,虽然这里没有图,但我用文字给你画出来)。
Android相机架构分四层:
- 应用层:Camera App,调用Android Camera API
- Framework层:CameraService,核心调度中心
- HAL层:Camera HAL,硬件抽象接口
- Kernel层:V4L2、Sensor驱动、ISP驱动
说白了,App想拍照,得经过Framework,Framework再找HAL,HAL最后跟硬件打交道。每一层都有明确的职责。
关键点:HAL层是承上启下的桥梁。它把硬件细节藏起来,给上层提供统一的接口。我见过不少团队在HAL层做各种「骚操作」,结果上层一升级就崩了。记住,HAL的职责是抽象,不是魔改。
我个人习惯把HAL层再拆成两部分:
- Camera Provider:负责管理Camera设备,提供服务
- Camera Device:具体的相机设备,比如后摄、前摄
这两者的关系,有点像「酒店前台」和「客房」。前台(Provider)帮你分配房间(Device),具体住进去之后,房间里的设施才归你管。
10.2 HAL3 vs HAL1区别
这个问题面试常考,也是实际开发中绕不开的坑。
| 对比项 | HAL1 | HAL3 |
|---|---|---|
| 设计理念 | 简单、固定流程 | 灵活、可配置 |
| 数据流 | 单路输出 | 多路输出(支持多路流) |
| 控制方式 | 同步操作 | 异步操作(Request/Result模型) |
| 性能 | 延迟较高 | 低延迟、高吞吐 |
| 适用场景 | 低端机、功能机 | 中高端机、多摄、HDR等 |
你想想看,HAL1就像老式胶片相机——按下快门,咔嚓一张,完事。HAL3呢,更像单反——你可以调光圈、快门、ISO,还能连拍、录视频的同时拍照。
HAL1的痛点:
- 一次只能处理一个请求
- 不支持多路流(比如预览+拍照同时)
- 扩展性差,加个新功能得改框架
HAL3的优势:
- 基于Request/Result模型,异步处理
- 支持多路流(预览流、拍照流、分析流)
- 每个Request可以独立配置参数
避坑指南:我曾经在HAL3上踩过一个坑——多路流配置时,Stream的format和size必须匹配。比如预览流用NV21,拍照流用JPEG,这两个流的宽高比如果不一致,底层ISP会报错。后来我加了个校验逻辑,才彻底解决。
为什么Google要推HAL3?说白了,手机相机越来越复杂。双摄、三摄、HDR、夜景模式……HAL1根本扛不住。HAL3的灵活性,让厂商可以自由发挥,又不破坏框架的稳定性。
10.3 Camera Provider服务启动流程
这部分我建议你重点关注。Provider启动失败,整个相机都用不了。
启动流程大致分四步:
- 注册服务:CameraProvider进程启动,向hwservicemanager注册
- 枚举设备:Provider扫描底层硬件,发现Camera设备
- 创建Device:为每个设备创建CameraDevice实例
- 等待请求:进入消息循环,等待Framework调用
代码层面,核心逻辑在 CameraProvider.cpp 里:
// 伪代码,展示核心流程
status_t CameraProvider::start() {
// 1. 初始化硬件抽象层
status_t ret = initializeHAL();
if (ret != OK) {
ALOGE("HAL初始化失败");
return ret;
}
// 2. 枚举所有Camera设备
uint32_t numDevices = 0;
ret = getNumberOfCameras(&numDevices);
if (ret != OK) {
ALOGE("获取设备数量失败");
return ret;
}
// 3. 为每个设备创建设备对象
for (uint32_t i = 0; i < numDevices; i++) {
sp<CameraDevice> device = new CameraDevice(i);
mDevices.push_back(device);
}
// 4. 注册到hwservicemanager
ret = registerAsService();
if (ret != OK) {
ALOGE("注册服务失败");
return ret;
}
ALOGI("CameraProvider启动完成,共%d个设备", numDevices);
return OK;
}
嗯,这里要注意:initializeHAL() 这一步最容易出问题。我遇到过的情况是,Sensor驱动没加载成功,导致HAL初始化返回-ENODEV。排查了半天,最后发现是dts配置里电源时序不对。
警告:Provider启动时,千万不要在 initializeHAL() 里做耗时操作。比如加载大固件、校准数据等。否则会导致系统启动超时,Watchdog直接把你干掉。我建议把耗时操作放到设备首次打开时做。
启动完成后,Provider会监听Framework的请求。每次App打开相机,Framework会通过Binder调用Provider的 open() 方法,返回一个CameraDevice给上层。
这里有个细节:Provider是运行在独立进程里的(cameraserver)。这样做的好处是,即使Provider挂了,也不会影响系统其他服务。坏处是,进程间通信有开销,对性能有影响。
我个人习惯在Provider启动时,加一个健康检查线程。每隔几秒ping一下底层硬件,如果发现设备异常,主动重启Provider。这个做法帮我避免了好几次线上问题。
最后总结一下:
- 相机HAL架构分四层,HAL是核心桥梁
- HAL3比HAL1更灵活,适合现代多摄场景
- Provider启动流程要稳,初始化别做耗时操作
下一章咱们聊聊Camera Device的具体实现,包括open/close流程、Stream配置这些实战内容。到时候我会拿一个实际项目中的代码片段来分析,敬请期待。