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