Camera Metadata原理

概述

在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,一般是1
    uint32_t                 flags;             // FLAG_SORTED,标记当前是否有对entry进行排序(根据entry的tag从小到大)。好处:排序后可以使用二分查找,可以提升性能。
    metadata_size_t          entry_count;       // 当前entry的数量,初始化为0
    metadata_size_t          entry_capacity;    // entry的最大数量
    metadata_uptrdiff_t      entries_start; 	// entry的起始地址(类型为:camera_metadata_buffer_entry)
    metadata_size_t          data_count;		// 当前data的数量(类型为uint8),初始化为0
    metadata_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_RATIONAL
    uint8_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_RATIONAL
    size_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. 计算size
    void *buffer = calloc(1, memory_needed);											  	// 2. 分配memory
    camera_metadata_t *metadata = place_camera_metadata(									// 3. 生成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;
}
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 boundary
    memory_needed = ALIGN_TO(memory_needed, ENTRY_ALIGNMENT);
    memory_needed += sizeof(camera_metadata_buffer_entry_t[entry_count]);
    // Start buffer list at aligned boundary
    memory_needed = ALIGN_TO(memory_needed, DATA_ALIGNMENT);
    memory_needed += sizeof(uint8_t[data_count]);
    // Make sure camera metadata can be stacked in continuous memory
    memory_needed = ALIGN_TO(memory_needed, METADATA_PACKET_ALIGNMENT);
    return memory_needed;
}
camera_metadata_t *place_camera_metadata
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值