Android Camera之CameraMetadata分析

Camera(5)metadata Metadata 是整个 Android camera 中类似于高楼大厦中的管道系统一样,贯穿了整个 Camera 的 Framework 和 vendor HAL。Metadata 里面包含了所有的控制、参数、返回值等等。Metadata分为Android原生的Metadata和芯片商或者用户自定义的Metadata,即Android Tag和Vendor Tag。Vendor Tag在Hal中定义,并在camx和chi目录中使用。 阅读详情
AI摘要

本文深入剖析了Android系统中Camera_metadata的数据结构及其内存布局。通过代码示例详细解释了Camera_metadata的构造过程、数据组织方式以及更新机制。对于理解相机模块的工作原理及参数配置具有重要意义。

感兴趣可以加QQ群85486140,大家一起交流相互学习下!

一、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

这些文件的调用和包含关系如下图所示。

 

 其中camera_metadata_tag_info.h包含了所有的基本宏,其中包含了下面的section枚举类型,在代码中可以看到,每一个section的大小是64K,因为他们根据之前枚举变量向左偏移16位(64K喽)。每个段有64K,目前在本世纪够用了。根据这样层层包扎,所以说每一个tag的值都是不同的,而且有序的组织在一起。

 

 

/**
 * 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,
    .............
}

 

 

 


 

下图我们就以ANDROID_FLASH_START section来说事吧。由于它的枚举值为4,那么它的tag的基址就是4x64K了。它包含了6个有效的tag,如下图最右侧的tag表。最后一个标示为end结束标志。

二、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.c
const 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.c
tag_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;
}

 

Camera metadata 阅读详情

相关推荐

Android 之 Metadata简析

!最后放上一个大概的Android学习方向及思路(详细的内容太多了~),提供给大家:对于程序员来说,要学习的知识内容、技术有太多太多,这里就先放上一部分,其他的内容有机会在后面的文章向大家呈现出来,不过我自己所有的学习资料都整理成了一个文档,一直在不断学习,希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜!Android架构师之路很漫长,一起共勉吧!

2401_84132589的博客 423

Camera MetaData介绍_独家原创

Camera Metadata 详解

weixin_36389889的博客 5032

Android Camera 内存问题剖析

Raphael 是西瓜视频 Android 团队开发的一款 native 内存监控工具,在字节跳动内部产品(如西瓜、抖音、头条等)上广泛用于监控 native 内存泄漏问题。在抖音 7.8.0-8.3.0 上搜集到大量因虚拟内存触顶而 crash 的内存日志现场(如 pthread_create、GL error、EGL_BAD_ALLOC),其中 60%以上都是 camera 相关的内存泄漏,占整体 crash 的 15%以上(Java & Native)。

2401_87256643的博客 1369

Camera MetaData 介绍

和你一起终身学习,这里是程序员Android经典好文推荐,通过阅读本文,您将收获以下知识点:一、Camera MetaData 作用简介二、MetaData 定义介绍三、Camera Me...

程序员Android 6076

Android Camera 内存问题剖析,腾讯安卓面试

由于 native 内存分配的频率过高,获取 Java 层堆栈又比较耗时,在拦截 native 内存分配时并不适合直接频繁抓取 Java 堆栈。Native 内存不同于 Java 内存,单从拦截到的数据很难直观给出结论。通常对于内存等资源不合理使用导致的资源不足而引发的问题都很难归因,从拦截到的数据来看,CameraMetaData 所引用的内存最大,嫌疑也最大,基于此决定剖析一下这个问题 初步分析 分析 native 内存的分配和释放 通过拦截到的堆栈可以看出,CameraMetaData 的创建堆栈.

m0_64603776的博客 998

Android P之Camera HAL3流程分析(3)

我们使用TextureView显示相机预览数据,Camera2的预览和拍照数据都是使用CameraCaptureSession会话来请求的     private void startPreview() {         SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();         mSurfaceTextu...

Vincentywj的博客 2424

Android Camera序列8(Qualcomm )

Android Camera序列8(Qualcomm )---Camera MetaData介绍

ListQ的博客 310

Android Camera 内存问题剖析,android项目开发实战入门电子书

由于 native 内存分配的频率过高,获取 Java 层堆栈又比较耗时,在拦截 native 内存分配时并不适合直接频繁抓取 Java 堆栈。Native 内存不同于 Java 内存,单从拦截到的数据很难直观给出结论。通常对于内存等资源不合理使用导致的资源不足而引发的问题都很难归因,从拦截到的数据来看,CameraMetaData 所引用的内存最大,嫌疑也最大,基于此决定剖析一下这个问题 初步分析 分析 native 内存的分配和释放 通过拦截到的堆栈可以看出,CameraMetaData 的创建堆栈.

m0_64604042的博客 273

Camera元数据完全指南:设计原理、buffer配对与VTS调试

元数据(Metadata)是Android相机系统中连接应用与硬件的中枢,它以键值对形式描述每一帧图像背后的传感器状态、曝光参数与3A结果。理解其设计本质上就是掌握CaptureRequest、CaptureResult和静态能力三者的协作关系,这也是解析“preparing metadata卡住”等异常的关键。在实际工程中,metadata与buffer的生命周期必须严格配对,否则会导致高帧率预览时数据错乱。同时,VTS测试对metadata的合法性有严格校验,常见失败多源于tag缺失或越界。从HAL初始

weixin_33801856的博客 332

Android Camera2 Hal3(一)初始化

前段时间在4412的开发板上面调通了camera hardware,现在准备来梳理下camera框架。 我这里的camera device api版本使用的是3.0;camera module api版本使用的是2.3;CameraService会根据这个api版本初始化不同的client,例如这里将初始化Camera2Client。 mDevice.common.version = CAMER...

我将带头加班的博客 4600

Linux下自动化解析q7签名文件的有效期与时间戳

本文详细介绍了在Linux环境下自动化解析q7签名文件有效期与时间戳的方法。通过编写高效脚本,开发者可以批量提取签名时间信息,验证签名有效性,并集成到CI/CD流程中。文章提供了完整的脚本实现,包括错误处理、批量处理功能,并解决了常见环境问题,特别适用于国产操作系统如UOS的开发场景。

weixin_30872499的博客 519

Camera2 CameraMetadata分析

Camera2 CameraMetadata分析

yaoming168的专栏 2584

【gridsample】地平线征程6芯片中gridsample算子的优化与部署实践

本文详细介绍了地平线征程6芯片中gridsample算子的优化与部署实践,重点探讨了PTQ和QAT量化方案在BEV感知任务中的应用。通过双线性插值优化、内存布局调整和混合精度配置等关键技术,显著提升了算子在自动驾驶场景下的性能和精度,为开发者提供了实用的部署指南和调优建议。

zero1的博客 1019

Android P camera metadata 分析一

Android Camera 1 使用Parameters 保存相机的状态, APK下发的命令等信息。 frameworks/av/services/camera/libcameraservice/api1/client2/Parameters.cpp Android camera2 很重要的改进是采用session 方式控制相机。 Camera 2 使用 camera metadata...

qq_38907791的博客 1783

mysql 计算不同胜率的活跃玩家的次日,3日,7日留存率

它根据每天的日期(logymd)和最高段位(max_rank)、胜率段落(win_rate_segment)进行分组,并计算总玩家人数(total_players)、第二天的留存人数(next_day_players)、第三天的留存人数(day3_players)和第七天的留存人数(day7_players)。注意:不是新手留存,是段位活跃玩家留存;最终的结果则是将留存数据进行展示,按照日期(date)、最高段位(rank_level)、胜率段落(win_rate_segment)进行排序。

weixin_42698221的博客 1836

高杂合度基因组组装实战:Hifiasm参数调优与purge_dups过滤效果对比(附猪毛菜案例)

本文深入探讨了高杂合度基因组组装中Hifiasm参数调优与purge_dups过滤的实战效果对比,通过猪毛菜案例详细解析了两种方法的适用场景与优化策略。文章重点分析了purge_dups在contig过滤中的显著效果,为基因组组装提供了关键的技术参考。

weixin_29232121的博客 495

Camera Framework流程系列(一)setRepeatingRequest 流程

应用层调用 CameraCaptureSession.setRepeatingRequest 方法时,会触发框架层和 HAL 层的一系列交互。

weixin_44668516的博客 2129

5G 模组 FM650 USB模式查询及网络配置

完成以上配置后,5G模组可进行搜网,入网成功后,上位机获取 5G IP。

十年通信老兵的技术修炼之路——从3G到5G,从地面基站到卫星通信。 1323

【高通SDM660平台】(8) --- Camera MetaData介绍

【高通SDM660平台】Camera Metadata一、Camera Metadata 作用简介二、Metadata 定义介绍2.1 Camera Metadata 内存分布2.2 基本宏定义 camera_metadata_tags.h2.3 基本API定义 camera_metadata.h2.4 产商API自定义 camera_vendor_tags.h2.5 将宏与字符串绑定 camer...

|~~~热爱生活、努力学习的小伙汁~~~| 6223

27、视网膜血管识别与分类的数据集和验证过程

本文介绍了视网膜血管自动识别与分类方法所使用的数据集及其验证过程。通过30张高分辨率的近红外反射视网膜图像,结合专业医生的手动标注结果,构建了包含597个血管段的基准数据集。研究采用准确率、召回率、F1分数和混淆矩阵等评估指标对模型进行验证,并提出了特征增强和分类器优化策略以提升性能。实验结果显示,该方法在复杂区域具有较强的鲁棒性和较高的准确性,为临床应用提供了可靠的依据。

i7j8k9l的博客 148

C# Modbus通信从入门到精通(2)——Modbus RTU协议原理

本文详细的介绍了Modbus RTU协议的原理,由多年经验高级工程师实战总结而成,绝对干货!

qq_34059233的博客 6383

android 内存分析工具_Android Camera 内存问题剖析

本文通过一类 Android 机型上相机拍摄过程中的 native 内存 OOM 的问题展开,借助内存快照裁剪回捞和 Native 内存监控工具的赋能,来深入剖析此类问题。背景Raphael 是西瓜视频 Android 团队开发的一款 native 内存监控工具,在字节跳动内部产品(如西瓜、抖音、头条等)上广泛用于监控 native 内存泄漏问题。在抖音 7.8.0-8.3.0 上搜集到大量因虚拟...

weixin_39846553的博客 461

MATLAB点云处理总目录

点云处理研究学习

weixin_44329757的博客 2555
上一篇: android底层库libutils之string8,string16研究
下一篇: android camera动态库加载过程
armwind
博客等级 码龄12年 406粉丝 152原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值