2、HAL3核心数据结构详解

好,咱们今天来啃HAL3里最核心的几个数据结构。说实话,我刚接触HAL3时,也被这些结构体绕得头晕。但搞懂了它们,整个Camera HAL的骨架你就抓住了。

我个人习惯,每次看一个新模块,先看它的数据结构。数据结构决定了数据流,数据流决定了代码怎么写。HAL3的核心数据结构就四个:camera3_device_tcamera3_callback_ops_tcamera3_stream_tcamera3_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管理和同步问题——那可是坑最多的地方。