文章目录

  • 概述
  • 相关文件路径
  • 数据结构
  • metadata关键函数接口
    • allocate_camera_metadata (分配metadata)
    • find_camera_metadata_entry(从metadata中根据tag查找value)
    • add_camera_metadata_entry(增加tag和value到metadata)
    • delete_camera_metadata_entry(删除tag)
    • update_camera_metadata_entry(更新tag的value值)
  • tag分类
    • android tag
    • vendor tag
  • tag 命名

概述

在Camera2 + HAL3的架构中,App --> Framework --> HAL通过metadata的方式来传递参数。metadata实际上就是一个参数对(key, value),比如设置AE mode为auto,传下来的是类似(10,1)这样的参数对(AE mode的key为10,参数auto的value为1)。然后在HAL中通过10来获取设置值1。

本文通过剖析metadata的数据结构以及一些关键函数调用来探索metadata的原理。

相关文件路径

/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

数据结构

在头文件 system/media/camera/include/system/camera_metadata.h 有如下定义:

struct camera_metadata;
typedef struct camera_metadata camera_metadata_t;

camera_metadata_t 就是外部访问使用metadata的结构体。

这里有一个很有意思的用法:camera_metadata_t 的类型实际上是 camera_metadata,而 camera_metadata 是在 camera_metadata.c 中实现的。
外部文件只能inlcude头文件camera_metadata.h,这意味着如果外部只能看到camera_metadata_t,不能看到camera_metadata。所以无法直接访问camera_metadata的成员。
操控camera_metadata的唯一方式就是通过调用camera_metadata.h提供的函数接口。通过这种方式,实现了C语言的封装功能。

描述camera metadata的数据结构如下:
(1) struct camera_metadata;

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;                // metadata的总大小uint32_t                 version;            // CURRENT_METADATA_VERSION,一般是1uint32_t                 flags;             // FLAG_SORTED,标记当前是否有对entry进行排序(根据entry的tag从小到大)。好处:排序后可以使用二分查找,可以提升性能。metadata_size_t          entry_count;       // 当前entry的数量,初始化为0metadata_size_t          entry_capacity;    // entry的最大数量metadata_uptrdiff_t      entries_start;    // entry的起始地址(类型为:camera_metadata_buffer_entry)metadata_size_t          data_count;        // 当前data的数量(类型为uint8),初始化为0metadata_size_t          data_capacity;        // data的最大数量metadata_uptrdiff_t      data_start;        // data的起始地址uint32_t                 padding;           // 8字节对齐,不够就填充到这metadata_vendor_id_t     vendor_id;          // 标记平台的id,default值为CAMERA_METADATA_INVALID_ID
};

(2) struct camera_metadata_buffer_entry;

typedef struct camera_metadata_buffer_entry {uint32_t tag;                               // tag的key值uint32_t count;                              // tag的value对应的data的数量。比如data的类型为uint8_t ,count为100。则总共100个字节。union {uint32_t offset;                        // offset标记当前的key值对应的value在data_start中的位置uint8_t  value[4];                     // 当value占用的字节数<=4时,直接存储到这里(省空间)} data;                                        uint8_t  type;                              // TYPE_BYTE、TYPE_INT32、TYPE_FLOAT、TYPE_INT64、TYPE_DOUBLE、TYPE_RATIONALuint8_t  reserved[3];
} camera_metadata_buffer_entry_t;

(3) struct camera_metadata_entry ;

typedef struct camera_metadata_entry {size_t   index;                                // 该entry在当前metadta里面的index(0 ~ entry_count-1)uint32_t tag;                             // tag的key值uint8_t  type;                               // TYPE_BYTE、TYPE_INT32、TYPE_FLOAT、TYPE_INT64、TYPE_DOUBLE、TYPE_RATIONALsize_t   count;                              // tag的value对应的data的数量。比如data的类型为uint8_t ,count为100。则总共100个字节。union {uint8_t *u8;int32_t *i32;float   *f;int64_t *i64;double  *d;camera_metadata_rational_t *r;} data;                                       // tag的value对应的data值
} camera_metadata_entry_t;

下图比较直观总结出三个结构体之间的关系:

metadata的基本操作就是增(增加tag)、删(删除tag)、查(根据tag查找对应的value)、改(修改tag对应的value)。到这里metadata的原理基本上可以推导出来了,以“查”为例:
(1) 当用户拿到 camera_metadata 以及对应的tag后,需要从该meta中,找到对应的value。
(2) 从metadata的 entries_start 成员中可以拿到entry的首地址,再根据 entry_count 可以遍历所有的entry。
(3) 根据tag来逐一比较camera_metadata_buffer_entry中的 tag 。就可以找到该tag对应的entry。
(4) 根据 counttype 可以计算出value的字节数。当字节数<=4的时候,直接取 data.value;否则就根据 offset 从metadata的 data_start 找到对应的value。
(5) 将其转换为结构体 camera_metadata_entry_t,返回给用户。用户通过count和type就可以找到该tag对应的value啦。

PS:这里有一个非常重要也非常容易出bug的地方,就是返回的entry,里面的data指向的是实际数据的地址。
所以如果直接改写data里面的内容,会覆盖之前的数据。 一定要记得做memcpy。

metadata关键函数接口

如前文所提到,用户只能通过调用函数接口,来访问camera_metadata_t里面的内容。函数接口实现的源码位于:system/media/camera/src/camera_metadata.c。这个文件有上千行,这里仅提到几个关键的函数。

allocate_camera_metadata (分配metadata)

// 传入max entry和max data,给metadata分配地址空间
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);    // 1. 计算sizevoid *buffer = calloc(1, memory_needed);                                               // 2. 分配memorycamera_metadata_t *metadata = place_camera_metadata(                                 // 3. 生成metadatabuffer, 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;
}
size_t calculate_camera_metadata_size(size_t entry_count,size_t data_count) {size_t memory_needed = sizeof(camera_metadata_t);// Start entry list at aligned boundarymemory_needed = ALIGN_TO(memory_needed, ENTRY_ALIGNMENT);memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]);// Start buffer list at aligned boundarymemory_needed = ALIGN_TO(memory_needed, DATA_ALIGNMENT);memory_needed += sizeof(uint8_t[data_count]);// Make sure camera metadata can be stacked in continuous memorymemory_needed = ALIGN_TO(memory_needed, METADATA_PACKET_ALIGNMENT);return 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;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;metadata->version = CURRENT_METADATA_VERSION;metadata->flags = 0;metadata->entry_count = 0;metadata->entry_capacity = entry_capacity;metadata->entries_start =ALIGN_TO(sizeof(camera_metadata_t), ENTRY_ALIGNMENT);metadata->data_count = 0;metadata->data_capacity = data_capacity;metadata->size = memory_needed;size_t data_unaligned = (uint8_t*)(get_entries(metadata) +metadata->entry_capacity) - (uint8_t*)metadata;metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);metadata->vendor_id = CAMERA_METADATA_INVALID_VENDOR_ID;assert(validate_camera_metadata_structure(metadata, NULL) == OK);return metadata;
}

find_camera_metadata_entry(从metadata中根据tag查找value)

这个函数的基本逻辑可以参考上文描述从metadata中“查”tag的逻辑。

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) {// Sorted entries, do a binary searchcamera_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 searchcamera_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;
}

add_camera_metadata_entry(增加tag和value到metadata)

int add_camera_metadata_entry(camera_metadata_t *dst, uint32_t tag, const void *data, size_t data_count) {// 1.根据tag,找到该tag对应的value的type。这个函数的具体实现不再粘贴出来,里面涉及到tag section相关结构体,后文描述int type = get_local_camera_metadata_tag_type(tag, dst);        if (type == -1) {ALOGE("%s: Unknown tag %04x.", __FUNCTION__, tag);return ERROR;}// 2.将tag和data添加到metadata中。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;// 1. 计算size,并进行4字节判断。如果小于4字节,将返回0。size_t data_bytes = calculate_camera_metadata_entry_data_size(type, data_count);if (data_bytes + dst->data_count > dst->data_capacity) return ERROR;// 2. 计算数据的sizesize_t data_payload_bytes = data_count * camera_metadata_type_size[type];// 3. 生成camera_metadata_buffer_entry_tcamera_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;// 4. copy数据到entry中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;}// 5. 增加一个entrydst->entry_count++;dst->flags &= ~FLAG_SORTED;    // add后,是没有经过排序的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);
}

delete_camera_metadata_entry(删除tag)

删除的逻辑相对有点复杂,因为tag对应的value可能在data数组的中间,需要后面的内容,覆盖要删除的内容。

int delete_camera_metadata_entry(camera_metadata_t *dst, size_t index) {if (dst == NULL) return ERROR;if (index >= dst->entry_count) return ERROR;// 1. 根据index,找到对应的entrycamera_metadata_buffer_entry_t *entry = get_entries(dst) + index;// 2. 获取value的sizesize_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type,entry->count);if (data_bytes > 0) {// 3. data_bypes > 0,value的size>4字节,所以存储的data数组中// 这里开始对data数组的内容进行memmove// Shift data buffer to overwrite deleted datauint8_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;  // data_count是数组总长度,offset是value的起始位置,data_types是value的长度。相减就是value后面的数据的长度memmove(start, end, length);  // value后面的数据向前移动到start位置,从end开始计算length个字节// 4. 更新当前tag之后的entry的offset// Update all entry indices to account for shiftcamera_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;}// 5. 移动entry// Shift entry arraymemmove(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);
}

update_camera_metadata_entry(更新tag的value值)

在调用 update_camera_metadata_entry() 更新tag前,一定要通过 find_camera_metadata_entry() 找到对应的entry,通过该entry获取其index(即entry在metadata的index)。
PS:参数updated_entry,是用于获取update之后的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) {if (dst == NULL) return ERROR;if (index >= dst->entry_count) return ERROR;// 1. 根据index找到对应的entrycamera_metadata_buffer_entry_t *entry = get_entries(dst) + index;// 2. data_bytes是新的value的size,如果小于4,就是0; //    data_payload_bytes是新的value真正的size;//    entry_bytes是就的value的sizesize_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) {// 新的value和旧的value的size不同时,需要进行下述操作// 3. 确定data的容量是否可以满足新的value// May need to shift/add to data arrayif (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {// No roomreturn ERROR;}// 4. 删除旧的tag对应的value,实现类似delete函数if (entry_bytes != 0) {// Remove old datauint8_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 shiftcamera_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;}}// 5. 将新的tag对应的value插入到最后方if (data_bytes != 0) {// Append new dataentry->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) {// 6. data的size相等时直接override// data size unchanged, reuse same data locationmemcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);}if (data_bytes == 0) {// Data fits into entrymemcpy(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;
}

tag分类

tag从归属方可以被分类两类:(1) android平台原生tag。如ANDROID_CONTROL_AE_MODE用于控制AE曝光方式(auto、manual等)。 (2) vendor tag(platfrom如Qcom/SumSung/MTK新增tag)。

android tag

tag是通过section的方式来进行分类的,如下:

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;

上面都是android原生tag的section,每一个section支持的tag总数最大是65536(1<<16)。

vendor tag

vendor tag必须从0x8000000开始使用

tag 命名

从上文可以了解到,tag实际上是一个UINT32的key值。如果在coding的时候,总是使用一个UINT32类型的数字是描述tag,是一件非常难以容忍的事情。

实际上tag是有char型的名字的,平时使用的时候,对于android原生tag,既可以使用char型的名字来获取tag,也可以直接使用宏。但是对于平台定义的vendor tag,基本山都是使用tag name的方式来读写tag。

system/media/camera/src/camera_metadata_tag_info.c 定义了android原生tag name和type。

hardware/libhardware/modules/camera/CameraHAL.cpp 有一个deamon,描述如何在HAL实现vendor tag。这里不再赘述。

Camera Metadata原理相关推荐

  1. Camera成像原理(raw图如何产生的)

    1.前言 本文主要介绍了camera的成像原理和raw图像的生成原理,当然在此之前,我们先介绍camera基础知识 2.Camera基础知识 镜头(lens) 镜头实际上就是一个凸透镜,将外部的光线折 ...

  2. Camera MetaData 介绍

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

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

    一.Camera MetaData 作用简介 简单来说,Camera 设置参数,以前都是调用 SetParameter()/Paramters() 来实现下发或获取参数. 而现在新的 Camera A ...

  4. 转一篇写的比较好的camera文档[Camera 图像处理原理分析]

    色彩篇(一) 1         前言 做为拍照手机的核心模块之一,camera sensor效果的调整,涉及到众多的参数,如果对基本的光学原理及sensor软/硬件对图像处理的原理能有深入的理解和把 ...

  5. Camera 图像处理原理分析

    http://blog.chinaunix.net/uid-24486720-id-370942.html 1         前言 做为拍照手机的核心模块之一,camera sensor效果的调整, ...

  6. Camera成像原理(二十四)

    目前各大手机厂商每年都在推出各种关于手机摄像头的新卖点以求引起消费者关注.了解摄像头是怎样工作可以更好地帮助我们理解目前的消费级产品,其中摄像头工作的核心概念就是数字成像系统.   本期主要对数字成像 ...

  7. Camera图像处理原理及实例分析 - 色彩篇

    转自:http://blog.csdn.net/colorant/ 前言 做为拍照手机的核心模块之一,camera sensor效果的调整,涉及到众多的参数,如果对基本的光学原理及sensor软/硬件 ...

  8. Camera结构原理

    一.Camera的成像原理 景物通过镜头(LENS)生成的光学图像投射到图像传感器(Sensor)表面上,然后转为模拟的电信号,经过 A/D(模数转换)转换后变为数字图像信号,再送到数字信号处理芯片( ...

  9. camera摄像原理之三:色温和自动白平衡【转】

    转自:http://blog.csdn.net/ghostyu/article/details/7912863 色温的定义:将黑体从绝对零度开始加温,温度每升高一度称为1开氏度(用字母K表示),当温度 ...

最新文章

  1. [QA]Python字节码优化问题
  2. 漫谈16S的前世今生
  3. ajax上传等待效果,ajax等待服务器响应添加等待效果
  4. Launcher3删除开机引导页面
  5. 一个QQ旋风的BUG
  6. maven 构建web程序出现问题
  7. 轻松掌握Ajax.net系列教程二:部署Ajax Control Toolkit
  8. 教育行业的互联网焦虑症
  9. laravel -admin 禁止某一行删除
  10. JAVA面试题之经典题型
  11. 开始gentoo之旅
  12. ByteBuffer分散和聚集的应用场景
  13. 深度学习TF—10.循环神经网络RNN及其变体LSTM、GRU实战
  14. MySQL可视化工具下载安装及连接
  15. 《鸟哥的Linux私房菜》第四版导学
  16. Keil4 keil5兼容
  17. 北京地标《信息化项目软件开发费用测算规范》完成公开征求意见
  18. kali的ip转发失败怎么解决?
  19. c#web网页连接数据库产品出库入库数据更新
  20. 深度学习中的IoU概念理解

热门文章

  1. 大学生创业思路项目计算机,关于组织申报2019年省级、国家级大学生创新创业训练计划项目的通知...
  2. azure 和 aws比较_AWS,Azure和Google Cloud Backup解决方案比较
  3. EditPlus 格式化 Unicode,编辑properties
  4. 路路通 GPS软件 脱壳+破解 纪要 (RtNavi.exe)
  5. gradle新建项目报错
  6. 微小区版v11.2.3 小区 物业 智慧
  7. 如何给pdf添加页码?
  8. 怎么用计算机算几何倍增,城市规划中对计算机仿真技术应用.doc
  9. wex5 生成动态轮播图代码
  10. 人家裁员我加薪, 这个80后凭什么身价1200亿?