第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启动失败,整个相机都用不了。

启动流程大致分四步:

  1. 注册服务:CameraProvider进程启动,向hwservicemanager注册
  2. 枚举设备:Provider扫描底层硬件,发现Camera设备
  3. 创建Device:为每个设备创建CameraDevice实例
  4. 等待请求:进入消息循环,等待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配置这些实战内容。到时候我会拿一个实际项目中的代码片段来分析,敬请期待。