3. HAL3核心数据结构深度解析

各位同学,今天我们来啃一块硬骨头——HAL3的五个核心数据结构。说实话,我刚接触HAL3时,也被这些结构体绕得晕头转向。但后来我发现,只要理解了它们之间的「关系网」,一切就豁然开朗了。

核心观点:这五个结构体,本质上就是Camera HAL与Framework之间的「通信协议」。Framework通过它们下发指令,HAL通过它们上报结果。

HAL3 核心数据结构关系图 Framework 层(CameraService / CameraDeviceClient) camera3_device_t HAL设备句柄 ops 函数指针表 camera3_callback_ops_t 回调函数表 结果上报通道 注册回调 camera3_stream_t 数据流描述 格式/尺寸/方向 camera3_capture_request_t 拍照请求 settings + buffers camera3_capture_result_t 拍照结果 metadata + buffers configure_streams() process_capture_request() HAL处理 process_capture_result() 数据流向:Framework → device → request → HAL处理 → result → callback → Framework 图例: 数据结构 调用关系 / 数据流向 Framework 层

3.1 camera3_device_t —— HAL的「门面」

这个结构体,说白了就是Framework眼中HAL的样子。它定义在 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:继承自hw_device_t,是Android硬件抽象层的通用头。里面包含了打开、关闭设备的函数指针。
  • ops:这是核心!一个函数指针表,Framework通过它调用HAL的各种能力。比如初始化、配置流、提交请求等。
  • priv:私有数据指针。我习惯在这里挂载HAL内部的状态机、传感器信息等。你想想看,如果没有这个字段,HAL自己的数据往哪放?

个人经验:我在项目中遇到过,有些厂商在priv里塞了太多东西,导致内存泄漏。建议只放必要的上下文指针,别把整个HAL对象都塞进去。

3.2 camera3_callback_ops_t —— 反向通道

Framework不能一直等着HAL干活吧?它得知道什么时候结果回来了。这个结构体就是HAL向Framework上报结果的「电话线」。

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:上报拍照结果。包括metadata、图像buffer、以及各种状态信息。
  • notify:上报异步事件。比如快门事件、错误事件、缓冲区释放等。

嗯,这里要注意:这两个回调必须线程安全。HAL可能在任何线程中调用它们,Framework内部会做同步处理,但HAL自己不能假设调用上下文。

避坑指南:我曾经在notify回调里直接调用了process_capture_result,结果造成了死锁。因为Framework内部对这两个回调的处理路径不同,混用会导致资源竞争。切记:各走各的路。

3.3 camera3_stream_t —— 数据流的「管道」

拍照的数据从哪来?到哪去?这就靠stream来描述了。它定义了图像的格式、尺寸、方向、以及buffer的管理方式。

typedef struct camera3_stream {
    int stream_type;        // CAMERA3_STREAM_OUTPUT / INPUT / BIDIRECTIONAL
    uint32_t width;
    uint32_t height;
    int format;             // HAL_PIXEL_FORMAT_xxx
    android_dataspace_t data_space;
    int rotation;
    int usage;
    uint32_t max_buffers;
    void *priv;
    int reserved[6];
} camera3_stream_t;

关键字段解读:

字段 说明 我的建议
stream_type 输出流、输入流、双向流 预览用OUTPUT,ZSL用BIDIRECTIONAL
format 像素格式,如NV12、YV12、BLOB等 尽量用平台原生支持的格式,避免格式转换
max_buffers HAL需要的最大buffer数 别设太大,浪费内存;也别设太小,容易丢帧
usage buffer的使用标记,如GRALLOC_USAGE_HW_CAMERA 一定要和Gralloc协商好,否则分配失败

核心要点:stream的配置发生在configure_streams()调用中。一旦配置完成,stream的width、height、format就不能再改了。Framework会基于这个配置来分配buffer。

3.4 camera3_capture_request_t —— 拍照「指令」

Framework想拍照了,它得告诉HAL:用什么参数、拍哪几个流、输出到哪些buffer。这就是capture_request的作用。

typedef struct camera3_capture_request {
    uint32_t frame_number;
    const camera3_stream_buffer_t *input_buffer;
    uint32_t num_output_buffers;
    const camera3_stream_buffer_t *output_buffers;
    const camera_metadata_t *settings;
    int32_t sensor_orientation;
} camera3_capture_request_t;

几个容易踩坑的点:

  • frame_number:每个请求的唯一ID。HAL处理完后,必须原样返回。我见过有人把frame_number搞混,结果Framework那边对不上号,直接报错。
  • settings:一个camera_metadata_t结构,包含了AE、AF、AWB等所有控制参数。注意:settings可以为NULL,表示使用上一次的设置。但第一次请求不能为NULL。
  • output_buffers:输出buffer数组。每个buffer对应一个stream。Framework保证buffer已经分配好,HAL只需要往里面填数据。

个人习惯:我每次收到request,第一件事就是检查settings是否为NULL。如果是,就从内部缓存中取上一帧的settings。这样能避免很多空指针问题。

3.5 camera3_capture_result_t —— 拍照「答卷」

HAL处理完请求后,通过process_capture_result回调,把结果塞进这个结构体,还给Framework。

typedef struct camera3_capture_result {
    uint32_t frame_number;
    const camera_metadata_t *result;
    uint32_t num_output_buffers;
    const camera3_stream_buffer_t *output_buffers;
    uint32_t num_input_buffers;
    const camera3_stream_buffer_t *input_buffers;
    uint32_t partial_result;
} camera3_capture_result_t;

这里有个重要的概念——partial_result:

为什么会有这个字段?因为metadata可能很大,HAL可以分多次返回。比如先返回AE/AWB的结果,再返回AF的结果。partial_result表示这是第几次部分结果。Framework会等所有部分结果都到齐了,才认为这一帧处理完成。

避坑指南:我曾经在partial_result上栽过跟头。当时我一次性返回了所有metadata,但partial_result设成了1。Framework以为还有后续结果,一直等,结果超时了。正确的做法是:如果一次性返回全部,partial_result要设为比实际部分数大1的值,或者直接设为0表示完整结果。

3.6 它们如何协同工作?

好了,五个结构体都讲完了。我们来串一下整个流程:

  1. Framework通过camera3_device_t.ops调用open()打开设备。
  2. HAL通过camera3_callback_ops_t注册回调,告诉Framework「有事打我电话」。
  3. Framework调用configure_streams(),传入camera3_stream_t数组,告诉HAL「我要这些格式的流」。
  4. 每次拍照,Framework构造一个camera3_capture_request_t,通过process_capture_request()发给HAL。
  5. HAL处理完后,构造camera3_capture_result_t,通过回调返回给Framework。

你看,是不是很清晰?说白了就是:Framework下指令,HAL干活,然后通过回调交作业。

总结:这五个结构体是HAL3的基石。你如果能把它们的关系理清楚,HAL3的框架就掌握了80%。剩下的就是具体实现了。

可可精品资料库