4. notify()回调详解:快门回调、错误回调、3A回调、元数据回调
好,咱们接着聊。上一节我们把 process_capture_result() 讲透了,这一节轮到 notify() 了。
说实话,notify() 这个回调在 HAL 层里看着不起眼,但它承担的任务非常关键。它不像 process_capture_result() 那样频繁调用,可一旦被触发,往往意味着发生了「大事」——要么是快门按下了,要么是出错了,要么是 3A 状态变了。
我个人习惯把 notify() 比作「报警器」。它不负责传输图像数据,只负责传递「事件」。Camera Framework 通过它来感知底层发生了什么。
核心要点:notify() 回调传递的是 camera3_notify_msg_t 结构体,里面包含三种消息类型:快门消息、错误消息、以及 3A/元数据消息。
4.1 回调接口定义
先看一眼接口长什么样:
typedef struct camera3_callback_ops {
// ... 其他回调 ...
void (*notify)(const struct camera3_callback_ops *ops,
const camera3_notify_msg_t *msg);
} camera3_callback_ops_t;
嗯,很简洁。传入一个 msg 指针,里面就是事件内容。那 camera3_notify_msg_t 长什么样?
typedef struct camera3_notify_msg {
uint32_t type;
union {
camera3_shutter_msg_t shutter;
camera3_error_msg_t error;
camera3_3a_msg_t three_a;
} message;
} camera3_notify_msg_t;
你看,type 字段决定了 message 联合体里哪个成员有效。取值有三种:
| type 值 | 对应结构体 | 含义 |
|---|---|---|
CAMERA3_MSG_SHUTTER |
camera3_shutter_msg_t |
快门回调 |
CAMERA3_MSG_ERROR |
camera3_error_msg_t |
错误回调 |
CAMERA3_MSG_3A |
camera3_3a_msg_t |
3A/元数据回调 |
4.2 快门回调:最常用的通知
快门回调,说白了就是告诉 Framework:「传感器已经曝光了,你可以记录时间戳了。」
结构体定义:
typedef struct camera3_shutter_msg {
uint32_t frame_number;
uint64_t timestamp;
} camera3_shutter_msg_t;
这里有两个字段:frame_number 对应你提交的请求帧号,timestamp 是曝光开始的时间戳,单位是纳秒。
我的经验:时间戳必须用 CLOCK_MONOTONIC,不能用 CLOCK_REALTIME。否则在 CTS 测试里会挂掉。我曾经有个同事用了系统时间,结果 CTS 跑了一晚上全红了……
什么时候触发快门回调?
- 对于 JPEG 或 RAW 输出:在传感器开始曝光时触发
- 对于 YUV 或 HEIC 输出:在传感器开始曝光时触发
- 对于 ZSL 模式:在传感器曝光完成时触发
你想想看,Framework 拿到这个时间戳后,会用它来做什么?
- 计算拍照延迟(shutter lag)
- 同步多摄像头的时间轴
- 给应用层提供「按下快门」的反馈
所以,这个回调必须及时。我建议在传感器中断服务函数里直接调用 notify(),不要放到工作线程里排队。否则时间戳就不准了。
4.3 错误回调:出了问题怎么办
错误回调是 HAL 向 Framework 报告「我出事了」的唯一通道。结构体如下:
typedef struct camera3_error_msg {
uint32_t frame_number;
int error_stream_id;
int error_code;
} camera3_error_msg_t;
error_code 定义了错误的类型:
| 错误码 | 含义 | 影响范围 |
|---|---|---|
CAMERA3_MSG_ERROR_DEVICE |
设备级错误 | 整个 Camera 设备不可用 |
CAMERA3_MSG_ERROR_REQUEST |
请求级错误 | 单个 capture request 失败 |
CAMERA3_MSG_ERROR_RESULT |
结果级错误 | 某个 result 无法生成 |
CAMERA3_MSG_ERROR_BUFFER |
Buffer 级错误 | 某个 stream 的 buffer 出错 |
注意:设备级错误一旦上报,Framework 会直接关闭 Camera。所以除非万不得已,不要轻易报 CAMERA3_MSG_ERROR_DEVICE。
我曾经在一个项目里遇到过:ISP 的 3A 统计模块偶尔会挂死,导致某个 request 的 result 出不来。当时我犯了个错——报了设备级错误,结果整个相机都打不开了。后来改成报 CAMERA3_MSG_ERROR_REQUEST,Framework 只是丢掉这一帧,用户几乎感觉不到。
所以,我的建议是:
- 能报
BUFFER就别报RESULT - 能报
RESULT就别报REQUEST - 能报
REQUEST就别报DEVICE
错误粒度越细,对用户体验的影响越小。
4.4 3A回调:自动对焦、自动曝光、自动白平衡
3A 回调是 Android 5.0 之后引入的,用来替代老旧的 auto_focus 和 auto_exposure 回调。结构体如下:
typedef struct camera3_3a_msg {
uint32_t frame_number;
camera3_3a_mode_t mode;
camera3_3a_trigger_t trigger;
int32_t af_trigger_id;
int32_t ae_trigger_id;
int32_t awb_trigger_id;
} camera3_3a_msg_t;
这里 mode 和 trigger 是枚举类型,定义了 3A 的状态机变化。比如:
CAMERA3_3A_MODE_AF:自动对焦模式CAMERA3_3A_MODE_AE:自动曝光模式CAMERA3_3A_MODE_AWB:自动白平衡模式CAMERA3_3A_TRIGGER_START:触发开始CAMERA3_3A_TRIGGER_CANCEL:触发取消CAMERA3_3A_TRIGGER_COMPLETE:触发完成
你可能会问:「这些信息不是已经在 result metadata 里了吗?为什么还要单独回调?」
嗯,问得好。原因很简单——实时性。result metadata 要等到整个 pipeline 处理完才能返回,而 3A 状态变化需要立即通知 Framework。比如用户按下了对焦按钮,Framework 需要马上知道对焦是否完成,不能等一帧图像处理完。
我的习惯:3A 回调只在触发状态发生变化时才调用。如果 3A 一直处于稳定状态,不要每帧都发。否则 Framework 那边会收到大量无用的通知,浪费 CPU。
4.5 元数据回调:其实是个「伪概念」
标题里提到了「元数据回调」,这里需要澄清一下。
在 Android Camera HAL3 的设计中,并没有独立的元数据回调。所谓的「元数据回调」,其实是指 CAMERA3_MSG_3A 消息里携带的 camera3_3a_msg_t 结构体。它里面包含了 AF、AE、AWB 的触发 ID 和状态信息,这些本质上就是元数据。
为什么大家会把它叫成「元数据回调」?
我猜是因为早期 Android 版本(4.x)里,有一个独立的 notify_metadata() 回调。到了 HAL3 时代,这个功能被合并到 notify() 里了。但很多老工程师还是习惯这么叫。
所以,你如果在代码里看到有人注释写「元数据回调」,别懵,指的就是 CAMERA3_MSG_3A。
4.6 实战:如何正确实现 notify()
最后,我给出一个实际项目中用到的实现模板:
static void my_notify(const struct camera3_callback_ops *ops,
const camera3_notify_msg_t *msg) {
// 获取 HAL 设备实例
my_camera_device_t *dev = (my_camera_device_t *)ops;
switch (msg->type) {
case CAMERA3_MSG_SHUTTER: {
// 快门回调:直接转发,不要延迟
dev->callback_ops->notify(dev->callback_ops, msg);
break;
}
case CAMERA3_MSG_ERROR: {
// 错误回调:先记录日志,再转发
ALOGE("Error on frame %d, stream %d, code %d",
msg->message.error.frame_number,
msg->message.error.error_stream_id,
msg->message.error.error_code);
dev->callback_ops->notify(dev->callback_ops, msg);
break;
}
case CAMERA3_MSG_3A: {
// 3A 回调:只在状态变化时转发
if (is_3a_state_changed(dev, &msg->message.three_a)) {
dev->callback_ops->notify(dev->callback_ops, msg);
}
break;
}
default:
ALOGW("Unknown notify type: %d", msg->type);
break;
}
}
避坑指南:千万不要在 notify() 里做耗时操作。我曾经见过有人在错误回调里写日志文件,结果导致 I/O 阻塞,整个 Camera pipeline 都卡住了。notify() 应该只做转发,最多加个简单的日志打印。
好了,这一节就到这里。下一节我们会讲 process_capture_result() 和 notify() 的协同工作——这两个回调怎么配合才能保证 Camera 流畅运行。