1. Camera Metadata 定义
元数据简单来说就关于数据的数据。它是数据的描述和上下文,有助于组织、查找和理解数据。 Camera3 中,为了实现 Per-frame controls 引入了 metadata 机制,使用 metadata 包涵相机控制参数、硬件参数、返回值等。
2. Android API
在Camera2 + HAL3的架构中,App -> Framework -> HAL 通过 metadata 的方式来传递参数。 Metadata主要是通过一个tag(key),去找对应的data。用于传递参数和配置信息,减少内存拷贝操作。
相关代码:
system/media/camera/include/system/camera_metadata.h system/media/camera/include/system/camera_metadata_tags.h system/media/camera/src/camera_metadata.c system/media/camera/src/camera_metadata_tag_info.c
2.1 基本结构体
camera_metadata_t 是 Android Metadata 的基本结构体,在 camera_metadata.c 中实现:
typedef struct camera_metadata camera_metadata_t;
typedef uint32_t metadata_uptrdiff_t;
typedef uint32_t metadata_size_t;
typedef uint64_t metadata_vendor_id_t;
struct camera_metadata {
metadata_size_t size;
uint32_t version;
uint32_t flags;
metadata_size_t entry_count;
metadata_size_t entry_capacity;
metadata_uptrdiff_t entries_start;
metadata_size_t data_count;
metadata_size_t data_capacity;
metadata_uptrdiff_t data_start;
uint32_t padding;
metadata_vendor_id_t vendor_id;
};
camera_metadata_buffer_entry_t 是 camera_metadata_t 中用于存放 tag 和 data 信息的键值对:
camera_metadata_buffer_entry
typedef struct camera_metadata_buffer_entry {
uint32_t tag;
uint32_t count;
union {
uint32_t offset;
uint8_t value[4];
} data;
uint8_t type;
uint8_t reserved[3];
} camera_metadata_buffer_entry_t;
camera_metadata.c 中,列出了 camera_metadata 的内存布局。 这是一块 camera_metadata.size 字节的连续内存:
|-----------------------------------------------|---------
| camera_metadata_t | 区域一:
| | 存储 camera_metadata_t 结构体定义
|-----------------------------------------------|---------
| reserved for future expansion | 区域二:
| | 保留区,供未来使用
|-----------------------------------------------|---------
| camera_metadata_buffer_entry_t #0 |
|-----------------------------------------------| 区域三:
| .... | 储存所有 Tag 结构体定义
|-----------------------------------------------| TAG[0]~TAG[entry_count-1]
| camera_metadata_buffer_entry_t #entry_count-1 |
|-----------------------------------------------|---------
| free space for | 区域四:
| (entry_capacity-entry_count) entries | 剩余未使用的 Tag 结构体的保留内存
|-----------------------------------------------|---------
| start of camera_metadata.data | 区域五:
| | Tag对应的具体 metadata 数据
|-----------------------------------------------|---------
| free space for | 区域六:
| (data_capacity-data_count) bytes | 剩余未使用的 Tag 占用的内存
|-----------------------------------------------|---------
下图更详细地显示了 camera_metadata 中的内存布局:
camera_metadata_entry_t 是对 camera_metadata_t 中某个 entry 的引用,用做 metada 相关方法返回值:
camera_metadata_entry_t
typedef struct camera_metadata_entry {
size_t index;
uint32_t tag;
uint8_t type;
size_t count;
union {
uint8_t *u8;
int32_t *i32;
float *f;
int64_t *i64;
double *d;
camera_metadata_rational_t *r;
} data;
} camera_metadata_entry_t;
camera_metadata_t 、camera_metadata_buffer_entry_t 和 camera_metadata_entry_t 关系如下:
2.2 增删改查
camera_metadata.h 中定义了 camera_metadata 相关的函数接口。以下是其中部分方法的介绍:
方法 | 用途 |
---|
camera_metadata_t* allocate_camera_metadata(size_t entry_capacity, size_t data_capacity) | metadata 内存分配 | void free_camera_metadata(camera_metadata_t *metadata) | 释放 metadata 对应内存 | int add_camera_metadata_entry(camera_metadata_t *dst, uint32_t tag, const void *data, size_t data_count) | 在 metadata 中增加 tag 和 value | int delete_camera_metadata_entry(camera_metadata_t *dst, size_t index) | 删除 tag | int update_camera_metadata_entry(camera_metadata_t *dst, size_t index, const void *data, size_t data_count, camera_metadata_entry_t *updated_entry) | 更新 tag 的 value 值 | int find_camera_metadata_entry(camera_metadata_t *src, uint32_t tag, camera_metadata_entry_t *entry) | 根据 tag 查找 value |
int add_camera_metadata_entry(camera_metadata_t *dst, uint32_t tag, const void *data, size_t data_count) {
int type = get_local_camera_metadata_tag_type(tag, dst);
if (type == -1) {
ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag);
return ERROR;
}
return add_camera_metadata_entry_raw(dst, tag, type, data, data_count);
}
static int add_camera_metadata_entry_raw(camera_metadata_t *dst, uint32_t tag, uint8_t type, const void *data, size_t data_count) {
if (dst == NULL) return ERROR;
if (dst->entry_count == dst->entry_capacity) return ERROR;
if (data_count && data == NULL) return ERROR;
size_t data_bytes = calculate_camera_metadata_entry_data_size(type, data_count);
if (data_bytes + dst->data_count > dst->data_capacity) return ERROR;
size_t data_payload_bytes = data_count * camera_metadata_type_size[type];
camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count;
memset(entry, 0, sizeof(camera_metadata_buffer_entry_t));
entry->tag = tag;
entry->type = type;
entry->count = data_count;
if (data_bytes == 0) {
memcpy(entry->data.value, data, data_payload_bytes);
} else {
entry->data.offset = dst->data_count;
memcpy(get_data(dst) + entry->data.offset, data,
data_payload_bytes);
dst->data_count += data_bytes;
}
dst->entry_count++;
dst->flags &= ~FLAG_SORTED;
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
size_t calculate_camera_metadata_entry_data_size(uint8_t type,
size_t data_count) {
if (type >= NUM_TYPES) return 0;
size_t data_bytes = data_count *
camera_metadata_type_size[type];
return data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT);
}
int delete_camera_metadata_entry(camera_metadata_t *dst, size_t index) {
if (dst == NULL) return ERROR;
if (index >= dst->entry_count) return ERROR;
camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type,
entry->count);
if (data_bytes > 0) {
uint8_t *start = get_data(dst) + entry->data.offset;
uint8_t *end = start + data_bytes;
size_t length = dst->data_count - entry->data.offset - data_bytes;
memmove(start, end, length);
camera_metadata_buffer_entry_t *e = get_entries(dst);
size_t i;
for (i = 0; i < dst->entry_count; i++) {
if (calculate_camera_metadata_entry_data_size(e->type, e->count) > 0
&& e->data.offset > entry->data.offset)
{
e->data.offset -= data_bytes;
}
++e;
}
dst->data_count -= data_bytes;
}
memmove(entry, entry + 1, sizeof(camera_metadata_buffer_entry_t) * (dst->entry_count - index - 1) );
dst->entry_count -= 1;
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
size_t calculate_camera_metadata_entry_data_size(uint8_t type,
size_t data_count) {
if (type >= NUM_TYPES) return 0;
size_t data_bytes = data_count *
camera_metadata_type_size[type];
return data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT);
}
int update_camera_metadata_entry(camera_metadata_t *dst,
size_t index,
const void *data,
size_t data_count,
camera_metadata_entry_t *updated_entry) {
if (dst == NULL) return ERROR;
if (index >= dst->entry_count) return ERROR;
camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type, data_count);
size_t data_payload_bytes = data_count * camera_metadata_type_size[entry->type];
size_t entry_bytes = calculate_camera_metadata_entry_data_size(entry->type, entry->count);
if (data_bytes != entry_bytes) {
if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {
return ERROR;
}
if (entry_bytes != 0) {
uint8_t *start = get_data(dst) + entry->data.offset;
uint8_t *end = start + entry_bytes;
size_t length = dst->data_count - entry->data.offset - entry_bytes;
memmove(start, end, length);
dst->data_count -= entry_bytes;
camera_metadata_buffer_entry_t *e = get_entries(dst);
size_t i;
for (i = 0; i < dst->entry_count; i++) {
if (calculate_camera_metadata_entry_data_size(
e->type, e->count) > 0 &&
e->data.offset > entry->data.offset) {
e->data.offset -= entry_bytes;
}
++e;
}
}
if (data_bytes != 0) {
entry->data.offset = dst->data_count;
memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
dst->data_count += data_bytes;
}
} else if (data_bytes != 0) {
memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
}
if (data_bytes == 0) {
memcpy(entry->data.value, data,
data_payload_bytes);
}
entry->count = data_count;
if (updated_entry != NULL) {
get_camera_metadata_entry(dst,
index,
updated_entry);
}
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
int find_camera_metadata_entry(camera_metadata_t *src, uint32_t tag, camera_metadata_entry_t *entry) {
if (src == NULL) return ERROR;
uint32_t index;
if (src->flags & FLAG_SORTED) {
camera_metadata_buffer_entry_t *search_entry = NULL;
camera_metadata_buffer_entry_t key;
key.tag = tag;
search_entry = bsearch(&key,
get_entries(src),
src->entry_count,
sizeof(camera_metadata_buffer_entry_t),
compare_entry_tags);
if (search_entry == NULL) return NOT_FOUND;
index = search_entry - get_entries(src);
} else {
camera_metadata_buffer_entry_t *search_entry = get_entries(src);
for (index = 0; index < src->entry_count; index++, search_entry++) {
if (search_entry->tag == tag) {
break;
}
}
if (index == src->entry_count) return NOT_FOUND;
}
return get_camera_metadata_entry(src, index,
entry);
}
int get_camera_metadata_entry(camera_metadata_t *src,
size_t index,
camera_metadata_entry_t *entry) {
if (src == NULL || entry == NULL) return ERROR;
if (index >= src->entry_count) return ERROR;
camera_metadata_buffer_entry_t *buffer_entry = get_entries(src) + index;
entry->index = index;
entry->tag = buffer_entry->tag;
entry->type = buffer_entry->type;
entry->count = buffer_entry->count;
if (buffer_entry->count *
camera_metadata_type_size[buffer_entry->type] > 4) {
entry->data.u8 = get_data(src) + buffer_entry->data.offset;
} else {
entry->data.u8 = buffer_entry->data.value;
}
return OK;
}
默认情况下,camera_metadata_t 中的 entry 不会被排序,也没有强制要求不同 entry 的 tag 不能相同。 在存在多个相同 tag 时,find 返回结果并不能保证是哪一个对应的 entry 内容。 如果 metadata 内容已排序,add 后会变为未排序状态。
3. Metadata Tag
tag index 实际上是一个 UINT32 的 key 值。其中高16位代表 section 部分,低 16 位代表 tag 部分。 camera_metadata_tags.h 中定义了 Android 原生 tag 的 section 枚举、各 section index 范围,以及 tag 枚举。
typedef enum camera_metadata_section {
ANDROID_COLOR_CORRECTION,
ANDROID_CONTROL,
ANDROID_DEMOSAIC,
ANDROID_EDGE,
ANDROID_FLASH,
ANDROID_FLASH_INFO,
ANDROID_HOT_PIXEL,
ANDROID_JPEG,
ANDROID_LENS,
ANDROID_LENS_INFO,
ANDROID_NOISE_REDUCTION,
ANDROID_QUIRKS,
ANDROID_REQUEST,
ANDROID_SCALER,
ANDROID_SENSOR,
ANDROID_SENSOR_INFO,
ANDROID_SHADING,
ANDROID_STATISTICS,
ANDROID_STATISTICS_INFO,
ANDROID_TONEMAP,
ANDROID_LED,
ANDROID_INFO,
ANDROID_BLACK_LEVEL,
ANDROID_SYNC,
ANDROID_REPROCESS,
ANDROID_DEPTH,
ANDROID_LOGICAL_MULTI_CAMERA,
ANDROID_DISTORTION_CORRECTION,
ANDROID_HEIC,
ANDROID_HEIC_INFO,
ANDROID_SECTION_COUNT,
VENDOR_SECTION = 0x8000
} camera_metadata_section_t;
当 tag_section 大于 0x8000 时,camera_metadata.h 中的相关方法,会跳转到 vendor 对应接口。 vendor tag 相关的操作定义在 camera_vendor_tags.h 中
get_local_camera_metadata_tag_name_vendor_id
const char *get_local_camera_metadata_tag_name_vendor_id(uint32_t tag,
metadata_vendor_id_t id) {
uint32_t tag_section = tag >> 16;
if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
id != CAMERA_METADATA_INVALID_VENDOR_ID) {
return vendor_cache_ops->get_tag_name(tag, id);
} else if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
return vendor_tag_ops->get_tag_name(
vendor_tag_ops,
tag);
}
if (tag_section >= ANDROID_SECTION_COUNT ||
tag >= camera_metadata_section_bounds[tag_section][1] ) {
return NULL;
}
uint32_t tag_index = tag & 0xFFFF;
return tag_info[tag_section][tag_index].tag_name;
}
为了方便使用,Android 中给 tag 定义了 char* 类型的名字。 在使用时,可以直接通过名字来获取 tag。
tag index 与 tag name 的对应关系可以在 camera_metadata_tag_info.c 中进行查看。 system/media/camera/docs/docs.html 中详细列举了所有 Android 原生 camera meta。
4. CameraMetadata 分类
Android CameraMetadata 分为 controls、dynamic 及 static 三类:
- Static: camera characteristics,用于描述相机 logic device 参数和对应功能,供 APP 查询使用;
- Controls: request settings,随每帧请求下发的控制信息,会绑定对应的 image callback;
- Dynamic: result metadata,将 HAL 层值回传给 APP。
CaptureRequest 中绑定的 Metadata 存放于 CaptureRequest.settings:
CaptureRequest
struct V3_4::CaptureRequest final {
V3_2::CaptureRequest v3_2;
hidl_vec<PhysicalCameraSetting> physicalCameraSettings;
};
struct V3_2::CaptureRequest final {
uint32_t frameNumber;
uint64_t fmqSettingsSize;
hidl_vec<uint8_t> settings;
StreamBuffer inputBuffer;
hidl_vec<StreamBuffer> outputBuffers;
};
CaptureResult 中绑定的 Metadata 存放于 CaptureResult.result:
CaptureResult
struct V3_4::CaptureResult final {
V3_2::CaptureResult v3_2;
hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata;
};
struct V3_2::CaptureResult final {
uint32_t frameNumber;
uint64_t fmqResultSize;
hidl_vec<uint8_t> result;
hidl_vec<StreamBuffer> outputBuffers;
StreamBuffer inputBuffer;
uint32_t partialResult;
};
android/hardware/camera/device/3.2/types.h
参考文章: Camera Metadata原理 Android Camera HAL3-metadata
|