/* * Copyright (C) 2016 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_Exif" #include #include #include #include #include #include "Exif.h" #include #include #include #include #include #include #include "fake-pipeline2/Sensor.h" // For GPS timestamping we want to ensure we use a 64-bit time_t, 32-bit // platforms have time64_t but 64-bit platforms do not. #if defined(__LP64__) #include using Timestamp = time_t; #define TIMESTAMP_TO_TM(timestamp, tm) gmtime_r(timestamp, tm) #else #include using Timestamp = time64_t; #define TIMESTAMP_TO_TM(timestamp, tm) gmtime64_r(timestamp, tm) #endif namespace android { // A prefix that is used for tags with the "undefined" format to indicate that // the contents are ASCII encoded. See the user comment section of the EXIF spec // for more details http://www.exif.org/Exif2-2.PDF static const unsigned char kAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 // "ASCII\0\0\0" }; // Remove an existing EXIF entry from |exifData| if it exists. This is useful // when replacing existing data, it's easier to just remove the data and // re-allocate it than to adjust the amount of allocated data. static void removeExistingEntry(ExifData* exifData, ExifIfd ifd, int tag) { ExifEntry* entry = exif_content_get_entry(exifData->ifd[ifd], static_cast(tag)); if (entry) { exif_content_remove_entry(exifData->ifd[ifd], entry); } } static ExifEntry* allocateEntry(int tag, ExifFormat format, unsigned int numComponents) { ExifMem* mem = exif_mem_new_default(); ExifEntry* entry = exif_entry_new_mem(mem); unsigned int size = numComponents * exif_format_get_size(format); entry->data = reinterpret_cast(exif_mem_alloc(mem, size)); entry->size = size; entry->tag = static_cast(tag); entry->components = numComponents; entry->format = format; exif_mem_unref(mem); return entry; } // Create an entry and place it in |exifData|, the entry is initialized with an // array of floats from |values| template static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, const float (&values)[N], float denominator = 1000.0) { removeExistingEntry(exifData, ifd, tag); ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_RATIONAL, N); exif_content_add_entry(exifData->ifd[ifd], entry); unsigned int rationalSize = exif_format_get_size(EXIF_FORMAT_RATIONAL); for (size_t i = 0; i < N; ++i) { ExifRational rational = { static_cast(values[i] * denominator), static_cast(denominator) }; exif_set_rational(&entry->data[i * rationalSize], byteOrder, rational); } // Unref entry after changing owner to the ExifData struct exif_entry_unref(entry); return true; } // Create an entry with a single float |value| in it and place it in |exifData| static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, const float value, float denominator = 1000.0) { float values[1] = { value }; // Recycling functions is good for the environment return createEntry(exifData, ifd, tag, values, denominator); } // Create an entry and place it in |exifData|, the entry contains the raw data // pointed to by |data| of length |size|. static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, const unsigned char* data, size_t size, ExifFormat format = EXIF_FORMAT_UNDEFINED) { removeExistingEntry(exifData, ifd, tag); ExifEntry* entry = allocateEntry(tag, format, size); memcpy(entry->data, data, size); exif_content_add_entry(exifData->ifd[ifd], entry); // Unref entry after changing owner to the ExifData struct exif_entry_unref(entry); return true; } // Create an entry and place it in |exifData|, the entry is initialized with // the string provided in |value| static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, const char* value) { unsigned int length = strlen(value) + 1; const unsigned char* data = reinterpret_cast(value); return createEntry(exifData, ifd, tag, data, length, EXIF_FORMAT_ASCII); } // Create an entry and place it in |exifData|, the entry is initialized with a // single byte in |value| //static bool createEntry(ExifData* exifData, // ExifIfd ifd, // int tag, // uint8_t value) { // return createEntry(exifData, ifd, tag, &value, 1, EXIF_FORMAT_BYTE); //} // Create an entry and place it in |exifData|, the entry is default initialized // by the exif library based on |tag| static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag) { removeExistingEntry(exifData, ifd, tag); ExifEntry* entry = exif_entry_new(); exif_content_add_entry(exifData->ifd[ifd], entry); exif_entry_initialize(entry, static_cast(tag)); // Unref entry after changing owner to the ExifData struct exif_entry_unref(entry); return true; } // Create an entry with a single EXIF LONG (32-bit value) and place it in // |exifData|. static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, int value) { removeExistingEntry(exifData, ifd, tag); ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_LONG, 1); exif_content_add_entry(exifData->ifd[ifd], entry); exif_set_long(entry->data, byteOrder, value); // Unref entry after changing owner to the ExifData struct exif_entry_unref(entry); return true; } // Create an entry with a single EXIF SHORT (16-bit value) and place it in // |exifData|. static bool createEntry(ExifData* exifData, ExifIfd ifd, int tag, uint16_t value) { removeExistingEntry(exifData, ifd, tag); ExifByteOrder byteOrder = exif_data_get_byte_order(exifData); ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_SHORT, 1); exif_content_add_entry(exifData->ifd[ifd], entry); exif_set_short(entry->data, byteOrder, value); // Unref entry after changing owner to the ExifData struct exif_entry_unref(entry); return true; } static bool getCameraParam(const CameraParameters& parameters, const char* parameterKey, const char** outValue) { const char* value = parameters.get(parameterKey); if (value) { *outValue = value; return true; } return false; } static bool getCameraParam(const CameraParameters& parameters, const char* parameterKey, float* outValue) { const char* value = parameters.get(parameterKey); if (value) { *outValue = parameters.getFloat(parameterKey); return true; } return false; } static bool getCameraParam(const CameraParameters& parameters, const char* parameterKey, int64_t* outValue) { const char* value = parameters.get(parameterKey); if (value) { char trailing = 0; // Attempt to scan an extra character and then make sure it was not // scanned by checking that the return value indicates only one item. // This way we fail on any trailing characters if (sscanf(value, "%" SCNd64 "%c", outValue, &trailing) == 1) { return true; } } return false; } // Convert a GPS coordinate represented as a decimal degree value to sexagesimal // GPS coordinates comprised of ' " static void convertGpsCoordinate(float degrees, float (*result)[3]) { float absDegrees = fabs(degrees); // First value is degrees without any decimal digits (*result)[0] = floor(absDegrees); // Subtract degrees so we only have the fraction left, then multiply by // 60 to get the minutes float minutes = (absDegrees - (*result)[0]) * 60.0f; (*result)[1] = floor(minutes); // Same thing for seconds but here we store seconds with the fraction float seconds = (minutes - (*result)[1]) * 60.0f; (*result)[2] = seconds; } // Convert a UNIX epoch timestamp to a timestamp comprised of three floats for // hour, minute and second, and a date part that is represented as a string. static bool convertTimestampToTimeAndDate(int64_t timestamp, float (*timeValues)[3], std::string* date) { Timestamp time = timestamp; struct tm utcTime; if (TIMESTAMP_TO_TM(&time, &utcTime) == nullptr) { ALOGE("Could not decompose timestamp into components"); return false; } (*timeValues)[0] = utcTime.tm_hour; (*timeValues)[1] = utcTime.tm_min; (*timeValues)[2] = utcTime.tm_sec; char buffer[64] = {}; if (strftime(buffer, sizeof(buffer), "%Y:%m:%d", &utcTime) == 0) { ALOGE("Could not construct date string from timestamp"); return false; } *date = buffer; return true; } // Convert and store key values in CameraMetadata static void convertToMetadata(const CameraParameters& src, CameraMetadata& dst) { int64_t longValue; float floatValue, floatGps[3]; const char* stringValue; // Orientation if (getCameraParam(src, CameraParameters::KEY_ROTATION, &longValue)) { int32_t degrees = (int32_t)longValue; dst.update(ANDROID_JPEG_ORIENTATION, °rees, 1); } // Focal length if (getCameraParam(src, CameraParameters::KEY_FOCAL_LENGTH, &floatValue)) { dst.update(ANDROID_LENS_FOCAL_LENGTH, &floatValue, 1); } // GPS latitude longitude and altitude if (getCameraParam(src, CameraParameters::KEY_GPS_LATITUDE, &floatGps[0]) && getCameraParam(src, CameraParameters::KEY_GPS_LONGITUDE, &floatGps[1]) && getCameraParam(src, CameraParameters::KEY_GPS_ALTITUDE, &floatGps[2])) { double gps[3]; gps[0] = (double)floatGps[0]; gps[1] = (double)floatGps[1]; gps[2] = (double)floatGps[2]; dst.update(ANDROID_JPEG_GPS_COORDINATES, gps, 3); } // GPS timestamp and datestamp if (getCameraParam(src, CameraParameters::KEY_GPS_TIMESTAMP, &longValue)) { dst.update(ANDROID_JPEG_GPS_TIMESTAMP, &longValue, 1); } // GPS processing method if (getCameraParam(src, CameraParameters::KEY_GPS_PROCESSING_METHOD, &stringValue)) { dst.update(ANDROID_JPEG_GPS_PROCESSING_METHOD, (unsigned char*)stringValue, strlen(stringValue)); } } // Create Exif data common for both HAL1 and HAL3 static ExifData* createExifDataCommon(const CameraMetadata& params, int width, int height) { ExifData* exifData = exif_data_new(); exif_data_set_option(exifData, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); exif_data_set_data_type(exifData, EXIF_DATA_TYPE_COMPRESSED); exif_data_set_byte_order(exifData, EXIF_BYTE_ORDER_INTEL); // Create mandatory exif fields and set their default values exif_data_fix(exifData); float triplet[3]; const char* stringValue; int32_t degrees; float focalLength; // Datetime, creating and initializing a datetime tag will automatically // set the current date and time in the tag so just do that. createEntry(exifData, EXIF_IFD_0, EXIF_TAG_DATE_TIME); // Make and model std::vector prop(PROPERTY_VALUE_MAX); property_get("ro.product.manufacturer", &prop[0], ""); createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MAKE, &prop[0]); property_get("ro.product.model", &prop[0], ""); createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MODEL, &prop[0]); // Width and height if (width > 0 && height > 0) { createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width); createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, height); } camera_metadata_ro_entry_t entry; entry = params.find(ANDROID_LENS_FOCAL_LENGTH); focalLength = (entry.count > 0) ? entry.data.f[0] : 5.0f; createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, focalLength); entry = params.find(ANDROID_JPEG_ORIENTATION); degrees = (entry.count > 0) ? entry.data.i32[0] : 0; ALOGV("degrees %d focalLength %f", degrees, focalLength); enum { EXIF_ROTATE_CAMERA_CW0 = 1, EXIF_ROTATE_CAMERA_CW90 = 6, EXIF_ROTATE_CAMERA_CW180 = 3, EXIF_ROTATE_CAMERA_CW270 = 8, }; uint16_t exifOrien = 1; switch (degrees) { case 0: exifOrien = EXIF_ROTATE_CAMERA_CW0; break; case 90: exifOrien = EXIF_ROTATE_CAMERA_CW90; break; case 180: exifOrien = EXIF_ROTATE_CAMERA_CW180; break; case 270: exifOrien = EXIF_ROTATE_CAMERA_CW270; break; } createEntry(exifData, EXIF_IFD_0, EXIF_TAG_ORIENTATION, exifOrien); // GPS information entry = params.find(ANDROID_JPEG_GPS_COORDINATES); if (entry.count > 0) { ALOGV("Latitude %f Longitude %f Altitude %f", entry.data.d[0], entry.data.d[1], entry.data.d[2]); convertGpsCoordinate(entry.data.d[0], &triplet); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, triplet); const char* ref = entry.data.d[0] < 0.0f ? "S" : "N"; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, ref); // GPS longitude and reference, reference indicates sign, store unsigned convertGpsCoordinate(entry.data.d[1], &triplet); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, triplet); ref = entry.data.d[1] < 0.0f ? "W" : "E"; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, ref); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE, static_cast(fabs(entry.data.d[2]))); int ref1; // 1 indicated below sea level, 0 indicates above sea level ref1 = entry.data.d[2] < 0.0f ? 1 : 0; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, ref1); } int64_t timestamp = 0; entry = params.find(ANDROID_JPEG_GPS_TIMESTAMP); if (entry.count > 0) { timestamp = entry.data.i64[0]; std::string date; if (convertTimestampToTimeAndDate(timestamp, &triplet, &date)) { createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_TIME_STAMP, triplet, 1.0f); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_DATE_STAMP, date.c_str()); } } // GPS processing method entry = params.find(ANDROID_JPEG_GPS_PROCESSING_METHOD); if (entry.count > 0) { stringValue = (const char*)entry.data.u8; ALOGV("ANDROID_JPEG_GPS_PROCESSING_METHOD(len=%d) %s", entry.count, stringValue); std::vector data; // Because this is a tag with an undefined format it has to be prefixed // with the encoding type. Insert an ASCII prefix first, then the // actual string. Undefined tags do not have to be null terminated. data.insert(data.end(), std::begin(kAsciiPrefix), std::end(kAsciiPrefix)); data.insert(data.end(), stringValue, stringValue + entry.count); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_PROCESSING_METHOD, &data[0], data.size()); } return exifData; } ExifData* createExifData(const CameraMetadata& params, int width, int height) { ExifData* exifData = createExifDataCommon(params, width, height); // Exposure Time camera_metadata_ro_entry entry; entry= params.find(ANDROID_SENSOR_EXPOSURE_TIME); int64_t exposureTimesNs = (entry.count > 0) ? entry.data.i64[0] : Sensor::kExposureTimeRange[0]; createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, exposureTimesNs/1000000000.0f, 1000000000); // Aperture entry = params.find(ANDROID_LENS_APERTURE); float aperture = (entry.count > 0) ? entry.data.f[0] : 2.8; createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, aperture); // Flash, 0 for off entry = params.find(ANDROID_FLASH_MODE); uint16_t flash = (entry.count > 0) ? entry.data.i32[0] : 0; createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash); // White balance, 0 for auto, 1 for manual. entry = params.find(ANDROID_CONTROL_AWB_MODE); uint16_t awb = 1; if (entry.count > 0 && entry.data.i32[0] == ANDROID_CONTROL_AWB_MODE_AUTO) { awb = 0; } createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, awb); // ISO entry = params.find(ANDROID_SENSOR_SENSITIVITY); int isoSpeedRating = (entry.count > 0) ? entry.data.i32[0] : Sensor::kSensitivityRange[0]; createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, (uint16_t)isoSpeedRating); // Date and time createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED); // Sub second time createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, "0"); createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, "0"); createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, "0"); return exifData; } ExifData* createExifData(const CameraParameters& params) { int width = -1, height = -1; CameraMetadata cameraMetadata; convertToMetadata(params, cameraMetadata); params.getPictureSize(&width, &height); ExifData* exifData = createExifDataCommon(cameraMetadata, width, height); return exifData; } void freeExifData(ExifData* exifData) { exif_data_free(exifData); } } // namespace android