Android Camera之CameraMetadata分析
来源:互联网 发布:持有期收益率知乎 编辑:程序博客网 时间:2024/06/02 04:49
一、camera_metadata简介
Camera_metadata数据结构在Camera流程中起到了很大重要,可以说所有的自顶层下发给hal层的参数都是通过camera_metadata传递的。今天我们就来好好看看它到底如何保存的,以及它的数据组织形式如何表现。和Camera_metadata数据结构相关的主要有以下几个文件:
system/media/camera/include/system/Camera_metadata_tags.h
system/media/camera/src/Camera_metadata_tag_info.c
system/media/camera/src/Camera_metadata.c
system/media/camera/include/system/Camera_metadata.h
Framework/av/camera/CameraMetadata.cpp
Framework/av/include/camera/CameraMetadata.h
这些文件的调用和包含关系如下图所示。
/** * Top level hierarchy definitions for camera metadata. *_INFO sections are for * the static metadata that can be retrived without opening the camera device. * New sections must be added right before ANDROID_SECTION_COUNT to maintain * existing enumerations. */typedef enum camera_metadata_section { ANDROID_COLOR_CORRECTION, ANDROID_CONTROL, ANDROID_DEMOSAIC, ANDROID_EDGE, .............}<pre name="code" class="cpp">/** * 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, .............}
二、camera_metadata 内存结构
在camera_metadata.c代码中有下面一副图,这里就是camera_metadata的内存分布。camera_metadata数据结构是一块连续的内存空间。在内存开始处放置的是一个struct camera_metadata的对象,这里面记录了该数据块包含的基本信息,具体大家就看下面代码中的结构体注释吧。紧接着头部下面是entry数据空间,这个空间记录了每一个tag的数据,数据大小和在数据区的偏移地址。紧接着entry下面的是真正的数据区域了,这个区域保存了所有大小大于4字节的Tag数据。具体后面代码中有详细描述。
android代码中的一个Camera_Metadata数据内存块中最小基本单元是struct camera_metadata_buffer_entry,总的entry数目等信息需要struct camera_metadata数据来维护.
/** * 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 数据结构=header | * | | * |-----------------------------------------------| * | reserved for future expansion | * |-----------------------------------------------| * | camera_metadata_buffer_entry_t #0 | * |-----------------------------------------------| * | .... | * |-----------------------------------------------| * | camera_metadata_buffer_entry_t #entry_count-1 | * |-----------------------------------------------| * | free space for | * | (entry_capacity-entry_count) entries | * |-----------------------------------------------| * | start of camera_metadata.data | * | | * |-----------------------------------------------| * | free space for | * | (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. */我们来回忆一下,之前我们在分析string8的时候,就是这种情况,真正的string字符串内存空间就是sharedBuffer头下面的数据空间。大家可以去参考一下博文struct camera_metadata { metadata_size_t size; //整个metadata数据大小 uint32_t version; //版本号,我们不用管它 uint32_t flags; metadata_size_t entry_count; //已经添加TAG的入口数量,(即内存块中已经包含多少TAG了) metadata_size_t entry_capacity;//最大能容纳TAG的入口数量(即最大能放多少tag) metadata_uptrdiff_t entries_start; // Offset from camera_metadata entry数据区域相对开始处的偏移 metadata_size_t data_count; //记录数据段当前已用的内存空间 metadata_size_t data_capacity; //总的数据段内存空间 metadata_uptrdiff_t data_start; // Offset from camera_metadata data数据区相对开始处的偏移 uint8_t reserved[]; 保留};
三、camera_metadata 代码探索
我在代码中大部分情况下,都是看到直接用camera_metadata定义一个对象的。如下简单的2行测试代码,下面的代码即是对代码的注释,也是对一个事例的跟踪探索。struct camera_metadata static_info;static_info.update(ANDROID_FLASH_MODE,5,1);
由上面的定义,我们来看看相应的构造函数,可以看到构造函数基本什么也没做,只是初始化了两个变量mBuffer,mLocked ,有同学可能已经担心了,没有分配内存空间,怎么去更新值啊,别急我们往下接着看。
CameraMetadata::CameraMetadata() : mBuffer(NULL), mLocked(false) {}
下图是update()方法的整个实现,其中看起很简短,但是其中做了很多工作,我们一一来分解。那些简单的代码,我就不列出来了,大家自己查阅源代码吧。
status_t CameraMetadata::update(uint32_t tag, const int32_t *data, size_t data_count) { status_t res; if (mLocked) { ALOGE("%s: CameraMetadata is locked", __FUNCTION__); return INVALID_OPERATION; } if ( (res = checkType(tag, TYPE_INT32)) != OK) { //这里检查的是参数中传进来的tag类型是否和之前预定义的一致,以防出错。 return res; } return updateImpl(tag, (const void*)data, data_count); //重任的接力棒传到了这里}status_t CameraMetadata::updateImpl(uint32_t tag, const void *data, size_t data_count) { status_t res; if (mLocked) { ALOGE("%s: CameraMetadata is locked", __FUNCTION__); return INVALID_OPERATION; } int type = get_camera_metadata_tag_type(tag); //获取tag的Type,为后面计算内存做准备,提前剧透返回的是1,即一个字节。 //感兴趣的可以我们跳到下一个函数中,稍后回来 DOWN,DOWN if (type == -1) { ALOGE("%s: Tag %d not found", __FUNCTION__, tag); return BAD_VALUE; } size_t data_size = calculate_camera_metadata_entry_data_size(type, //传入的参数个数1,type=Byte_Type 即1个字节。 data_count); //函数进行完毕了,我们得到data_size =0.啊,是0,继续往下看。 res = resizeIfNeeded(1, data_size); //终于我们来到了干实事的地方了。DOWN DOWN if (res == OK) { camera_metadata_entry_t entry; res = find_camera_metadata_entry(mBuffer, tag, &entry); if (res == NAME_NOT_FOUND) { //针对ANDROID_FLASH_MODE,上面返回的就是NOT_FOUND res = add_camera_metadata_entry(mBuffer, //由于之前没有添加过,这里会将ANDROID_FLASH_MODE写入大的数据块中。 tag, data, data_count); } else if (res == OK) { res = update_camera_metadata_entry(mBuffer, //上一次find操作找到的话,说明这一次进行的是更新操作。 entry.index, data, data_count, NULL); } } if (res != OK) { ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)", __FUNCTION__, get_camera_metadata_section_name(tag), get_camera_metadata_tag_name(tag), tag, strerror(-res), res); } IF_ALOGV() { ALOGE_IF(validate_camera_metadata_structure(mBuffer, /*size*/NULL) != OK, "%s: Failed to validate metadata structure after update %p", __FUNCTION__, mBuffer); } return res;}int get_camera_metadata_tag_type(uint32_t tag) { uint32_t tag_section = tag >> 16; //这里我们传进去的Tag是ANDROID_FLASH_MODE,即4<<16+2.(+2请看看开始那个长图最右边偏移),由移动16位得到4. if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) { return vendor_tag_ops->get_camera_vendor_tag_type( vendor_tag_ops, tag); } if (tag_section >= ANDROID_SECTION_COUNT || //这里就是判断tag_section段序号是否已经超出了界限。 tag >= camera_metadata_section_bounds[tag_section][1] ) { //看看下面对应的结构体(4<<16+2) < ANDROID_FLASH_END return -1; } uint32_t tag_index = tag & 0xFFFF; //这里按位与(4<<16+2) & 0xFFFF = 2; return tag_info[tag_section][tag_index].tag_type; //这里tag_section=4 tag_index=2.请看下面的tag_info结构体喽, //返回的tag_type = TYPE_BYTE,好了得到这个值,我们可以继续上面的分析。UP,UP }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]; //看下面结构体就知道了,这里data_bytes = 1 * 1; return data_bytes <= 4 ? 0 : ALIGN_TO(data_bytes, DATA_ALIGNMENT); //由于这里<=4成立,返回的是0,居然是0.我们回去UP,UP}//下面结构体在camera_metadata.cconst size_t camera_metadata_type_size[NUM_TYPES] = { [TYPE_BYTE] = sizeof(uint8_t), [TYPE_INT32] = sizeof(int32_t), [TYPE_FLOAT] = sizeof(float), [TYPE_INT64] = sizeof(int64_t), [TYPE_DOUBLE] = sizeof(double), [TYPE_RATIONAL] = sizeof(camera_metadata_rational_t)};//下面两个结构都在camera_metadata_tag_info.ctag_info_t *tag_info[ANDROID_SECTION_COUNT] = { android_color_correction, android_control, android_demosaic, android_edge, android_flash, //我们ANDROID_FLASH_MODE tag的section_count=4 ,那就是这个喽。 android_flash_info, ......}static tag_info_t android_flash[ANDROID_FLASH_END - ANDROID_FLASH_START] = { [ ANDROID_FLASH_FIRING_POWER - ANDROID_FLASH_START ] = { "firingPower", TYPE_BYTE }, [ ANDROID_FLASH_FIRING_TIME - ANDROID_FLASH_START ] = { "firingTime", TYPE_INT64 }, [ ANDROID_FLASH_MODE - ANDROID_FLASH_START ] = { "mode", TYPE_BYTE }, //代码访问的是tag_type域,返回的就是TYPE_BYTE了。 [ ANDROID_FLASH_COLOR_TEMPERATURE - ANDROID_FLASH_START ] = { "colorTemperature", TYPE_BYTE }, [ ANDROID_FLASH_MAX_ENERGY - ANDROID_FLASH_START ] = { "maxEnergy", TYPE_BYTE }, [ ANDROID_FLASH_STATE - ANDROID_FLASH_START ] = { "state", TYPE_BYTE },};//下面这个结构保存了真实的section界限,便于查找我们目前不可能用完64K的地址空间^_^ ^_^ ^_^unsigned int camera_metadata_section_bounds[ANDROID_SECTION_COUNT][2] = { [ANDROID_COLOR_CORRECTION] = { ANDROID_COLOR_CORRECTION_START, ANDROID_COLOR_CORRECTION_END }, [ANDROID_CONTROL] = { ANDROID_CONTROL_START, ANDROID_CONTROL_END }, [ANDROID_DEMOSAIC] = { ANDROID_DEMOSAIC_START, ANDROID_DEMOSAIC_END }, [ANDROID_EDGE] = { ANDROID_EDGE_START, ANDROID_EDGE_END }, [ANDROID_FLASH] = { ANDROID_FLASH_START, //刚才上面我们得到section num =4,就是这里了,而且取的ANDROID_FLASH_END ANDROID_FLASH_END }, ......}
下面这段代码算是camerametedata中最重要的,也是最不好理解的一个函数,这个函数的作用是根据传入的额外tag_entry和额外的data大小,重新计算实际的entry_count.如果新计算的new_entry >entry_cap(即之前的entry_count加上新加入的entry_count 大于之前实际的entry_cap,超额了),那么就将new_entry_count 放大2倍。由此可见entry_count呈指数倍增加。同样DataCount也是一样的道理,这里就不多说了,我们看代码,看代码,read the fucking code。
status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) { //由上面可以得到extraEntries =1,extraData = 0 if (mBuffer == NULL) { //开始的地方我们看得到mBuffer被初始化成NULL mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2); //这里可知传入的参数是(2,0),我们看的出,它提前多申请了一个entry的空间。 if (mBuffer == NULL) { ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); return NO_MEMORY; } } else { //由于这里我们是第一次申请mBuffer空间,所以这里就不执行了。 size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer); //获取当前metadata保存的Tag数量 size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer); //获取当前metadata数据快能最大存放tag的数量。 size_t newEntryCount = currentEntryCount + extraEntries; //得到更新后我们实际存放的tag数量。 newEntryCount = (newEntryCount > currentEntryCap) ? newEntryCount * 2 : currentEntryCap; //看到了吧,如果这个实际存放的数量,已经超出它能存放的范围,那么就把能力放大2倍,指数倍增加。 size_t currentDataCount = get_camera_metadata_data_count(mBuffer); //获取当前数据区已经使用了多少字节。 size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer); //数据区总的大小。 size_t newDataCount = currentDataCount + //当前已使用的数据 + 将要加入进来的数据=实际要存入数据区的数据 extraData; newDataCount = (newDataCount > currentDataCap) ? newDataCount * 2 : currentDataCap; //如果实际要存放的数据大于目前数据区总的大小,那么就把数据区放大2倍,同样呈指数增加 if (newEntryCount > currentEntryCap || newDataCount > currentDataCap) { camera_metadata_t *oldBuffer = mBuffer; mBuffer = allocate_camera_metadata(newEntryCount, //如果超处之前的能力,就会重新申请buffer newDataCount); if (mBuffer == NULL) { ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); return NO_MEMORY; } append_camera_metadata(mBuffer, oldBuffer); //将之前的数据拷贝到新的mbuffer中。 free_camera_metadata(oldBuffer); //释放老的mBuffer,你懂得 } } return OK;}
下面第一个函数allocate_camera_metadata()是重新根据入口数和数据大小计算、申请buffer。紧接着第二个place_camera_metadata()就是对刚申请的buffer,初始化一些变量,为后面更新,插入tag数据做准备。
camera_metadata_t *allocate_camera_metadata(size_t entry_capacity, size_t data_capacity) { //传入的参数是(2,0) if (entry_capacity == 0) return NULL; size_t memory_needed = calculate_camera_metadata_size(entry_capacity, //代码实现在下面,返回的是header+2*sizeof(entry)大小 data_capacity); void *buffer = malloc(memory_needed); //malloc申请一块连续的内存。 return place_camera_metadata(buffer, memory_needed, //到这个函数就是要给header化妆了,看下面紧挨着的方法 entry_capacity, data_capacity);}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; if (entry_capacity == 0) return NULL; size_t memory_needed = calculate_camera_metadata_size(entry_capacity, //再一次计算需要的内存大小,为何?? data_capacity); if (memory_needed > dst_size) return NULL; camera_metadata_t *metadata = (camera_metadata_t*)dst; //mbuffer赋成camera_metadata_t 结构体指针,用意何在 metadata->version = CURRENT_METADATA_VERSION; //版本号, metadata->flags = 0;//没有排序标志 metadata->entry_count = 0; //初始化entry_count =0,之前一次也没有更新过吧。 metadata->entry_capacity = entry_capacity; //最大的入口数量,针对我们ANDROID_FLASH_MODE这里是2个。 metadata->entries_start = ALIGN_TO(sizeof(camera_metadata_t), ENTRY_ALIGNMENT); //entry数据域的开始处紧挨着camera_metadata_t 头部。 metadata->data_count = 0; //初始化为0 metadata->data_capacity = data_capacity; //因为没有申请内存,这里也是0 metadata->size = memory_needed; //总的内存大小 size_t data_unaligned = (uint8_t*)(get_entries(metadata) + metadata->entry_capacity) - (uint8_t*)metadata; //这里大家要好好考虑一下,为什么加的是metadata->entry_capacity) ^_^ metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT); //计算data数据区域的偏移地址。数据区域紧挨着entry区域末尾。 return metadata;//根据入口数量和实际数量,计算实际camera_metadata需要的内存块大小(header+sizeof(camera_entry)+sizeof(data)。size_t calculate_camera_metadata_size(size_t entry_count, size_t data_count) { //针对我们上面讲的例子,传入的参数是(2,0) size_t memory_needed = sizeof(camera_metadata_t); //看好了,这里计算header大小了。 // Start entry list at aligned boundary memory_needed = ALIGN_TO(memory_needed, ENTRY_ALIGNMENT); //按字节对齐后的大小,我们就不关心了。 memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]); //紧接着是entry数据区的大小了,这里申请了2个entry内存空间。 // Start buffer list at aligned boundary memory_needed = ALIGN_TO(memory_needed, DATA_ALIGNMENT); //同样对齐 memory_needed += sizeof(uint8_t[data_count]); //这里我们data_count = 0,这里就不加了。 return memory_needed; //返回的最后算出的大小,我们回到开始处。UP,UP}
find_camera_metadata_entry函数非常好理解,获取对应tag的entry结构体,并将数据保存在entry传入的参数中。紧接着add_camera_metadata_entry()就是当我们没有查找到我们要查找到的tag时,这时候我们就是知道这是一个新的tag,需要添加进大数据结构中。在下面的几个函数中会遇到2个entry结构体,他们一个是内部的数据结构,用于记录tag数据的,还有一个是在外部引用。使用时,要注意啦。
struct camera_metadata_buffer_entry_t; //内部使用
struct camera_metadata_entry ; //外部使用,
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) { //之前初始化时,flags = 0,这里不成立,跳到else处 // 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++) { //这里由于entry_count =0 因为根本就没有添加任何东西。 if (search_entry->tag == tag) { break; } } if (index == src->entry_count) return NOT_FOUND; //返回NOT_FOUNT } return get_camera_metadata_entry(src, index, //找到index的tag entry entry);}int add_camera_metadata_entry(camera_metadata_t *dst, uint32_t tag, const void *data, size_t data_count) { //这里传入的参数为(mBuffer,ANDROID_FLASH_MODE,5,1) int type = get_camera_metadata_tag_type(tag); if (type == -1) { ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag); return ERROR; } return add_camera_metadata_entry_raw(dst, //这里传入的参数为(mBuffer,ANDROID_FLASH_MODE,BYTE_TYPE,5,1) DOWN tag, type, data, data_count);} //下面是真正干实事的方法,这里会将外部传入的tag信息,存放到各自的家中,以及一些其它的附属信息。你懂的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 == NULL) return ERROR; size_t data_bytes = calculate_camera_metadata_entry_data_size(type, data_count); //计算要使用的内存大小这里1*1,但是返回的是0 if (data_bytes + dst->data_count > dst->data_capacity) return ERROR; //用的空间+当前数据位置指针,不能大于数据最大空间。 size_t data_payload_bytes = data_count * camera_metadata_type_size[type]; //data_count =1,data_payload_bytes =1; camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count;//得到当前空闲的entry对象。 memset(entry, 0, sizeof(camera_metadata_buffer_entry_t)); //清0 entry->tag = tag; //ANDROID_FLASH_MODE. entry->type = type; //BYTE_TYPE entry->count = data_count; //没有占用data数据域,这里就是0了。 if (data_bytes == 0) { memcpy(entry->data.value, data, data_payload_bytes); //小于4字节的,直接放到entry数据域。 } 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++; //入口位置记录指针+1. dst->flags &= ~FLAG_SORTED; return OK; //到这里ANDROID_FLASH_MODE就添加进去了。}
4.updata流程图与find操作
1)流程图
下面流程图中有些yes和no标识,在生成图片的时候,就不见了。大家理解意思就行。最难理解是下图中红色虚线框住的区域,大家要对照着源码自己在理解一下。由于在ubutnu下使用Dia画的流程图,所以就用简单的英语描述了一下。
2)find操作
find操作也是很简单,根据传入的tag,到entry区域查找是否有该tag,有的话就该tag的数据保存到struct camera_metadata_entry 结构体中,供外部使用。
camera_metadata_entry_t CameraMetadata::find(uint32_t tag) { status_t res; camera_metadata_entry entry; if (mLocked) { ALOGE("%s: CameraMetadata is locked", __FUNCTION__); entry.count = 0; return entry; } res = find_camera_metadata_entry(mBuffer, tag, &entry); if (CC_UNLIKELY( res != OK )) { entry.count = 0; entry.data.u8 = NULL; } return entry;}
- Android Camera之CameraMetadata分析
- Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互
- Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互
- Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互
- Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互
- Android Camera API2中采用CameraMetadata用于从APP到HAL的参数交互
- 【Android】Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Android 4.0 Camera架构分析之Camera初始化
- Struts2 中的数据传输
- android实现中间卡位下方viewpager效果展示
- PHP实现验证码的制作与校验
- 区间dp
- 个人笔记--JavaScript高级程序设计(第三版)--第五章
- Android Camera之CameraMetadata分析
- 跨时钟域数据同步
- Python(List和Tuple类型)
- HDU 5540 Secrete Master Plan(水)
- 深入理解Android之Gradle
- 《淘宝技术这十年》札记
- 表现层功能
- dispatchTouchEvent源码解析
- 光耦组各种门电路