2、HAL3核心数据结构详解
好,咱们今天来啃HAL3里最核心的几个数据结构。说实话,我刚接触HAL3时,也被这些结构体绕得头晕。但搞懂了它们,整个Camera HAL的骨架你就抓住了。
我个人习惯,每次看一个新模块,先看它的数据结构。数据结构决定了数据流,数据流决定了代码怎么写。HAL3的核心数据结构就四个:camera3_device_t、camera3_callback_ops_t、camera3_stream_t、camera3_capture_request_t。咱们一个一个来。
2.1 camera3_device_t:HAL设备的“身份证”
这个结构体,说白了就是HAL设备在Framework眼中的样子。它定义在hardware/libhardware/include/hardware/camera3.h里。
typedef struct camera3_device {
hw_device_t common;
camera3_device_ops_t *ops;
void *priv;
} camera3_device_t;
你看,它有三个成员:
- common:这是Android硬件抽象层的通用头,所有硬件设备都得有。里面包含了设备打开、关闭等基础操作。
- ops:这个是指向操作函数表的指针。所有你能对Camera设备做的事情,比如初始化配置、提交请求、flush等,都通过这个指针调用。
- priv:私有数据指针。你可以把任何你想带的东西挂在这里。我在项目中通常把设备上下文、状态机、调试信息都塞到这里。
重点来了:camera3_device_t的生命周期是从HAL模块打开到关闭。你所有的状态管理,都要围绕这个结构体展开。
嗯,这里要注意一点。很多新手会忽略common里的close函数。我见过一个项目,设备关闭时没清理DMA缓冲区,结果下次打开直接崩了。所以,close回调里一定要做好资源释放。
2.2 camera3_callback_ops_t:Framework的“耳朵”
这个结构体,是HAL向Framework汇报结果的通道。它是一组函数指针,Framework注册给HAL用的。
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:提交拍照请求后,HAL处理完了,通过这个回调把结果送回Framework。包括图像数据、metadata等。
- notify:用于发送异步通知,比如快门事件、错误信息等。
我的经验:process_capture_result这个回调一定要尽快返回。我曾经在回调里做了大量图像后处理,结果导致Framework的buffer队列堵死,预览卡成PPT。正确的做法是,回调里只做数据传递,耗时操作放到工作线程里。
你想想看,Framework把回调注册给你,就是信任你能及时反馈。如果你在回调里磨磨蹭蹭,整个Camera pipeline都会受影响。
2.3 camera3_stream_t:数据流的“管道”
这个结构体描述了一条数据流。什么是数据流?就是图像数据从Sensor到内存的路径。预览是一条流,拍照是一条流,录像也是一条流。
typedef struct camera3_stream {
int stream_type;
uint32_t width;
uint32_t height;
int format;
uint32_t usage;
uint32_t max_buffers;
const camera3_stream_configuration_t *priv;
void *reserved[2];
} camera3_stream_t;
关键字段:
- stream_type:CAMERA3_STREAM_OUTPUT(输出流,比如预览、拍照)或CAMERA3_STREAM_INPUT(输入流,比如 reprocess)。
- width/height:图像分辨率。注意,这个分辨率是Framework期望的,但HAL可以调整。调整后Framework会重新配置。
- format:像素格式。常见的有HAL_PIXEL_FORMAT_YCbCr_420_888、HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED等。
- usage:使用标志。告诉HAL这些buffer会被怎么用,比如是给GPU纹理用,还是给视频编码器用。
- max_buffers:HAL请求的最大buffer数。这个值很关键,设大了浪费内存,设小了容易丢帧。
避坑指南:我曾经在一个项目里,把max_buffers设成了1。结果预览时,Framework把buffer给了HAL,HAL还没处理完,Framework又要下一个buffer。因为没有空闲buffer,直接报错。后来我改成3,问题解决。一般建议预览流设3-4个,拍照流设1-2个。
2.4 camera3_capture_request_t:一次拍照的“订单”
这个结构体,就是Framework给HAL下的一次拍照指令。它包含了所有需要的信息:拍什么、怎么拍、结果放哪里。
typedef struct camera3_capture_request {
uint32_t frame_number;
const camera3_stream_buffer_t *input_buffers;
uint32_t num_input_buffers;
const camera3_stream_buffer_t *output_buffers;
uint32_t num_output_buffers;
const camera_metadata_t *settings;
int32_t *physcam_id;
} camera3_capture_request_t;
核心字段:
- frame_number:帧序号。每个请求都有唯一序号,用于追踪和匹配结果。
- input_buffers/output_buffers:输入输出buffer。输入buffer用于reprocess场景,输出buffer就是你要填数据的buffer。
- settings:拍照参数。这是一个camera_metadata_t结构体,里面包含了AE、AF、AWB等所有控制参数。
- physcam_id:物理相机ID。多摄场景下,用来指定用哪个物理相机。
关键点:frame_number必须严格递增。Framework就是靠这个序号来匹配请求和结果的。如果你返回的结果里frame_number对不上,Framework会直接丢弃。
我记得有一次调试,发现预览画面总是卡顿。查了半天,发现是HAL在处理请求时,把frame_number搞乱了。一个请求处理完,返回的frame_number却是另一个请求的。Framework那边匹配不上,直接丢掉了结果。从那以后,我每次处理请求都会加一个断言,确保frame_number正确。
2.5 它们之间的关系
这四个结构体不是孤立的,它们共同构成了HAL3的数据流骨架:
| 结构体 | 角色 | 生命周期 |
|---|---|---|
| camera3_device_t | 设备句柄,HAL的入口 | 设备打开到关闭 |
| camera3_callback_ops_t | 结果回传通道 | 设备打开到关闭 |
| camera3_stream_t | 数据流描述 | configure_streams到设备关闭 |
| camera3_capture_request_t | 单次拍照指令 | 提交到结果返回 |
流程是这样的:Framework通过camera3_device_t的ops调用configure_streams配置流,然后通过process_capture_request提交camera3_capture_request_t。HAL处理完后,通过camera3_callback_ops_t的回调返回结果。
个人建议:刚开始写HAL时,可以先画一个数据流图。把每个结构体在哪个阶段出现、谁创建谁销毁都标清楚。我每次接手新项目,第一件事就是画这个图。画完了,代码怎么写心里就有数了。
好了,这四个核心数据结构就讲到这里。说白了,它们就是HAL3世界的“四大天王”。搞懂了它们,你就能理解Framework和HAL之间是怎么对话的。下一节,咱们聊聊这些结构体在实际代码中怎么用,特别是buffer管理和同步问题——那可是坑最多的地方。