IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Camera Metadata -> 正文阅读

[移动开发]Android Camera Metadata

1. Camera Metadata 定义

元数据简单来说就关于数据的数据。它是数据的描述和上下文,有助于组织、查找和理解数据。
Metadata 示意
Camera3 中,为了实现 Per-frame controls 引入了 metadata 机制,使用 metadata 包涵相机控制参数、硬件参数、返回值等。

2. Android API

在Camera2 + HAL3的架构中,App -> Framework -> HAL 通过 metadata 的方式来传递参数。
Metadata主要是通过一个tag(key),去找对应的data。用于传递参数和配置信息,减少内存拷贝操作。

相关代码:

system/media/camera/include/system/camera_metadata.h
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

2.1 基本结构体

camera_metadata_t 是 Android Metadata 的基本结构体,在 camera_metadata.c 中实现:

typedef struct camera_metadata camera_metadata_t;

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的数量(TAG),初始化为0
    metadata_size_t          entry_capacity; // entry的最大数量
    metadata_uptrdiff_t      entries_start;  // entry的起始地址,4字节对齐(类型为 camera_metadata_buffer_entry*)
    metadata_size_t          data_count;     // 当前data的数量(类型为 uint8),初始化为0
    metadata_size_t          data_capacity;  // data的最大数量
    metadata_uptrdiff_t      data_start;     // data的起始地址,8字节对齐(类型为 camera_metadata_data_t*)
    uint32_t                 padding;        // 8字节对齐,不够就填充到这
    metadata_vendor_id_t     vendor_id;      // 标记平台的id,default值为CAMERA_METADATA_INVALID_ID
}; 

camera_metadata_buffer_entry_tcamera_metadata_t 中用于存放 tag 和 data 信息的键值对:

camera_metadata_buffer_entry
typedef struct camera_metadata_buffer_entry {
    uint32_t tag;               // tag的key值
    uint32_t count;             // tag的value对应的data的数量
    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;

camera_metadata.c 中,列出了 camera_metadata 的内存布局。
这是一块 camera_metadata.size 字节的连续内存:

|-----------------------------------------------|---------
| camera_metadata_t                             | 区域一:
|                                               | 存储 camera_metadata_t 结构体定义
|-----------------------------------------------|---------
| reserved for future expansion                 | 区域二:
|                                               | 保留区,供未来使用
|-----------------------------------------------|---------
| camera_metadata_buffer_entry_t #0             |
|-----------------------------------------------| 区域三:
| ....                                          | 储存所有 Tag 结构体定义
|-----------------------------------------------| TAG[0]~TAG[entry_count-1]
| camera_metadata_buffer_entry_t #entry_count-1 |
|-----------------------------------------------|---------
| free space for                                | 区域四:
| (entry_capacity-entry_count) entries          | 剩余未使用的 Tag 结构体的保留内存
|-----------------------------------------------|---------
| start of camera_metadata.data                 | 区域五:
|                                               | Tag对应的具体 metadata 数据
|-----------------------------------------------|---------
| free space for                                | 区域六:
| (data_capacity-data_count) bytes              | 剩余未使用的 Tag 占用的内存
|-----------------------------------------------|---------

下图更详细地显示了 camera_metadata 中的内存布局:
camera_metadata 内存划分

camera_metadata_entry_t 是对 camera_metadata_t 中某个 entry 的引用,用做 metada 相关方法返回值:

camera_metadata_entry_t
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的数量
    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;

camera_metadata_tcamera_metadata_buffer_entry_tcamera_metadata_entry_t 关系如下:
camera_metadata 结构体间的关系

2.2 增删改查

camera_metadata.h 中定义了 camera_metadata 相关的函数接口。以下是其中部分方法的介绍:

方法用途
camera_metadata_t* allocate_camera_metadata(size_t entry_capacity, size_t data_capacity)metadata 内存分配
void free_camera_metadata(camera_metadata_t *metadata)释放 metadata 对应内存
int add_camera_metadata_entry(camera_metadata_t *dst, uint32_t tag, const void *data, size_t data_count)在 metadata 中增加 tag 和 value
int delete_camera_metadata_entry(camera_metadata_t *dst, size_t index)删除 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)更新 tag 的 value 值
int find_camera_metadata_entry(camera_metadata_t *src, uint32_t tag, camera_metadata_entry_t *entry)根据 tag 查找 value

add_camera_metadata_entry

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. 计算数据的size
    size_t data_payload_bytes = data_count * camera_metadata_type_size[type];
 
    // 3. 生成camera_metadata_buffer_entry_t
    camera_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. 增加一个entry
    dst->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

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,找到对应的entry
    camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
 
    // 2. 获取value的size
    size_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 data
        uint8_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 shift
        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;
        }
        dst->data_count -= data_bytes;
    }
 
    // 5. 移动entry
    // Shift entry array
    memmove(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

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找到对应的entry
    camera_metadata_buffer_entry_t *entry = get_entries(dst) + index;
 
    // 2. data_bytes是新的value的size,如果小于4,就是0;
    //    data_payload_bytes是新的value真正的size;
    //    entry_bytes是就的value的size
    size_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 array
        if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) {
            // No room
            return ERROR;
        }
 
        // 4. 删除旧的tag对应的value,实现类似delete函数
        if (entry_bytes != 0) {
            // Remove old 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
            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;
            }
        }
 
        // 5. 将新的tag对应的value插入到最后方
        if (data_bytes != 0) {
            // Append new data
            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) {
        // 6. data的size相等时直接override
 
        // data size unchanged, reuse same data location
        memcpy(get_data(dst) + entry->data.offset, data, data_payload_bytes);
    }
 
    if (data_bytes == 0) {
        // Data fits into entry
        memcpy(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;
}

更新流程

find_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) {
        // 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;
    }
 
    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;
}

查找流程

默认情况下,camera_metadata_t 中的 entry 不会被排序,也没有强制要求不同 entry 的 tag 不能相同。
在存在多个相同 tag 时,find 返回结果并不能保证是哪一个对应的 entry 内容。
如果 metadata 内容已排序,add 后会变为未排序状态。

3. Metadata Tag

tag index 实际上是一个 UINT32 的 key 值。其中高16位代表 section 部分,低 16 位代表 tag 部分。
camera_metadata_tags.h 中定义了 Android 原生 tag 的 section 枚举、各 section index 范围,以及 tag 枚举。

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 // 最高位为 1
} camera_metadata_section_t;

32bit tag index

当 tag_section 大于 0x8000 时,camera_metadata.h 中的相关方法,会跳转到 vendor 对应接口。
vendor tag 相关的操作定义在 camera_vendor_tags.h

get_local_camera_metadata_tag_name_vendor_id
const char *get_local_camera_metadata_tag_name_vendor_id(uint32_t tag,
        metadata_vendor_id_t id) {
 
    // 取出 section 部分
    uint32_t tag_section = tag >> 16;
     
    // 判断是否为 vendor tag
    if (tag_section >= VENDOR_SECTION && vendor_cache_ops != NULL &&
                id != CAMERA_METADATA_INVALID_VENDOR_ID) {
            return vendor_cache_ops->get_tag_name(tag, id);
    } else  if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) {
        return vendor_tag_ops->get_tag_name(
            vendor_tag_ops,
            tag);
    }
     
    // index 值是否合法
    if (tag_section >= ANDROID_SECTION_COUNT ||
        tag >= camera_metadata_section_bounds[tag_section][1] ) {
        return NULL;
    }
    uint32_t tag_index = tag & 0xFFFF;
     
    // 非 vendor tag 会从 camera_metadata_tag_info.c 中获取
    return tag_info[tag_section][tag_index].tag_name;
}

为了方便使用,Android 中给 tag 定义了 char* 类型的名字。
在使用时,可以直接通过名字来获取 tag。

tag index 与 tag name 的对应关系可以在 camera_metadata_tag_info.c 中进行查看。
system/media/camera/docs/docs.html 中详细列举了所有 Android 原生 camera meta。

4. CameraMetadata 分类

Android CameraMetadata 分为 controls、dynamic 及 static 三类:

  • Static: camera characteristics,用于描述相机 logic device 参数和对应功能,供 APP 查询使用;
  • Controls: request settings,随每帧请求下发的控制信息,会绑定对应的 image callback;
  • Dynamic: result metadata,将 HAL 层值回传给 APP。

CaptureRequest 中绑定的 Metadata 存放于 CaptureRequest.settings:

CaptureRequest
struct V3_4::CaptureRequest final {
    // 旧版本参数
    V3_2::CaptureRequest v3_2;
     
    // vector 中包含了 logical camera 下,各物理相机的设置信息
    // 为空时,使用 .v3_2 字段的值
    hidl_vec<PhysicalCameraSetting> physicalCameraSettings;
};
 
struct V3_2::CaptureRequest final {
    // 由 framework 设置的递增整数,用于唯一标识此 capture
    uint32_t frameNumber;
     
    // 快速消息队列中设置数据的大小
    //
    // 不为 0 时:
    //   从请求队列 ICameraDeviceSession.getCaptureRequestMetadataQueue 中读取设置信息;
    // 为 0 时:
    //   从 .settings 字段中读取设置信息
    uint64_t fmqSettingsSize;
     
    // 仅在 fmqSettingsSize 为 0 时使用该字段,且:
    //   settings 将包含 request 在 capture 和 process 时所需的参数(App下发的Meta)
    // 此时,若 settings 为 NULL:
    //   表示 settings 内容与最近一次 capture request 的 settings 相同
    hidl_vec<uint8_t> settings;
     
    // 用于此 request 的输入缓冲区(如果有)
    //
    // inputBuffer::buffer 为 NULL,代表 inputBuffer 无效:
    //   inputBuffer 中的其他信息(如 bufferId)需要被忽略;
    //   同时,该 request 也需要获取新的图像内容。
    // 若inputBuffer有效:
    //   该 request 需要对 inputBuffer 中包含的图像进行再处理(reprocess),
    //   HAL 必须在后续的 processCaptureResult 调用中将 inputBuffer 释放回客户端
    //
    // HAL 在访问 inputBuffer 前,需要等待获取 sync fence
    StreamBuffer inputBuffer;
     
    // 待写入 image data 的 stream buffer 数组;
    // HAL 层在写入前,必须等待 acquire fence
    hidl_vec<StreamBuffer> outputBuffers;
};

CaptureResult 中绑定的 Metadata 存放于 CaptureResult.result:

CaptureResult
struct V3_4::CaptureResult final {
    V3_2::CaptureResult v3_2;
    hidl_vec<PhysicalCameraMetadata> physicalCameraMetadata;
};
 
struct V3_2::CaptureResult final {
    uint32_t frameNumber;
     
    // 快速消息队列中设置数据的大小
    //
    // 不为 0 时:
    //   从请求队列 ICameraDeviceSession.getCaptureResultMetadataQueue 中读取设置信息;
    // 为 0 时:
    //   从当 .result 字段中读取设置信息
    uint64_t fmqResultSize;
     
    // 此 capture 的 result metadata
    hidl_vec<uint8_t> result;
     
    hidl_vec<StreamBuffer> outputBuffers;
    StreamBuffer inputBuffer;
    uint32_t partialResult;
};

android/hardware/camera/device/3.2/types.h

参考文章:
Camera Metadata原理
Android Camera HAL3-metadata

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-01 15:22:00  更:2022-06-01 15:22:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 0:32:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码