第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() 的核心逻辑:

  1. 验证 stream 配置:检查分辨率、格式是否支持
  2. 分配 buffer 池:根据 stream 数量创建对应的 gralloc buffer
  3. 配置 ISP pipeline:设置 sensor 输出尺寸、帧率
  4. 返回 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 指路。每一步都有它的职责,千万别偷懒。