6、Android Camera HAL架构:HAL3 vs HAL1对比

各位同学,今天我们来聊聊Android Camera HAL架构。这个话题,说白了就是Camera驱动和上层框架之间的"翻译官"。我做了这么多年Camera驱动,见过太多人在HAL层栽跟头。嗯,咱们今天就把这块硬骨头啃下来。

6.1 HAL3 vs HAL1:为什么Google强推HAL3?

先说说HAL1。我记得2012年左右刚接触Android Camera时,HAL1还是主流。它设计得比较简单——一个预览通道,一个拍照通道,互不干扰。但问题来了,你想同时做预览、拍照、录像?HAL1就抓瞎了。

HAL3就不一样了。它把Camera抽象成一个"请求-响应"的流水线。你想想看,这就像工厂里的生产线——你下订单(Request),生产线干活(Capture),最后交货(Result)。每个环节都可以独立控制,灵活多了。

对比项 HAL1 HAL3
架构模式 固定流水线 请求-响应模型
多流支持 有限(通常2路) 灵活(N路)
逐帧控制 不支持 支持
性能开销 中等
Google推荐 已废弃 强制

核心结论:HAL3不是简单的升级,而是架构重构。Google强推HAL3,是因为它支持多摄像头、高帧率、逐帧参数控制等现代Camera特性。HAL1已经跟不上时代了。

6.2 Camera HAL核心接口

HAL层有三个核心结构体,我建议你像背身份证号一样记住它们。

camera_module_t

这是HAL的入口。系统启动时,会先加载这个模块。它负责枚举摄像头、初始化设备。我在项目中遇到过一个问题——模块加载失败,结果整个Camera服务起不来。排查了半天,发现是so库路径写错了。

typedef struct camera_module {
    hw_module_t common;
    int (*get_number_of_cameras)(void);
    int (*get_camera_info)(int camera_id, struct camera_info *info);
    int (*open)(const struct hw_module_t* module, const char* id,
                struct hw_device_t** device);
} camera_module_t;

camera_device_t(HAL1)

HAL1的设备接口。操作比较简单——start_preview、take_picture这些。但每个操作都是阻塞的,你调了take_picture,就得等它拍完才能干别的。

camera3_device_t(HAL3)

HAL3的设备接口。核心是process_capture_request。你下发一个请求,它异步处理,完成后回调。这才是现代Camera该有的样子。

typedef struct camera3_device {
    hw_device_t common;
    int (*initialize)(const struct camera3_device *,
                      const camera3_callback_ops_t *callback_ops);
    int (*configure_streams)(const struct camera3_device *,
                             camera3_stream_configuration_t *config);
    int (*process_capture_request)(const struct camera3_device *,
                                   camera3_capture_request_t *request);
    void (*dump)(const struct camera3_device *, int fd);
} camera3_device_t;

6.3 Camera3 HAL Pipeline:Request->Capture->Result

这个Pipeline是HAL3的精髓。我画个图帮你理解。

Camera3 HAL Pipeline Request 应用下发请求 Capture 硬件捕获数据 Result 回调返回结果 详细流程 settings + buffers ISP处理 + 3A统计 metadata + buffers 每个Request独立,Pipeline深度可配置 支持多Request并行处理(通常3-5个)

流程很简单:应用下发Request,HAL驱动硬件捕获,最后返回Result。但这里有个坑——Request里包含了settings(metadata)和buffers。settings控制曝光、增益、白平衡等参数,buffers就是存放图像数据的内存。

个人经验:我曾经在调试高帧率录像时,发现帧率上不去。查了半天,原来是Pipeline深度设得太浅,导致硬件经常空闲等待。后来把深度从2调到4,帧率就稳了。这个参数在configure_streams时设置。

6.4 Metadata系统:CameraMetadata / ACameraMetadata

Metadata是HAL3的灵魂。它用键值对的方式描述Camera的各种参数和状态。比如曝光时间、ISO、对焦位置、人脸检测结果等等。

Android提供了两套API:C++层的CameraMetadata和NDK层的ACameraMetadata。底层实现是一样的,都是基于一个有序的键值对数组。

// 创建一个metadata并设置参数
CameraMetadata metadata;
uint8_t mode = ANDROID_CONTROL_AE_MODE_ON;
metadata.update(ANDROID_CONTROL_AE_MODE, &mode, 1);

int64_t exposure_time = 10000000; // 10ms
metadata.update(ANDROID_SENSOR_EXPOSURE_TIME, &exposure_time, 1);

// 下发到HAL
request.settings = metadata.getAndLock();

注意:Metadata的key定义在system/media/camera/include/system/camera_metadata_tags.h中。我建议你把这个文件打印出来贴在工位上。调试时经常要查某个tag的用途和数据类型。

6.5 Buffer管理:Gralloc / ION / DMA-BUF

Buffer管理是Camera性能的关键。说白了,就是图像数据在内存里怎么分配、怎么传递、怎么释放。

Android早期用PMEM,后来换成ION。现在主流是DMA-BUF + Gralloc的组合。我简单说说它们的关系:

  • Gralloc:图形内存分配器。负责分配显存或系统内存,管理Buffer的共享和同步。
  • ION:内核内存分配器。提供连续/非连续物理内存,支持DMA访问。
  • DMA-BUF:内核缓冲区共享机制。让不同驱动(如ISP、Display、GPU)共享同一块内存。

实际流程是这样的:

  1. 应用通过Gralloc申请Buffer
  2. Buffer通过DMA-BUF传递给Camera HAL
  3. HAL把Buffer填满图像数据
  4. Buffer再通过DMA-BUF传给Display或Encoder

避坑指南:我曾经遇到过一个诡异的问题——预览画面偶尔卡顿。排查了三天,发现是Buffer同步没做好。HAL还在写数据,Display就开始读了,导致画面撕裂。解决方案是在DMA-BUF上正确使用fence同步机制。

嗯,Buffer管理这块水很深。我建议你从ION驱动源码开始看起,理解了底层的内存分配策略,上层的问题就迎刃而解了。


好了,这一章的内容就到这里。HAL3的架构、核心接口、Pipeline、Metadata和Buffer管理,这些都是Camera驱动开发的基础。下一章我们会深入HAL3的初始化流程,看看configure_streams到底干了什么。

公众号:蓝海资料掘金营,微信 deep3321