1. Camera HAL架构概览:Android Camera整体架构、HAL3 vs HAL1区别、HAL接口定义
大家好,我是你们的Camera驱动讲师。今天咱们聊聊Camera HAL架构。说实话,这个主题我讲了不下几十遍了,但每次都有新感悟。Android Camera系统,说白了就是一条从摄像头硬件到上层应用的完整链路。而HAL层,就是这条链路上最关键的“翻译官”。
1.1 Android Camera整体架构
先看整体架构。Android Camera系统分四层:
- 应用层:你手机里的相机App,调用Camera API
- Framework层:Android系统提供的CameraService,管理摄像头资源
- HAL层:硬件抽象层,连接Framework和硬件驱动
- Kernel层:V4L2驱动、Sensor驱动,真正操作硬件
我个人习惯把HAL层比作“中间人”。上层App说“我要拍照”,Framework翻译成HAL接口调用,HAL再告诉驱动“该干活了”。反过来,驱动采集到图像数据,通过HAL回调一层层往上送。
关键点:HAL层是Android Camera性能瓶颈的重灾区。我见过太多项目,上层调优做得飞起,结果HAL回调卡成PPT。嗯,这里要注意,HAL层的设计直接决定了相机启动速度、拍照延迟、预览流畅度。
1.2 HAL3 vs HAL1:到底差在哪?
很多刚入行的朋友问我:“HAL1和HAL3到底选哪个?”我的回答很直接:能用HAL3就别用HAL1。为什么?
| 对比项 | HAL1(传统模式) | HAL3(Camera3模式) |
|---|---|---|
| 设计理念 | 固定流水线,操作简单 | 灵活管线,可定制 |
| 数据流 | 单路输出,预览/拍照互斥 | 多路输出,可同时预览+拍照+录像 |
| 控制粒度 | 粗粒度,只能开关功能 | 细粒度,可控制每一帧的参数 |
| 性能 | 延迟高,吞吐量低 | 延迟低,吞吐量高 |
| 兼容性 | 老设备支持好 | 新设备标配 |
我在项目中遇到过一件事:某款手机用HAL1做双摄虚化,结果预览和拍照切换时总有黑屏。后来改成HAL3,用多路输出同时处理预览帧和拍照帧,问题迎刃而解。说白了,HAL3就是为现代相机需求设计的——你要同时做人脸检测、美颜、HDR、录像,HAL1那套单线程流水线根本扛不住。
避坑指南:我曾经在移植HAL3时踩过一个坑——metadata配置不全。HAL3要求每个请求都必须携带完整的metadata,少一个tag就可能crash。所以,如果你从HAL1迁移到HAL3,记得把metadata的“户口本”查清楚。
1.3 HAL接口定义:到底长什么样?
HAL接口定义在hardware/libhardware/include/hardware/camera.h和camera3.h里。咱们直接看代码:
// HAL1的接口定义(简化版)
typedef struct camera_device {
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
int (*close)(struct hw_device_t* device);
int (*set_preview_window)(struct camera_device* dev,
struct preview_stream_ops* window);
int (*start_preview)(struct camera_device* dev);
int (*stop_preview)(struct camera_device* dev);
int (*take_picture)(struct camera_device* dev);
// ... 其他接口
} camera_device_t;
// HAL3的接口定义(核心部分)
typedef struct camera3_device {
int (*initialize)(const struct camera3_device* dev,
const camera3_callback_ops_t* callback_ops);
int (*configure_streams)(const struct camera3_device* dev,
camera3_stream_configuration_t* config);
int (*process_capture_request)(const struct camera3_device* dev,
camera3_capture_request_t* request);
void (*dump)(const struct camera3_device* dev, int fd);
// ... 其他接口
} camera3_device_t;
看到区别了吗?HAL1的接口是“功能导向”的——你要预览就调start_preview,要拍照就调take_picture。而HAL3是“请求导向”的——你只需要提交一个capture_request,里面包含你要的帧类型、参数、输出buffer,剩下的HAL自己调度。
你想想看,这种设计带来的好处是什么?灵活性。在HAL3里,你可以同时提交多个请求,每个请求可以指定不同的输出流。比如:请求A输出预览帧到SurfaceView,请求B输出全分辨率帧到ImageReader,请求C输出YUV帧给算法模块。这在HAL1里几乎不可能实现。
注意:HAL3的回调机制是异步的。你提交请求后,HAL通过process_capture_result回调返回结果。这个回调可能在任何线程触发,所以你的代码必须线程安全。我曾经见过一个项目,因为回调里直接操作UI线程,导致死锁——嗯,血的教训。
1.4 回调机制:HAL的灵魂
说到回调,这是HAL3最核心的设计。回调函数定义在camera3_callback_ops_t结构体中:
typedef struct camera3_callback_ops {
void (*process_capture_result)(const struct camera3_callback_ops* ops,
const camera3_capture_result_t* result);
void (*notify)(const struct camera3_callback_ops* ops,
const camera3_notify_msg_t* msg);
} camera3_callback_ops_t;
process_capture_result负责返回图像数据和metadata,notify负责返回事件通知(比如快门事件、错误事件)。这两个回调是HAL和Framework之间的“高速公路”,所有数据都通过它们传输。
我个人习惯在实现HAL时,把回调函数设计成“轻量级”的——回调里只做数据拷贝和状态标记,不做耗时操作。为什么?因为回调是在HAL的线程里执行的,如果你在回调里做图像处理,会阻塞后续的请求处理,导致帧率下降。
总结一下:HAL3的回调机制,说白了就是“你提交请求,我异步返回结果”。这种设计让Camera系统可以同时处理多个请求,实现高吞吐量。但代价是代码复杂度上升,你需要处理好同步、线程安全、内存管理等问题。
好了,这一章就到这里。下一章咱们深入HAL3的初始化流程,看看initialize函数里到底发生了什么。到时候我会分享一个我在调试某款ISP芯片时遇到的“初始化顺序”问题——保证让你有收获。