| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <inttypes.h> |
| #include <system/camera_metadata.h> |
| #include <camera_metadata_hidden.h> |
| |
| #define LOG_TAG "camera_metadata" |
| #include <cutils/log.h> |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| |
| #define OK 0 |
| #define ERROR 1 |
| #define NOT_FOUND (-ENOENT) |
| |
| #define ALIGN_TO(val, alignment) \ |
| (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1)) |
| |
| /** |
| * A single metadata entry, storing an array of values of a given type. If the |
| * array is no larger than 4 bytes in size, it is stored in the data.value[] |
| * array; otherwise, it can found in the parent's data array at index |
| * data.offset. |
| */ |
| #define ENTRY_ALIGNMENT ((size_t) 4) |
| typedef struct camera_metadata_buffer_entry { |
| uint32_t tag; |
| uint32_t count; |
| union { |
| uint32_t offset; |
| uint8_t value[4]; |
| } data; |
| uint8_t type; |
| uint8_t reserved[3]; |
| } camera_metadata_buffer_entry_t; |
| |
| typedef uint32_t metadata_uptrdiff_t; |
| typedef uint32_t metadata_size_t; |
| |
| /** |
| * 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 | |
| * | | |
| * |-----------------------------------------------| |
| * | 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. |
| */ |
| #define METADATA_ALIGNMENT ((size_t) 4) |
| struct camera_metadata { |
| metadata_size_t size; |
| uint32_t version; |
| uint32_t flags; |
| metadata_size_t entry_count; |
| metadata_size_t entry_capacity; |
| metadata_uptrdiff_t entries_start; // Offset from camera_metadata |
| metadata_size_t data_count; |
| metadata_size_t data_capacity; |
| metadata_uptrdiff_t data_start; // Offset from camera_metadata |
| uint8_t reserved[]; |
| }; |
| |
| /** |
| * A datum of metadata. This corresponds to camera_metadata_entry_t::data |
| * with the difference that each element is not a pointer. We need to have a |
| * non-pointer type description in order to figure out the largest alignment |
| * requirement for data (DATA_ALIGNMENT). |
| */ |
| #define DATA_ALIGNMENT ((size_t) 8) |
| typedef union camera_metadata_data { |
| uint8_t u8; |
| int32_t i32; |
| float f; |
| int64_t i64; |
| double d; |
| camera_metadata_rational_t r; |
| } camera_metadata_data_t; |
| |
| /** |
| * The preferred alignment of a packet of camera metadata. In general, |
| * this is the lowest common multiple of the constituents of a metadata |
| * package, i.e, of DATA_ALIGNMENT and ENTRY_ALIGNMENT. |
| */ |
| #define MAX_ALIGNMENT(A, B) (((A) > (B)) ? (A) : (B)) |
| #define METADATA_PACKET_ALIGNMENT \ |
| MAX_ALIGNMENT(MAX_ALIGNMENT(DATA_ALIGNMENT, METADATA_ALIGNMENT), ENTRY_ALIGNMENT); |
| |
| /** Versioning information */ |
| #define CURRENT_METADATA_VERSION 1 |
| |
| /** Flag definitions */ |
| #define FLAG_SORTED 0x00000001 |
| |
| /** Tag information */ |
| |
| typedef struct tag_info { |
| const char *tag_name; |
| uint8_t tag_type; |
| } tag_info_t; |
| |
| #include "camera_metadata_tag_info.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) |
| }; |
| |
| const char *camera_metadata_type_names[NUM_TYPES] = { |
| [TYPE_BYTE] = "byte", |
| [TYPE_INT32] = "int32", |
| [TYPE_FLOAT] = "float", |
| [TYPE_INT64] = "int64", |
| [TYPE_DOUBLE] = "double", |
| [TYPE_RATIONAL] = "rational" |
| }; |
| |
| static camera_metadata_buffer_entry_t *get_entries( |
| const camera_metadata_t *metadata) { |
| return (camera_metadata_buffer_entry_t*) |
| ((uint8_t*)metadata + metadata->entries_start); |
| } |
| |
| static uint8_t *get_data(const camera_metadata_t *metadata) { |
| return (uint8_t*)metadata + metadata->data_start; |
| } |
| |
| size_t get_camera_metadata_alignment() { |
| return METADATA_PACKET_ALIGNMENT; |
| } |
| |
| camera_metadata_t *allocate_copy_camera_metadata_checked( |
| const camera_metadata_t *src, |
| size_t src_size) { |
| |
| if (src == NULL) { |
| return NULL; |
| } |
| |
| void *buffer = malloc(src_size); |
| memcpy(buffer, src, src_size); |
| |
| camera_metadata_t *metadata = (camera_metadata_t*) buffer; |
| if (validate_camera_metadata_structure(metadata, &src_size) != OK) { |
| free(buffer); |
| return NULL; |
| } |
| |
| return 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); |
| void *buffer = malloc(memory_needed); |
| camera_metadata_t *metadata = place_camera_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; |
| } |
| |
| 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); |
| |
| assert(validate_camera_metadata_structure(metadata, NULL) == OK); |
| return metadata; |
| } |
| void free_camera_metadata(camera_metadata_t *metadata) { |
| free(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]); |
| return memory_needed; |
| } |
| |
| size_t get_camera_metadata_size(const camera_metadata_t *metadata) { |
| if (metadata == NULL) return ERROR; |
| |
| return metadata->size; |
| } |
| |
| size_t get_camera_metadata_compact_size(const camera_metadata_t *metadata) { |
| if (metadata == NULL) return ERROR; |
| |
| return calculate_camera_metadata_size(metadata->entry_count, |
| metadata->data_count); |
| } |
| |
| size_t get_camera_metadata_entry_count(const camera_metadata_t *metadata) { |
| return metadata->entry_count; |
| } |
| |
| size_t get_camera_metadata_entry_capacity(const camera_metadata_t *metadata) { |
| return metadata->entry_capacity; |
| } |
| |
| size_t get_camera_metadata_data_count(const camera_metadata_t *metadata) { |
| return metadata->data_count; |
| } |
| |
| size_t get_camera_metadata_data_capacity(const camera_metadata_t *metadata) { |
| return metadata->data_capacity; |
| } |
| |
| camera_metadata_t* copy_camera_metadata(void *dst, size_t dst_size, |
| const camera_metadata_t *src) { |
| size_t memory_needed = get_camera_metadata_compact_size(src); |
| |
| if (dst == NULL) return NULL; |
| if (dst_size < memory_needed) return NULL; |
| |
| camera_metadata_t *metadata = |
| place_camera_metadata(dst, dst_size, src->entry_count, src->data_count); |
| |
| metadata->flags = src->flags; |
| metadata->entry_count = src->entry_count; |
| metadata->data_count = src->data_count; |
| |
| memcpy(get_entries(metadata), get_entries(src), |
| sizeof(camera_metadata_buffer_entry_t[metadata->entry_count])); |
| memcpy(get_data(metadata), get_data(src), |
| sizeof(uint8_t[metadata->data_count])); |
| |
| assert(validate_camera_metadata_structure(metadata, NULL) == OK); |
| return metadata; |
| } |
| |
| int validate_camera_metadata_structure(const camera_metadata_t *metadata, |
| const size_t *expected_size) { |
| |
| if (metadata == NULL) { |
| ALOGE("%s: metadata is null!", __FUNCTION__); |
| return ERROR; |
| } |
| |
| // Check that the metadata pointer is well-aligned first. |
| { |
| static const struct { |
| const char *name; |
| size_t alignment; |
| } alignments[] = { |
| { |
| .name = "camera_metadata", |
| .alignment = METADATA_ALIGNMENT |
| }, |
| { |
| .name = "camera_metadata_buffer_entry", |
| .alignment = ENTRY_ALIGNMENT |
| }, |
| { |
| .name = "camera_metadata_data", |
| .alignment = DATA_ALIGNMENT |
| }, |
| }; |
| |
| for (size_t i = 0; i < sizeof(alignments)/sizeof(alignments[0]); ++i) { |
| uintptr_t aligned_ptr = ALIGN_TO(metadata, alignments[i].alignment); |
| |
| if ((uintptr_t)metadata != aligned_ptr) { |
| ALOGE("%s: Metadata pointer is not aligned (actual %p, " |
| "expected %p) to type %s", |
| __FUNCTION__, metadata, |
| (void*)aligned_ptr, alignments[i].name); |
| return ERROR; |
| } |
| } |
| } |
| |
| /** |
| * Check that the metadata contents are correct |
| */ |
| |
| if (expected_size != NULL && metadata->size > *expected_size) { |
| ALOGE("%s: Metadata size (%" PRIu32 ") should be <= expected size (%zu)", |
| __FUNCTION__, metadata->size, *expected_size); |
| return ERROR; |
| } |
| |
| if (metadata->entry_count > metadata->entry_capacity) { |
| ALOGE("%s: Entry count (%" PRIu32 ") should be <= entry capacity " |
| "(%" PRIu32 ")", |
| __FUNCTION__, metadata->entry_count, metadata->entry_capacity); |
| return ERROR; |
| } |
| |
| const metadata_uptrdiff_t entries_end = |
| metadata->entries_start + metadata->entry_capacity; |
| if (entries_end < metadata->entries_start || // overflow check |
| entries_end > metadata->data_start) { |
| |
| ALOGE("%s: Entry start + capacity (%" PRIu32 ") should be <= data start " |
| "(%" PRIu32 ")", |
| __FUNCTION__, |
| (metadata->entries_start + metadata->entry_capacity), |
| metadata->data_start); |
| return ERROR; |
| } |
| |
| const metadata_uptrdiff_t data_end = |
| metadata->data_start + metadata->data_capacity; |
| if (data_end < metadata->data_start || // overflow check |
| data_end > metadata->size) { |
| |
| ALOGE("%s: Data start + capacity (%" PRIu32 ") should be <= total size " |
| "(%" PRIu32 ")", |
| __FUNCTION__, |
| (metadata->data_start + metadata->data_capacity), |
| metadata->size); |
| return ERROR; |
| } |
| |
| // Validate each entry |
| const metadata_size_t entry_count = metadata->entry_count; |
| camera_metadata_buffer_entry_t *entries = get_entries(metadata); |
| |
| for (size_t i = 0; i < entry_count; ++i) { |
| |
| if ((uintptr_t)&entries[i] != ALIGN_TO(&entries[i], ENTRY_ALIGNMENT)) { |
| ALOGE("%s: Entry index %zu had bad alignment (address %p)," |
| " expected alignment %zu", |
| __FUNCTION__, i, &entries[i], ENTRY_ALIGNMENT); |
| return ERROR; |
| } |
| |
| camera_metadata_buffer_entry_t entry = entries[i]; |
| |
| if (entry.type >= NUM_TYPES) { |
| ALOGE("%s: Entry index %zu had a bad type %d", |
| __FUNCTION__, i, entry.type); |
| return ERROR; |
| } |
| |
| // TODO: fix vendor_tag_ops across processes so we don't need to special |
| // case vendor-specific tags |
| uint32_t tag_section = entry.tag >> 16; |
| int tag_type = get_camera_metadata_tag_type(entry.tag); |
| if (tag_type != (int)entry.type && tag_section < VENDOR_SECTION) { |
| ALOGE("%s: Entry index %zu had tag type %d, but the type was %d", |
| __FUNCTION__, i, tag_type, entry.type); |
| return ERROR; |
| } |
| |
| size_t data_size = |
| calculate_camera_metadata_entry_data_size(entry.type, |
| entry.count); |
| |
| if (data_size != 0) { |
| camera_metadata_data_t *data = |
| (camera_metadata_data_t*) (get_data(metadata) + |
| entry.data.offset); |
| |
| if ((uintptr_t)data != ALIGN_TO(data, DATA_ALIGNMENT)) { |
| ALOGE("%s: Entry index %zu had bad data alignment (address %p)," |
| " expected align %zu, (tag name %s, data size %zu)", |
| __FUNCTION__, i, data, DATA_ALIGNMENT, |
| get_camera_metadata_tag_name(entry.tag) ?: "unknown", |
| data_size); |
| return ERROR; |
| } |
| |
| size_t data_entry_end = entry.data.offset + data_size; |
| if (data_entry_end < entry.data.offset || // overflow check |
| data_entry_end > metadata->data_capacity) { |
| |
| ALOGE("%s: Entry index %zu data ends (%zu) beyond the capacity " |
| "%" PRIu32, __FUNCTION__, i, data_entry_end, |
| metadata->data_capacity); |
| return ERROR; |
| } |
| |
| } else if (entry.count == 0) { |
| if (entry.data.offset != 0) { |
| ALOGE("%s: Entry index %zu had 0 items, but offset was non-0 " |
| "(%" PRIu32 "), tag name: %s", __FUNCTION__, i, entry.data.offset, |
| get_camera_metadata_tag_name(entry.tag) ?: "unknown"); |
| return ERROR; |
| } |
| } // else data stored inline, so we look at value which can be anything. |
| } |
| |
| return OK; |
| } |
| |
| int append_camera_metadata(camera_metadata_t *dst, |
| const camera_metadata_t *src) { |
| if (dst == NULL || src == NULL ) return ERROR; |
| |
| if (dst->entry_capacity < src->entry_count + dst->entry_count) return ERROR; |
| if (dst->data_capacity < src->data_count + dst->data_count) return ERROR; |
| |
| memcpy(get_entries(dst) + dst->entry_count, get_entries(src), |
| sizeof(camera_metadata_buffer_entry_t[src->entry_count])); |
| memcpy(get_data(dst) + dst->data_count, get_data(src), |
| sizeof(uint8_t[src->data_count])); |
| if (dst->data_count != 0) { |
| camera_metadata_buffer_entry_t *entry = get_entries(dst) + dst->entry_count; |
| for (size_t i = 0; i < src->entry_count; i++, entry++) { |
| if ( calculate_camera_metadata_entry_data_size(entry->type, |
| entry->count) > 0 ) { |
| entry->data.offset += dst->data_count; |
| } |
| } |
| } |
| if (dst->entry_count == 0) { |
| // Appending onto empty buffer, keep sorted state |
| dst->flags |= src->flags & FLAG_SORTED; |
| } else if (src->entry_count != 0) { |
| // Both src, dst are nonempty, cannot assume sort remains |
| dst->flags &= ~FLAG_SORTED; |
| } else { |
| // Src is empty, keep dst sorted state |
| } |
| dst->entry_count += src->entry_count; |
| dst->data_count += src->data_count; |
| |
| assert(validate_camera_metadata_structure(dst, NULL) == OK); |
| return OK; |
| } |
| |
| camera_metadata_t *clone_camera_metadata(const camera_metadata_t *src) { |
| int res; |
| if (src == NULL) return NULL; |
| camera_metadata_t *clone = allocate_camera_metadata( |
| get_camera_metadata_entry_count(src), |
| get_camera_metadata_data_count(src)); |
| if (clone != NULL) { |
| res = append_camera_metadata(clone, src); |
| if (res != OK) { |
| free_camera_metadata(clone); |
| clone = NULL; |
| } |
| } |
| assert(validate_camera_metadata_structure(clone, NULL) == OK); |
| return clone; |
| } |
| |
| 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); |
| } |
| |
| 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; |
| |
| size_t data_bytes = |
| calculate_camera_metadata_entry_data_size(type, data_count); |
| if (data_bytes + dst->data_count > dst->data_capacity) return ERROR; |
| |
| size_t data_payload_bytes = |
| data_count * camera_metadata_type_size[type]; |
| 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; |
| |
| 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; |
| } |
| dst->entry_count++; |
| dst->flags &= ~FLAG_SORTED; |
| assert(validate_camera_metadata_structure(dst, NULL) == OK); |
| return OK; |
| } |
| |
| int add_camera_metadata_entry(camera_metadata_t *dst, |
| uint32_t tag, |
| const void *data, |
| size_t data_count) { |
| |
| 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, |
| tag, |
| type, |
| data, |
| data_count); |
| } |
| |
| static int compare_entry_tags(const void *p1, const void *p2) { |
| uint32_t tag1 = ((camera_metadata_buffer_entry_t*)p1)->tag; |
| uint32_t tag2 = ((camera_metadata_buffer_entry_t*)p2)->tag; |
| return tag1 < tag2 ? -1 : |
| tag1 == tag2 ? 0 : |
| 1; |
| } |
| |
| int sort_camera_metadata(camera_metadata_t *dst) { |
| if (dst == NULL) return ERROR; |
| if (dst->flags & FLAG_SORTED) return OK; |
| |
| qsort(get_entries(dst), dst->entry_count, |
| sizeof(camera_metadata_buffer_entry_t), |
| compare_entry_tags); |
| dst->flags |= FLAG_SORTED; |
| |
| assert(validate_camera_metadata_structure(dst, NULL) == OK); |
| return OK; |
| } |
| |
| 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; |
| } |
| |
| int get_camera_metadata_ro_entry(const camera_metadata_t *src, |
| size_t index, |
| camera_metadata_ro_entry_t *entry) { |
| return get_camera_metadata_entry((camera_metadata_t*)src, index, |
| (camera_metadata_entry_t*)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 find_camera_metadata_ro_entry(const camera_metadata_t *src, |
| uint32_t tag, |
| camera_metadata_ro_entry_t *entry) { |
| return find_camera_metadata_entry((camera_metadata_t*)src, tag, |
| (camera_metadata_entry_t*)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; |
| |
| camera_metadata_buffer_entry_t *entry = get_entries(dst) + index; |
| size_t data_bytes = calculate_camera_metadata_entry_data_size(entry->type, |
| entry->count); |
| |
| if (data_bytes > 0) { |
| // 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; |
| memmove(start, end, length); |
| |
| // 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; |
| } |
| // 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; |
| } |
| |
| 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; |
| |
| camera_metadata_buffer_entry_t *entry = get_entries(dst) + index; |
| |
| 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) { |
| // May need to shift/add to data array |
| if (dst->data_capacity < dst->data_count + data_bytes - entry_bytes) { |
| // No room |
| return ERROR; |
| } |
| 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; |
| } |
| } |
| |
| 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) { |
| // 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; |
| } |
| |
| static const vendor_tag_ops_t *vendor_tag_ops = NULL; |
| |
| const char *get_camera_metadata_section_name(uint32_t tag) { |
| uint32_t tag_section = tag >> 16; |
| if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) { |
| return vendor_tag_ops->get_section_name( |
| vendor_tag_ops, |
| tag); |
| } |
| if (tag_section >= ANDROID_SECTION_COUNT) { |
| return NULL; |
| } |
| return camera_metadata_section_names[tag_section]; |
| } |
| |
| const char *get_camera_metadata_tag_name(uint32_t tag) { |
| uint32_t tag_section = tag >> 16; |
| if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) { |
| return vendor_tag_ops->get_tag_name( |
| vendor_tag_ops, |
| tag); |
| } |
| if (tag_section >= ANDROID_SECTION_COUNT || |
| tag >= camera_metadata_section_bounds[tag_section][1] ) { |
| return NULL; |
| } |
| uint32_t tag_index = tag & 0xFFFF; |
| return tag_info[tag_section][tag_index].tag_name; |
| } |
| |
| int get_camera_metadata_tag_type(uint32_t tag) { |
| uint32_t tag_section = tag >> 16; |
| if (tag_section >= VENDOR_SECTION && vendor_tag_ops != NULL) { |
| return vendor_tag_ops->get_tag_type( |
| vendor_tag_ops, |
| tag); |
| } |
| if (tag_section >= ANDROID_SECTION_COUNT || |
| tag >= camera_metadata_section_bounds[tag_section][1] ) { |
| return -1; |
| } |
| uint32_t tag_index = tag & 0xFFFF; |
| return tag_info[tag_section][tag_index].tag_type; |
| } |
| |
| int set_camera_metadata_vendor_tag_ops(const vendor_tag_query_ops_t* ops) { |
| // **DEPRECATED** |
| (void) ops; |
| ALOGE("%s: This function has been deprecated", __FUNCTION__); |
| return ERROR; |
| } |
| |
| // Declared in system/media/private/camera/include/camera_metadata_hidden.h |
| int set_camera_metadata_vendor_ops(const vendor_tag_ops_t* ops) { |
| vendor_tag_ops = ops; |
| return OK; |
| } |
| |
| static void print_data(int fd, const uint8_t *data_ptr, uint32_t tag, int type, |
| int count, |
| int indentation); |
| |
| void dump_camera_metadata(const camera_metadata_t *metadata, |
| int fd, |
| int verbosity) { |
| dump_indented_camera_metadata(metadata, fd, verbosity, 0); |
| } |
| |
| void dump_indented_camera_metadata(const camera_metadata_t *metadata, |
| int fd, |
| int verbosity, |
| int indentation) { |
| if (metadata == NULL) { |
| dprintf(fd, "%*sDumping camera metadata array: Not allocated\n", |
| indentation, ""); |
| return; |
| } |
| unsigned int i; |
| dprintf(fd, |
| "%*sDumping camera metadata array: %" PRIu32 " / %" PRIu32 " entries, " |
| "%" PRIu32 " / %" PRIu32 " bytes of extra data.\n", indentation, "", |
| metadata->entry_count, metadata->entry_capacity, |
| metadata->data_count, metadata->data_capacity); |
| dprintf(fd, "%*sVersion: %d, Flags: %08x\n", |
| indentation + 2, "", |
| metadata->version, metadata->flags); |
| camera_metadata_buffer_entry_t *entry = get_entries(metadata); |
| for (i=0; i < metadata->entry_count; i++, entry++) { |
| |
| const char *tag_name, *tag_section; |
| tag_section = get_camera_metadata_section_name(entry->tag); |
| if (tag_section == NULL) { |
| tag_section = "unknownSection"; |
| } |
| tag_name = get_camera_metadata_tag_name(entry->tag); |
| if (tag_name == NULL) { |
| tag_name = "unknownTag"; |
| } |
| const char *type_name; |
| if (entry->type >= NUM_TYPES) { |
| type_name = "unknown"; |
| } else { |
| type_name = camera_metadata_type_names[entry->type]; |
| } |
| dprintf(fd, "%*s%s.%s (%05x): %s[%" PRIu32 "]\n", |
| indentation + 2, "", |
| tag_section, |
| tag_name, |
| entry->tag, |
| type_name, |
| entry->count); |
| |
| if (verbosity < 1) continue; |
| |
| if (entry->type >= NUM_TYPES) continue; |
| |
| size_t type_size = camera_metadata_type_size[entry->type]; |
| uint8_t *data_ptr; |
| if ( type_size * entry->count > 4 ) { |
| if (entry->data.offset >= metadata->data_count) { |
| ALOGE("%s: Malformed entry data offset: %" PRIu32 " (max %" PRIu32 ")", |
| __FUNCTION__, |
| entry->data.offset, |
| metadata->data_count); |
| continue; |
| } |
| data_ptr = get_data(metadata) + entry->data.offset; |
| } else { |
| data_ptr = entry->data.value; |
| } |
| int count = entry->count; |
| if (verbosity < 2 && count > 16) count = 16; |
| |
| print_data(fd, data_ptr, entry->tag, entry->type, count, indentation); |
| } |
| } |
| |
| static void print_data(int fd, const uint8_t *data_ptr, uint32_t tag, |
| int type, int count, int indentation) { |
| static int values_per_line[NUM_TYPES] = { |
| [TYPE_BYTE] = 16, |
| [TYPE_INT32] = 4, |
| [TYPE_FLOAT] = 8, |
| [TYPE_INT64] = 2, |
| [TYPE_DOUBLE] = 4, |
| [TYPE_RATIONAL] = 2, |
| }; |
| size_t type_size = camera_metadata_type_size[type]; |
| char value_string_tmp[CAMERA_METADATA_ENUM_STRING_MAX_SIZE]; |
| uint32_t value; |
| |
| int lines = count / values_per_line[type]; |
| if (count % values_per_line[type] != 0) lines++; |
| |
| int index = 0; |
| int j, k; |
| for (j = 0; j < lines; j++) { |
| dprintf(fd, "%*s[", indentation + 4, ""); |
| for (k = 0; |
| k < values_per_line[type] && count > 0; |
| k++, count--, index += type_size) { |
| |
| switch (type) { |
| case TYPE_BYTE: |
| value = *(data_ptr + index); |
| if (camera_metadata_enum_snprint(tag, |
| value, |
| value_string_tmp, |
| sizeof(value_string_tmp)) |
| == OK) { |
| dprintf(fd, "%s ", value_string_tmp); |
| } else { |
| dprintf(fd, "%hhu ", |
| *(data_ptr + index)); |
| } |
| break; |
| case TYPE_INT32: |
| value = |
| *(int32_t*)(data_ptr + index); |
| if (camera_metadata_enum_snprint(tag, |
| value, |
| value_string_tmp, |
| sizeof(value_string_tmp)) |
| == OK) { |
| dprintf(fd, "%s ", value_string_tmp); |
| } else { |
| dprintf(fd, "%" PRId32 " ", |
| *(int32_t*)(data_ptr + index)); |
| } |
| break; |
| case TYPE_FLOAT: |
| dprintf(fd, "%0.8f ", |
| *(float*)(data_ptr + index)); |
| break; |
| case TYPE_INT64: |
| dprintf(fd, "%" PRId64 " ", |
| *(int64_t*)(data_ptr + index)); |
| break; |
| case TYPE_DOUBLE: |
| dprintf(fd, "%0.8f ", |
| *(double*)(data_ptr + index)); |
| break; |
| case TYPE_RATIONAL: { |
| int32_t numerator = *(int32_t*)(data_ptr + index); |
| int32_t denominator = *(int32_t*)(data_ptr + index + 4); |
| dprintf(fd, "(%d / %d) ", |
| numerator, denominator); |
| break; |
| } |
| default: |
| dprintf(fd, "??? "); |
| } |
| } |
| dprintf(fd, "]\n"); |
| } |
| } |