3. Metadata数据结构详解:camera_metadata_t结构体、entry与buffer布局、内存对齐与序列化
Camera HAL层与Framework之间传递的Metadata并非简单的键值对,而是一种经过精心设计的、紧凑的二进制数据结构。理解其底层布局是进行高效读写、调试和序列化的基础。本章将深入剖析camera_metadata_t结构体、camera_metadata_entry_t的访问方式、Buffer的内存布局与对齐规则,以及序列化/反序列化的核心机制。
3.1 camera_metadata_t 结构体总览
camera_metadata_t是Android Camera子系统中最核心的数据结构,它定义在system/media/camera/include/system/camera_metadata.h中。该结构体采用“头部+数据区”的连续内存布局设计,旨在最小化内存碎片并支持高效的跨进程传输(通过binder或共享内存)。
其简化后的结构体定义如下:
typedef struct camera_metadata {
/** 元数据版本号,用于兼容性检查 */
uint32_t version;
/** 当前已使用的entry数量 */
uint32_t entry_count;
/** 当前已使用的data槽位数量(每个槽位4字节) */
uint32_t data_count;
/** 当前已分配的entry容量 */
uint32_t entry_capacity;
/** 当前已分配的data槽位总容量 */
uint32_t data_capacity;
/** 保留字段,用于未来扩展 */
uint32_t padding;
/** 指向entry数组的偏移量(相对于结构体起始地址) */
uint32_t entry_off;
/** 指向data数组的偏移量(相对于结构体起始地址) */
uint32_t data_off;
/** 存储实际entry和data的连续缓冲区起始地址 */
uint8_t buffer[0];
} camera_metadata_t;
该结构体通过buffer[0](零长度数组)实现了灵活的内存布局。整个内存块分为三个区域:
| 区域 | 内容 | 说明 |
|---|---|---|
| 头部 | camera_metadata_t 固定字段 |
包含版本、计数、容量和偏移量信息,用于快速导航。 |
| Entry数组 | camera_metadata_entry_t 数组 |
每个entry描述一个tag的元信息(类型、计数、数据索引)。 |
| Data池 | 原始数据缓冲区 | 以4字节为单位对齐存储所有tag的实际值。 |
3.2 Entry与Data的详细布局
每个camera_metadata_entry_t并不直接存储数据值,而是存储指向Data池的索引。这种间接访问方式使得数据可以紧凑排列,并支持复杂类型(如数组、矩形)。
3.2.1 camera_metadata_entry_t 结构
typedef struct camera_metadata_entry {
/** 标签ID,如 ANDROID_SENSOR_EXPOSURE_TIME */
uint32_t tag;
/** 数据类型,如 TYPE_BYTE, TYPE_INT32, TYPE_FLOAT, TYPE_INT64, TYPE_DOUBLE, TYPE_RATIONAL */
uint8_t type;
/** 数据元素个数(非字节数) */
uint32_t count;
/** 指向实际数据的指针(由库函数计算得出,非序列化字段) */
union {
void *data;
uint8_t *u8;
int32_t *i32;
float *f;
int64_t *i64;
double *d;
camera_metadata_rational_t *r;
};
/** 数据在Data池中的起始索引(以4字节为单位) */
size_t data_index;
} camera_metadata_entry_t;
data_index是Data池中的槽位索引,而非字节偏移。例如,一个TYPE_INT64类型的数据占用2个槽位(8字节)。data指针是在调用find_camera_metadata_entry()时由库函数根据data_index和type动态计算出来的,方便开发者直接访问。
3.2.2 Data池的内存对齐规则
Data池以4字节(一个槽位)为基本单位进行管理,但内部数据必须满足其自然对齐要求。这是保证跨平台兼容性和访问效率的关键。
| 数据类型 | 类型标识 | 单个元素大小 | 对齐要求 | 占用槽位数 |
|---|---|---|---|---|
| BYTE | 0 | 1 字节 | 1 字节 | 1 (4字节对齐) |
| INT32 | 1 | 4 字节 | 4 字节 | 1 |
| FLOAT | 2 | 4 字节 | 4 字节 | 1 |
| INT64 | 3 | 8 字节 | 8 字节 | 2 |
| DOUBLE | 4 | 8 字节 | 8 字节 | 2 |
| RATIONAL | 5 | 8 字节 (int32_t numerator + int32_t denominator) | 4 字节 | 2 |
TYPE_INT32数据后紧跟一个TYPE_INT64数据,需要在INT32后填充4个字节,使INT64从8字节对齐的地址开始。开发者手动构造Metadata时,必须使用camera_metadata_* API,这些API会自动处理对齐,切勿直接操作内存。
3.3 序列化与反序列化机制
序列化是将camera_metadata_t转换为可传输的字节流的过程,反序列化则是其逆过程。由于camera_metadata_t本身就是连续内存块,序列化变得非常直接。
3.3.1 序列化过程
- 计算总大小: 调用
get_camera_metadata_size()获取整个结构体占用的字节数。 - 分配缓冲区: 分配一个大小为总字节数的
uint8_t缓冲区。 - 内存拷贝: 使用
memcpy将camera_metadata_t结构体及其后的Entry数组和Data池完整拷贝到新缓冲区。 - 传输: 将序列化后的字节流通过Binder或共享内存发送给Framework。
// 序列化示例
camera_metadata_t *metadata = ...; // 假设已填充好数据
size_t size = get_camera_metadata_size(metadata);
uint8_t *serialized_buffer = new uint8_t[size];
memcpy(serialized_buffer, metadata, size);
// 现在 serialized_buffer 可以用于传输
// 接收方可以通过 reinterpret_cast<camera_metadata_t*>(serialized_buffer) 直接访问
3.3.2 反序列化过程
- 接收字节流: 从传输层获取序列化后的字节流。
- 类型转换: 将字节流指针强制转换为
camera_metadata_t*。 - 有效性检查: 验证
version、entry_count、data_count等字段的合理性,防止恶意数据或损坏。 - 克隆(可选): 如果需要修改,必须调用
clone_camera_metadata()创建一个可写的副本,因为原始缓冲区可能是只读的。
camera_metadata_t的连续内存布局,序列化/反序列化仅需一次memcpy,无需遍历每个entry进行逐个编码。这使得它在高帧率场景下(如预览、录像)具有极低的延迟。
3.4 调试技巧:使用dumpsys与日志
在开发过程中,经常需要检查Metadata的内容是否正确。Android提供了多种调试手段:
- dumpsys media.camera: 可以打印出当前Camera设备的所有静态、动态和请求Metadata。这是最常用的调试方法。
- camera_metadata_dump(): 在HAL层代码中直接调用此函数,可以将
camera_metadata_t的内容格式化输出到logcat,方便在运行时跟踪。 - camera_metadata_usage(): 打印当前Metadata的内存使用情况,包括entry和data的容量与使用量,有助于发现内存泄漏或过度分配问题。
// 在HAL代码中调试Metadata
void dump_metadata(const camera_metadata_t *metadata) {
ALOGD("=== Metadata Dump ===");
camera_metadata_dump(metadata, 0); // 0表示不缩进
ALOGD("=== Metadata Usage ===");
camera_metadata_usage(metadata);
}
camera_metadata_usage()检查entry_count和data_count是否在预期范围内。其次,使用camera_metadata_dump()对比HAL端和Framework端的Metadata内容,定位不一致的tag。
理解camera_metadata_t的底层数据结构,是编写高效、稳定Camera HAL的基础。掌握其内存布局、对齐规则和序列化机制,能够帮助开发者避免常见的踩内存、对齐错误和性能瓶颈问题。