CameraHal: Add more Exif data for video snapshot

Fixes b/5582076.  Requires changes to Ducati and external/jhead.

1. Add API to get and set ancillary data metadata so
   we can use the values to fill in Exif data.
2. Add support for additional tags.

Change-Id: I537e683839c59e92a4a20ff62653b6d46e303f53
Signed-off-by: Tyler Luu <tluu@ti.com>
Signed-off-by: Iliyan Malchev <malchev@google.com>
diff --git a/camera/Encoder_libjpeg.cpp b/camera/Encoder_libjpeg.cpp
index 0240fb0..40b237b 100644
--- a/camera/Encoder_libjpeg.cpp
+++ b/camera/Encoder_libjpeg.cpp
@@ -44,6 +44,7 @@
 }
 
 #define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))
+#define MIN(x,y) ((x < y) ? x : y)
 
 namespace android {
 struct string_pair {
@@ -305,7 +306,7 @@
 }
 
 status_t ExifElementsTable::insertElement(const char* tag, const char* value) {
-    int value_length = 0;
+    unsigned int value_length = 0;
     status_t ret = NO_ERROR;
 
     if (!value || !tag) {
@@ -331,6 +332,13 @@
         table[position].GpsTag = FALSE;
         table[position].Tag = TagNameToValue(tag);
         exif_tag_count++;
+
+        // jhead isn't taking datetime tag...this is a WA
+        if (strcmp(tag, TAG_DATETIME) == 0) {
+            ImageInfo.numDateTimeTags = 1;
+            memcpy(ImageInfo.DateTime, value,
+                   MIN(ARRAY_SIZE(ImageInfo.DateTime), value_length + 1));
+        }
     }
 
     table[position].DataLength = 0;
diff --git a/camera/OMXCameraAdapter/OMXCameraAdapter.cpp b/camera/OMXCameraAdapter/OMXCameraAdapter.cpp
index 01d37ea..c5dff6c 100755
--- a/camera/OMXCameraAdapter/OMXCameraAdapter.cpp
+++ b/camera/OMXCameraAdapter/OMXCameraAdapter.cpp
@@ -1757,7 +1757,6 @@
     OMX_ERRORTYPE eError = OMX_ErrorNone;
     OMXCameraPortParameters *mPreviewData = NULL;
     OMXCameraPortParameters *measurementData = NULL;
-    OMX_CONFIG_EXTRADATATYPE extraDataControl;
 
     LOG_FUNCTION_NAME;
 
@@ -1869,15 +1868,7 @@
     // whether the preview frame is a snapshot
     if ( OMX_ErrorNone == eError)
         {
-        OMX_INIT_STRUCT_PTR (&extraDataControl, OMX_CONFIG_EXTRADATATYPE);
-        extraDataControl.nPortIndex = OMX_ALL;
-        extraDataControl.eExtraDataType = OMX_AncillaryData;
-        extraDataControl.bEnable = OMX_TRUE;
-
-        eError =  OMX_SetConfig(mCameraAdapterParameters.mHandleComp,
-                               ( OMX_INDEXTYPE ) OMX_IndexConfigOtherExtraDataControl,
-                               &extraDataControl);
-        GOTO_EXIT_IF((eError!=OMX_ErrorNone), eError);
+        ret =  setExtraData(true, OMX_ALL, OMX_AncillaryData);
         }
 
 
@@ -2918,7 +2909,7 @@
     CameraFrame cameraFrame;
     OMX_TI_PLATFORMPRIVATE *platformPrivate;
     OMX_OTHER_EXTRADATATYPE *extraData;
-    OMX_TI_ANCILLARYDATATYPE *ancillaryData;
+    OMX_TI_ANCILLARYDATATYPE *ancillaryData = NULL;
     bool snapshotFrame = false;
 
     res1 = res2 = NO_ERROR;
@@ -2951,7 +2942,6 @@
             }
 
         recalculateFPS();
-
             {
             Mutex::Autolock lock(mFaceDetectionLock);
             if ( mFaceDetectionRunning && !mFaceDetectionPaused ) {
@@ -2977,6 +2967,16 @@
             {
             typeOfFrame = CameraFrame::SNAPSHOT_FRAME;
             mask = (unsigned int)CameraFrame::SNAPSHOT_FRAME;
+
+            // video snapshot gets ancillary data and wb info from last snapshot frame
+            mCaptureAncillaryData = ancillaryData;
+            mWhiteBalanceData = NULL;
+            extraData = getExtradata((OMX_OTHER_EXTRADATATYPE*) platformPrivate->pMetaDataBuffer,
+                                     (OMX_EXTRADATATYPE) OMX_WhiteBalance);
+            if ( NULL != extraData )
+                {
+                mWhiteBalanceData = (OMX_TI_WHITEBALANCERESULTTYPE*) extraData->data;
+                }
             }
         else
             {
@@ -3063,7 +3063,7 @@
             // populate exif data and pass to subscribers via quirk
             // subscriber is in charge of freeing exif data
             ExifElementsTable* exif = new ExifElementsTable();
-            setupEXIF_libjpeg(exif);
+            setupEXIF_libjpeg(exif, mCaptureAncillaryData, mWhiteBalanceData);
             cameraFrame.mQuirks |= CameraFrame::HAS_EXIF_DATA;
             cameraFrame.mCookie2 = (void*) exif;
             }
@@ -3380,6 +3380,40 @@
     return false;
 }
 
+status_t OMXCameraAdapter::setExtraData(bool enable, OMX_U32 nPortIndex, OMX_EXT_EXTRADATATYPE eType) {
+    status_t ret = NO_ERROR;
+    OMX_ERRORTYPE eError = OMX_ErrorNone;
+    OMX_CONFIG_EXTRADATATYPE extraDataControl;
+
+    LOG_FUNCTION_NAME;
+
+    if (OMX_StateInvalid == mComponentState) {
+        CAMHAL_LOGEA("OMX component is in invalid state");
+        return -EINVAL;
+    }
+
+    OMX_INIT_STRUCT_PTR (&extraDataControl, OMX_CONFIG_EXTRADATATYPE);
+
+    extraDataControl.nPortIndex = nPortIndex;
+    extraDataControl.eExtraDataType = eType;
+    extraDataControl.eCameraView = OMX_2D;
+
+    if (enable) {
+        extraDataControl.bEnable = OMX_TRUE;
+    } else {
+        extraDataControl.bEnable = OMX_FALSE;
+    }
+
+    eError =  OMX_SetConfig(mCameraAdapterParameters.mHandleComp,
+                           (OMX_INDEXTYPE) OMX_IndexConfigOtherExtraDataControl,
+                            &extraDataControl);
+
+    LOG_FUNCTION_NAME_EXIT;
+
+    return (ret | ErrorUtils::omxToAndroidError(eError));
+}
+
+
 OMX_OTHER_EXTRADATATYPE *OMXCameraAdapter::getExtradata(OMX_OTHER_EXTRADATATYPE *extraData, OMX_EXTRADATATYPE type)
 {
   if ( NULL != extraData )
diff --git a/camera/OMXCameraAdapter/OMXCapture.cpp b/camera/OMXCameraAdapter/OMXCapture.cpp
index 9ba438c..43d0725 100644
--- a/camera/OMXCameraAdapter/OMXCapture.cpp
+++ b/camera/OMXCameraAdapter/OMXCapture.cpp
@@ -775,6 +775,12 @@
         }
     }
 
+    // need to enable wb data for video snapshot to fill in exif data
+    if ((ret == NO_ERROR) && (mCapMode == VIDEO_MODE)) {
+        // video snapshot uses wb data from snapshot frame
+        ret = setExtraData(true, mCameraAdapterParameters.mPrevPortIndex, OMX_WhiteBalance);
+    }
+
     //OMX shutter callback events are only available in hq mode
     if ( (HIGH_QUALITY == mCapMode) || (HIGH_QUALITY_ZSL== mCapMode))
         {
@@ -866,6 +872,7 @@
 
 EXIT:
     CAMHAL_LOGEB("Exiting function %s because of ret %d eError=%x", __FUNCTION__, ret, eError);
+    setExtraData(false, mCameraAdapterParameters.mPrevPortIndex, OMX_WhiteBalance);
     mWaitingForSnapshot = false;
     mCaptureSignalled = false;
     performCleanupAfterError();
@@ -945,6 +952,13 @@
             goto EXIT;
         }
     }
+
+    // had to enable wb data for video snapshot to fill in exif data
+    // now that we are done...disable
+    if ((ret == NO_ERROR) && (mCapMode == VIDEO_MODE)) {
+        ret = setExtraData(false, mCameraAdapterParameters.mPrevPortIndex, OMX_WhiteBalance);
+    }
+
     CAMHAL_LOGDB("Capture set - 0x%x", eError);
 
     mCaptureSignalled = true; //set this to true if we exited because of timeout
diff --git a/camera/OMXCameraAdapter/OMXExif.cpp b/camera/OMXCameraAdapter/OMXExif.cpp
index 9744cf4..32bfd7d 100755
--- a/camera/OMXCameraAdapter/OMXExif.cpp
+++ b/camera/OMXCameraAdapter/OMXExif.cpp
@@ -497,7 +497,9 @@
     return ret;
 }
 
-status_t OMXCameraAdapter::setupEXIF_libjpeg(ExifElementsTable* exifTable)
+status_t OMXCameraAdapter::setupEXIF_libjpeg(ExifElementsTable* exifTable,
+                                             OMX_TI_ANCILLARYDATATYPE* pAncillaryData,
+                                             OMX_TI_WHITEBALANCERESULTTYPE* pWhiteBalanceData)
 {
     status_t ret = NO_ERROR;
     OMX_ERRORTYPE eError = OMX_ErrorNone;
@@ -544,7 +546,6 @@
                      pTime->tm_hour,
                      pTime->tm_min,
                      pTime->tm_sec );
-
             ret = exifTable->insertElement(TAG_DATETIME, temp_value);
         }
      }
@@ -658,6 +659,129 @@
         }
     }
 
+    // fill in short and ushort tags
+    if (NO_ERROR == ret) {
+        char temp_value[2];
+        temp_value[1] = '\0';
+
+        // AWB
+        if (mParameters3A.WhiteBallance == OMX_WhiteBalControlAuto) {
+            temp_value[0] = '0';
+        } else {
+            temp_value[0] = '1';
+        }
+        exifTable->insertElement(TAG_WHITEBALANCE, temp_value);
+
+        // MeteringMode
+        // TODO(XXX): only supporting this metering mode at the moment, may change in future
+        temp_value[0] = '2';
+        exifTable->insertElement(TAG_METERING_MODE, temp_value);
+
+        // ExposureProgram
+        // TODO(XXX): only supporting this exposure program at the moment, may change in future
+        temp_value[0] = '3';
+        exifTable->insertElement(TAG_EXPOSURE_PROGRAM, temp_value);
+
+        // ColorSpace
+        temp_value[0] = '1';
+        exifTable->insertElement(TAG_COLOR_SPACE, temp_value);
+
+        temp_value[0] = '2';
+        exifTable->insertElement(TAG_SENSING_METHOD, temp_value);
+
+        temp_value[0] = '1';
+        exifTable->insertElement(TAG_CUSTOM_RENDERED, temp_value);
+    }
+
+    if (pAncillaryData && (NO_ERROR == ret)) {
+        unsigned int numerator = 0, denominator = 0;
+        char temp_value[256];
+        unsigned int temp_num = 0;
+
+        // DigitalZoomRatio
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%u/%u",
+                 pAncillaryData->nDigitalZoomFactor, 1024);
+        exifTable->insertElement(TAG_DIGITALZOOMRATIO, temp_value);
+
+        // ExposureTime
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%u/%u",
+                 pAncillaryData->nExposureTime, 1000000);
+        exifTable->insertElement(TAG_EXPOSURETIME, temp_value);
+
+        // ApertureValue and FNumber
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%u/%u",
+                 pAncillaryData->nApertureValue, 100);
+        exifTable->insertElement(TAG_FNUMBER, temp_value);
+        exifTable->insertElement(TAG_APERTURE, temp_value);
+
+        // ISO
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%u,0,0",
+                 pAncillaryData->nCurrentISO);
+        exifTable->insertElement(TAG_ISO_EQUIVALENT, temp_value);
+
+        // ShutterSpeed
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%f",
+                 log(pAncillaryData->nExposureTime) / log(2));
+        ExifElementsTable::stringToRational(temp_value, &numerator, &denominator);
+        snprintf(temp_value, sizeof(temp_value)/sizeof(char), "%u/%u", numerator, denominator);
+        exifTable->insertElement(TAG_SHUTTERSPEED, temp_value);
+
+        // Flash
+        if (mParameters3A.FlashMode == OMX_IMAGE_FlashControlAuto) {
+            if(pAncillaryData->nFlashStatus) temp_num = 0x19; // Flash fired, auto mode
+            else temp_num = 0x18; // Flash did not fire, auto mode
+        } else if (mParameters3A.FlashMode == OMX_IMAGE_FlashControlOn) {
+            if(pAncillaryData->nFlashStatus) temp_num = 0x9; // Flash fired, compulsory flash mode
+            else temp_num = 0x10; // Flash did not fire, compulsory flash mode
+        } else if(pAncillaryData->nFlashStatus) {
+            temp_num = 0x1; // Flash fired
+        } else {
+            temp_num = 0x0; // Flash did not fire
+        }
+        snprintf(temp_value,
+                 sizeof(temp_value)/sizeof(char),
+                 "%u", temp_num);
+        exifTable->insertElement(TAG_FLASH, temp_value);
+
+        if (pWhiteBalanceData) {
+            unsigned int lightsource = 0;
+            unsigned int colourtemp = pWhiteBalanceData->nColorTemperature;
+            bool flash_fired = (temp_num & 0x1); // value from flash above
+
+            // stole this from framework/tools_library/src/tools_sys_exif_tags.c
+            if( colourtemp <= 3200 ) {
+                lightsource = 3; // Tungsten
+            } else if( colourtemp > 3200 && colourtemp <= 4800 ) {
+                lightsource = 2; // Fluorescent
+            } else if( colourtemp > 4800 && colourtemp <= 5500 ) {
+                lightsource = 1; // Daylight
+            } else if( colourtemp > 5500 && colourtemp <= 6500 ) {
+                lightsource = 9; // Fine weather
+            } else if( colourtemp > 6500 ) {
+                lightsource = 10; // Cloudy weather
+            }
+
+            if(flash_fired) {
+                lightsource = 4; // Flash
+            }
+
+            snprintf(temp_value,
+                    sizeof(temp_value)/sizeof(char),
+                    "%u", lightsource);
+            exifTable->insertElement(TAG_LIGHT_SOURCE, temp_value);
+        }
+    }
+
     LOG_FUNCTION_NAME_EXIT;
 
     return ret;
diff --git a/camera/OMXCameraAdapter/OMXFD.cpp b/camera/OMXCameraAdapter/OMXFD.cpp
index 38c7a6e..938ccf6 100644
--- a/camera/OMXCameraAdapter/OMXFD.cpp
+++ b/camera/OMXCameraAdapter/OMXFD.cpp
@@ -125,7 +125,6 @@
 {
     status_t ret = NO_ERROR;
     OMX_ERRORTYPE eError = OMX_ErrorNone;
-    OMX_CONFIG_EXTRADATATYPE extraDataControl;
     OMX_CONFIG_OBJDETECTIONTYPE objDetection;
 
     LOG_FUNCTION_NAME;
@@ -170,27 +169,11 @@
 
     if ( NO_ERROR == ret )
         {
-        OMX_INIT_STRUCT_PTR (&extraDataControl, OMX_CONFIG_EXTRADATATYPE);
-        extraDataControl.nPortIndex = mCameraAdapterParameters.mPrevPortIndex;
-        extraDataControl.eExtraDataType = OMX_FaceDetection;
-        extraDataControl.eCameraView = OMX_2D;
-        if  ( enable )
-            {
-            extraDataControl.bEnable = OMX_TRUE;
-            }
-        else
-            {
-            extraDataControl.bEnable = OMX_FALSE;
-            }
+        ret = setExtraData(enable, mCameraAdapterParameters.mPrevPortIndex, OMX_FaceDetection);
 
-        eError =  OMX_SetConfig(mCameraAdapterParameters.mHandleComp,
-                                ( OMX_INDEXTYPE ) OMX_IndexConfigOtherExtraDataControl,
-                                &extraDataControl);
-        if ( OMX_ErrorNone != eError )
+        if ( NO_ERROR != ret )
             {
-            CAMHAL_LOGEB("Error while configuring face detection extra data 0x%x",
-                         eError);
-            ret = -1;
+            CAMHAL_LOGEA("Error while configuring face detection extra data");
             }
         else
             {
diff --git a/camera/inc/Encoder_libjpeg.h b/camera/inc/Encoder_libjpeg.h
index e3e9ac5..e4dfc0f 100755
--- a/camera/inc/Encoder_libjpeg.h
+++ b/camera/inc/Encoder_libjpeg.h
@@ -43,6 +43,7 @@
                                             void* cookie2,
                                             void* cookie3);
 
+// these have to match strings defined in external/jhead/exif.c
 static const char TAG_MODEL[] = "Model";
 static const char TAG_MAKE[] = "Make";
 static const char TAG_FOCALLENGTH[] = "FocalLength";
@@ -61,6 +62,21 @@
 static const char TAG_GPS_TIMESTAMP[] = "GPSTimeStamp";
 static const char TAG_GPS_DATESTAMP[] = "GPSDateStamp";
 static const char TAG_ORIENTATION[] = "Orientation";
+static const char TAG_FLASH[] = "Flash";
+static const char TAG_DIGITALZOOMRATIO[] = "DigitalZoomRatio";
+static const char TAG_EXPOSURETIME[] = "ExposureTime";
+static const char TAG_APERTURE[] = "ApertureValue";
+static const char TAG_ISO_EQUIVALENT[] = "ISOSpeedRatings";
+static const char TAG_WHITEBALANCE[] = "WhiteBalance";
+static const char TAG_LIGHT_SOURCE[] = "LightSource";
+static const char TAG_METERING_MODE[] = "MeteringMode";
+static const char TAG_EXPOSURE_PROGRAM[] = "ExposureProgram";
+static const char TAG_COLOR_SPACE[] = "ColorSpace";
+static const char TAG_CPRS_BITS_PER_PIXEL[] = "CompressedBitsPerPixel";
+static const char TAG_FNUMBER[] = "FNumber";
+static const char TAG_SHUTTERSPEED[] = "ShutterSpeedValue";
+static const char TAG_SENSING_METHOD[] = "SensingMethod";
+static const char TAG_CUSTOM_RENDERED[] = "CustomRendered";
 
 class ExifElementsTable {
     public:
diff --git a/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h b/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h
index 463032a..981e22d 100644
--- a/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h
+++ b/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h
@@ -446,7 +446,8 @@
                                BaseCameraAdapter::AdapterState state);
     status_t convertGPSCoord(double coord, int &deg, int &min, int &sec, int &secDivisor);
     status_t setupEXIF();
-    status_t setupEXIF_libjpeg(ExifElementsTable*);
+    status_t setupEXIF_libjpeg(ExifElementsTable*, OMX_TI_ANCILLARYDATATYPE*,
+                               OMX_TI_WHITEBALANCERESULTTYPE*);
 
     //Focus functionality
     status_t doAutoFocus();
@@ -640,6 +641,7 @@
     status_t setAutoConvergence(OMX_TI_AUTOCONVERGENCEMODETYPE pACMode, OMX_S32 pManualConverence);
     status_t getAutoConvergence(OMX_TI_AUTOCONVERGENCEMODETYPE *pACMode, OMX_S32 *pManualConverence);
 
+    status_t setExtraData(bool enable, OMX_U32, OMX_EXT_EXTRADATATYPE);
     OMX_OTHER_EXTRADATATYPE *getExtradata(OMX_OTHER_EXTRADATATYPE *extraData, OMX_EXTRADATATYPE type);
 
     class CommandHandler : public Thread {
@@ -873,6 +875,8 @@
     int mSnapshotCount;
     bool mCaptureConfigured;
     unsigned int mPendingCaptureSettings;
+    OMX_TI_ANCILLARYDATATYPE* mCaptureAncillaryData;
+    OMX_TI_WHITEBALANCERESULTTYPE* mWhiteBalanceData;
 
     //Temporal bracketing management data
     mutable Mutex mBracketingLock;