diff options
author | Tyler Luu <tluu@ti.com> | 2011-09-07 22:19:09 -0500 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2011-09-12 16:15:48 -0700 |
commit | 36e9bdd56757ff8048e08f6e52f234480c44f122 (patch) | |
tree | 5eccf8d64f6fd808fc2d1a6ed8f590cdc49607ef | |
parent | 6b5eaf29c3d17a24731bf9271bd0d199d433813e (diff) | |
download | omap4xxx-omapzoom-36e9bdd56757ff8048e08f6e52f234480c44f122.tar.gz |
CameraHal: Add Exif support to video snapshot
Use jhead library to insert Exif to jpeg stream
returned from libjpeg.
Change-Id: Ia6398180b7ef3c1b3ddcb35e489527289565fef5
Signed-off-by: Tyler Luu <tluu@ti.com>
-rw-r--r-- | camera/Android.mk | 6 | ||||
-rw-r--r-- | camera/AppCallbackNotifier.cpp | 45 | ||||
-rw-r--r-- | camera/Encoder_libjpeg.cpp | 93 | ||||
-rw-r--r-- | camera/OMXCameraAdapter/OMX3A.cpp | 3 | ||||
-rw-r--r-- | camera/OMXCameraAdapter/OMXCameraAdapter.cpp | 7 | ||||
-rw-r--r-- | camera/OMXCameraAdapter/OMXExif.cpp | 158 | ||||
-rw-r--r-- | camera/inc/CameraHal.h | 6 | ||||
-rw-r--r-- | camera/inc/Encoder_libjpeg.h | 43 | ||||
-rw-r--r-- | camera/inc/OMXCameraAdapter/OMXCameraAdapter.h | 2 |
9 files changed, 351 insertions, 12 deletions
diff --git a/camera/Android.mk b/camera/Android.mk index d9f2b043..e6b1b0d5 100644 --- a/camera/Android.mk +++ b/camera/Android.mk @@ -65,7 +65,8 @@ LOCAL_C_INCLUDES += \ hardware/ti/omap4xxx/domx/mm_osal/inc \ frameworks/base/include/media/stagefright \ frameworks/base/include/media/stagefright/openmax \ - external/jpeg + external/jpeg \ + external/jhead LOCAL_SHARED_LIBRARIES:= \ libui \ @@ -79,7 +80,8 @@ LOCAL_SHARED_LIBRARIES:= \ libgui \ libdomx \ libion \ - libjpeg + libjpeg \ + libexif LOCAL_CFLAGS := -fno-short-enums -DCOPY_IMAGE_BUFFER diff --git a/camera/AppCallbackNotifier.cpp b/camera/AppCallbackNotifier.cpp index 9ff79460..4302988d 100644 --- a/camera/AppCallbackNotifier.cpp +++ b/camera/AppCallbackNotifier.cpp @@ -41,13 +41,13 @@ void AppCallbackNotifierEncoderCallback(size_t jpeg_size, { if (cookie1) { AppCallbackNotifier* cb = (AppCallbackNotifier*) cookie1; - cb->EncoderDoneCb(jpeg_size, src, type, cookie2); + cb->EncoderDoneCb(jpeg_size, src, type, cookie2, cookie3); } } /*--------------------NotificationHandler Class STARTS here-----------------------------*/ -void AppCallbackNotifier::EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFrame::FrameType type, void* cookie1) +void AppCallbackNotifier::EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFrame::FrameType type, void* cookie1, void* cookie2) { camera_memory_t* encoded_mem = NULL; @@ -61,10 +61,30 @@ void AppCallbackNotifier::EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFr // encoded buffer since MemoryHeapBase and MemoryBase are passed as // one camera_memory_t structure. How fix this? - camera_memory_t* picture = mRequestMemory(-1, jpeg_size, 1, NULL); + camera_memory_t* picture = NULL; - if(picture && picture->data && encoded_mem && encoded_mem->data) { - memcpy(picture->data, encoded_mem->data, jpeg_size); + if(encoded_mem && encoded_mem->data) { + if (cookie2) { + ExifElementsTable* exif = (ExifElementsTable*) cookie2; + Section_t* exif_section = NULL; + + exif->insertExifToJpeg((unsigned char*) encoded_mem->data, jpeg_size); + exif_section = FindSection(M_EXIF); + + if (exif_section) { + picture = mRequestMemory(-1, jpeg_size + exif_section->Size, 1, NULL); + if (picture && picture->data) { + exif->saveJpeg((unsigned char*) picture->data, jpeg_size + exif_section->Size); + } + } + delete exif; + cookie2 = NULL; + } else { + picture = mRequestMemory(-1, jpeg_size, 1, NULL); + if (picture && picture->data) { + memcpy(picture->data, encoded_mem->data, jpeg_size); + } + } } { @@ -89,6 +109,10 @@ void AppCallbackNotifier::EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFr picture->release(picture); } + if (cookie2) { + delete (ExifElementsTable*) cookie2; + } + mFrameProvider->returnFrame(src, type); LOG_FUNCTION_NAME_EXIT; @@ -629,6 +653,7 @@ void AppCallbackNotifier::notifyFrame() int encode_quality = 100; const char* valstr = NULL; + void* exif_data = NULL; camera_memory_t* raw_picture = mRequestMemory(-1, frame->mLength, 1, NULL); if(raw_picture) { @@ -643,7 +668,11 @@ void AppCallbackNotifier::notifyFrame() } } - CAMHAL_LOGVB("encoder(%p, %d, %p, %d, %d, %d, %d,%p,%d,%p,%p, %d)", + if (CameraFrame::HAS_EXIF_DATA & frame->mQuirks) { + exif_data = frame->mCookie2; + } + + CAMHAL_LOGVB("encoder(%p, %d, %p, %d, %d, %d, %d,%p,%d,%p,%p,%p)", (uint8_t*)frame->mBuffer, frame->mLength, (uint8_t*)buf, @@ -655,7 +684,7 @@ void AppCallbackNotifier::notifyFrame() frame->mFrameType, this, raw_picture, - NULL); + exif_data); sp<Encoder_libjpeg> encoder = new Encoder_libjpeg((uint8_t*)frame->mBuffer, frame->mLength, @@ -668,7 +697,7 @@ void AppCallbackNotifier::notifyFrame() (CameraFrame::FrameType)frame->mFrameType, this, raw_picture, - NULL); + exif_data); encoder->run(); encoder.clear(); diff --git a/camera/Encoder_libjpeg.cpp b/camera/Encoder_libjpeg.cpp index dfdd9348..99650cc8 100644 --- a/camera/Encoder_libjpeg.cpp +++ b/camera/Encoder_libjpeg.cpp @@ -41,8 +41,21 @@ extern "C" { #include "jerror.h" } +#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0])) + namespace android { +struct string_pair { + const char* string1; + const char* string2; +}; +static string_pair degress_to_exif_lut [] = { + // degrees, exif_orientation + {"0", "1"}, + {"90", "6"}, + {"180", "3"}, + {"270", "8"}, +}; struct libjpeg_destination_mgr : jpeg_destination_mgr { libjpeg_destination_mgr(uint8_t* input, int size); @@ -101,6 +114,86 @@ static void uyvy_to_yuv(uint8_t* dst, uint32_t* src, int width) { } } +/* public static functions */ +const char* ExifElementsTable::degreesToExifOrientation(const char* degrees) { + for (int i = 0; i < ARRAY_SIZE(degress_to_exif_lut); i++) { + if (!strcmp(degrees, degress_to_exif_lut[i].string1)) { + return degress_to_exif_lut[i].string2; + } + } + return NULL; +} + +void ExifElementsTable::insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size) { + ReadMode_t read_mode = (ReadMode_t)(READ_METADATA | READ_IMAGE); + + ResetJpgfile(); + if (ReadJpegSectionsFromBuffer(jpeg, jpeg_size, read_mode)) { + jpeg_opened = true; + create_EXIF(table, exif_tag_count, gps_tag_count); + } +} + +void ExifElementsTable::saveJpeg(unsigned char* jpeg, size_t jpeg_size) { + if (jpeg_opened) { + WriteJpegToBuffer(jpeg, jpeg_size); + DiscardData(); + jpeg_opened = false; + } +} + +/* public functions */ +ExifElementsTable::~ExifElementsTable() { + int num_elements = gps_tag_count + exif_tag_count; + + for (int i = 0; i < num_elements; i++) { + if (table[i].Value) { + free(table[i].Value); + } + } + + if (jpeg_opened) { + DiscardData(); + } +} + +status_t ExifElementsTable::insertElement(const char* tag, const char* value) { + int value_length = 0; + status_t ret = NO_ERROR; + + if (!value || !tag) { + return -EINVAL; + } + + if (position >= MAX_EXIF_TAGS_SUPPORTED) { + CAMHAL_LOGEA("Max number of EXIF elements already inserted"); + return NO_MEMORY; + } + + value_length = strlen(value); + + if (IsGpsTag(tag)) { + table[position].GpsTag = TRUE; + table[position].Tag = GpsTagNameToValue(tag); + gps_tag_count++; + } else { + table[position].GpsTag = FALSE; + table[position].Tag = TagNameToValue(tag); + exif_tag_count++; + } + + table[position].DataLength = 0; + table[position].Value = (char*) malloc(sizeof(char) * (value_length + 1)); + + if (table[position].Value) { + strncpy(table[position].Value, value, value_length); + table[position].DataLength = value_length + 1; + } + + position++; + return ret; +} + /* private member functions */ size_t Encoder_libjpeg::encode() { jpeg_compress_struct cinfo; diff --git a/camera/OMXCameraAdapter/OMX3A.cpp b/camera/OMXCameraAdapter/OMX3A.cpp index 530e8ae4..2466d68e 100644 --- a/camera/OMXCameraAdapter/OMX3A.cpp +++ b/camera/OMXCameraAdapter/OMX3A.cpp @@ -29,7 +29,8 @@ #include "OMXCameraAdapter.h" #include "ErrorUtils.h" - +#undef TRUE +#undef FALSE #define TRUE "true" #define FALSE "false" diff --git a/camera/OMXCameraAdapter/OMXCameraAdapter.cpp b/camera/OMXCameraAdapter/OMXCameraAdapter.cpp index 79a42d9a..6f604cd6 100644 --- a/camera/OMXCameraAdapter/OMXCameraAdapter.cpp +++ b/camera/OMXCameraAdapter/OMXCameraAdapter.cpp @@ -2920,6 +2920,13 @@ OMX_ERRORTYPE OMXCameraAdapter::OMXCameraAdapterFillBufferDone(OMX_IN OMX_HANDLE // before returning to framework typeOfFrame = CameraFrame::IMAGE_FRAME; cameraFrame.mQuirks |= CameraFrame::ENCODE_RAW_YUV422I_TO_JPEG; + + // populate exif data and pass to subscribers via quirk + // subscriber is in charge of freeing exif data + ExifElementsTable* exif = new ExifElementsTable(); + setupEXIF_libjpeg(exif); + cameraFrame.mQuirks |= CameraFrame::HAS_EXIF_DATA; + cameraFrame.mCookie2 = (void*) exif; } else { diff --git a/camera/OMXCameraAdapter/OMXExif.cpp b/camera/OMXCameraAdapter/OMXExif.cpp index 64eddc64..5da60ac5 100644 --- a/camera/OMXCameraAdapter/OMXExif.cpp +++ b/camera/OMXCameraAdapter/OMXExif.cpp @@ -535,6 +535,164 @@ status_t OMXCameraAdapter::setupEXIF() return ret; } +status_t OMXCameraAdapter::setupEXIF_libjpeg(ExifElementsTable* exifTable) +{ + status_t ret = NO_ERROR; + OMX_ERRORTYPE eError = OMX_ErrorNone; + struct timeval sTv; + struct tm *pTime; + OMXCameraPortParameters * capData = NULL; + + LOG_FUNCTION_NAME; + + capData = &mCameraAdapterParameters.mCameraPortParams[mCameraAdapterParameters.mImagePortIndex]; + + if ((NO_ERROR == ret) && (mEXIFData.mModelValid)) { + ret = exifTable->insertElement(TAG_MODEL, mParams.get(TICameraParameters::KEY_EXIF_MODEL)); + } + + if ((NO_ERROR == ret) && (mEXIFData.mMakeValid)) { + ret = exifTable->insertElement(TAG_MAKE, mParams.get(TICameraParameters::KEY_EXIF_MAKE)); + } + + if ((NO_ERROR == ret)) { + ret = exifTable->insertElement(TAG_FOCALLENGTH, + mParams.get(CameraParameters::KEY_FOCAL_LENGTH)); + } + + if ((NO_ERROR == ret)) { + int status = gettimeofday (&sTv, NULL); + pTime = localtime (&sTv.tv_sec); + char temp_value[EXIF_DATE_TIME_SIZE + 1]; + if ((0 == status) && (NULL != pTime)) { + snprintf(temp_value, EXIF_DATE_TIME_SIZE, + "%04d:%02d:%02d %02d:%02d:%02d", + pTime->tm_year + 1900, + pTime->tm_mon + 1, + pTime->tm_mday, + pTime->tm_hour, + pTime->tm_min, + pTime->tm_sec ); + + ret = exifTable->insertElement(TAG_DATETIME, temp_value); + } + } + + if ((NO_ERROR == ret)) { + char temp_value[5]; + snprintf(temp_value, sizeof(temp_value)/sizeof(char) - 1, "%lu", capData->mWidth); + ret = exifTable->insertElement(TAG_IMAGE_WIDTH, temp_value); + } + + if ((NO_ERROR == ret)) { + char temp_value[5]; + snprintf(temp_value, sizeof(temp_value)/sizeof(char) - 1, "%lu", capData->mHeight); + ret = exifTable->insertElement(TAG_IMAGE_LENGTH, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mLatValid)) { + char temp_value[256]; // arbitrarily long string + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d/%d,%d/%d,%d/%d", + abs(mEXIFData.mGPSData.mLatDeg), 1, + abs(mEXIFData.mGPSData.mLatMin), 1, + abs(mEXIFData.mGPSData.mLatSec), 1); + ret = exifTable->insertElement(TAG_GPS_LAT, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mLatValid)) { + ret = exifTable->insertElement(TAG_GPS_LAT_REF, mEXIFData.mGPSData.mLatRef); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mLongValid)) { + char temp_value[256]; // arbitrarily long string + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d/%d,%d/%d,%d/%d", + abs(mEXIFData.mGPSData.mLongDeg), 1, + abs(mEXIFData.mGPSData.mLongMin), 1, + abs(mEXIFData.mGPSData.mLongSec), 1); + ret = exifTable->insertElement(TAG_GPS_LONG, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mLongValid)) { + ret = exifTable->insertElement(TAG_GPS_LONG_REF, mEXIFData.mGPSData.mLongRef); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mAltitudeValid)) { + char temp_value[256]; // arbitrarily long string + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d/%d", + abs( mEXIFData.mGPSData.mAltitude), 1); + ret = exifTable->insertElement(TAG_GPS_ALT, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mAltitudeValid)) { + char temp_value[5]; + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d", mEXIFData.mGPSData.mAltitudeRef); + ret = exifTable->insertElement(TAG_GPS_ALT_REF, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mMapDatumValid)) { + ret = exifTable->insertElement(TAG_GPS_MAP_DATUM, mEXIFData.mGPSData.mMapDatum); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mProcMethodValid)) { + char temp_value[GPS_PROCESSING_SIZE]; + + memcpy(temp_value, EXIFASCIIPrefix, sizeof(EXIFASCIIPrefix)); + memcpy(temp_value + sizeof(EXIFASCIIPrefix), + mEXIFData.mGPSData.mProcMethod, + (GPS_PROCESSING_SIZE - sizeof(EXIFASCIIPrefix))); + + ret = exifTable->insertElement(TAG_GPS_PROCESSING_METHOD, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mVersionIdValid)) { + char temp_value[256]; // arbitrarily long string + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d,%d,%d,%d", + mEXIFData.mGPSData.mVersionId[0], + mEXIFData.mGPSData.mVersionId[1], + mEXIFData.mGPSData.mVersionId[2], + mEXIFData.mGPSData.mVersionId[3]); + ret = exifTable->insertElement(TAG_GPS_VERSION_ID, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mTimeStampValid)) { + char temp_value[256]; // arbitrarily long string + snprintf(temp_value, + sizeof(temp_value)/sizeof(char) - 1, + "%d/%d,%d/%d,%d/%d", + mEXIFData.mGPSData.mTimeStampHour, 1, + mEXIFData.mGPSData.mTimeStampMin, 1, + mEXIFData.mGPSData.mTimeStampSec, 1); + ret = exifTable->insertElement(TAG_GPS_TIMESTAMP, temp_value); + } + + if ((NO_ERROR == ret) && (mEXIFData.mGPSData.mDatestampValid) ) { + ret = exifTable->insertElement(TAG_GPS_DATESTAMP, mEXIFData.mGPSData.mDatestamp); + } + + if ((NO_ERROR == ret) && mParams.get(CameraParameters::KEY_ROTATION) ) { + const char* exif_orient = + ExifElementsTable::degreesToExifOrientation(mParams.get(CameraParameters::KEY_ROTATION)); + + if (exif_orient) { + ret = exifTable->insertElement(TAG_ORIENTATION, exif_orient); + } + } + + LOG_FUNCTION_NAME_EXIT; + + return ret; +} + status_t OMXCameraAdapter::convertGPSCoord(double coord, int °, int &min, diff --git a/camera/inc/CameraHal.h b/camera/inc/CameraHal.h index 36a3504e..f19aceab 100644 --- a/camera/inc/CameraHal.h +++ b/camera/inc/CameraHal.h @@ -238,11 +238,13 @@ class CameraFrame enum FrameQuirks { ENCODE_RAW_YUV422I_TO_JPEG = 0x1 << 0, + HAS_EXIF_DATA = 0x1 << 1, }; //default contrustor CameraFrame(): mCookie(NULL), + mCookie2(NULL), mBuffer(NULL), mFrameType(0), mTimestamp(0), @@ -257,6 +259,7 @@ class CameraFrame //copy constructor CameraFrame(const CameraFrame &frame) : mCookie(frame.mCookie), + mCookie2(frame.mCookie2), mBuffer(frame.mBuffer), mFrameType(frame.mFrameType), mTimestamp(frame.mTimestamp), @@ -269,6 +272,7 @@ class CameraFrame mQuirks(frame.mQuirks) {} void *mCookie; + void *mCookie2; void *mBuffer; int mFrameType; nsecs_t mTimestamp; @@ -540,7 +544,7 @@ public: status_t useMetaDataBufferMode(bool enable); - void EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFrame::FrameType type, void* cookie1); + void EncoderDoneCb(size_t jpeg_size, uint8_t* src, CameraFrame::FrameType type, void* cookie1, void* cookie2); //Internal class definitions class NotificationThread : public Thread { diff --git a/camera/inc/Encoder_libjpeg.h b/camera/inc/Encoder_libjpeg.h index 24ce6725..8aaae023 100644 --- a/camera/inc/Encoder_libjpeg.h +++ b/camera/inc/Encoder_libjpeg.h @@ -27,12 +27,17 @@ #include <utils/threads.h> #include <utils/RefBase.h> +extern "C" { +#include "jhead.h" +} namespace android { /** * libjpeg encoder class - uses libjpeg to encode yuv */ +#define MAX_EXIF_TAGS_SUPPORTED 30 + typedef void (*encoder_libjpeg_callback_t) (size_t jpeg_size, uint8_t* src, CameraFrame::FrameType type, @@ -40,6 +45,44 @@ typedef void (*encoder_libjpeg_callback_t) (size_t jpeg_size, void* cookie2, void* cookie3); +static const char TAG_MODEL[] = "Model"; +static const char TAG_MAKE[] = "Make"; +static const char TAG_FOCALLENGTH[] = "FocalLength"; +static const char TAG_DATETIME[] = "DateTime"; +static const char TAG_IMAGE_WIDTH[] = "ImageWidth"; +static const char TAG_IMAGE_LENGTH[] = "ImageLength"; +static const char TAG_GPS_LAT[] = "GPSLatitude"; +static const char TAG_GPS_LAT_REF[] = "GPSLatitudeRef"; +static const char TAG_GPS_LONG[] = "GPSLongitude"; +static const char TAG_GPS_LONG_REF[] = "GPSLongitudeRef"; +static const char TAG_GPS_ALT[] = "GPSAltitude"; +static const char TAG_GPS_ALT_REF[] = "GPSAltitudeRef"; +static const char TAG_GPS_MAP_DATUM[] = "GPSMapDatum"; +static const char TAG_GPS_PROCESSING_METHOD[] = "GPSProcessingMethod"; +static const char TAG_GPS_VERSION_ID[] = "GPSVersionID"; +static const char TAG_GPS_TIMESTAMP[] = "GPSTimeStamp"; +static const char TAG_GPS_DATESTAMP[] = "GPSDateStamp"; +static const char TAG_ORIENTATION[] = "Orientation"; + +class ExifElementsTable { + public: + ExifElementsTable() : + gps_tag_count(0), exif_tag_count(0), position(0), + jpeg_opened(false) { } + ~ExifElementsTable(); + + status_t insertElement(const char* tag, const char* value); + void insertExifToJpeg(unsigned char* jpeg, size_t jpeg_size); + void saveJpeg(unsigned char* picture, size_t jpeg_size); + static const char* degreesToExifOrientation(const char*); + private: + ExifElement_t table[MAX_EXIF_TAGS_SUPPORTED]; + unsigned int gps_tag_count; + unsigned int exif_tag_count; + unsigned int position; + bool jpeg_opened; +}; + class Encoder_libjpeg : public Thread { public: Encoder_libjpeg(uint8_t* src, diff --git a/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h b/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h index b7d7f2d5..c01ca026 100644 --- a/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h +++ b/camera/inc/OMXCameraAdapter/OMXCameraAdapter.h @@ -34,6 +34,7 @@ #include "General3A_Settings.h" #include "BaseCameraAdapter.h" +#include "Encoder_libjpeg.h" #include "DebugUtils.h" @@ -425,6 +426,7 @@ private: BaseCameraAdapter::AdapterState state); status_t convertGPSCoord(double coord, int °, int &min, int &sec, int &secDivisor); status_t setupEXIF(); + status_t setupEXIF_libjpeg(ExifElementsTable*); //Focus functionality status_t doAutoFocus(); |