4. Camera HAL 初始化流程:HAL Module 加载、camera_info 填充、open() 与 close() 接口实现
Camera HAL 的初始化是上层 Camera Service 与底层硬件驱动之间的第一个关键握手阶段。本章将深入剖析 HAL Module 的加载机制、camera_info 静态信息的填充策略,以及 open() 与 close() 接口的标准实现模式。理解这一流程是后续进行多摄像头适配、HAL 层性能调优的基础。
4.1 HAL Module 加载机制
在 Android 系统中,Camera Service 通过 HAL_MODULE_INFO_SYM 符号找到 HAL 库的入口。该符号是一个 hw_module_t 结构体,其中包含了 HAL 的版本号、作者信息以及最重要的 open 函数指针。
核心要点: HAL 库必须导出
HAL_MODULE_INFO_SYM 符号,否则 Camera Service 无法加载该库。加载失败时,logcat 中会出现 load: module=xxx failed 的错误信息。
加载流程分为以下步骤:
- 动态库加载: Camera Service 通过
dlopen()打开camera.xxx.so库文件。 - 符号查找: 通过
dlsym()获取HAL_MODULE_INFO_SYM符号地址。 - Module 结构验证: 检查
hw_module_t.common.module_api_version是否匹配当前系统版本。 - 调用 open(): 通过
module->methods->open()创建 HAL 设备实例。
| 字段 | 类型 | 说明 |
|---|---|---|
tag |
uint32_t | 必须设置为 HARDWARE_MODULE_TAG |
module_api_version |
uint16_t | HAL 模块 API 版本,如 HAL_MODULE_API_VERSION(1, 0) |
hal_api_version |
uint16_t | HAL 层 API 版本,通常与 module_api_version 一致 |
id |
const char* | 必须为 HARDWARE_CAMERA_HAL |
name |
const char* | HAL 模块名称,如 "Camera HAL v3.4" |
methods |
hw_module_methods_t* | 包含 open 函数指针 |
移植提示: 在嵌入式平台上,如果使用 V4L2 驱动,HAL 库的加载路径通常为
/vendor/lib/hw/camera.xxx.so。确保 Android.bp 或 Android.mk 中正确指定了 relative_install_path 为 hw。
4.2 camera_info 静态信息填充
camera_info 是 HAL 层向 Camera Service 报告摄像头能力的关键数据结构。它包含设备 ID、朝向、安装角度、是否支持闪光灯等静态属性。该信息在 get_camera_info() 回调中填充,通常在 HAL 初始化阶段完成。
以下是一个典型的 camera_info 填充示例:
int get_camera_info(int camera_id, struct camera_info *info) {
if (camera_id < 0 || camera_id >= CAMERA_NUM) {
ALOGE("Invalid camera id: %d", camera_id);
return -EINVAL;
}
// 填充设备 ID
info->device_id = camera_id;
// 设置摄像头朝向:前置或后置
if (camera_id == 0) {
info->facing = CAMERA_FACING_BACK;
info->orientation = 90; // 后置摄像头旋转角度
} else {
info->facing = CAMERA_FACING_FRONT;
info->orientation = 270; // 前置摄像头旋转角度
}
// 资源成本:0-100,数值越大表示资源消耗越高
info->resource_cost = 50;
// 冲突设备:如果同时打开该设备会冲突的设备 ID 列表
info->conflicting_devices = NULL;
info->conflicting_devices_length = 0;
return 0;
}
常见错误:
orientation 值设置错误会导致预览画面旋转方向异常。对于后置摄像头,通常为 90 度或 270 度;前置摄像头通常为 270 度或 90 度,具体取决于硬件安装角度。务必参考硬件规格书确认。
4.3 open() 与 close() 接口实现
open() 是 HAL 层最核心的接口,负责初始化硬件设备、分配资源、创建数据通路。close() 则负责释放资源、关闭设备。这两个接口必须成对实现,且要保证线程安全。
4.3.1 open() 接口实现要点
- 参数验证: 检查
hw_module_t和hw_device_t指针是否有效。 - 设备打开: 通过 V4L2 或平台私有驱动接口打开摄像头设备节点。
- 内存分配: 为 Gralloc Buffer、Metadata 等分配内存池。
- 初始化流配置: 设置默认的流格式和分辨率。
- 注册回调: 将
camera3_device_ops函数表注册到设备结构体中。
static int camera_device_open(const struct hw_module_t *module,
const char *name,
struct hw_device_t **device) {
if (module == NULL || name == NULL || device == NULL) {
ALOGE("Invalid parameters");
return -EINVAL;
}
// 解析摄像头 ID
int camera_id = atoi(name);
if (camera_id < 0 || camera_id >= CAMERA_NUM) {
ALOGE("Invalid camera id: %s", name);
return -ENODEV;
}
// 分配设备上下文
camera3_device_t *camera_dev = (camera3_device_t *)
malloc(sizeof(camera3_device_t));
if (camera_dev == NULL) {
ALOGE("Failed to allocate camera device");
return -ENOMEM;
}
memset(camera_dev, 0, sizeof(camera3_device_t));
// 填充 hw_device_t 公共字段
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;
// 注册 camera3_device_ops
camera_dev->ops = &camera3_ops;
// 打开 V4L2 设备节点
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 %s: %s", dev_node, strerror(errno));
free(camera_dev);
return -ENODEV;
}
// 保存私有数据
camera_dev->priv = (void *)create_private_context(fd, camera_id);
*device = &camera_dev->common;
ALOGI("Camera device %d opened successfully", camera_id);
return 0;
}
4.3.2 close() 接口实现要点
- 停止所有流: 确保所有正在运行的流已经停止,释放未处理的请求。
- 关闭设备节点: 关闭 V4L2 或平台驱动的文件描述符。
- 释放内存: 释放 Metadata、Buffer 池、私有上下文等资源。
- 置空指针: 将设备结构体指针置空,防止野指针。
static int camera_device_close(struct hw_device_t *device) {
if (device == NULL) {
ALOGE("Device is NULL");
return -EINVAL;
}
camera3_device_t *camera_dev = (camera3_device_t *)device;
private_context_t *priv = (private_context_t *)camera_dev->priv;
if (priv != NULL) {
// 停止所有正在运行的流
stop_all_streams(priv);
// 关闭 V4L2 设备节点
if (priv->fd >= 0) {
close(priv->fd);
priv->fd = -1;
}
// 释放 Metadata 和 Buffer 池
free_metadata_pool(priv);
free_buffer_pool(priv);
// 释放私有上下文
free(priv);
camera_dev->priv = NULL;
}
free(camera_dev);
ALOGI("Camera device closed successfully");
return 0;
}
工程经验: 在嵌入式平台上,
close() 接口中务必添加超时机制。如果某个流正在等待硬件完成,而硬件出现异常,close() 可能会被阻塞。建议设置 2-3 秒的超时,超时后强制关闭设备节点。
4.4 初始化流程总览
下图展示了从 HAL Module 加载到设备打开的整体流程:
4.5 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| HAL 库加载失败 | 库路径错误或符号未导出 | 检查 dlopen 返回的 dlerror 信息;确认 HAL_MODULE_INFO_SYM 已正确声明 |
| open() 返回 -ENODEV | 设备节点不存在或权限不足 | 检查 /dev/video* 节点是否存在;确认 SELinux 策略允许访问 |
| camera_info 朝向错误 | orientation 值设置与硬件安装角度不匹配 | 通过 v4l2-ctl --all 查看驱动报告的旋转信息;对照硬件规格书修正 |
| close() 卡死 | 流未停止或硬件未响应 | 在 close 中添加超时机制;检查驱动是否有未完成的 DMA 传输 |
安全提示: 在多摄像头系统中,
open() 和 close() 可能被多个线程同时调用。必须使用互斥锁(如 pthread_mutex_t)保护共享资源,防止竞态条件导致设备状态不一致。
4.6 本章小结
Camera HAL 初始化流程是嵌入式平台摄像头适配的基石。开发者需要掌握:
- HAL Module 加载: 确保
HAL_MODULE_INFO_SYM正确导出,库文件路径符合系统规范。 - camera_info 填充: 准确设置朝向、旋转角度等静态信息,避免上层应用出现方向错误。
- open() 与 close() 实现: 严格遵循资源分配与释放的对称原则,添加超时和错误处理机制。