4. HAL模块初始化流程:open()函数实现、camera_info初始化、静态摄像头信息填充

HAL模块的初始化是相机系统启动的第一个关键步骤,它决定了上层框架能否正确识别并操作底层硬件。本章深入剖析 open() 函数的实现细节、camera_info 的初始化流程以及静态摄像头信息的填充策略。

4.1 open() 函数实现

open() 是 HAL 模块与 Camera Service 之间的第一个握手函数。当 Service 调用 hw_module_t->open() 时,HAL 需要完成硬件设备句柄的创建、资源预分配以及基础能力探测。

核心职责: 创建 camera_device_t 实例,绑定操作函数表,初始化底层驱动接口。

4.1.1 函数签名与数据结构

// hardware/libhardware/include/hardware/camera_common.h
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);
} camera_module_t;

// open() 函数原型(通过 hw_module_t 的 methods 调用)
int camera_device_open(const struct hw_module_t* module,
                       const char* id,
                       struct hw_device_t** device);
参数 说明 典型值
module HAL 模块句柄,包含模块版本、作者、方法表 HAL_MODULE_INFO_SYM
id 摄像头逻辑 ID,通常为 "0"、"1" 等字符串 "0"
device 输出参数,指向新创建的 camera_device_t 非空指针

4.1.2 标准实现步骤

  1. 参数校验:检查 module 和 id 有效性,确保 camera_id 在合法范围内。
  2. 内存分配:为 camera_device_t 及其私有数据分配连续内存。
  3. 函数表绑定:将 ops 结构体中的函数指针指向具体实现(如 start_preview、take_picture)。
  4. 硬件初始化:打开 V4L2 设备节点或 ISP 驱动接口,获取传感器能力。
  5. 资源预分配:创建流处理线程、缓冲区队列、同步锁等基础设施。
  6. 返回设备句柄:将 hw_device_t* 赋值给输出参数。
// 简化版 open() 实现示例
static int zycam_device_open(const struct hw_module_t* module,
                              const char* id,
                              struct hw_device_t** device) {
    if (id == NULL || device == NULL) return -EINVAL;

    int camera_id = atoi(id);
    if (camera_id < 0 || camera_id >= CAMERA_NUM) return -ENOENT;

    zycam_device_t* dev = (zycam_device_t*)malloc(sizeof(zycam_device_t));
    if (!dev) return -ENOMEM;
    memset(dev, 0, sizeof(*dev));

    // 填充公共设备结构
    dev->device.common.tag     = HARDWARE_DEVICE_TAG;
    dev->device.common.version = CAMERA_DEVICE_API_VERSION_3_5;
    dev->device.common.module = (hw_module_t*)module;
    dev->device.common.close  = zycam_device_close;

    // 绑定 Camera 3.x 操作函数表
    dev->device.ops = &zycam_device_ops;

    // 打开底层驱动
    dev->fd = open("/dev/video" ZYCAM_VIDEO_NODE, O_RDWR);
    if (dev->fd < 0) {
        free(dev);
        return -ENODEV;
    }

    // 初始化传感器参数
    zycam_sensor_init(dev);

    *device = &dev->device.common;
    return 0;
}
常见陷阱: 在 open() 中不要执行耗时操作(如 ISP 固件加载),否则会导致 Camera Service 超时(默认 5 秒)。建议将重初始化操作延迟到 configure_streams() 阶段。

4.2 camera_info 初始化

camera_info 是 HAL 向框架暴露的静态能力描述结构,包含设备方向、支持的分辨率、帧率范围等关键信息。框架通过 get_camera_info() 获取这些信息,用于构建应用层的 Camera Characteristics。

4.2.1 camera_info 结构定义

// system/core/camera/include/system/camera_metadata.h
typedef struct camera_info {
    int           facing;          // CAMERA_FACING_BACK / FRONT
    int           orientation;     // 图像方向(0/90/180/270)
    uint32_t      resource_cost;   // 资源开销(0-100)
    char          device_version[64]; // 设备版本字符串
    const camera_metadata_t* static_camera_characteristics; // 静态元数据
} camera_info_t;
字段 含义 典型值
facing 摄像头朝向 CAMERA_FACING_BACK (0)
orientation 传感器安装角度 90(竖屏手机后置)
resource_cost 同时打开多个摄像头时的资源消耗 50
static_camera_characteristics 指向静态元数据 buffer 的指针 由 build_static_metadata() 生成

4.2.2 初始化流程

  1. 读取硬件配置:从设备树(Device Tree)或配置文件获取摄像头物理属性。
  2. 填充基础字段:设置 facing、orientation、resource_cost。
  3. 构建静态元数据:调用 build_static_metadata() 生成 camera_metadata_t。
  4. 版本标记:填写 device_version 字符串,用于框架兼容性判断。
// camera_info 初始化示例
static int zycam_get_camera_info(int camera_id,
                                  struct camera_info* info) {
    if (camera_id < 0 || camera_id >= CAMERA_NUM || !info)
        return -EINVAL;

    // 1. 基础信息
    info->facing      = (camera_id == 0) ? CAMERA_FACING_BACK
                                         : CAMERA_FACING_FRONT;
    info->orientation = 90;  // 后置 90 度
    info->resource_cost = 50;

    // 2. 构建静态元数据
    info->static_camera_characteristics =
        zycam_build_static_metadata(camera_id);

    // 3. 版本信息
    strlcpy(info->device_version,
            CAMERA_DEVICE_API_VERSION_3_5_STR,
            sizeof(info->device_version));

    return 0;
}

4.3 静态摄像头信息填充

静态摄像头信息以 camera_metadata_t 的形式存在,包含数百个元数据标签(Tag)。这些信息在 HAL 初始化时一次性构建,之后只读。框架通过 get_camera_metadata() 系列函数访问。

4.3.1 核心静态元数据分类

分类 关键 Tag 说明
传感器能力 ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE 有效像素阵列尺寸
输出格式 ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS 支持的输出流配置(分辨率+格式)
帧率范围 ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES AE 目标帧率范围列表
闪光灯 ANDROID_FLASH_INFO_AVAILABLE 是否支持闪光灯
对焦模式 ANDROID_CONTROL_AF_AVAILABLE_MODES 支持的对焦模式列表

4.3.2 构建静态元数据的最佳实践

  • 使用 Metadata Builder 工具:Android 提供了 camera_metadata_writer 库,避免手动计算 buffer 大小。
  • 从硬件抽象层获取真实能力:通过 V4L2 VIDIOC_ENUM_FMT 和 VIDIOC_ENUM_FRAMESIZES 枚举传感器支持的格式和分辨率。
  • 缓存机制:静态元数据在 HAL 生命周期内不变,构建后应缓存指针,避免重复构建。
  • 版本兼容:对于 Android 10+,必须包含 ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL 标签。
// 静态元数据构建片段
static camera_metadata_t* zycam_build_static_metadata(int id) {
    // 分配元数据 buffer(估算大小)
    camera_metadata_t* metadata = allocate_camera_metadata(
        ZYCAM_METADATA_ENTRY_COUNT,
        ZYCAM_METADATA_DATA_SIZE);

    // 填充传感器尺寸
    int32_t pixel_array[2] = { 3264, 2448 };
    add_camera_metadata_entry(metadata,
        ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE,
        pixel_array, 2);

    // 填充支持的输出配置
    int32_t stream_configs[] = {
        // 格式, 宽, 高, 方向
        HAL_PIXEL_FORMAT_YCbCr_420_SP, 1920, 1080,
        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
        HAL_PIXEL_FORMAT_BLOB, 3264, 2448,
        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT,
        // ... 更多配置
    };
    add_camera_metadata_entry(metadata,
        ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
        stream_configs,
        sizeof(stream_configs) / sizeof(int32_t));

    // 填充硬件支持级别
    uint8_t hw_level = ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
    add_camera_metadata_entry(metadata,
        ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL,
        &hw_level, 1);

    return metadata;
}
调试技巧: 使用 adb shell dumpsys media.camera 可以查看框架接收到的静态元数据内容,用于验证 HAL 填充是否正确。

4.4 初始化流程总览

下图展示了从 HAL 模块加载到 camera_info 返回的完整初始化流程:

HAL 模块初始化流程 Camera Service hw_get_module() module->open() 硬件驱动初始化 get_camera_info() build_static_metadata() 返回 camera_info 给 Service

4.5 关键检查清单

在完成 HAL 初始化实现后,建议逐项验证以下要点:

检查项 验证方法 常见问题
open() 返回值 确认返回 0 表示成功,负值表示错误码 返回 -ENODEV 但驱动正常
camera_info.facing 与物理摄像头朝向一致 前后置混淆
orientation 传感器安装角度正确 预览图像旋转 90 度
静态元数据完整性 使用 dumpsys media.camera 检查 缺少必要 Tag 导致 Service 崩溃
内存泄漏 反复 open/close 后检查内存 元数据 buffer 未释放
实战建议: 在开发初期,先实现一个最小化的静态元数据(仅包含传感器尺寸和输出格式),确保 HAL 能够被框架正常识别。后续再逐步补充其他能力标签,降低调试复杂度。