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 时间戳来做同步,比如多摄像头的时间对齐。

注意:shutter 事件里的 timestamp 必须是硬件时间戳,不能是软件模拟的。我曾经见过一个厂商用 gettimeofday 来填充,结果多摄同步时画面错位得一塌糊涂。

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 的返回也有讲究。你可以:

  1. 和 metadata 一起返回:最直接的方式,buffer 和 metadata 同时给到 Framework。
  2. 分多次返回:先返回 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 系统,那是另一个让人又爱又恨的东西。到时候见。