文章目录
- 简介
- allocate_camera_metadata
- add_camera_metadata_entry
- delete_camera_metadata_entry
- update_camera_metadata_entry
- find_camera_metadata_entry
点赞收藏加关注,下次找我不迷路。
也欢迎关注微信公众号 无限无羡
期待与你的相识!
简介
初识camera metadata是不容易理解的,最起码笔者是这样。但是不理解又是不行的,所以只能硬着头皮去看源码,去归纳总结。简单来说,camera metadata就是一块用来存储camera相关参数的内存。比如拍照时的闪光灯,是打开还是关闭还是自动,这个参数就是存储在这块内存当中的。当然,camera的参数有很多,其类型有很多。camera metadata以一定的规则将这些信息全部存储起来,然后再用相同的规则取出。我们先给一张内存分布图,大家在看代码解析时,可以参考这张图来看。
allocate_camera_metadata
对着代码一行一行解读是费力不讨好的,既然是一块内存,我们不妨从内存的分配谈起。
// system/media/camera/include/system/camera_metadata.h文件中
/**
* Allocate a new camera_metadata structure, with some initial space for entries
* and extra data. The entry_capacity is measured in entry counts, and
* data_capacity in bytes. The resulting structure is all contiguous in memory,
* and can be freed with free_camera_metadata().
*/
ANDROID_API
camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
size_t data_capacity);
通过注释可以知道,这个函数是给entries和extra data分配一块连续的内存空间,而entries是以单位个来衡量的,其容量为entry_capacity个,而extra data是以字节来衡量的,其容量为data_capacity个字节。当我们不再使用这块内存时可以通过free_camera_metadata进行内存的释放。至于这里的enties和extra data具体指什么,我们先不关心。接下来我们去看下这个函数的实现。
// system/media/camera/src/camera_metadata.c
// 这里先关注一下,返回的是一个camera_metadata_t类型的地址
camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
size_t data_capacity) {
// 应该是通过这个调用计算出了需要分配内存的大小
size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
data_capacity);
// 调用calloc函数进行内存分配,分配1块大小为memory_needed的连续内存并且初始化为0
void *buffer = calloc(1, memory_needed);
// 这里是将分配的内存地址赋值给一个camera_metadata_t的指针,然后对其中的一些参数进行初始化
// 比如版本号,后面会详解
camera_metadata_t *metadata = place_camera_metadata(
buffer, memory_needed, entry_capacity, data_capacity);
if (!metadata) {
/* This should not happen when memory_needed is the same
* calculated in this function and in place_camera_metadata.
*/
free(buffer);
}
// 返回内存地址
return metadata;
}
请注意,上面分配内存的函数返回的是一个camera_metadata_t类型的指针,再继续分析代码之前,我们有必要先了解下这个类型。
// system/media/camera/include/system/camera_metadata.h
/**
* A packet of metadata. This is a list of metadata entries, each of which has
* an integer tag to identify its meaning, 'type' and 'count' field, and the
* data, which contains a 'count' number of entries of type 'type'. The packet
* has a fixed capacity for entries and for extra data. A new entry uses up one
* entry slot, and possibly some amount of data capacity; the function
* calculate_camera_metadata_entry_data_size() provides the amount of data
* capacity that would be used up by an entry.
*
* Entries are not sorted by default, and are not forced to be unique - multiple
* entries with the same tag are allowed. The packet will not dynamically resize
* when full.
*
* The packet is contiguous in memory, with size in bytes given by
* get_camera_metadata_size(). Therefore, it can be copied safely with memcpy()
* to a buffer of sufficient size. The copy_camera_metadata() function is
* intended for eliminating unused capacity in the destination packet.
*/
struct camera_metadata;
typedef struct camera_metadata camera_metadata_t;
这里的注释很多,但是却很重要,我们来翻译一下。
camera_metadata里面存储的是一个metadata entries的列表,每一个entry都包含了一个标识变量tag,不同entry的tag可以是相同的。entry还有其他成员比如type、count等。这块内存是有固定的容量的(分配内存时传入的两个参数决定了它的容量),当内存占满时不会自动扩容。这是一块连续的内存,大小可以通过get_camera_metadata_size来得到(其实就是上面计算出来的大小)。可以通过memcpy进行安全的拷贝操作,可以调用copy_camera_metadata来将内存中没有使用的内存大小消除,得到真正占用的内存的大小的结构体。
(上面的翻译大家姑且先看,或者英文好的理解能力强的应该比我翻译的要更好)
下面我们去看看这个结构体定义的地方。
这里相对就比较形象地展示出了这块内存的内容,我看网上其他网友总结这里的时候,我每次看完都还是没有理解透彻,可能是我的悟性比较差,希望这里我能够给大家讲清楚。
// system/media/camera/src/camera_metadata.c
/**
* A packet of metadata. This is a list of entries, each of which may point to
* its values stored at an offset in data.
*
* It is assumed by the utility functions that the memory layout of the packet
* is as follows:
*
* |-----------------------------------------------|
* | camera_metadata_t | // 这块内存开头是一个camera_metadata_t类型的结构体
* | |
* |-----------------------------------------------|
* | reserved for future expansion | // 接下来是一块预留的内存,用以未来给camera_metadata_t扩展使用
* |-----------------------------------------------| // 比如未来要往加一个字段,这里预留的就派上用场了
* | camera_metadata_buffer_entry_t #0 | // 接下来就开始保存entry的内容,类型是camera_metadata_buffer_entry_t
* |-----------------------------------------------|
* | .... | // 都是entry,
* |-----------------------------------------------|
* | camera_metadata_buffer_entry_t #entry_count-1 | // 最后一个entry
* |-----------------------------------------------|
* | free space for | // 这里也好理解,就是我分配的entry capacity跟实际的entry count的关系,
* | (entry_capacity-entry_count) entries | // count <= capacity,这里就是没用完的地方,单位是entry,比如还剩5个entry
* |-----------------------------------------------|
* | start of camera_metadata.data | // 这里开始存放data,这个data其实是属于entry的数据,当data的数据小于4字
* | | // 的时候会存在entry本身的内存里面,否则会根据其offset存在这块区域
* |-----------------------------------------------|
* | free space for | // 这里跟上上面entry一样的,data分配的空间没有使用完的地方
* | (data_capacity-data_count) bytes | // 单位是字节
* |-----------------------------------------------|
*
* With the total length of the whole packet being camera_metadata.size bytes.
*
* In short, the entries and data are contiguous in memory after the metadata
* header.
*/
#define METADATA_ALIGNMENT ((size_t) 4)
struct camera_metadata {
metadata_size_t size; // 分配的内存的大小
uint32_t version; // 版本号
uint32_t flags; // 标记当前是否有对entry进行排序,0:不排序, 1: 排序
metadata_size_t entry_count; // 这个count初始为0,每增加一个entry就加1
metadata_size_t entry_capacity; // 分配的固定的也是最大的entry的个数
metadata_uptrdiff_t entries_start; // 开始存放entry结构体的地方,这里表示的是相对于整块内存首地址的偏移量
metadata_size_t data_count; // 实际内存中的count,字节为单位,每有一个大于4字节的data,这里就递增
metadata_size_t data_capacity; // 分配的固定的也是最大的data的字节数
metadata_uptrdiff_t data_start; // 开始存储data的地址,这里表示的是相对于整块内存首地址的偏移量
uint32_t padding; // padding to 8 bytes boundary, 字节对齐,默认是8字节对齐
metadata_vendor_id_t vendor_id; // 厂商自己定义的tag开始的字段
};
通过上面可以看出,通过camera_metadata这个结构体是可以找到这块内存中的任意一个entry的信息的。后面的增删改查操作也是基于这个结构体来实现的。其实说了这么多,里面存储的是entry数据,那么这个entry的庐山真面目如何呢,我们来看下。
// system/media/camera/src/camera_metadata.c
// 需要注意,这个结构体定义是在.c文件中,说明对外是不可见的
/**
* A single metadata entry, storing an array of values of a given type. If the
* array is no larger than 4 bytes in size, it is stored in the data.value[]
* array; otherwise, it can found in the parent's data array at index
* data.offset.
*/
#define ENTRY_ALIGNMENT ((size_t) 4)
typedef struct camera_metadata_buffer_entry {
uint32_t tag; // 每个entry都有一个tag,可以理解为key
// 这个count到这里的话是不太好理解的,我举一个例子大家就很容易理解了。
// 假设我这个entry存储的数据是TYPE_INT32类型(占用4个字节),那么这个count就表示这个数据占用多少个TYPE_INT32(4字节)
// 也就是说这个entry的data占用的内存大小为count * sizeof(TYPE_INT32) = 32字节
uint32_t count; //
union {// 存放tag对应的value信息的,如果data小于等于4字节,则存放在data.value里,
// 否则存放在偏移地址为data_start+为offset的内存中
// 这里请大家暂停思考一下,为什么小于等于4字节存储在value里面,大于4字节存放在另外的地方呢?
// 因为这个data是一个共用体,对于共用体来说offset和value共用内存,而这里offset是uint32_t占用4字节,
// uint8_t value[4]也是占用4字节。所以小于等于4字节存储在value里,offset此时是不使用的。当大于4字节
// 时,value不使用,offset存储地址。这样做的目的是当数据小于等于4字节可以不用使用offset处的data区域的地址,
// 达到节省内存的目的。(希望我讲清楚了,不清楚的话大家多看几遍)
uint32_t offset;
uint8_t value[4];
} data;
uint8_t type; // data value的类型:TYPE_BYTE、TYPE_INT32、TYPE_FLOAT、TYPE_INT64
uint8_t reserved[3]; // 预留的内存供未来使用
} camera_metadata_buffer_entry_t;
entry的结构体介绍完了,其实还有一个共用体我们需要介绍下,它就是camera_metadata_data,当entry的value大于4字节时,其value就存储在这个共用体里面。这个比较简单,大家看一下其定义。
// system/media/camera/src/camera_metadata.c
/**
* A datum of metadata. This corresponds to camera_metadata_entry_t::data
* with the difference that each element is not a pointer. We need to have a
* non-pointer type description in order to figure out the largest alignment
* requirement for data (DATA_ALIGNMENT).
*/
#define DATA_ALIGNMENT ((size_t) 8)
typedef union camera_metadata_data {
uint8_t u8;
int32_t i32;
float f;
int64_t i64;
double d;
camera_metadata_rational_t r;
} camera_metadata_data_t;
好,到现在为止,几个重要的数据结构已经介绍完了,下面我们接着看下分配内存的实现,继续看是如何计算出所需内存的大小的
// system/media/camera/src/camera_metadata.c
size_t calculate_camera_metadata_size(size_t entry_count,
size_t data_count) {
// 先拿到camera_metadata_t的大小
// 我们前面说到,camera_metadata_t的后面有一块预留内存供未来拓展字段使用,那么
// 这里怎么没有看到预留内存是多大呢?其实大家想想就能明天,我如果在camera_metadata_t
// 中增加了几个字段的时候,这里的sizeof是不是也会跟着增加呢?
size_t memory_needed = sizeof(camera_metadata_t);
// Start entry list at aligned boundary
// 作4字节对齐
memory_needed = ALIGN_TO(memory_needed, ENTRY_ALIGNMENT);
// 再加上entry * entry_count的大小,注意这里entry_count实际是entry_capacity
memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]);
// Start buffer list at aligned boundary
// 作8字节对齐
memory_needed = ALIGN_TO(memory_needed, DATA_ALIGNMENT);
// 加上data_count, 因为data就是以字节为单位的,所以直接加data_count
// 同样,这里data_count是data_capacity
memory_needed += sizeof(uint8_t[data_count]);
// Make sure camera metadata can be stacked in continuous memory
// 8字节对齐
memory_needed = ALIGN_TO(memory_needed, METADATA_PACKET_ALIGNMENT);
return memory_needed;
}
在allocate_camera_metadata函数中,还有一个place_camera_metadata函数,我们看下其实现
// system/media/camera/src/camera_metadata.c
// dst: 使用calloc分配的内存地址
// dst_size: 上面计算出的memory_needed
camera_metadata_t *place_camera_metadata(void *dst,
size_t dst_size,
size_t entry_capacity,
size_t data_capacity) {
if (dst == NULL) return NULL;
// 又计算了一次内存大小,原因是拷贝函数copy_camera_metadata中也调用了place_camera_metadata
// 拷贝时,src size是不能大于dst size的,所以下面我们会看到有个比较
size_t memory_needed = calculate_camera_metadata_size(entry_capacity,
data_capacity);
// 这里的memory_needed详单与src size
if (memory_needed > dst_size) {
ALOGE("%s: Memory needed to place camera metadata (%zu) > dst size (%zu)", __FUNCTION__,
memory_needed, dst_size);
return NULL;
}
// 对camera_metadata_t进行初始化,将dst地址赋值给metadata地址,
// 这样metadata就是整个内存的首地址了,也就将camera_metadata_t
// 放到了内存的开始位置
camera_metadata_t *metadata = (camera_metadata_t*)dst;
metadata->version = CURRENT_METADATA_VERSION; // 版本号为1
metadata->flags = 0; // 0表示不对entry进行排序
metadata->entry_count = 0; // 初始大小都为0,data也一样,后面有插入时递增
metadata->entry_capacity = entry_capacity; // 最大容量entry
metadata->entries_start = // entry的起始地址,在camera_metadata_t后面做字节对齐后就是entry_start
ALIGN_TO(sizeof(camera_metadata_t), ENTRY_ALIGNMENT);
metadata->data_count = 0; // 初始value为0
metadata->data_capacity = data_capacity; // 最大value字节数
metadata->size = memory_needed; // 申请的内存大小
// entry_start + entry_capacity的大小
size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
metadata->entry_capacity) - (uint8_t*)metadata;
// entry_start + entry_capacity作8字节对齐后,就是data_start在metadata中的相对地址
metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
// vendor_id有api可以设置,我们放到下个章节详细讲解vendor metadata
metadata->vendor_id = CAMERA_METADATA_INVALID_VENDOR_ID;
assert(validate_camera_metadata_structure(metadata, NULL) == OK);
return metadata;
// 这个函数其实就是初始化了分配的内存中的头部部分,也就是camera_metadata_t所占用的内存,
// 剩余未分配的内存里面现在全是0
}
add_camera_metadata_entry
我们常说增删改查,下面我们就先看下如何“增”。
// system/media/camera/include/system/camera_metadata.h
/**
* Add a metadata entry to a metadata structure. Returns 0 if the addition
* succeeded. Returns a non-zero value if there is insufficient reserved space
* left to add the entry, or if the tag is unknown. data_count is the number of
* entries in the data array of the tag's type, not a count of
* bytes. Vendor-defined tags can not be added using this method, unless
* set_vendor_tag_query_ops() has been called first. Entries are always added to
* the end of the structure (highest index), so after addition, a
* previously-sorted array will be marked as unsorted.
*
* Returns 0 on success. A non-0 value is returned on error.
*/
// 返回0表示成功
// dst: 就是我们前面分配的metadata指针
// tag: 要插入的entry的tag
// data: 要插入的数据buffer
// data_count: 指的是需要的类型数据在上面的data中占用的个数
// 比如,entry的type是TYPE_INT32,那么要写入的data大小为
// data_count*sizeof(int32_t)个字节。我们看下面的函数的具体
// 实现的时候可以更好的理解。
ANDROID_API
int add_camera_metadata_entry(camera_metadata_t *dst,
uint32_t tag,
const void *data,
size_t data_count);
下面我们看下增加一个entry的具体实现
// system/media/camera/src/camera_metadata.c
int add_camera_metadata_entry(camera_metadata_t *dst,
uint32_t tag,
const void *data,
size_t data_count) {
// 很简单,根据tag拿到其存储的value的type,也就是这个type其实是一开始就定义好的
int type = get_local_camera_metadata_tag_type(tag, dst);
if (type == -1) {
ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag);
return ERROR;
}
// 拿到type之后再继续调用这个函数,其中多了一个type参数
return add_camera_metadata_entry_raw(dst,
tag,
type,
data,
data_count);
}
能够根据tag拿到type,说明一开始有些东西是初始化好的,或者说有地方去定义好的,到这里我们有必要再多了解一下这里的tag了。
tag分为系统原生的tag和厂商自己定义的tag(vendor tag),tag是分组的,每一组tag表示同一功能的不同属性,每一个分组我们又叫一个section。
// system/media/camera/include/system/camera_metadata_tags.h
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_AUTOMOTIVE,
ANDROID_AUTOMOTIVE_LENS,
ANDROID_SECTION_COUNT,
// 厂商自定义section从这里开始
VENDOR_SECTION = 0x8000
} camera_metadata_section_t;
为了保证每一个section能容纳足够的tag,系统给每个section预留了64K的空间,从前面我们可以知道tag的类型是uint32_t,也就是32位4字节。
// system/media/camera/include/system/camera_metadata_tags.h
/**
* Hierarchy positions in enum space. All vendor extension tags must be
* defined with tag >= VENDOR_SECTION_START
*/
typedef enum camera_metadata_section_start {
ANDROID_COLOR_CORRECTION_START = ANDROID_COLOR_CORRECTION << 16,
ANDROID_CONTROL_START = ANDROID_CONTROL << 16,
ANDROID_DEMOSAIC_START = ANDROID_DEMOSAIC << 16,
ANDROID_EDGE_START = ANDROID_EDGE << 16,
ANDROID_FLASH_START = ANDROID_FLASH << 16,
ANDROID_FLASH_INFO_START = ANDROID_FLASH_INFO << 16,
ANDROID_HOT_PIXEL_START = ANDROID_HOT_PIXEL << 16,
ANDROID_JPEG_START = ANDROID_JPEG << 16,
ANDROID_LENS_START = ANDROID_LENS << 16,
ANDROID_LENS_INFO_START = ANDROID_LENS_INFO << 16,
ANDROID_NOISE_REDUCTION_START = ANDROID_NOISE_REDUCTION << 16,
ANDROID_QUIRKS_START = ANDROID_QUIRKS << 16,
ANDROID_REQUEST_START = ANDROID_REQUEST << 16,
ANDROID_SCALER_START = ANDROID_SCALER << 16,
ANDROID_SENSOR_START = ANDROID_SENSOR << 16,
ANDROID_SENSOR_INFO_START = ANDROID_SENSOR_INFO << 16,
ANDROID_SHADING_START = ANDROID_SHADING << 16,
ANDROID_STATISTICS_START = ANDROID_STATISTICS << 16,
ANDROID_STATISTICS_INFO_START = ANDROID_STATISTICS_INFO << 16,
ANDROID_TONEMAP_START = ANDROID_TONEMAP << 16,
ANDROID_LED_START = ANDROID_LED << 16,
ANDROID_INFO_START = ANDROID_INFO << 16,
ANDROID_BLACK_LEVEL_START = ANDROID_BLACK_LEVEL << 16,
ANDROID_SYNC_START = ANDROID_SYNC << 16,
ANDROID_REPROCESS_START = ANDROID_REPROCESS << 16,
ANDROID_DEPTH_START = ANDROID_DEPTH << 16,
ANDROID_LOGICAL_MULTI_CAMERA_START
= ANDROID_LOGICAL_MULTI_CAMERA
<< 16,
ANDROID_DISTORTION_CORRECTION_START
= ANDROID_DISTORTION_CORRECTION
<< 16,
ANDROID_HEIC_START = ANDROID_HEIC << 16,
ANDROID_HEIC_INFO_START = ANDROID_HEIC_INFO << 16,
ANDROID_AUTOMOTIVE_START = ANDROID_AUTOMOTIVE << 16,
ANDROID_AUTOMOTIVE_LENS_START = ANDROID_AUTOMOTIVE_LENS << 16,
VENDOR_SECTION_START = VENDOR_SECTION << 16
} camera_metadata_section_start_t;
如上,已知每个tag的类型是4字节32位,那么左移16位后说明其高16位已经是确定的了,那么其一共能容纳的tag的数量就是低16位来决定了,一共就是2的16次方64K,也就是每个section的tag数最大为64K,本世纪应该够用了。当然,每个section不可能64k全定义完,下面我们看一下系统定义好的section。我们看到,每个section都有start和end,当我们要给一个section新加一个tag的时候必须添加在其end的前面。
typedef enum camera_metadata_tag {
ANDROID_COLOR_CORRECTION_MODE = // enum | public | HIDL v3.2
ANDROID_COLOR_CORRECTION_START,
ANDROID_COLOR_CORRECTION_TRANSFORM, // rational[] | public | HIDL v3.2
ANDROID_COLOR_CORRECTION_GAINS, // float[] | public | HIDL v3.2
ANDROID_COLOR_CORRECTION_ABERRATION_MODE, // enum | public | HIDL v3.2
ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES,
// byte[] | public | HIDL v3.2
ANDROID_COLOR_CORRECTION_END,
ANDROID_CONTROL_AE_ANTIBANDING_MODE = // enum | public | HIDL v3.2
ANDROID_CONTROL_START,
ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, // int32 | public | HIDL v3.2
ANDROID_CONTROL_AE_LOCK, // enum | public | HIDL v3.2
ANDROID_CONTROL_AE_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AE_REGIONS, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_AE_TARGET_FPS_RANGE, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, // enum | public | HIDL v3.2
ANDROID_CONTROL_AF_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AF_REGIONS, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_AF_TRIGGER, // enum | public | HIDL v3.2
ANDROID_CONTROL_AWB_LOCK, // enum | public | HIDL v3.2
ANDROID_CONTROL_AWB_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AWB_REGIONS, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_CAPTURE_INTENT, // enum | public | HIDL v3.2
ANDROID_CONTROL_EFFECT_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_SCENE_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_AE_AVAILABLE_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_AE_COMPENSATION_RANGE, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_AE_COMPENSATION_STEP, // rational | public | HIDL v3.2
ANDROID_CONTROL_AF_AVAILABLE_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_AVAILABLE_EFFECTS, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_AVAILABLE_SCENE_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES,
// byte[] | public | HIDL v3.2
ANDROID_CONTROL_AWB_AVAILABLE_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_MAX_REGIONS, // int32[] | ndk_public | HIDL v3.2
ANDROID_CONTROL_SCENE_MODE_OVERRIDES, // byte[] | system | HIDL v3.2
ANDROID_CONTROL_AE_PRECAPTURE_ID, // int32 | system | HIDL v3.2
ANDROID_CONTROL_AE_STATE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AF_STATE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AF_TRIGGER_ID, // int32 | system | HIDL v3.2
ANDROID_CONTROL_AWB_STATE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS,
// int32[] | hidden | HIDL v3.2
ANDROID_CONTROL_AE_LOCK_AVAILABLE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AWB_LOCK_AVAILABLE, // enum | public | HIDL v3.2
ANDROID_CONTROL_AVAILABLE_MODES, // byte[] | public | HIDL v3.2
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE, // int32[] | public | HIDL v3.2
ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST, // int32 | public | HIDL v3.2
ANDROID_CONTROL_ENABLE_ZSL, // enum | public | HIDL v3.2
ANDROID_CONTROL_AF_SCENE_CHANGE, // enum | public | HIDL v3.3
ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_MAX_SIZES,
// int32[] | ndk_public | HIDL v3.5
ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_ZOOM_RATIO_RANGES,
// float[] | ndk_public | HIDL v3.5
ANDROID_CONTROL_EXTENDED_SCENE_MODE, // enum | public | HIDL v3.5
ANDROID_CONTROL_ZOOM_RATIO_RANGE, // float[] | public | HIDL v3.5
ANDROID_CONTROL_ZOOM_RATIO, // float | public | HIDL v3.5
ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION,
// int32[] | hidden | HIDL v3.6
ANDROID_CONTROL_AF_REGIONS_SET, // enum | fwk_only
ANDROID_CONTROL_AE_REGIONS_SET, // enum | fwk_only
ANDROID_CONTROL_AWB_REGIONS_SET, // enum | fwk_only
ANDROID_CONTROL_END,
// 此处省略很多tag
......
} camera_metadata_tag_t;
每个section中有很多tag,而每个tag所存储的数据类型却不一定是相同的,所以我们必须指定,同时,每个tag都有自己的name,这些信息存储在一个tag_info_t的结构体中。
// system/media/camera/src/camera_metadata.c
typedef struct tag_info {
const char *tag_name;
uint8_t tag_type;
} tag_info_t;
其name和type的初始化如下(我们以ANDROID_COLOR_CORRECTION为例)
// system/media/camera/src/camera_metadata_tag_info.c
// 结构体的大小为end-start,每个tag的下标就是tag-start
static tag_info_t android_color_correction[ANDROID_COLOR_CORRECTION_END -
ANDROID_COLOR_CORRECTION_START] = {
// ANDROID_COLOR_CORRECTION_MODE的name是“mode”, 其类型是TYPE_BYTE
// 下标为0的元素
[ ANDROID_COLOR_CORRECTION_MODE - ANDROID_COLOR_CORRECTION_START ] =
{ "mode", TYPE_BYTE },
// ANDROID_COLOR_CORRECTION_TRANSFORM的name是“transform”,类型是TYPE_RATIONAL
// 下标为1的元素
[ ANDROID_COLOR_CORRECTION_TRANSFORM - ANDROID_COLOR_CORRECTION_START ] =
{ "transform", TYPE_RATIONAL },
// 下标为2的元素
[ ANDROID_COLOR_CORRECTION_GAINS - ANDROID_COLOR_CORRECTION_START ] =
{ "gains", TYPE_FLOAT },
// 下标为3的元素
[ ANDROID_COLOR_CORRECTION_ABERRATION_MODE - ANDROID_COLOR_CORRECTION_START ] =
{ "aberrationMode", TYPE_BYTE },
// 下标为4的元素
[ ANDROID_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES - ANDROID_COLOR_CORRECTION_START ] =
{ "availableAberrationModes", TYPE_BYTE },
};
// android_color_correction会被放到一个tag_info_t的二维数组中,如下:
// 这里的顺序跟section定义的顺序必须一致,这样就可以从tag_info中以section为下标
// 找到其信息
tag_info_t *tag_info[ANDROID_SECTION_COUNT] = {
android_color_correction,
......
};
// 这样就可以在tag_info中根据根据
到这里,我们把tag和section相关的介绍完了,那么我们继续分析(为了使大家不用在去前面找,这里再贴一下前面分析到的地方)
// system/media/camera/src/camera_metadata.c
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;
}
// 拿到type之后再继续调用这个函数,其中多了一个type参数
return add_camera_metadata_entry_raw(dst,
tag,
type,
data,
data_count);
}
// 看下get_local_camera_metadata_tag_type的实现
// system/media/camera/src/camera_metadata.c
int get_local_camera_metadata_tag_type(uint32_t tag,
const camera_metadata_t *meta) {
// meta不为NULL,所以id为meta->vendor_id的值
metadata_vendor_id_t id = (NULL == meta) ? CAMERA_METADATA_INVALID_VENDOR_ID :
meta->vendor_id;
return get_local_camera_metadata_tag_type_vendor_id(tag, id);
}
// system/media/camera/src/camera_metadata.c
// 这里假设我们添加的是系统section,非vendor_section
int get_local_camera_metadata_tag_type_vendor_id(uint32_t tag,
metadata_vendor_id_t id) {
// tag右移16位得到的是tag所在的section
uint32_t tag_section = tag >> 16;
// 下面列出了两种VENDOR_SECTION的场景,这里需要对vendor_cache_ops或者
// vendor_tag_ops进行初始化,这两个是全局变量,我们下章节详解VENDOR_SECTION。
//VENDOR_SECTION且设置了vendor_id的话从vendor_cache_ops查找type
if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
id != CAMERA_METADATA_INVALID_VENDOR_ID) {
return vendor_cache_ops->get_tag_type(tag, id);
// 如果是VENDOR_SECTION且vendor_tag_ops不为空,就从vendor_tag_ops里面查找type
} else if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
return vendor_tag_ops->get_tag_type(
vendor_tag_ops,
tag);
}
// 超出了section范围
if (tag_section >= ANDROID_SECTION_COUNT ||
tag >= camera_metadata_section_bounds[tag_section][1] ) {
return -1;
}
// 这里注意,0xFFFF的高16位全为0,低16位全为1,进行按位与运算后,
// 就得到了低16位的值,也就是每个tag相对于其section_start的偏移
uint32_t tag_index = tag & 0xFFFF;
// tag_section的第tag_index个tag
return tag_info[tag_section][tag_index].tag_type;
}
根据tag拿到其对应的存储数据的类型的方法就分析完了,我们继续往下分析增加entry的实现
// system/media/camera/src/camera_metadata.c
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;
// data为空的话也返回,没必要往下加了
if (data_count && data == NULL) return ERROR;
// 拿到需要存储的value所占用的字节数
size_t data_bytes =
calculate_camera_metadata_entry_data_size(type, data_count);
// calculate_camera_metadata_entry_data_size start
size_t calculate_camera_metadata_entry_data_size(uint8_t type,
size_t data_count) {
// 不再定义的类型范围内,也就是不支持此类型
if (type >= NUM_TYPES) return 0;
// data_count乘以type占用的字节数就是整个value占用的字节数
size_t data_bytes = data_count *
camera_metadata_type_size[type];
// value所需字节数小于4返回0,到时候存储在data.value里面,否则按8字节对齐返回,
// 保存在偏移量为offset的data区,
return data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT);
}
// calculate_camera_metadata_entry_data_size end
// data占用的空间超出了分配的最大空间,返回错误
if (data_bytes + dst->data_count > dst->data_capacity) return ERROR;
// value真正占用的字节数
size_t data_payload_bytes =
data_count * camera_metadata_type_size[type];
// 新的entry的地址
camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count;
// 初始化为0
memset(entry, 0, sizeof(camera_metadata_buffer_entry_t));
// 赋值
entry->tag = tag;
entry->type = type;
entry->count = data_count;
if (data_bytes == 0) {
// value小于等于4字节,存储到data.value
memcpy(entry->data.value, data,
data_payload_bytes);
} else {
// value大于4字节,在data区末尾添加value
entry->data.offset = dst->data_count;
memcpy(get_data(dst) + entry->data.offset, data,
data_payload_bytes);
// 更新data_count,这里的data_bytes大于等于data_payload_bytes,
// 因为做了8字节对齐
dst->data_count += data_bytes;
}
// entry_count递增
dst->entry_count++;
// 不排序
dst->flags &= ~FLAG_SORTED;
// 对齐检查,整个内存必须保证METADATA_ALIGNMENT,ENTRY_ALIGNMENT,
// DATA_ALIGNMENT都能对齐,否则抛出异常。
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
delete_camera_metadata_entry
接着我们来谈谈增删改查中的“删”。
// system/media/camera/include/system/camera_metadata.h
/**
* Delete an entry at given index. This is an expensive operation, since it
* requires repacking entries and possibly entry data. This also invalidates any
* existing camera_metadata_entry.data pointers to this buffer. Sorting is
* maintained.
*/
ANDROID_API
int delete_camera_metadata_entry(camera_metadata_t *dst,
size_t index);
从注释中我们可以看出,整个操作是消耗资源比较高的,跟我们正常的数据删除原理类似,你要删除最后一个还好说,如果说要删除中间的某个元素,那么整个内存的后半部分都得前移,所以比较消耗资源。
我们看下其实现
// system/media/camera/src/camera_metadata.c
// 参数index表示要查询的entry是第几个,也就是从entry_start开始
// 的第几个entry
int delete_camera_metadata_entry(camera_metadata_t *dst,
size_t index) {
// 参数检查
if (dst == NULL) return ERROR;
if (index >= dst->entry_count) return ERROR;
// 得到要删除的entry地址
camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
// 拿到该entry中的value占用的字节数,这个函数我们前面已经分析过
size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type,
entry->count);
// 大于0,说明字节数是大于4的,存储在data区段
if (data_bytes > 0) {
// Shift data buffer to overwrite deleted data
// 拿到data的地址
uint8_t *start = get_data(dst) + entry->data.offset;
// data结束的地址
uint8_t *end = start + data_bytes;
// length为data区段中去除要删除的entry data后剩余的data value占用的字节数
size_t length = dst->data_count - entry->data.offset - data_bytes;
// 剩余的data区段向上移动到要删除的entry的offset处,也就是将要删除的entry data
// 进行了覆盖
memmove(start, end, length);
// Update all entry indices to account for shift
// 很显然的是,data进行了移动,那么对应的entry中的offset字段也得更新。
// 将每一个在要删除的entry后面的entry的data.offset前移data_bytes
// 个字节(其data.value大于4字节的,小于4字节的不用处理的,因为不在
// data区段存储value)
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;
}
// 整个的data_count减去data_bytes
dst->data_count -= data_bytes;
}
// Shift entry array
// 将存储entry的区域,把要删除的entry的后面的entry前移。
// 如果存储的数据小于等于4字节的话直接执行这里一段就OK了,
// 那就是只移动entry,因为data区没有存储数据
memmove(entry, entry + 1,
sizeof(camera_metadata_buffer_entry_t) *
(dst->entry_count - index - 1) );
// entry_cunt减1
dst->entry_count -= 1;
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
删除的代码理解起来比较简单,就是其中的内存数据的移动比较消耗性能。
update_camera_metadata_entry
接着我们来谈谈增删改查中的“改”。
// system/media/camera/include/system/camera_metadata.h
// 如果data的大小不变,其算法复杂度为O(1),否则为O(N)
/**
* Updates a metadata entry with new data. If the data size is changing, may
* need to adjust the data array, making this an O(N) operation. If the data
* size is the same or still fits in the entry space, this is O(1). Maintains
* sorting, but invalidates camera_metadata_entry instances that point to the
* updated entry. If a non-NULL value is passed in to entry, the entry structure
* is updated to match the new buffer state. Returns a non-zero value if there
* is no room for the new data in the buffer.
*/
ANDROID_API
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);
看下其实现
// system/media/camera/src/camera_metadata.c
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;
// 拿到要更新的entry的地址
camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
// 计算出要更新的data的大小,小于等于4返回0,否则内存对齐后返回
size_t data_bytes =
calculate_camera_metadata_entry_data_size(entry->type,
data_count);
// 要更新的data的大小的实际大小
size_t data_payload_bytes =
data_count * camera_metadata_type_size[entry->type];
// 目前entry的data占用的大小
size_t entry_bytes =
calculate_camera_metadata_entry_data_size(entry->type,
entry->count);
// 更新前后大小不一致
if (data_bytes != entry_bytes) {
// May need to shift/add to data array
// 大了,超出了capacity,返回错误
if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {
// No room
return ERROR;
}
// 大于4字节的情况
if (entry_bytes != 0) {
// Remove old data
// 这个代码熟悉吗,跟删除操作了删除data的地方一样的
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;
// Update all entry indices to account for shift
// offset更新
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) {
// Append new data
// 更add entry里的代码又一样了
// 所以就是进行了一次先删除,后插入的操作
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) {
// data size unchanged, reuse same data location
// 如果data大小不变,只更新内存的数据既可以了
memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
}
if (data_bytes == 0) {
// Data fits into entry
// 如果小于等于4字节,则更新data.value字段即可
memcpy(entry->data.value, data,
data_payload_bytes);
}
// 更新count
entry->count = data_count;
// 这里的updated_entry是一个出参
// 不为空的话返回更新后的entry信息
if (updated_entry != NULL) {
get_camera_metadata_entry(dst,
index,
updated_entry);
}
assert(validate_camera_metadata_structure(dst, NULL) == OK);
return OK;
}
find_camera_metadata_entry
接着我们来谈谈增删改查中的“查”。
// system/media/camera/include/system/camera_metadata.h
// 1. 建议先排序,后查询
// 2. 如果有多个相同的tag存在,则返回哪一个是不确定的
// 3. 返回类型,也就是出参camera_metadata_entry_t跟
// camera_metadata_buffer_entry_t是有区别的
/**
* Find an entry with given tag value. If not found, returns -ENOENT. Otherwise,
* returns entry contents like get_camera_metadata_entry.
*
* If multiple entries with the same tag exist, does not have any guarantees on
* which is returned. To speed up searching for tags, sort the metadata
* structure first by calling sort_camera_metadata().
*/
ANDROID_API
int find_camera_metadata_entry(camera_metadata_t *src,
uint32_t tag,
camera_metadata_entry_t *entry);
看下其实现
// system/media/camera/src/camera_metadata.c
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;
// 如果已经排序了,则做二分查找,
// 有多个相同tag存在的话,返回哪一个就不确定了
if (src->flags & FLAG_SORTED) {
// Sorted entries, do a binary search
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 {
// Not sorted, linear search
// 没有排序,线性查找,这样返回的就是找到的第一个
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;
}
// 返回,这个entry是出参,这里看下这个实现吧,比较简单
// get_camera_metadata_entry start
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;
// 拿到index对应的entry
camera_metadata_buffer_entry_t *buffer_entry = get_entries(src) + index;
// 给camera_metadata_entry_t类型的结构体赋值
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;
}
// get_camera_metadata_entry end
return get_camera_metadata_entry(src, index,
entry);
}
以上,增删改查的基本操作就介绍完毕。其实还有很多的函数我们没有讲解,但是如果掌握了上面4个基本操作的话,其他的实现也就很容易理解了。本文中没有介绍vendor_section的地方,是为了先掌握camera metadata的基本原理,后面理解vendor section也就容易了,关于vendor section还是比较重要的,后面会再开一个章节单独介绍。
本节完,谢谢!