4. Camera HAL3 初始化流程:open()、initialize()、configure_streams() 的调用时序与参数解析
Camera HAL3 的初始化流程是 Camera Service 与 Vendor HAL 建立通信通道、协商数据格式并分配内存的关键阶段。该流程由三个核心接口调用组成:open()、initialize() 和 configure_streams()。理解它们的调用时序、参数含义以及 Vendor 层的实现约束,是进行 HAL 适配与调试的基础。
核心要点: 初始化流程是 Camera Service 主导的“握手”过程。open() 建立设备连接,initialize() 交换能力与回调,configure_streams() 最终确定数据管道。任何一步的失败或参数不匹配,都会导致 Camera 无法打开或预览黑屏。
4.1 调用时序与状态机
HAL3 的初始化遵循严格的状态机转换。Camera Service 按照固定顺序调用这三个接口,Vendor 层必须正确维护内部状态,否则会触发 Service 侧的异常处理。
4.2 open():建立设备连接
open() 是 HAL 模块加载后的第一个调用,用于实例化一个 camera 设备。其原型定义在 camera_module_t 中:
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
参数解析
| 参数 | 类型 | 说明 |
|---|---|---|
| module | const struct hw_module_t* | 指向 HAL 模块实例的指针,Vendor 可通过它访问模块级信息。 |
| id | const char* | Camera ID 字符串,例如 "0"、"1",对应 camera_info_t 中的设备标识。 |
| device | struct hw_device_t** | 输出参数,指向新创建的 camera3_device_t 结构体指针。 |
Vendor 实现要点: 在 open() 中,Vendor 应分配
camera3_device_t 结构体,并填充其中的 ops 函数指针表(包括 initialize、configure_streams、process_capture_request 等)。同时,应初始化底层硬件驱动句柄,但此时不要分配流内存。
4.3 initialize():交换能力与回调
initialize() 是 Camera Service 与 HAL 建立双向通信的关键步骤。Service 传入回调接口,HAL 返回设备静态信息。
int (*initialize)(const struct camera3_device *device,
const camera3_callback_ops_t *callback_ops);
参数解析
| 参数 | 类型 | 说明 |
|---|---|---|
| device | const struct camera3_device* | 由 open() 创建的设备实例。 |
| callback_ops | const camera3_callback_ops_t* | Service 提供的回调函数表,包含 process_capture_result、notify 等。 |
回调函数表 (camera3_callback_ops_t)
- process_capture_result:HAL 通过此回调将已处理完成的 capture result 返回给 Service。
- notify:HAL 通过此回调发送异步事件,如快门事件、错误事件、缓冲区释放信号等。
常见错误: Vendor 在 initialize() 中必须保存 callback_ops 指针,否则后续无法向 Service 返回数据。同时,initialize() 不应执行耗时操作(如 ISP 加载),否则可能导致 Service 超时(默认 5 秒)。
4.4 configure_streams():协商数据管道
configure_streams() 是初始化流程中最复杂、最关键的接口。它决定了 Camera 输出的格式、分辨率、帧率以及内存分配策略。
int (*configure_streams)(const struct camera3_device *device,
camera3_stream_configuration_t *stream_list);
参数解析
| 参数 | 类型 | 说明 |
|---|---|---|
| device | const struct camera3_device* | 设备实例。 |
| stream_list | camera3_stream_configuration_t* | 包含所有待配置 stream 的数组及操作模式。 |
camera3_stream_configuration_t 结构体
typedef struct camera3_stream_configuration {
uint32_t num_streams; // stream 数量
camera3_stream_t **streams; // stream 指针数组
uint32_t operation_mode; // 操作模式
const camera3_stream_buffer_set_t *buffer_set; // 可选,用于 buffer 管理
} camera3_stream_configuration_t;
操作模式 (operation_mode)
| 模式常量 | 值 | 说明 |
|---|---|---|
| CAMERA3_STREAM_CONFIGURATION_NORMAL_MODE | 0x0000 | 标准预览/拍照模式,最常用。 |
| CAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE | 0x0001 | 高速模式,用于高帧率录像(如 120fps)。 |
| CAMERA3_STREAM_CONFIGURATION_VENDOR_MODE_START | 0x8000 | Vendor 自定义模式起始值。 |
camera3_stream_t 关键字段
| 字段 | 类型 | 说明 |
|---|---|---|
| stream_type | int | CAMERA3_STREAM_OUTPUT (输出) 或 CAMERA3_STREAM_INPUT (输入)。 |
| width / height | uint32_t | 图像分辨率,Vendor 必须支持。 |
| format | int | 像素格式,如 HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED、HAL_PIXEL_FORMAT_YCbCr_420_888。 |
| usage | uint32_t | Gralloc 使用标志,指示 buffer 的预期用途(如 GPU 纹理、视频编码器)。 |
| max_buffers | uint32_t | Vendor 建议的最大 buffer 数量,Service 可据此分配。 |
| priv | void* | Vendor 私有数据指针,用于关联硬件管道。 |
Vendor 实现要点: configure_streams() 是 Vendor 进行硬件管道重配置的入口。Vendor 需要根据 stream_list 中的格式和分辨率,设置 ISP、传感器输出尺寸、MIPI 通道等。如果请求的配置硬件不支持,应返回 -EINVAL 并打印详细日志。
4.5 参数校验与常见错误码
Vendor 层在实现这三个接口时,必须对输入参数进行严格校验。以下是常见的错误码及其含义:
| 错误码 | 值 | 含义 | 常见触发场景 |
|---|---|---|---|
| -ENODEV | -19 | 设备不存在或未初始化 | open() 时 ID 无效;initialize() 时设备未 open |
| -EINVAL | -22 | 无效参数 | configure_streams() 中分辨率/格式不支持 |
| -ENOMEM | -12 | 内存不足 | open() 分配设备结构体失败 |
| -EUSERS | -87 | 资源冲突 | 多个 stream 争用同一硬件资源 |
| -EBUSY | -16 | 设备忙 | 在已有 pipeline 运行时重复 configure_streams() |
4.6 调试工具链实战建议
- 日志打点: 在 open()、initialize()、configure_streams() 的入口和出口添加
ALOGD日志,打印关键参数(ID、stream 数量、格式、分辨率)。 - 参数转储: 在 configure_streams() 中,将 stream_list 内容转储到
/data/vendor/camera/目录下的 dump 文件,便于离线分析。 - 状态机检查: 在每次接口调用时,检查当前 HAL 内部状态是否合法,非法状态直接返回 -ENODEV 并打印错误栈。
- 超时监控: 使用
adb shell dumpsys media.camera查看 Service 侧的调用超时统计,定位 Vendor 实现中的耗时瓶颈。
实战案例: 某平台在 configure_streams() 中返回 -EINVAL,原因是 HAL 未实现
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED 到实际硬件格式的映射。通过添加格式转换表并打印匹配日志,问题得以解决。