1a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson/*
2a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * Copyright (C) 2016 The Android Open Source Project
3a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson *
4a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * Licensed under the Apache License, Version 2.0 (the "License");
5a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * you may not use this file except in compliance with the License.
6a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * You may obtain a copy of the License at
7a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson *
8a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson *      http://www.apache.org/licenses/LICENSE-2.0
9a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson *
10a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * Unless required by applicable law or agreed to in writing, software
11a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * distributed under the License is distributed on an "AS IS" BASIS,
12a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * See the License for the specific language governing permissions and
14a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson * limitations under the License.
15a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson */
16a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
17a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include "Exif.h"
18a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
19a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#define LOG_NDEBUG 0
20a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#define LOG_TAG "EmulatedCamera_Exif"
21a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <cutils/log.h>
22a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
23a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <inttypes.h>
24a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <math.h>
25a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <stdint.h>
26a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
27a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <camera/CameraParameters.h>
28a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <libexif/exif-data.h>
29a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <libexif/exif-entry.h>
30a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <libexif/exif-ifd.h>
31a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <libexif/exif-tag.h>
32a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
33a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <string>
34a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson#include <vector>
35a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
36248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson// For GPS timestamping we want to ensure we use a 64-bit time_t, 32-bit
37248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson// platforms have time64_t but 64-bit platforms do not.
38248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#if defined(__LP64__)
39248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#include <time.h>
40248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johanssonusing Timestamp = time_t;
41248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#define TIMESTAMP_TO_TM(timestamp, tm) gmtime_r(timestamp, tm)
42248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#else
43248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#include <time64.h>
44248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johanssonusing Timestamp = time64_t;
45248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#define TIMESTAMP_TO_TM(timestamp, tm) gmtime64_r(timestamp, tm)
46248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson#endif
47248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson
48a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonnamespace android {
49a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
50a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// A prefix that is used for tags with the "undefined" format to indicate that
51a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// the contents are ASCII encoded. See the user comment section of the EXIF spec
52a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// for more details http://www.exif.org/Exif2-2.PDF
53a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic const unsigned char kAsciiPrefix[] = {
54a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 // "ASCII\0\0\0"
55a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson};
56a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
57b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson// Remove an existing EXIF entry from |exifData| if it exists. This is useful
58b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson// when replacing existing data, it's easier to just remove the data and
59b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson// re-allocate it than to adjust the amount of allocated data.
60b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johanssonstatic void removeExistingEntry(ExifData* exifData, ExifIfd ifd, int tag) {
61b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    ExifEntry* entry = exif_content_get_entry(exifData->ifd[ifd],
62b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                                              static_cast<ExifTag>(tag));
63b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    if (entry) {
64b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson        exif_content_remove_entry(exifData->ifd[ifd], entry);
65b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    }
66b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson}
67b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson
68a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic ExifEntry* allocateEntry(int tag,
69a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                                ExifFormat format,
70a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                                unsigned int numComponents) {
71a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifMem* mem = exif_mem_new_default();
72a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifEntry* entry = exif_entry_new_mem(mem);
73a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
74a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    unsigned int size = numComponents * exif_format_get_size(format);
75a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    entry->data = reinterpret_cast<unsigned char*>(exif_mem_alloc(mem, size));
76a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    entry->size = size;
77a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    entry->tag = static_cast<ExifTag>(tag);
78a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    entry->components = numComponents;
79a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    entry->format = format;
80a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
81a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_mem_unref(mem);
82a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return entry;
83a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
84a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
85a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry and place it in |exifData|, the entry is initialized with an
86a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// array of floats from |values|
87a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssontemplate<size_t N>
88a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
89a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
90a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag,
91a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        const float (&values)[N],
92a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        float denominator = 1000.0) {
93b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    removeExistingEntry(exifData, ifd, tag);
94a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
95a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_RATIONAL, N);
96a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_content_add_entry(exifData->ifd[ifd], entry);
97a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    unsigned int rationalSize = exif_format_get_size(EXIF_FORMAT_RATIONAL);
98a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    for (size_t i = 0; i < N; ++i) {
99a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        ExifRational rational = {
100a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson            static_cast<uint32_t>(values[i] * denominator),
101a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson            static_cast<uint32_t>(denominator)
102a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        };
103a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
104a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        exif_set_rational(&entry->data[i * rationalSize], byteOrder, rational);
105a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
106a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
107a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Unref entry after changing owner to the ExifData struct
108a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_entry_unref(entry);
109a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return true;
110a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
111a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
112a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry with a single float |value| in it and place it in |exifData|
113a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
114a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
115a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag,
116a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        const float value,
117a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        float denominator = 1000.0) {
118a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float values[1] = { value };
119a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Recycling functions is good for the environment
120a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return createEntry(exifData, ifd, tag, values, denominator);
121a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
122a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
123a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry and place it in |exifData|, the entry contains the raw data
124a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// pointed to by |data| of length |size|.
125a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
126a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
127a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag,
128a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        const unsigned char* data,
129a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        size_t size,
130a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifFormat format = EXIF_FORMAT_UNDEFINED) {
131b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    removeExistingEntry(exifData, ifd, tag);
132a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifEntry* entry = allocateEntry(tag, format, size);
133a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    memcpy(entry->data, data, size);
134a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_content_add_entry(exifData->ifd[ifd], entry);
135a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Unref entry after changing owner to the ExifData struct
136a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_entry_unref(entry);
137a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return true;
138a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
139a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
140a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry and place it in |exifData|, the entry is initialized with
141a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// the string provided in |value|
142a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
143a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
144a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag,
145a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        const char* value) {
146a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    unsigned int length = strlen(value) + 1;
147a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    const unsigned char* data = reinterpret_cast<const unsigned char*>(value);
148a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return createEntry(exifData, ifd, tag, data, length, EXIF_FORMAT_ASCII);
149a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
150a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
151a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry and place it in |exifData|, the entry is initialized with a
152a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// single byte in |value|
153a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
154a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
155a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag,
156a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        uint8_t value) {
157a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return createEntry(exifData, ifd, tag, &value, 1, EXIF_FORMAT_BYTE);
158a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
159a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
160a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Create an entry and place it in |exifData|, the entry is default initialized
161a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// by the exif library based on |tag|
162a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
163a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        ExifIfd ifd,
164a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        int tag) {
165b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    removeExistingEntry(exifData, ifd, tag);
166a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifEntry* entry = exif_entry_new();
167a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_content_add_entry(exifData->ifd[ifd], entry);
168a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_entry_initialize(entry, static_cast<ExifTag>(tag));
169a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Unref entry after changing owner to the ExifData struct
170a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_entry_unref(entry);
171a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return true;
172a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
173a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
174b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson// Create an entry with a single EXIF LONG (32-bit value) and place it in
175b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson// |exifData|.
176b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johanssonstatic bool createEntry(ExifData* exifData,
177b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                        ExifIfd ifd,
178b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                        int tag,
179b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                        int value) {
180b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    removeExistingEntry(exifData, ifd, tag);
181b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
182b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_LONG, 1);
183b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    exif_content_add_entry(exifData->ifd[ifd], entry);
184b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    exif_set_long(entry->data, byteOrder, value);
185b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson
186b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    // Unref entry after changing owner to the ExifData struct
187b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    exif_entry_unref(entry);
188b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    return true;
189b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson}
190b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson
191a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool getCameraParam(const CameraParameters& parameters,
192a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           const char* parameterKey,
193a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           const char** outValue) {
194a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    const char* value = parameters.get(parameterKey);
195a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (value) {
196a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        *outValue = value;
197a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        return true;
198a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
199a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return false;
200a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
201a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
202a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool getCameraParam(const CameraParameters& parameters,
203a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           const char* parameterKey,
204a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           float* outValue) {
205a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    const char* value = parameters.get(parameterKey);
206a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (value) {
207a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        *outValue = parameters.getFloat(parameterKey);
208a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        return true;
209a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
210a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return false;
211a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
212a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
213a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool getCameraParam(const CameraParameters& parameters,
214a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           const char* parameterKey,
215a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                           int64_t* outValue) {
216a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    const char* value = parameters.get(parameterKey);
217a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (value) {
218a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        char dummy = 0;
219a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // Attempt to scan an extra character and then make sure it was not
220a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // scanned by checking that the return value indicates only one item.
221a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // This way we fail on any trailing characters
222a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        if (sscanf(value, "%" SCNd64 "%c", outValue, &dummy) == 1) {
223a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson            return true;
224a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        }
225a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
226a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return false;
227a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
228a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
229a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Convert a GPS coordinate represented as a decimal degree value to sexagesimal
230a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// GPS coordinates comprised of <degrees> <minutes>' <seconds>"
231a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic void convertGpsCoordinate(float degrees, float (*result)[3]) {
232a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float absDegrees = fabs(degrees);
233a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // First value is degrees without any decimal digits
234a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*result)[0] = floor(absDegrees);
235a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
236a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Subtract degrees so we only have the fraction left, then multiply by
237a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // 60 to get the minutes
238a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float minutes = (absDegrees - (*result)[0]) * 60.0f;
239a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*result)[1] = floor(minutes);
240a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
241a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Same thing for seconds but here we store seconds with the fraction
242a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float seconds = (minutes - (*result)[1]) * 60.0f;
243a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*result)[2] = seconds;
244a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
245a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
246a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// Convert a UNIX epoch timestamp to a timestamp comprised of three floats for
247a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson// hour, minute and second, and a date part that is represented as a string.
248a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonstatic bool convertTimestampToTimeAndDate(int64_t timestamp,
249a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                                          float (*timeValues)[3],
250a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                                          std::string* date) {
251248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson    Timestamp time = timestamp;
252a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    struct tm utcTime;
253248ebd6652f632d81c43d79a535be0680ae9ee33Bjoern Johansson    if (TIMESTAMP_TO_TM(&time, &utcTime) == nullptr) {
254a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        ALOGE("Could not decompose timestamp into components");
255a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        return false;
256a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
257a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*timeValues)[0] = utcTime.tm_hour;
258a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*timeValues)[1] = utcTime.tm_min;
259a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    (*timeValues)[2] = utcTime.tm_sec;
260a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
261a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    char buffer[64] = {};
262a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (strftime(buffer, sizeof(buffer), "%Y:%m:%d", &utcTime) == 0) {
263a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        ALOGE("Could not construct date string from timestamp");
264a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        return false;
265a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
266a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    *date = buffer;
267a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return true;
268a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
269a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
270a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern JohanssonExifData* createExifData(const CameraParameters& params) {
271a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    ExifData* exifData = exif_data_new();
272a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
273a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_data_set_option(exifData, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
274a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_data_set_data_type(exifData, EXIF_DATA_TYPE_COMPRESSED);
275a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_data_set_byte_order(exifData, EXIF_BYTE_ORDER_INTEL);
276a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
277a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Create mandatory exif fields and set their default values
278a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_data_fix(exifData);
279a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
280a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float triplet[3];
281a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    float floatValue = 0.0f;
282a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    const char* stringValue;
283a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
284a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Datetime, creating and initializing a datetime tag will automatically
285a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // set the current date and time in the tag so just do that.
286a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    createEntry(exifData, EXIF_IFD_0, EXIF_TAG_DATE_TIME);
287a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
288b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    // Picture size
289b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    int width = -1, height = -1;
290b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    params.getPictureSize(&width, &height);
291b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    if (width >= 0 && height >= 0) {
292b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson        createEntry(exifData, EXIF_IFD_EXIF,
293b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                    EXIF_TAG_PIXEL_X_DIMENSION, width);
294b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson        createEntry(exifData, EXIF_IFD_EXIF,
295b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson                    EXIF_TAG_PIXEL_Y_DIMENSION, height);
296b013ab25d76ab91e08236824a06bde64241b1539Bjoern Johansson    }
297a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // Focal length
298a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
299a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_FOCAL_LENGTH,
300a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &floatValue)) {
301a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, floatValue);
302a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
303a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // GPS latitude and reference, reference indicates sign, store unsigned
304a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
305a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_GPS_LATITUDE,
306a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &floatValue)) {
307a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        convertGpsCoordinate(floatValue, &triplet);
308a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, triplet);
309a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
310a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        const char* ref = floatValue < 0.0f ? "S" : "N";
311a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, ref);
312a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
313a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // GPS longitude and reference, reference indicates sign, store unsigned
314a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
315a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_GPS_LONGITUDE,
316a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &floatValue)) {
317a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        convertGpsCoordinate(floatValue, &triplet);
318a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, triplet);
319a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
320a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        const char* ref = floatValue < 0.0f ? "W" : "E";
321a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, ref);
322a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
323a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // GPS altitude and reference, reference indicates sign, store unsigned
324a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
325a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_GPS_ALTITUDE,
326a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &floatValue)) {
327a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE,
328a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                    static_cast<float>(fabs(floatValue)));
329a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
330a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // 1 indicated below sea level, 0 indicates above sea level
331a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        uint8_t ref = floatValue < 0.0f ? 1 : 0;
332a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, ref);
333a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
334a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // GPS timestamp and datestamp
335a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    int64_t timestamp = 0;
336a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
337a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_GPS_TIMESTAMP,
338a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &timestamp)) {
339a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        std::string date;
340a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        if (convertTimestampToTimeAndDate(timestamp, &triplet, &date)) {
341a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson            createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_TIME_STAMP,
342a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        triplet, 1.0f);
343a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson            createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_DATE_STAMP,
344a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                        date.c_str());
345a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        }
346a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
347a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
348a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    // GPS processing method
349a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    if (getCameraParam(params,
350a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       CameraParameters::KEY_GPS_PROCESSING_METHOD,
351a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                       &stringValue)) {
352a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        std::vector<unsigned char> data;
353a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // Because this is a tag with an undefined format it has to be prefixed
354a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // with the encoding type. Insert an ASCII prefix first, then the
355a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        // actual string. Undefined tags do not have to be null terminated.
356a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        data.insert(data.end(),
357a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                    std::begin(kAsciiPrefix),
358a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                    std::end(kAsciiPrefix));
359a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        data.insert(data.end(), stringValue, stringValue + strlen(stringValue));
360a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson        createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_PROCESSING_METHOD,
361a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson                    &data[0], data.size());
362a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    }
363a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
364a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    return exifData;
365a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
366a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
367a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johanssonvoid freeExifData(ExifData* exifData) {
368a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson    exif_data_free(exifData);
369a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}
370a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
371a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson}  // namespace android
372a11bd58ddc5ce90e7bc66e1cf9c6ed0bad61c376Bjoern Johansson
373