3. HAL3 核心数据结构详解
好,咱们进入正题。HAL3 的这几个核心数据结构,说白了就是 Camera HAL 的骨架。你如果不把它们吃透,后面写代码肯定会踩坑。我当年刚接触 HAL3 时,就被 camera3_stream_t 的 usage 字段坑过一回,后面我会讲到。
咱们一个一个来看。
3.1 camera3_device_t:你的 HAL 设备句柄
这个结构体,是 Framework 和 HAL 沟通的入口。每个 Camera 设备,对应一个 camera3_device_t 实例。
它的定义大概长这样:
typedef struct camera3_device {
hw_device_t common;
camera3_device_ops_t *ops;
void *priv;
} camera3_device_t;
三个字段,各司其职:
- common:标准的 Android HAL 设备头。里面包含了
open、close这些基础操作。说白了,就是让上层能统一管理所有 HAL 设备。 - ops:这个最重要。它是一个函数指针表,指向你实现的
initialize、configure_streams、process_capture_request等核心方法。Framework 调用你的 HAL,就是通过这个 ops。 - priv:私有数据指针。你可以把任何你想存的东西挂在这里。我个人习惯把设备上下文、状态机、调试信息都塞进去。
重点:ops 里的函数,必须全部实现。少一个,Framework 就会 crash。我见过有人漏了 flush,结果在 CTS 测试时直接挂掉。
3.2 camera3_callback_ops_t:你给 Framework 的回调
这个结构体,是 HAL 用来通知 Framework 的。比如:
- 拍照完成了,把结果 buffer 送回去
- 出错了,报告错误码
- Metadata 更新了,通知上层
它的定义:
typedef struct camera3_callback_ops {
void (*process_capture_result)(const struct camera3_callback_ops *,
const camera3_capture_result_t *result);
void (*notify)(const struct camera3_callback_ops *,
const camera3_notify_msg_t *msg);
} camera3_callback_ops_t;
两个回调:
- process_capture_result:每次 capture 完成后,你必须调用它。把
camera3_capture_result_t传回去。里面包含了 output buffers、metadata、partial result 等信息。 - notify:用于发送异步通知。比如快门事件、错误事件。嗯,这里要注意:错误通知一定要及时发,否则 Framework 会一直等你的 buffer,导致 ANR。
我的经验:我曾经在调试时,忘了在出错时调用 notify 发送 CAMERA3_MSG_ERROR_REQUEST。结果上层一直卡在等待状态,整个 Camera 服务都僵住了。后来查了三天才找到原因。
3.3 camera3_stream_t:数据流的描述
这个结构体,描述了 Camera 输出的数据流。比如预览流、拍照流、视频流。每个流都有自己的配置。
关键字段:
| 字段 | 说明 |
|---|---|
stream_type |
CAMERA3_STREAM_OUTPUT(输出)或 CAMERA3_STREAM_INPUT(输入,用于 reprocess) |
width / height |
图像分辨率。注意:必须是 configure_streams 时协商好的尺寸 |
format |
像素格式。比如 HAL_PIXEL_FORMAT_YCbCr_420_888、HAL_PIXEL_FORMAT_BLOB(用于 JPEG) |
usage |
Gralloc 的使用标记。比如 GRALLOC_USAGE_SW_READ_OFTEN、GRALLOC_USAGE_HW_TEXTURE |
max_buffers |
HAL 建议的 buffer 数量。Framework 会参考这个值来分配 |
priv |
HAL 私有数据。你可以把每个流的上下文挂在这里 |
避坑指南:usage 字段一定要和你的硬件匹配。我曾经在某个平台上,把预览流的 usage 设成了 GRALLOC_USAGE_HW_TEXTURE,但硬件实际上只支持 GRALLOC_USAGE_SW_READ_OFTEN。结果预览画面全是花屏,查了两天才发现是 usage 不匹配。
另外,format 的选择也很关键。比如:
- 预览流:一般用 YCbCr_420_888,兼容性好
- 拍照流:用 BLOB,因为 JPEG 编码后的数据是压缩的
- 视频流:用 IMPLEMENTATION_DEFINED,让硬件自己选最优格式
3.4 camera3_stream_buffer_t:单个 buffer 的描述
这个结构体,描述了一个具体的 buffer。它是在 process_capture_request 和 process_capture_result 之间传递的。
定义:
typedef struct camera3_stream_buffer {
camera3_stream_t *stream;
buffer_handle_t *buffer;
int status;
int acquire_fence;
int release_fence;
} camera3_stream_buffer_t;
字段含义:
- stream:指向所属的
camera3_stream_t。这样你就知道这个 buffer 是给哪个流用的。 - buffer:实际的 buffer handle。通过它,你可以拿到图像数据。
- status:buffer 的状态。比如
CAMERA3_BUFFER_STATUS_OK表示成功,CAMERA3_BUFFER_STATUS_ERROR表示出错。 - acquire_fence / release_fence:同步 fence。用于协调不同硬件模块之间的访问顺序。比如 ISP 写完了,GPU 才能读。
关键点:fence 的处理一定要小心。如果你不正确地等待 acquire_fence,可能会读到不完整的数据。如果你不正确地设置 release_fence,可能会造成死锁。我见过一个 case,release_fence 一直没 signal,结果整个 pipeline 都卡住了。
3.5 它们之间的关系
这几个结构体,不是孤立的。它们一起构成了 HAL3 的数据流模型:
- camera3_device_t 是入口,通过
ops接收 Framework 的请求。 - Framework 调用
configure_streams时,传进来一组 camera3_stream_t,描述了你需要输出哪些流。 - 每次 capture 请求,Framework 会传进来一个 camera3_capture_request_t,里面包含了 output buffers(即 camera3_stream_buffer_t 数组)。
- 你处理完数据后,通过 camera3_callback_ops_t 的
process_capture_result回调,把填充好的 camera3_stream_buffer_t 返回给 Framework。
说白了,就是一条流水线:请求进来 → 你处理 → 结果回去。而上面这四个结构体,就是这条流水线上的关键节点。
我的建议:刚开始写 HAL3 时,先把这几个结构体的生命周期理清楚。比如 camera3_stream_t 是在 configure_streams 时创建的,在 flush 或设备关闭时销毁。而 camera3_stream_buffer_t 是每次 capture 时临时分配的,用完就还回去。搞清楚了这些,后面写代码就顺了。
3.6 小结
嗯,这一章的内容就这些。四个核心结构体,每个都有自己的职责。你想想看,它们就像乐高积木,拼在一起就是完整的 HAL3 数据流。
下一章,我们会深入 configure_streams 的实现细节。到时候我会分享一些我在实际项目中遇到的坑,比如如何协商分辨率、如何处理 format 转换。敬请期待。