3. HAL 核心数据结构解析:内存布局与生命周期管理

在 Camera HAL 适配工作中,数据结构是驱动框架与硬件交互的“契约”。camera_metadata_t、camera3_stream_t 和 camera3_capture_request_t 是三个最核心的结构体,它们的内存布局直接决定了跨厂商兼容性,而生命周期管理则是避免内存泄漏和野指针的关键。

核心观点: 理解这三个结构体的内存布局,是解决多厂商平台兼容问题的第一步。错误的布局假设会导致数据错位、崩溃或性能下降。

3.1 camera_metadata_t:动态元数据的序列化容器

camera_metadata_t 是 Android Camera 框架中用于传递控制参数和结果元数据的核心结构。它采用紧凑的序列化布局,支持动态增长,但不同厂商的 HAL 实现可能对元数据条目顺序有不同假设。

3.1.1 内存布局

该结构体在内存中分为三个连续区域:

  1. 头部(Header): 包含总大小、条目数、剩余空间等元信息。
  2. 条目数组(Entries): 每个条目包含 Tag、类型、计数和数据偏移量。
  3. 数据区(Data): 实际存储的元数据值(如字符串、数组、结构体)。
camera_metadata_t 内存布局 Header size entry_count data_size flags reserved Entries Array entry[0]: tag, type, offset entry[1]: tag, type, offset entry[2]: tag, type, offset ... entry[n-1] Data Area 字符串数据 浮点数组 结构体 ... 关键:条目数组中的 offset 是相对于数据区起始位置的偏移量,而非绝对地址。

3.1.2 生命周期管理

阶段 操作 注意事项
分配 allocate_camera_metadata() 预估条目数和数据大小,避免频繁 realloc。
添加/更新 add_camera_metadata_entry() 检查返回值,确保数据区未满。
查找 find_camera_metadata_entry() 返回的是条目索引,不是直接指针。
释放 free_camera_metadata() 必须由分配者释放,避免 double-free。
适配技巧: 在跨厂商平台适配时,不要假设元数据条目的顺序。始终使用 Tag 进行查找,而不是通过索引访问。某些厂商的 HAL 可能会在元数据中插入私有 Tag,导致索引偏移。

3.2 camera3_stream_t:数据流的配置与绑定

camera3_stream_t 定义了从 HAL 到框架的单个图像流(如预览、拍照、视频)的属性和缓冲区管理方式。它是 camera3_stream_configuration_t 数组中的元素,在 configure_streams() 调用中配置。

3.2.1 关键字段与内存布局

camera3_stream_t 结构体字段布局 stream_type CAMERA3_STREAM_OUTPUT width 1920 height 1080 format HAL_PIXEL_FORMAT_YCbCr usage GRALLOC_USAGE_SW_READ max_buffers 4 priv void* (HAL 私有数据) data_space / rotation / ... 扩展字段 生命周期关键点 1. 由框架分配,HAL 填充字段。 2. HAL 不得修改 stream_type、width、height、format 等输入字段。 3. priv 指针由 HAL 管理,框架不会释放。

3.2.2 生命周期与所有权

  • 创建: 框架在 configure_streams() 调用前分配 camera3_stream_t 数组。
  • 配置: HAL 在 configure_streams() 中读取输入字段,并设置 max_buffers 和 priv。
  • 使用: 在 process_capture_request() 中,通过 camera3_stream_buffer_t 引用。
  • 销毁: 框架在 configure_streams() 再次调用或设备关闭时释放。
常见陷阱: 某些厂商的 HAL 会在 priv 中存储硬件特定的 DMA 缓冲区句柄。在适配时,必须确保 priv 的清理逻辑与厂商的硬件资源管理一致,否则会导致内存泄漏或 DMA 缓冲区泄漏。

3.3 camera3_capture_request_t:一次捕获的完整描述

该结构体封装了单次捕获请求的所有信息,包括目标流、输入缓冲区、元数据和设置。它是 HAL 处理 pipeline 的输入单元。

3.3.1 结构体组成

字段 类型 说明
frame_number uint32_t 帧序号,单调递增,用于追踪。
settings const camera_metadata_t* 本次捕获的元数据设置(AE/AWB/AF 等)。
input_buffer const camera3_stream_buffer_t* 输入缓冲区(用于 reprocessing)。
num_output_buffers uint32_t 输出缓冲区数量。
output_buffers const camera3_stream_buffer_t* 输出缓冲区数组。
num_physcam_settings uint32_t 物理相机设置数量(多摄)。
physcam_settings const camera_metadata_t* 物理相机设置数组。

3.3.2 生命周期与所有权规则

camera3_capture_request_t 生命周期流程 Framework 分配 request process_capture_request() HAL 读取 request Hardware 处理 pipeline process_capture_result() 所有权规则 1. request 本身由框架分配,HAL 不得释放。 2. settings 元数据在 process_capture_request() 返回后仍有效,直到 HAL 调用 process_capture_result()。 3. output_buffers 中的 buffer 句柄由 HAL 填充,框架负责释放 buffer 本身。 4. HAL 必须在 process_capture_result() 中返回所有 output_buffers,否则框架会卡死。

3.3.3 多摄场景下的物理相机设置

在多摄像头 HAL 中,physcam_settings 字段允许为每个物理相机指定独立的元数据。适配时需注意:

  • 索引对齐: physcam_settings 数组的索引必须与 physical_camera_ids 的顺序一致。
  • 生命周期: 物理相机设置的生命周期与主 request 的 settings 相同。
  • 兼容性: 某些旧版 HAL 可能不支持 physcam_settings,需要检查 HAL 版本。
实战案例: 在适配某厂商的 MTK 平台时,发现其 HAL 在 process_capture_request() 中直接修改了 settings 指向的元数据内容,违反了 HAL 规范。解决方案是在 HAL 层做一次深度拷贝,避免影响框架侧的元数据。

3.4 数据结构关系总览

三大核心数据结构关系图 camera3_capture_request_t frame_number settings output_buffers input_buffer physcam_settings 引用 camera_metadata_t settings / result physcam_settings 包含 camera3_stream_t stream 配置 camera3_stream_buffer_t stream* buffer status acquire_fence release_fence

3.5 适配避坑指南

结构体 常见问题 解决方案
camera_metadata_t 厂商私有 Tag 导致查找失败 使用 find_camera_metadata_entry() 并检查返回值,不要假设 Tag 存在。
camera3_stream_t priv 指针未正确初始化或释放 在 configure_streams() 中统一管理 priv 的生命周期。
camera3_capture_request_t output_buffers 未全部返回 在 HAL 的 pipeline 中建立缓冲区追踪表,确保每个 buffer 都有对应的 result。
跨厂商 结构体对齐方式不同(如 32-bit vs 64-bit) 使用 offsetof 和 sizeof 进行编译时检查,避免硬编码偏移。
总结: 掌握这三个结构体的内存布局和生命周期,是编写健壮、可移植 Camera HAL 的基础。在适配过程中,始终以规范文档为准,不要依赖特定厂商的实现细节。