Codecs: Add MetaDataMode support to software video encoders.

This support is needed to enable efficient video recording with
emulator and camera HAL 2.

- Update SoftAVCEncoder and SoftMPEG4Encoder to support MetaDataMode
  extension.
- Allow CameraSource to handle opaque pixel formats, so that
  MetaDataMode can be used.
- Remove hardware codec restriction for MetaDataMode

Bug: 6243944
Change-Id: I970eb3d55542a413b6d75a78f76d3a8583155601
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 727fd0d..e49c218 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1410,7 +1410,6 @@
 
     uint32_t encoder_flags = 0;
     if (mIsMetaDataStoredInVideoBuffers) {
-        encoder_flags |= OMXCodec::kHardwareCodecsOnly;
         encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers;
     }
 
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 3ddad93..a604c8f 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -111,6 +111,10 @@
        return OMX_TI_COLOR_FormatYUV420PackedSemiPlanar;
     }
 
+    if (!strcmp(colorFormat, CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE)) {
+        return OMX_COLOR_FormatAndroidOpaque;
+    }
+
     ALOGE("Uknown color format (%s), please add it to "
          "CameraSource::getColorFormat", colorFormat);
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 1d4ab32..8fbcb8a 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -301,6 +301,9 @@
             &matchingCodecs, &matchingCodecQuirks);
 
     if (matchingCodecs.isEmpty()) {
+        ALOGV("No matching codecs! (mime: %s, createEncoder: %s, "
+                "matchComponentName: %s, flags: 0x%x)",
+                mime, createEncoder ? "true" : "false", matchComponentName, flags);
         return NULL;
     }
 
diff --git a/media/libstagefright/codecs/avc/enc/Android.mk b/media/libstagefright/codecs/avc/enc/Android.mk
index 48923cf..0aecfa8 100644
--- a/media/libstagefright/codecs/avc/enc/Android.mk
+++ b/media/libstagefright/codecs/avc/enc/Android.mk
@@ -42,6 +42,7 @@
 
 LOCAL_C_INCLUDES := \
         frameworks/av/media/libstagefright/include \
+        frameworks/native/include/media/hardware \
         frameworks/native/include/media/openmax \
         $(LOCAL_PATH)/src \
         $(LOCAL_PATH)/include \
@@ -63,6 +64,7 @@
         libstagefright_foundation \
         libstagefright_omx \
         libutils \
+        libui
 
 
 LOCAL_MODULE := libstagefright_soft_h264enc
diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
index c6f658d..117f3f1 100644
--- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
+++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.cpp
@@ -22,11 +22,15 @@
 #include "avcenc_int.h"
 #include "OMX_Video.h"
 
+#include <HardwareAPI.h>
+#include <MetadataBufferType.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
+#include <ui/Rect.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include "SoftAVCEncoder.h"
 
@@ -171,6 +175,7 @@
       mVideoFrameRate(30),
       mVideoBitRate(192000),
       mVideoColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mStoreMetaDataInBuffers(false),
       mIDRFrameRefreshIntervalInSec(1),
       mAVCEncProfile(AVC_BASELINE),
       mAVCEncLevel(AVC_LEVEL2),
@@ -448,7 +453,7 @@
                 return OMX_ErrorUndefined;
             }
 
-            if (formatParams->nIndex > 1) {
+            if (formatParams->nIndex > 2) {
                 return OMX_ErrorNoMore;
             }
 
@@ -456,8 +461,10 @@
                 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
                 if (formatParams->nIndex == 0) {
                     formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
-                } else {
+                } else if (formatParams->nIndex == 1) {
                     formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+                } else {
+                    formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque;
                 }
             } else {
                 formatParams->eCompressionFormat = OMX_VIDEO_CodingAVC;
@@ -529,7 +536,9 @@
 
 OMX_ERRORTYPE SoftAVCEncoder::internalSetParameter(
         OMX_INDEXTYPE index, const OMX_PTR params) {
-    switch (index) {
+    int32_t indexFull = index;
+
+    switch (indexFull) {
         case OMX_IndexParamVideoErrorCorrection:
         {
             return OMX_ErrorNotImplemented;
@@ -560,7 +569,8 @@
             if (def->nPortIndex == 0) {
                 if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused ||
                     (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar &&
-                     def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar)) {
+                     def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar &&
+                     def->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) {
                     return OMX_ErrorUndefined;
                 }
             } else {
@@ -610,7 +620,7 @@
                 return OMX_ErrorUndefined;
             }
 
-            if (formatParams->nIndex > 1) {
+            if (formatParams->nIndex > 2) {
                 return OMX_ErrorNoMore;
             }
 
@@ -619,7 +629,9 @@
                     ((formatParams->nIndex == 0 &&
                       formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) ||
                     (formatParams->nIndex == 1 &&
-                     formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar))) {
+                     formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) ||
+                    (formatParams->nIndex == 2 &&
+                     formatParams->eColorFormat != OMX_COLOR_FormatAndroidOpaque) )) {
                     return OMX_ErrorUndefined;
                 }
                 mVideoColorFormat = formatParams->eColorFormat;
@@ -666,6 +678,31 @@
             return OMX_ErrorNone;
         }
 
+        case kStoreMetaDataExtensionIndex:
+        {
+            StoreMetaDataInBuffersParams *storeParams =
+                    (StoreMetaDataInBuffersParams*)params;
+            if (storeParams->nPortIndex != 0) {
+                ALOGE("%s: StoreMetadataInBuffersParams.nPortIndex not zero!",
+                        __FUNCTION__);
+                return OMX_ErrorUndefined;
+            }
+
+            mStoreMetaDataInBuffers = storeParams->bStoreMetaData;
+            ALOGV("StoreMetaDataInBuffers set to: %s",
+                    mStoreMetaDataInBuffers ? " true" : "false");
+
+            if (mStoreMetaDataInBuffers) {
+                mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar;
+                if (mInputFrameData == NULL) {
+                    mInputFrameData =
+                            (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
         default:
             return SimpleSoftOMXComponent::internalSetParameter(index, params);
     }
@@ -744,6 +781,8 @@
             }
         }
 
+        buffer_handle_t srcBuffer; // for MetaDataMode only
+
         // Get next input video frame
         if (mReadyForNextFrame) {
             // Save the input buffer info so that it can be
@@ -764,8 +803,28 @@
                 videoInput.height = ((mVideoHeight  + 15) >> 4) << 4;
                 videoInput.pitch = ((mVideoWidth + 15) >> 4) << 4;
                 videoInput.coding_timestamp = (inHeader->nTimeStamp + 500) / 1000;  // in ms
-                const void *inData = inHeader->pBuffer + inHeader->nOffset;
-                uint8_t *inputData = (uint8_t *) inData;
+                uint8_t *inputData = NULL;
+                if (mStoreMetaDataInBuffers) {
+                    if (inHeader->nFilledLen != 8) {
+                        ALOGE("MetaData buffer is wrong size! "
+                                "(got %lu bytes, expected 8)", inHeader->nFilledLen);
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                        return;
+                    }
+                    inputData =
+                            extractGrallocData(inHeader->pBuffer + inHeader->nOffset,
+                                    &srcBuffer);
+                    if (inputData == NULL) {
+                        ALOGE("Unable to extract gralloc buffer in metadata mode");
+                        mSignalledError = true;
+                        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                        return;
+                    }
+                    // TODO: Verify/convert pixel format enum
+                } else {
+                    inputData = (uint8_t *)inHeader->pBuffer + inHeader->nOffset;
+                }
 
                 if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
                     ConvertYUV420SemiPlanarToYUV420Planar(
@@ -790,12 +849,14 @@
                     if (encoderStatus < AVCENC_SUCCESS) {
                         ALOGE("encoderStatus = %d at line %d", encoderStatus, __LINE__);
                         mSignalledError = true;
+                        releaseGrallocData(srcBuffer);
                         notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
                         return;
                     } else {
                         ALOGV("encoderStatus = %d at line %d", encoderStatus, __LINE__);
                         inQueue.erase(inQueue.begin());
                         inInfo->mOwnedByUs = false;
+                        releaseGrallocData(srcBuffer);
                         notifyEmptyBufferDone(inHeader);
                         return;
                     }
@@ -829,6 +890,7 @@
             if (encoderStatus < AVCENC_SUCCESS) {
                 ALOGE("encoderStatus = %d at line %d", encoderStatus, __LINE__);
                 mSignalledError = true;
+                releaseGrallocData(srcBuffer);
                 notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
                 return;
             }
@@ -838,6 +900,7 @@
 
         inQueue.erase(inQueue.begin());
         inInfo->mOwnedByUs = false;
+        releaseGrallocData(srcBuffer);
         notifyEmptyBufferDone(inHeader);
 
         outQueue.erase(outQueue.begin());
@@ -881,6 +944,47 @@
     ALOGV("signalBufferReturned: %p", buffer);
 }
 
+OMX_ERRORTYPE SoftAVCEncoder::getExtensionIndex(
+        const char *name, OMX_INDEXTYPE *index) {
+    if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers")) {
+        *(int32_t*)index = kStoreMetaDataExtensionIndex;
+        return OMX_ErrorNone;
+    }
+    return OMX_ErrorUndefined;
+}
+
+uint8_t *SoftAVCEncoder::extractGrallocData(void *data, buffer_handle_t *buffer) {
+    OMX_U32 type = *(OMX_U32*)data;
+    status_t res;
+    if (type != kMetadataBufferTypeGrallocSource) {
+        ALOGE("Data passed in with metadata mode does not have type "
+                "kMetadataBufferTypeGrallocSource (%d), has type %ld instead",
+                kMetadataBufferTypeGrallocSource, type);
+        return NULL;
+    }
+    buffer_handle_t imgBuffer = *(buffer_handle_t*)((uint8_t*)data + 4);
+
+    const Rect rect(mVideoWidth, mVideoHeight);
+    uint8_t *img;
+    res = GraphicBufferMapper::get().lock(imgBuffer,
+            GRALLOC_USAGE_HW_VIDEO_ENCODER,
+            rect, (void**)&img);
+    if (res != OK) {
+        ALOGE("%s: Unable to lock image buffer %p for access", __FUNCTION__,
+                imgBuffer);
+        return NULL;
+    }
+
+    *buffer = imgBuffer;
+    return img;
+}
+
+void SoftAVCEncoder::releaseGrallocData(buffer_handle_t buffer) {
+    if (mStoreMetaDataInBuffers) {
+        GraphicBufferMapper::get().unlock(buffer);
+    }
+}
+
 }  // namespace android
 
 android::SoftOMXComponent *createSoftOMXComponent(
diff --git a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h
index a2587c6..23d5ff1 100644
--- a/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h
+++ b/media/libstagefright/codecs/avc/enc/SoftAVCEncoder.h
@@ -45,6 +45,10 @@
 
     virtual void onQueueFilled(OMX_U32 portIndex);
 
+    // Override SoftOMXComponent methods
+
+    virtual OMX_ERRORTYPE getExtensionIndex(
+            const char *name, OMX_INDEXTYPE *index);
 
     // Implement MediaBufferObserver
     virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -63,6 +67,10 @@
         kNumBuffers = 2,
     };
 
+    enum {
+        kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1
+    };
+
     // OMX input buffer's timestamp and flags
     typedef struct {
         int64_t mTimeUs;
@@ -74,6 +82,7 @@
     int32_t  mVideoFrameRate;
     int32_t  mVideoBitRate;
     int32_t  mVideoColorFormat;
+    bool     mStoreMetaDataInBuffers;
     int32_t  mIDRFrameRefreshIntervalInSec;
     AVCProfile mAVCEncProfile;
     AVCLevel   mAVCEncLevel;
@@ -100,6 +109,9 @@
     OMX_ERRORTYPE releaseEncoder();
     void releaseOutputBuffers();
 
+    uint8_t* extractGrallocData(void *data, buffer_handle_t *buffer);
+    void releaseGrallocData(buffer_handle_t buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(SoftAVCEncoder);
 };
 
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.mk b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
index 484180d..865cc9c 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.mk
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.mk
@@ -45,6 +45,7 @@
 LOCAL_C_INCLUDES := \
         frameworks/av/media/libstagefright/include \
         frameworks/native/include/media/openmax \
+        frameworks/native/include/media/hardware \
         $(LOCAL_PATH)/src \
         $(LOCAL_PATH)/include \
         $(LOCAL_PATH)/../common/include \
@@ -64,6 +65,7 @@
         libstagefright_foundation \
         libstagefright_omx \
         libutils \
+        libui
 
 
 LOCAL_MODULE := libstagefright_soft_mpeg4enc
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index a5a2332..8bc0275 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -21,11 +21,15 @@
 #include "mp4enc_api.h"
 #include "OMX_Video.h"
 
+#include <HardwareAPI.h>
+#include <MetadataBufferType.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/Utils.h>
+#include <ui/Rect.h>
+#include <ui/GraphicBufferMapper.h>
 
 #include "SoftMPEG4Encoder.h"
 
@@ -82,6 +86,7 @@
       mVideoFrameRate(30),
       mVideoBitRate(192000),
       mVideoColorFormat(OMX_COLOR_FormatYUV420Planar),
+      mStoreMetaDataInBuffers(false),
       mIDRFrameRefreshIntervalInSec(1),
       mNumInputFrames(-1),
       mStarted(false),
@@ -321,7 +326,7 @@
                 return OMX_ErrorUndefined;
             }
 
-            if (formatParams->nIndex > 1) {
+            if (formatParams->nIndex > 2) {
                 return OMX_ErrorNoMore;
             }
 
@@ -329,8 +334,10 @@
                 formatParams->eCompressionFormat = OMX_VIDEO_CodingUnused;
                 if (formatParams->nIndex == 0) {
                     formatParams->eColorFormat = OMX_COLOR_FormatYUV420Planar;
-                } else {
+                } else if (formatParams->nIndex == 1) {
                     formatParams->eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+                } else {
+                    formatParams->eColorFormat = OMX_COLOR_FormatAndroidOpaque;
                 }
             } else {
                 formatParams->eCompressionFormat =
@@ -420,7 +427,9 @@
 
 OMX_ERRORTYPE SoftMPEG4Encoder::internalSetParameter(
         OMX_INDEXTYPE index, const OMX_PTR params) {
-    switch (index) {
+    int32_t indexFull = index;
+
+    switch (indexFull) {
         case OMX_IndexParamVideoErrorCorrection:
         {
             return OMX_ErrorNotImplemented;
@@ -451,7 +460,8 @@
             if (def->nPortIndex == 0) {
                 if (def->format.video.eCompressionFormat != OMX_VIDEO_CodingUnused ||
                     (def->format.video.eColorFormat != OMX_COLOR_FormatYUV420Planar &&
-                     def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar)) {
+                     def->format.video.eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar &&
+                     def->format.video.eColorFormat != OMX_COLOR_FormatAndroidOpaque)) {
                     return OMX_ErrorUndefined;
                 }
             } else {
@@ -505,7 +515,7 @@
                 return OMX_ErrorUndefined;
             }
 
-            if (formatParams->nIndex > 1) {
+            if (formatParams->nIndex > 2) {
                 return OMX_ErrorNoMore;
             }
 
@@ -514,7 +524,9 @@
                     ((formatParams->nIndex == 0 &&
                       formatParams->eColorFormat != OMX_COLOR_FormatYUV420Planar) ||
                     (formatParams->nIndex == 1 &&
-                     formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar))) {
+                     formatParams->eColorFormat != OMX_COLOR_FormatYUV420SemiPlanar) ||
+                    (formatParams->nIndex == 2 &&
+                     formatParams->eColorFormat != OMX_COLOR_FormatAndroidOpaque) )) {
                     return OMX_ErrorUndefined;
                 }
                 mVideoColorFormat = formatParams->eColorFormat;
@@ -578,6 +590,31 @@
             return OMX_ErrorNone;
         }
 
+        case kStoreMetaDataExtensionIndex:
+        {
+            StoreMetaDataInBuffersParams *storeParams =
+                    (StoreMetaDataInBuffersParams*)params;
+            if (storeParams->nPortIndex != 0) {
+                ALOGE("%s: StoreMetadataInBuffersParams.nPortIndex not zero!",
+                        __FUNCTION__);
+                return OMX_ErrorUndefined;
+            }
+
+            mStoreMetaDataInBuffers = storeParams->bStoreMetaData;
+            ALOGV("StoreMetaDataInBuffers set to: %s",
+                    mStoreMetaDataInBuffers ? " true" : "false");
+
+            if (mStoreMetaDataInBuffers) {
+                mVideoColorFormat == OMX_COLOR_FormatYUV420SemiPlanar;
+                if (mInputFrameData == NULL) {
+                    mInputFrameData =
+                            (uint8_t *) malloc((mVideoWidth * mVideoHeight * 3 ) >> 1);
+                }
+            }
+
+            return OMX_ErrorNone;
+        }
+
         default:
             return SimpleSoftOMXComponent::internalSetParameter(index, params);
     }
@@ -640,9 +677,31 @@
             mSawInputEOS = true;
         }
 
+        buffer_handle_t srcBuffer; // for MetaDataMode only
         if (inHeader->nFilledLen > 0) {
-            const void *inData = inHeader->pBuffer + inHeader->nOffset;
-            uint8_t *inputData = (uint8_t *) inData;
+            uint8_t *inputData = NULL;
+            if (mStoreMetaDataInBuffers) {
+                if (inHeader->nFilledLen != 8) {
+                    ALOGE("MetaData buffer is wrong size! "
+                            "(got %lu bytes, expected 8)", inHeader->nFilledLen);
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                    return;
+                }
+                inputData =
+                        extractGrallocData(inHeader->pBuffer + inHeader->nOffset,
+                                &srcBuffer);
+                if (inputData == NULL) {
+                    ALOGE("Unable to extract gralloc buffer in metadata mode");
+                    mSignalledError = true;
+                    notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
+                        return;
+                }
+                // TODO: Verify/convert pixel format enum
+            } else {
+                inputData = (uint8_t *)inHeader->pBuffer + inHeader->nOffset;
+            }
+
             if (mVideoColorFormat != OMX_COLOR_FormatYUV420Planar) {
                 ConvertYUV420SemiPlanarToYUV420Planar(
                     inputData, mInputFrameData, mVideoWidth, mVideoHeight);
@@ -683,6 +742,7 @@
 
         inQueue.erase(inQueue.begin());
         inInfo->mOwnedByUs = false;
+        releaseGrallocData(srcBuffer);
         notifyEmptyBufferDone(inHeader);
 
         outQueue.erase(outQueue.begin());
@@ -697,6 +757,47 @@
     }
 }
 
+OMX_ERRORTYPE SoftMPEG4Encoder::getExtensionIndex(
+        const char *name, OMX_INDEXTYPE *index) {
+    if (!strcmp(name, "OMX.google.android.index.storeMetaDataInBuffers")) {
+        *(int32_t*)index = kStoreMetaDataExtensionIndex;
+        return OMX_ErrorNone;
+    }
+    return OMX_ErrorUndefined;
+}
+
+uint8_t *SoftMPEG4Encoder::extractGrallocData(void *data, buffer_handle_t *buffer) {
+    OMX_U32 type = *(OMX_U32*)data;
+    status_t res;
+    if (type != kMetadataBufferTypeGrallocSource) {
+        ALOGE("Data passed in with metadata mode does not have type "
+                "kMetadataBufferTypeGrallocSource (%d), has type %ld instead",
+                kMetadataBufferTypeGrallocSource, type);
+        return NULL;
+    }
+    buffer_handle_t imgBuffer = *(buffer_handle_t*)((uint8_t*)data + 4);
+
+    const Rect rect(mVideoWidth, mVideoHeight);
+    uint8_t *img;
+    res = GraphicBufferMapper::get().lock(imgBuffer,
+            GRALLOC_USAGE_HW_VIDEO_ENCODER,
+            rect, (void**)&img);
+    if (res != OK) {
+        ALOGE("%s: Unable to lock image buffer %p for access", __FUNCTION__,
+                imgBuffer);
+        return NULL;
+    }
+
+    *buffer = imgBuffer;
+    return img;
+}
+
+void SoftMPEG4Encoder::releaseGrallocData(buffer_handle_t buffer) {
+    if (mStoreMetaDataInBuffers) {
+        GraphicBufferMapper::get().unlock(buffer);
+    }
+}
+
 }  // namespace android
 
 android::SoftOMXComponent *createSoftOMXComponent(
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
index 3e90d54..cc4ea8f 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
@@ -43,6 +43,11 @@
 
     virtual void onQueueFilled(OMX_U32 portIndex);
 
+    // Override SoftOMXComponent methods
+
+    virtual OMX_ERRORTYPE getExtensionIndex(
+            const char *name, OMX_INDEXTYPE *index);
+
 protected:
     virtual ~SoftMPEG4Encoder();
 
@@ -51,6 +56,10 @@
         kNumBuffers = 2,
     };
 
+    enum {
+        kStoreMetaDataExtensionIndex = OMX_IndexVendorStartUnused + 1
+    };
+
     // OMX input buffer's timestamp and flags
     typedef struct {
         int64_t mTimeUs;
@@ -63,6 +72,7 @@
     int32_t  mVideoFrameRate;
     int32_t  mVideoBitRate;
     int32_t  mVideoColorFormat;
+    bool     mStoreMetaDataInBuffers;
     int32_t  mIDRFrameRefreshIntervalInSec;
 
     int64_t  mNumInputFrames;
@@ -80,6 +90,9 @@
     OMX_ERRORTYPE initEncoder();
     OMX_ERRORTYPE releaseEncoder();
 
+    uint8_t* extractGrallocData(void *data, buffer_handle_t *buffer);
+    void releaseGrallocData(buffer_handle_t buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4Encoder);
 };