3. camera3_callback_ops_t 结构体:回调机制、notify/process_capture_result 函数详解
好,咱们接着聊。上一章我们把 camera3_device_t 这个「门面」讲透了,今天要深入的是 HAL 层和 Framework 之间的「通信管道」——camera3_callback_ops_t。
说白了,这个结构体就是 HAL 向 Framework 汇报工作的接口。你想想看,HAL 处理完一个请求,总得告诉上层结果吧?什么时候拍完了、哪里出错了、数据流准备好了——这些信息都得通过回调来传递。
3.1 回调结构体概览
先看定义。这个结构体在 camera3.h 里,内容其实不多,就两个函数指针:
typedef struct camera3_callback_ops {
void (*notify)(const struct camera3_callback_ops *ops,
const camera3_notify_msg_t *msg);
void (*process_capture_result)(const struct camera3_callback_ops *ops,
const camera3_capture_result_t *result);
} camera3_callback_ops_t;
嗯,就两个。但这两个函数撑起了整个 HAL 到 Framework 的通信。我刚开始接触这个结构体时也觉得奇怪——这么重要的东西就两个回调?后来做多了才明白,大道至简。
3.2 notify 函数:事件通知
notify 负责传递「非数据类」的事件。比如快门事件、错误事件。它传递的是 camera3_notify_msg_t 结构体。
3.2.1 消息类型
| 类型 | 枚举值 | 说明 |
|---|---|---|
| CAMERA3_MSG_ERROR | 1 | 错误通知,比如设备挂了、请求超时 |
| CAMERA3_MSG_SHUTTER | 2 | 快门事件,传感器开始曝光了 |
这里有个细节:shutter 事件必须在 process_capture_result 之前发出。这是 Google 的硬性要求。为什么?因为 Framework 需要根据 shutter 时间戳来做同步,比如多摄像头的时间对齐。
3.2.2 错误类型
错误消息里有个 error_code 字段,常见的有:
- CAMERA3_MSG_ERROR_DEVICE:设备级错误,比如摄像头物理损坏。遇到这个,Framework 会直接断开连接。
- CAMERA3_MSG_ERROR_REQUEST:单个请求失败。比如 buffer 没准备好,或者 ISP 处理超时。
- CAMERA3_MSG_ERROR_RESULT:结果数据有问题。比如 metadata 格式不对。
- CAMERA3_MSG_ERROR_BUFFER:某个 buffer 处理失败。这个比较常见,比如 DMA 传输出错。
我个人习惯是:能报 REQUEST 级别的错误就别报 DEVICE 级别的。因为 DEVICE 错误会导致整个相机服务重启,用户体验很差。除非真的没救了,比如 I2C 通信断了。
3.3 process_capture_result 函数:结果回调
这个函数是 HAL 最核心的输出通道。每次拍照、预览、录像的结果都通过它传递。
3.3.1 结果结构体
typedef struct camera3_capture_result {
uint32_t frame_number;
const camera3_stream_buffer_t *output_buffers;
uint32_t num_output_buffers;
const camera3_stream_buffer_t *input_buffer;
const camera_metadata_t *result;
uint32_t partial_result;
int32_t num_physcam_metadata;
const camera_metadata_t **physcam_ids;
const camera_metadata_t **physcam_metadata;
} camera3_capture_result_t;
字段不少,但核心就几个:
- frame_number:对应哪个请求。这个必须和 process_capture_request 里的 frame_number 一致。
- output_buffers:处理好的图像 buffer。注意,这里可以一次返回多个 buffer,比如同时返回 YUV 和 JPEG。
- result:metadata,包含 3A 状态、时间戳、人脸信息等。
- partial_result:部分结果标记。这个很重要,后面细说。
3.3.2 部分结果机制
partial_result 是 Android 7.0 引入的。为什么需要它?
你想想看,一个拍照请求可能包含几十个 metadata 字段。如果等所有字段都准备好了再返回,那延迟就太大了。尤其是预览场景,用户希望尽快看到画面。
所以 Google 允许 HAL 分多次返回同一个请求的结果。每次返回一部分 metadata,用 partial_result 标记这是第几次部分结果。Framework 会把这些部分结果合并起来。
关键规则:
- partial_result 从 1 开始计数
- 最后一次返回时,partial_result 设为 0
- 每次返回的 metadata 不能重复
我在项目中遇到过一个问题:有个同事把 partial_result 从 0 开始计数,结果 Framework 认为第一次返回的就是完整结果,后面的部分结果全被忽略了。嗯,调试了两天才发现。
3.3.3 buffer 返回策略
output_buffers 的返回也有讲究。你可以:
- 和 metadata 一起返回:最直接的方式,buffer 和 metadata 同时给到 Framework。
- 分多次返回:先返回 metadata,buffer 稍后再给。这在处理 JPEG 编码时很常见——metadata 先出来,编码需要时间。
我个人建议:能一起返回就一起返回。分多次返回虽然灵活,但增加了 Framework 的处理复杂度,也容易出 bug。
3.4 回调的线程安全
这个必须单独拿出来说。Framework 对回调的调用是串行化的,也就是说同一时刻只有一个回调在执行。但 HAL 内部可能是多线程的,所以:
- 不要在回调里加锁,容易死锁
- 回调要尽快返回,别做耗时操作
- 如果 HAL 内部有多个线程产生结果,需要自己做好同步
我的经验:在 HAL 里维护一个结果队列,所有线程把结果丢到队列里,由一个专门的线程来调用回调。这样既避免了多线程问题,也方便做超时控制。
3.5 常见陷阱
讲几个我踩过的坑:
- frame_number 不连续:Framework 期望 frame_number 是递增的。如果你跳过了某个编号,Framework 会认为那个请求丢了,然后报错。
- 忘记释放 buffer:output_buffers 里的 buffer 是 Framework 分配的,HAL 用完就还给 Framework。但如果你在回调里又引用了一次,那就 double free 了。
- shutter 时间戳不准:这个前面说过。一定要用硬件时间戳,最好是传感器曝光开始的那个时刻。
- partial_result 乱序:比如先返回 partial_result=2,再返回 partial_result=1。Framework 会直接崩溃。
3.6 小结
camera3_callback_ops_t 虽然只有两个函数,但它们是 HAL 和 Framework 之间的「生命线」。notify 负责事件通知,process_capture_result 负责数据传递。理解好这两个函数,你就掌握了 Camera HAL3 通信的核心。
下一章我们会深入 metadata 系统,那是另一个让人又爱又恨的东西。到时候见。