第4章 HAL3 初始化流程:从 open() 到 metadata 初始化
好,咱们今天聊聊 HAL3 初始化流程。说实话,这部分是很多新手工程师的噩梦。我当年刚接触 HAL3 时,也被这一连串的调用搞得晕头转向。但说白了,初始化流程就四个核心步骤:open()、initialize()、configure_streams()、metadata 初始化。咱们一个一个拆开讲。
4.1 open() 函数实现
open() 是 HAL 的入口。Camera Service 通过它拿到你的设备句柄。嗯,这里要注意——open() 不是简单的 new 一个对象就完事了。
我个人习惯在 open() 里做三件事:
- 分配硬件资源:比如打开 sensor 驱动、初始化 ISP
- 创建 camera_device 结构体:填充 ops 函数指针表
- 初始化基础状态机:标记当前为 OPENED 状态
来看一个典型的实现片段:
int camera_device_open(const struct hw_module_t* module,
const char* name,
struct hw_device_t** device) {
// 1. 参数校验
if (module == NULL || name == NULL || device == NULL) {
ALOGE("Invalid parameters");
return -EINVAL;
}
// 2. 分配私有数据结构
camera_hal_device_t* hal_dev = (camera_hal_device_t*)
calloc(1, sizeof(camera_hal_device_t));
if (!hal_dev) {
ALOGE("Failed to allocate camera device");
return -ENOMEM;
}
// 3. 填充 camera_device 结构体
hal_dev->device.common.tag = HARDWARE_DEVICE_TAG;
hal_dev->device.common.version = CAMERA_DEVICE_API_VERSION_3_4;
hal_dev->device.common.module = (hw_module_t*)module;
hal_dev->device.common.close = camera_device_close;
// 4. 绑定 ops 函数表
hal_dev->device.ops = &camera_device_ops;
hal_dev->device.priv = hal_dev;
// 5. 初始化硬件(我习惯在这里做最轻量的初始化)
if (init_hardware(hal_dev) != 0) {
ALOGE("Hardware init failed");
free(hal_dev);
return -ENODEV;
}
*device = &hal_dev->device.common;
return 0;
}
我曾经在 open() 里做了太多事情——比如加载 3A 算法库、初始化所有 buffer 池。结果发现某些平台在 open() 阶段根本还没准备好 DMA 通道。后来我学乖了:open() 只做最必要的初始化,重活留给 initialize()。
4.2 initialize() 函数实现
initialize() 是 Camera Service 在 open() 之后立即调用的。它传入一个 camera3_callback_ops_t 指针。这个指针很重要——它是 HAL 和 Framework 通信的桥梁。
initialize() 的核心任务:
- 保存回调函数指针:比如 process_capture_result、notify 等
- 初始化 metadata 静态信息:比如 sensor 尺寸、支持的帧率
- 启动后台线程:比如 request 处理线程、3A 线程
你想想看,如果 initialize() 返回失败,整个相机服务就会崩溃。所以这里一定要做好异常处理。
int initialize(const struct camera3_device *device,
const camera3_callback_ops_t *callback_ops) {
if (device == NULL || callback_ops == NULL) {
return -EINVAL;
}
camera_hal_device_t* hal_dev = (camera_hal_device_t*)device->priv;
// 保存回调——这是后续所有事件上报的出口
hal_dev->callback_ops = callback_ops;
// 初始化静态 metadata(我习惯在这里一次性生成)
hal_dev->static_metadata = build_static_metadata(hal_dev);
if (hal_dev->static_metadata == NULL) {
ALOGE("Failed to build static metadata");
return -ENODEV;
}
// 创建处理线程
hal_dev->request_thread = new RequestThread(hal_dev);
hal_dev->request_thread->run("RequestThread");
return 0;
}
我建议在 initialize() 里不要做任何耗时超过 500ms 的操作。曾经有个厂商在 initialize() 里做 sensor 校准,结果导致相机启动时间超过 3 秒,直接被 CTS 测试判 fail。
4.3 configure_streams() 调用时机
configure_streams() 什么时候被调用?说白了,就是 Framework 需要配置数据流的时候。比如:
- 打开相机预览时
- 开始录像时
- 切换分辨率时
- 从预览切换到拍照时
但有个细节很多人忽略——configure_streams() 可能会被多次调用。每次调用都意味着 stream 配置发生了变化。
我在项目中遇到过一个问题:某款手机在切换前后摄像头时,Framework 会先调用 configure_streams() 把旧 stream 销毁,再重新配置。结果我们的 HAL 在销毁 stream 时没有正确释放 DMA buffer,导致内存泄漏。嗯,这个坑我踩了整整两天才定位到。
configure_streams() 的核心逻辑:
- 验证 stream 配置:检查分辨率、格式是否支持
- 分配 buffer 池:根据 stream 数量创建对应的 gralloc buffer
- 配置 ISP pipeline:设置 sensor 输出尺寸、帧率
- 返回 stream 的 usage 和 max_buffers
configure_streams() 返回后,Framework 会立即开始下发 capture request。所以你的 HAL 必须保证:configure_streams() 返回时,所有硬件资源已经就绪。
4.4 metadata 初始化
metadata 是 HAL3 的灵魂。说白了,它就是相机能力的说明书。Framework 通过 metadata 知道你的相机能做什么、不能做什么。
metadata 分三类:
| 类型 | 说明 | 初始化时机 |
|---|---|---|
| 静态 metadata | 硬件能力,如 sensor 尺寸、支持的 AE 模式 | initialize() 中一次性生成 |
| 动态 metadata | 每帧变化的数据,如曝光时间、增益 | 每次 capture 结果中更新 |
| 请求 metadata | Framework 下发的控制参数 | 每次 process_capture_request() 中解析 |
静态 metadata 的初始化是个体力活。我习惯用宏定义来组织:
static camera_metadata_t* build_static_metadata() {
// 创建一个 metadata 容器
camera_metadata_t* metadata = allocate_camera_metadata(
CAMERA_METADATA_ENTRY_MAX, CAMERA_METADATA_DATA_MAX);
// 填充 sensor 信息
int32_t sensor_size[] = {1920, 1080};
add_camera_metadata_entry(metadata,
ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
sensor_size, 2);
// 填充支持的帧率范围
int32_t fps_range[] = {30, 30};
add_camera_metadata_entry(metadata,
ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES,
fps_range, 2);
// ... 还有几十个 tag 要填
return metadata;
}
我曾经漏填了
ANDROID_REQUEST_PARTIAL_RESULT_COUNT 这个 tag。结果 Framework 一直收不到完整的 capture result,导致相机预览卡死。这个 tag 告诉 Framework 你的 HAL 会分几次返回结果。如果你只返回一次,就填 1。
最后说一个我自己的习惯:metadata 初始化完成后,一定要做一次完整性校验。我写了个脚本,遍历所有 mandatory 的 tag,检查是否都填了。这个习惯帮我避免了好几次线上事故。
好了,HAL3 初始化流程就这些。记住四个步骤的顺序:open() 开门、initialize() 铺路、configure_streams() 架桥、metadata 指路。每一步都有它的职责,千万别偷懒。