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的精髓。我画个图帮你理解。
流程很简单:应用下发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)共享同一块内存。
实际流程是这样的:
- 应用通过Gralloc申请Buffer
- Buffer通过DMA-BUF传递给Camera HAL
- HAL把Buffer填满图像数据
- Buffer再通过DMA-BUF传给Display或Encoder
避坑指南:我曾经遇到过一个诡异的问题——预览画面偶尔卡顿。排查了三天,发现是Buffer同步没做好。HAL还在写数据,Display就开始读了,导致画面撕裂。解决方案是在DMA-BUF上正确使用fence同步机制。
嗯,Buffer管理这块水很深。我建议你从ION驱动源码开始看起,理解了底层的内存分配策略,上层的问题就迎刃而解了。
好了,这一章的内容就到这里。HAL3的架构、核心接口、Pipeline、Metadata和Buffer管理,这些都是Camera驱动开发的基础。下一章我们会深入HAL3的初始化流程,看看configure_streams到底干了什么。