4. Camera HAL3 核心数据结构:camera3_device_t、camera3_callback_ops_t、camera3_stream_t 等关键结构体解析

好,咱们今天来啃一块硬骨头——HAL3 的核心数据结构。

说实话,我刚接触 HAL3 的时候,看着这些结构体也是一头雾水。什么 camera3_device_tcamera3_callback_ops_tcamera3_stream_t……名字长得差不多,功能却天差地别。后来在项目里踩了几次坑,才慢慢摸清楚它们的脾气。

这一章,我就带你把这些结构体一个一个拆开看。你搞懂了它们,HAL3 的框架也就通了七七八八。

4.1 camera3_device_t:HAL 层的“门面”

先看最顶层的结构体——camera3_device_t

你可以把它理解成 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 硬件抽象层的通用头。里面包含了设备打开、关闭、版本号等基础信息。说白了,这是 Android 硬件框架的“身份证”。
  • ops:操作函数指针表。这是核心中的核心。所有上层调用的接口——比如 initialize()configure_streams()process_capture_request()——都挂在这个指针表上。
  • priv:私有数据指针。你可以在这里挂你自己的 HAL 实现上下文。我习惯在这里放一个指向我自己定义的 CameraDevice 类的指针。

重点:ops 指针绝对不能为 NULL。否则上层一调用就崩。我曾经在移植一个老平台时,忘了初始化 ops,结果一打开相机就 segfault。查了半天才发现是这里的问题。

4.2 camera3_callback_ops_t:反向通知的“桥梁”

接下来是 camera3_callback_ops_t。这个结构体是干什么用的?

你想想看,HAL 处理完一帧数据,怎么通知 Framework 来取?总不能轮询吧。所以 Android 设计了一套回调机制。

camera3_callback_ops_t 就是这套回调机制的“接口契约”。它由 Framework 实现,然后通过 initialize() 传给 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。结果里包含图像 buffer、metadata 等。
  • notify:用于发送异步事件通知,比如快门事件、错误事件等。

我的经验:回调函数里不要做耗时操作。我曾经在回调里直接做图像后处理,结果导致 UI 卡顿。正确的做法是:回调里只做数据传递,真正的处理放到工作线程里。

4.3 camera3_stream_t:数据流的“管道”

再来说 camera3_stream_t。这个结构体描述了一条数据流——比如预览流、拍照流、录像流。

typedef struct camera3_stream {
    int stream_type;
    uint32_t width;
    uint32_t height;
    int format;
    uint32_t usage;
    uint32_t max_buffers;
    void *priv;
    android_dataspace_t data_space;
    int rotation;
    int id;
    camera3_stream_configuration_t *configuration;
} camera3_stream_t;

字段很多,我挑几个关键的讲:

  • stream_type:流类型。有 CAMERA3_STREAM_OUTPUT(输出流,比如预览)、CAMERA3_STREAM_INPUT(输入流,比如 reprocessing)等。
  • width / height:图像分辨率。这个由 Framework 根据应用需求决定。
  • format:像素格式。常见的有 HAL_PIXEL_FORMAT_YCbCr_420_888HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED 等。
  • usage:buffer 的使用标记。比如 GRALLOC_USAGE_HW_TEXTURE 表示 buffer 会被用于 GPU 纹理。
  • max_buffers:HAL 请求的最大 buffer 数量。这个值不能乱填,要根据硬件能力来。

注意:format 字段很容易踩坑。我记得有一次,Framework 请求的是 IMPLEMENTATION_DEFINED 格式,我直接返回了 NV12。结果预览画面颜色完全不对。后来才发现,IMPLEMENTATION_DEFINED 需要根据 usage 来协商实际格式。

4.4 camera3_stream_configuration_t:流的“集合”

单个流不够,多个流怎么管理?这就轮到 camera3_stream_configuration_t 了。

typedef struct camera3_stream_configuration {
    uint32_t num_streams;
    camera3_stream_t **streams;
    uint32_t operation_mode;
    const camera3_stream_buffer_set_t *input_buffers;
} camera3_stream_configuration_t;
  • num_streams:流的数量。
  • streams:指向流指针数组的指针。每个元素是一个 camera3_stream_t *
  • operation_mode:操作模式。比如 CAMERA3_STREAM_CONFIGURATION_NORMAL_MODECAMERA3_STREAM_CONFIGURATION_CONSTRAINED_HIGH_SPEED_MODE

这个结构体在 configure_streams() 调用中传入。HAL 需要根据这个配置来分配硬件资源。

4.5 camera3_capture_request_t 与 camera3_capture_result_t:请求与结果

最后看两个“成对”的结构体——请求和结果。

camera3_capture_request_t

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;
} camera3_capture_request_t;
  • frame_number:帧序号。每个请求唯一。
  • input_buffer:输入 buffer(用于 reprocessing 场景)。
  • output_buffers:输出 buffer 数组。每个 buffer 对应一个 stream。
  • settings:请求的 metadata。包含 AE/AWB/AF 等参数。

camera3_capture_result_t

typedef struct camera3_capture_result {
    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 *result;
    int32_t partial_result;
} camera3_capture_result_t;

结构和请求很像,但多了 partial_result 字段。这个字段用于支持“部分结果”——比如先返回 metadata,再返回 buffer。

我的建议:尽量使用 partial_result 机制。这样 Framework 可以先拿到 metadata 做预览参数更新,不用等 buffer 处理完。用户体验会好很多。

4.6 结构体之间的关系

这些结构体不是孤立的。它们之间有一条清晰的调用链:

  1. Framework 通过 camera3_device_t.ops->initialize() 传入 camera3_callback_ops_t
  2. Framework 调用 camera3_device_t.ops->configure_streams() 传入 camera3_stream_configuration_t
  3. Framework 调用 camera3_device_t.ops->process_capture_request() 传入 camera3_capture_request_t
  4. HAL 处理完成后,通过 camera3_callback_ops_t.process_capture_result() 返回 camera3_capture_result_t

嗯,这就是 HAL3 的数据流全貌。你把这些结构体串起来,整个 HAL3 的骨架就清晰了。

4.7 避坑指南

最后,分享几个我踩过的坑:

  • stream 的 max_buffers 不要设太大。设大了浪费内存,设小了可能丢帧。我一般根据硬件 pipeline 深度来定,比如 ISP 有 4 个 buffer 槽,我就设 4。
  • frame_number 必须严格递增。Framework 依赖这个序号来匹配请求和结果。我曾经在某个分支里忘了递增,结果导致 Framework 报错。
  • settings 不能为 NULL。即使没有参数变化,也要传一个空的 metadata。否则 Framework 会认为 HAL 不支持参数控制。

好了,这一章的内容就到这里。下一章我们会深入 configure_streams() 的实现细节——那才是真正考验 HAL 功底的地方。