4. Camera HAL 初始化流程:open() 接口实现、camera_info 填充、静态元数据配置
Camera HAL 的初始化流程是上层 Framework 与底层硬件驱动建立通信的第一道关卡。本章聚焦 open() 接口的实现细节、camera_info 结构体的正确填充,以及静态元数据(Static Metadata)的配置策略。这三个环节直接决定了相机设备能否被成功枚举、打开,以及上层应用能否获取到正确的设备能力描述。
4.1 open() 接口实现
open() 是 HAL 模块暴露给 Camera Service 的核心入口,负责创建并返回一个 camera_device_t 实例。其典型实现流程如下:
- 参数校验:检查
hw_module_t指针、camera_device_t二级指针、camera_id合法性。 - 设备实例分配:为自定义的
camera_device_t包装结构体分配内存,并初始化内部状态机。 - 硬件接口初始化:调用 V4L2、CSI 或平台私有驱动接口,打开传感器、ISP 等硬件通路。
- ops 函数表挂载:将
set_preview_window、start_preview、take_picture等标准操作函数指针赋值给camera_device_ops_t。 - 静态元数据预加载:从配置文件或驱动中读取传感器能力,构建
camera_metadata_t并缓存。
关键约束:
open() 必须支持并发调用(不同 camera_id),且同一 camera_id 在未 close() 前不可重复打开。返回的 camera_device_t 生命周期由 Camera Service 管理。
4.2 camera_info 填充
camera_info 是 HAL 通过 get_camera_info() 返回给 Framework 的设备描述结构体。其填充质量直接影响上层对摄像头数量、朝向、闪光灯能力的判断。
| 字段 | 类型 | 说明 | 常见值 |
|---|---|---|---|
device_version |
uint32_t | HAL 设备版本号 | HARDWARE_DEVICE_API_VERSION(1, 0) |
static_camera_characteristics |
camera_metadata_t* | 指向静态元数据的主指针 | 由 construct_default_metadata() 生成 |
resource_cost |
int | 设备资源开销(0~100) | 主摄 0,副摄 50,虚拟摄像头 100 |
conflicting_devices |
const char** | 互斥设备 ID 列表 | 如双摄同时使用时需标记 |
conflicting_devices_length |
int | 互斥设备数量 | 0 表示无互斥 |
移植经验:在多摄平台上,
resource_cost 和 conflicting_devices 必须根据硬件拓扑精确配置,否则可能导致 Camera Service 在并发打开时误判资源冲突,引发 open() 失败。
4.3 静态元数据配置
静态元数据(Static Metadata)是 HAL 向 Framework 声明摄像头硬件能力的核心数据结构。它通过 camera_metadata_t 的键值对形式组织,必须在 get_camera_info() 返回前完成填充。
4.3.1 元数据分类与关键 Tag
| 分类 | 关键 Tag 示例 | 配置来源 |
|---|---|---|
| 基础信息 | ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE |
Sensor 数据手册 |
| 帧率范围 | ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES |
驱动能力查询 |
| 输出格式 | ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS |
ISP 支持列表 |
| 对焦能力 | ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS |
模组规格书 |
| 闪光灯 | ANDROID_FLASH_INFO_AVAILABLE |
硬件 GPIO 配置 |
4.3.2 配置流程
- 创建元数据缓冲区:使用
allocate_camera_metadata()或clone_camera_metadata()分配初始空间。 - 逐项写入:通过
camera_metadata_put_*()系列函数写入整数、浮点、字符串或数组类型。 - 校验与排序:调用
camera_metadata_sort()确保内部索引有序,提升查找效率。 - 指针挂载:将最终
camera_metadata_t*赋值给camera_info.static_camera_characteristics。
常见陷阱:
- 漏配
-
- 静态元数据一旦返回,在 HAL 生命周期内不可变更;如需动态调整,应使用动态元数据(Result Metadata)。
- 漏配
ANDROID_SENSOR_ORIENTATION 会导致预览画面旋转异常。-
ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS 中输出尺寸必须与硬件实际支持严格一致,否则 configure_streams() 阶段会返回错误。- 静态元数据一旦返回,在 HAL 生命周期内不可变更;如需动态调整,应使用动态元数据(Result Metadata)。
4.4 初始化流程总览
下图展示了从 Camera Service 调用 open() 到静态元数据就绪的完整数据流与模块交互关系:
4.5 代码示例:open() 与静态元数据填充骨架
// HAL open() 实现骨架
static int camera_device_open(const struct hw_module_t* module,
const char* id,
struct hw_device_t** device) {
if (module == NULL || id == NULL || device == NULL)
return -EINVAL;
int camera_id = atoi(id);
if (camera_id < 0 || camera_id >= CAMERA_NUM)
return -ENODEV;
camera_device_t* dev = (camera_device_t*)malloc(sizeof(camera_device_t));
memset(dev, 0, sizeof(camera_device_t));
// 初始化 ops 函数表
dev->ops = &camera_device_ops;
dev->priv = init_camera_priv(camera_id);
// 预加载静态元数据
camera_metadata_t* metadata = construct_static_metadata(camera_id);
if (metadata == NULL) {
free(dev);
return -ENOMEM;
}
// 填充 camera_info(实际通过 get_camera_info 返回)
camera_info_t info;
info.device_version = HARDWARE_DEVICE_API_VERSION(1, 0);
info.static_camera_characteristics = metadata;
info.resource_cost = (camera_id == 0) ? 0 : 50;
info.conflicting_devices = NULL;
info.conflicting_devices_length = 0;
*device = (hw_device_t*)dev;
return 0;
}
// 静态元数据构造示例
static camera_metadata_t* construct_static_metadata(int camera_id) {
camera_metadata_t* meta = allocate_camera_metadata(
CAMERA_METADATA_ENTRY_MAX, CAMERA_METADATA_DATA_MAX);
int32_t pixel_array[2] = {1920, 1080};
camera_metadata_put_int32(meta,
ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixel_array, 2);
int32_t fps_ranges[2] = {15, 30};
camera_metadata_put_int32(meta,
ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fps_ranges, 2);
// ... 继续填充其他必要 Tag
camera_metadata_sort(meta);
return meta;
}
4.6 初始化验证清单
- 枚举测试:通过
get_camera_info()返回的device_version是否与预期一致。 - 元数据完整性:使用
camera_metadata_dump()检查所有强制 Tag 是否已填充。 - 并发打开:同时打开主摄与副摄,验证
resource_cost和conflicting_devices逻辑。 - 内存泄漏:反复
open()/close()后检查camera_metadata_t是否被正确释放。
调试技巧:在
open() 入口和 get_camera_info() 返回前添加 ALOGV 日志,打印 camera_id、metadata 地址和关键 Tag 值,可快速定位初始化失败根因。