4. Camera HAL层初始化流程:HAL模块加载、open()调用链、camera_info初始化、static_metadata填充
Camera HAL层的初始化是整个相机框架启动的基石。从应用层调用 Camera.open() 到底层硬件驱动就绪,中间经历了HAL模块的动态加载、设备接口的打开、相机信息结构的构建以及静态元数据的填充。理解这一流程,是定位初始化失败、设备枚举异常、元数据缺失等问题的前提。
4.1 整体初始化流程概览
初始化流程可划分为四个核心阶段,各阶段职责明确,依赖关系清晰:
| 阶段 | 关键函数/接口 | 主要产出 | 常见失败点 |
|---|---|---|---|
| HAL模块加载 | hw_get_module() / HAL_MODULE_INFO_SYM |
hw_module_t 结构体指针 |
动态库路径错误、符号未导出 |
| open()调用链 | module->common.methods->open() |
hw_device_t 设备句柄 |
权限不足、设备节点冲突 |
| camera_info初始化 | get_camera_info() |
camera_info 结构体(facing, orientation等) |
多摄像头ID映射错误 |
| static_metadata填充 | construct_default_request_settings() / 静态特征列表 |
camera_metadata_t 元数据包 |
Tag值遗漏、数据格式不匹配 |
关键原则: 初始化流程是严格顺序的——只有HAL模块加载成功,才能调用open();只有open()返回有效设备,才能查询camera_info;只有info正确,才能填充static_metadata。任何一步失败,后续步骤都不会执行。
4.2 HAL模块加载:从动态库到函数指针
Android框架通过 hw_get_module() 函数加载HAL实现库(通常为 camera.<platform>.so)。加载过程的核心是查找并解析 HAL_MODULE_INFO_SYM 符号,该符号指向一个 hw_module_t 结构体,其中包含了模块ID、版本号以及最重要的 open 函数指针。
// HAL模块结构体定义(简化)
struct hw_module_t {
uint32_t tag; // 必须为 HARDWARE_MODULE_TAG
uint16_t module_api_version;
uint16_t hal_api_version;
const char *id; // 例如 "camera"
const char *name;
const char *author;
struct hw_module_methods_t* methods; // 包含 open 函数指针
void* dso; // 动态库句柄
uint32_t reserved[32];
};
struct hw_module_methods_t {
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
};
调试提示: 如果 hw_get_module() 返回 -ENOENT,请检查 /vendor/lib/hw/ 或 /system/lib/hw/ 下是否存在对应 camera.<platform>.so 文件。使用 dlopen() + dlerror() 手动加载同一路径可获取更详细的错误信息。
4.3 open()调用链:从框架到设备
HAL模块的 open() 函数是框架与具体摄像头设备之间的桥梁。调用链通常如下:
- CameraService 调用
module->methods->open("0", &device),其中"0"是摄像头ID。 - HAL实现 根据ID查找对应的物理摄像头,分配
camera_device_t结构体。 - HAL实现 填充
camera_device_ops_t函数表,包括set_preview_window()、start_preview()、take_picture()等。 - HAL实现 打开底层驱动设备节点(如
/dev/video0),进行硬件初始化。 - 返回
0表示成功,设备句柄通过输出参数返回。
// open() 调用链典型实现片段
static int camera_device_open(const hw_module_t* module, const char* id,
hw_device_t** device) {
int camera_id = atoi(id);
if (camera_id < 0 || camera_id >= MAX_CAMERAS) {
ALOGE("Invalid camera ID: %s", id);
return -EINVAL;
}
camera_device_t* camera_dev = (camera_device_t*)malloc(sizeof(*camera_dev));
memset(camera_dev, 0, sizeof(*camera_dev));
camera_dev->common.tag = HARDWARE_DEVICE_TAG;
camera_dev->common.version = CAMERA_DEVICE_API_VERSION_3_4;
camera_dev->common.module = (hw_module_t*)module;
camera_dev->common.close = camera_device_close;
// 填充操作函数表
camera_dev->ops = &camera_device_ops;
// 打开驱动设备
char dev_node[32];
snprintf(dev_node, sizeof(dev_node), "/dev/video%d", camera_id);
int fd = open(dev_node, O_RDWR);
if (fd < 0) {
ALOGE("Failed to open device node %s: %s", dev_node, strerror(errno));
free(camera_dev);
return -ENODEV;
}
*device = &camera_dev->common;
return 0;
}
常见问题: open() 返回 -EBUSY 通常意味着摄像头已被其他进程占用。检查 lsof /dev/video* 或 ps -A | grep camera 确认是否有残留进程。在调试阶段,可以尝试 killall cameraserver 后重新测试。
4.4 camera_info初始化:设备属性上报
在open()成功后,框架通过 get_camera_info() 获取每个摄像头的静态属性。这些属性存储在 camera_info 结构体中,是上层应用判断摄像头类型(前置/后置)、方向、闪光灯支持等的基础。
typedef struct camera_info {
int device_version; // CAMERA_DEVICE_API_VERSION_3_4
uint32_t facing; // CAMERA_FACING_BACK / FRONT
int orientation; // 图像方向(0/90/180/270)
const char* resource_cost; // 资源消耗等级
struct camera_metadata* static_camera_characteristics; // 静态元数据
void* priv; // HAL私有数据
} camera_info_t;
| 字段 | 典型值 | 说明 | 调试检查点 |
|---|---|---|---|
facing |
0 (BACK) / 1 (FRONT) | 摄像头朝向 | 确认物理位置与枚举顺序一致 |
orientation |
90 / 270 | 传感器安装角度 | 预览方向异常时优先检查此值 |
device_version |
0x304 (HAL3.4) | API版本 | 与框架期望版本不匹配会导致open失败 |
4.5 static_metadata填充:元数据构建
static_camera_characteristics 是 camera_metadata_t 类型的数据包,包含了摄像头所有不可变的硬件能力描述。填充过程通常在HAL模块初始化时完成,并在 get_camera_info() 中返回指针。
关键元数据Tag包括:
- ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE:有效像素阵列尺寸
- ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE:有效成像区域
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS:支持的输出流格式与分辨率
- ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS:各分辨率最小帧时长
- ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES:支持的帧率范围
- ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS:可用焦距列表
- ANDROID_FLASH_INFO_AVAILABLE:是否支持闪光灯
// static_metadata 填充示例(使用 camera_metadata 构建API)
static camera_metadata_t* build_static_metadata() {
camera_metadata_t* metadata = allocate_camera_metadata(
CAMERA_METADATA_ENTRY_COUNT, CAMERA_METADATA_DATA_SIZE);
// 设置像素阵列大小
int32_t pixel_array_size[] = {3264, 2448};
add_camera_metadata_entry(metadata,
ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE, pixel_array_size, 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, 8);
// 设置帧率范围
int32_t fps_ranges[] = {15, 30, 30, 30};
add_camera_metadata_entry(metadata,
ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, fps_ranges, 4);
return metadata;
}
调试方法: 使用 dumpsys media.camera 可以查看框架层接收到的静态元数据内容。如果某个功能(如HDR、4K录像)不可用,首先检查对应的Tag是否在static_metadata中正确填充。使用 cam2utils 或 camera_metadata_tool 可以离线解析元数据二进制内容。
4.6 初始化流程SVG架构图
下图展示了从HAL模块加载到static_metadata填充的完整调用链与数据流关系:
4.7 初始化流程中的常见问题与调试方法
| 问题现象 | 可能原因 | 调试方法 |
|---|---|---|
| HAL模块加载失败 | 动态库路径错误、权限不足、符号未导出 | 检查 /vendor/lib/hw/camera.*.so 是否存在;使用 dlopen 手动加载并打印 dlerror() |
| open()返回-ENODEV | 设备节点不存在、驱动未加载、设备ID越界 | 检查 /dev/video* 节点;使用 ls -l /dev/video* 确认权限;dmesg | grep camera 查看驱动日志 |
| camera_info中facing错误 | 摄像头ID与物理位置映射错误 | 在HAL中打印 camera_id 与 facing 的对应关系;使用 Camera2 API 的 getCameraCharacteristics() 验证 |
| static_metadata缺失关键Tag | Tag未填充、数据格式错误、内存溢出 | 使用 dumpsys media.camera 导出元数据;对比CTS测试用例中的必选Tag列表 |
| 预览方向旋转90度 | orientation 值设置错误 |
确认传感器物理安装角度;调整 camera_info.orientation 为正确值(0/90/180/270) |
系统级调试建议: 在HAL层关键函数入口添加 ALOGD 日志,使用 logcat -s CameraHal 过滤。对于初始化时序问题,可以在 open() 和 get_camera_info() 前后添加时间戳打印,定位耗时瓶颈。