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 标准实现步骤
- 参数校验:检查
module和id有效性,确保 camera_id 在合法范围内。 - 内存分配:为
camera_device_t及其私有数据分配连续内存。 - 函数表绑定:将
ops结构体中的函数指针指向具体实现(如start_preview、take_picture)。 - 硬件初始化:打开 V4L2 设备节点或 ISP 驱动接口,获取传感器能力。
- 资源预分配:创建流处理线程、缓冲区队列、同步锁等基础设施。
- 返回设备句柄:将
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 初始化流程
- 读取硬件配置:从设备树(Device Tree)或配置文件获取摄像头物理属性。
- 填充基础字段:设置
facing、orientation、resource_cost。 - 构建静态元数据:调用
build_static_metadata()生成camera_metadata_t。 - 版本标记:填写
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 返回的完整初始化流程:
4.5 关键检查清单
在完成 HAL 初始化实现后,建议逐项验证以下要点:
| 检查项 | 验证方法 | 常见问题 |
|---|---|---|
open() 返回值 |
确认返回 0 表示成功,负值表示错误码 | 返回 -ENODEV 但驱动正常 |
camera_info.facing |
与物理摄像头朝向一致 | 前后置混淆 |
orientation |
传感器安装角度正确 | 预览图像旋转 90 度 |
| 静态元数据完整性 | 使用 dumpsys media.camera 检查 |
缺少必要 Tag 导致 Service 崩溃 |
| 内存泄漏 | 反复 open/close 后检查内存 | 元数据 buffer 未释放 |
实战建议: 在开发初期,先实现一个最小化的静态元数据(仅包含传感器尺寸和输出格式),确保 HAL 能够被框架正常识别。后续再逐步补充其他能力标签,降低调试复杂度。