Bug 1043274 - Use GraphicBuffer on GonkDecoderModule. r=edwin, r=sotaro
authorBlake <bwu@mozilla.com>
Wed, 12 Nov 2014 17:34:21 +0800
changeset 215535 2f9619594cf0fe1ab76ebd92de20791226bf8f75
parent 215534 87e94129e7b37f7afd0daa8d22a2bf1298898391
child 215536 f22af8dfea68d170ff153236c87cfb2a8b9497f1
push id27818
push userryanvm@gmail.com
push dateThu, 13 Nov 2014 20:19:09 +0000
treeherdermozilla-central@292ed84594c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin, sotaro
bugs1043274
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1043274 - Use GraphicBuffer on GonkDecoderModule. r=edwin, r=sotaro
dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
dom/media/fmp4/gonk/GonkVideoDecoderManager.h
dom/media/omx/MediaCodecProxy.cpp
dom/media/omx/MediaCodecProxy.h
--- a/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
@@ -200,22 +200,18 @@ GonkAudioDecoderManager::Output(int64_t 
     }
   }
 
   return NS_OK;
 }
 
 void GonkAudioDecoderManager::ReleaseAudioBuffer() {
   if (mAudioBuffer) {
-    sp<MetaData> metaData = mAudioBuffer->meta_data();
-    int32_t index;
-    metaData->findInt32(android::MediaCodecProxy::kKeyBufferIndex, &index);
-    mAudioBuffer->release();
+    mDecoder->ReleaseMediaBuffer(mAudioBuffer);
     mAudioBuffer = nullptr;
-    mDecoder->releaseOutputBuffer(index);
   }
 }
 
 nsresult
 GonkAudioDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
 {
   if (mDecoder == nullptr) {
     ALOG("Decoder is not inited");
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.cpp
@@ -17,16 +17,20 @@
 #include "stagefright/MediaBuffer.h"
 #include "stagefright/MetaData.h"
 #include "stagefright/MediaErrors.h"
 #include <stagefright/foundation/ADebug.h>
 #include <stagefright/foundation/AMessage.h>
 #include <stagefright/foundation/AString.h>
 #include <stagefright/foundation/ALooper.h>
 #include "mp4_demuxer/AnnexB.h"
+#include "GonkNativeWindow.h"
+#include "GonkNativeWindowClient.h"
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/TextureClient.h"
 
 #define READ_OUTPUT_BUFFER_TIMEOUT_US  3000
 
 #define LOG_TAG "GonkVideoDecoderManager"
 #include <android/log.h>
 #define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 
 #ifdef PR_LOGGING
@@ -41,22 +45,24 @@ typedef android::MediaCodecProxy MediaCo
 
 namespace mozilla {
 enum {
   kNotifyCodecReserved = 'core',
   kNotifyCodecCanceled = 'coca',
 };
 
 GonkVideoDecoderManager::GonkVideoDecoderManager(
-                                  mozilla::layers::ImageContainer* aImageContainer,
-		                  const mp4_demuxer::VideoDecoderConfig& aConfig)
+                           mozilla::layers::ImageContainer* aImageContainer,
+		           const mp4_demuxer::VideoDecoderConfig& aConfig)
   : mImageContainer(aImageContainer)
   , mConfig(aConfig)
   , mReaderCallback(nullptr)
   , mColorConverterBufferSize(0)
+  , mNativeWindow(nullptr)
+  , mPendingVideoBuffersLock("GonkVideoDecoderManager::mPendingVideoBuffersLock")
 {
   NS_ASSERTION(!NS_IsMainThread(), "Should not be on main thread.");
   MOZ_ASSERT(mImageContainer);
   MOZ_COUNT_CTOR(GonkVideoDecoderManager);
   mVideoWidth  = aConfig.display_width;
   mVideoHeight = aConfig.display_height;
   mDisplayWidth = aConfig.display_width;
   mDisplayHeight = aConfig.display_height;
@@ -93,35 +99,42 @@ GonkVideoDecoderManager::Init(MediaDataD
 
   mReaderCallback = aCallback;
 
   if (mLooper.get() != nullptr) {
     return nullptr;
   }
   // Create ALooper
   mLooper = new ALooper;
-  mLooper->setName("GonkVideoDecoderManager");
+  mManagerLooper = new ALooper;
+  mManagerLooper->setName("GonkVideoDecoderManager");
   // Register AMessage handler to ALooper.
-  mLooper->registerHandler(mHandler);
+  mManagerLooper->registerHandler(mHandler);
   // Start ALooper thread.
-  if (mLooper->start() != OK) {
+  if (mLooper->start() != OK || mManagerLooper->start() != OK ) {
     return nullptr;
   }
   mDecoder = MediaCodecProxy::CreateByType(mLooper, "video/avc", false, true, mVideoListener);
+  uint32_t capability = MediaCodecProxy::kEmptyCapability;
+  if (mDecoder->getCapability(&capability) == OK && (capability &
+      MediaCodecProxy::kCanExposeGraphicBuffer)) {
+    mNativeWindow = new GonkNativeWindow();
+  }
+
   return mDecoder;
 }
 
 nsresult
 GonkVideoDecoderManager::CreateVideoData(int64_t aStreamOffset, VideoData **v)
 {
   *v = nullptr;
   int64_t timeUs;
   int32_t keyFrame;
 
-  if (!(mVideoBuffer != nullptr && mVideoBuffer->data() != nullptr)) {
+  if (mVideoBuffer == nullptr) {
     ALOG("Video Buffer is not valid!");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
     ALOG("Decoder did not return frame time");
     return NS_ERROR_UNEXPECTED;
   }
@@ -145,80 +158,107 @@ GonkVideoDecoderManager::CreateVideoData
     // and we will preserve the ratio of the crop rectangle as it
     // was reported relative to the picture size reported by the container.
     picture.x = (mPicture.x * mFrameInfo.mWidth) / mInitialFrame.width;
     picture.y = (mPicture.y * mFrameInfo.mHeight) / mInitialFrame.height;
     picture.width = (mFrameInfo.mWidth * mPicture.width) / mInitialFrame.width;
     picture.height = (mFrameInfo.mHeight * mPicture.height) / mInitialFrame.height;
   }
 
-  uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
-  int32_t stride = mFrameInfo.mStride;
-  int32_t slice_height = mFrameInfo.mSliceHeight;
+  RefPtr<mozilla::layers::TextureClient> textureClient;
 
-  // Converts to OMX_COLOR_FormatYUV420Planar
-  if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
-    ARect crop;
-    crop.top = 0;
-    crop.bottom = mFrameInfo.mHeight;
-    crop.left = 0;
-    crop.right = mFrameInfo.mWidth;
-    yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
-    if (mColorConverter.convertDecoderOutputToI420(mVideoBuffer->data(),
-        mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
-        ReleaseVideoBuffer();
-        ALOG("Color conversion failed!");
-        return NS_ERROR_UNEXPECTED;
-    }
-      stride = mFrameInfo.mWidth;
-      slice_height = mFrameInfo.mHeight;
+  if ((mVideoBuffer->graphicBuffer().get())) {
+    textureClient = mNativeWindow->getTextureClientFromBuffer(mVideoBuffer->graphicBuffer().get());
   }
 
-  size_t yuv420p_y_size = stride * slice_height;
-  size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
-  uint8_t *yuv420p_y = yuv420p_buffer;
-  uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
-  uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
+  if (textureClient) {
+    GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get());
+    grallocClient->SetMediaBuffer(mVideoBuffer);
+    textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
+
+    *v = VideoData::Create(mInfo.mVideo,
+                          mImageContainer,
+                          aStreamOffset,
+                          timeUs,
+                          1, // We don't know the duration.
+                          textureClient,
+                          keyFrame,
+                          -1,
+                          picture);
 
-  // This is the approximate byte position in the stream.
-  int64_t pos = aStreamOffset;
+  } else {
+    if (!mVideoBuffer->data()) {
+      ALOG("No data in Video Buffer!");
+      return NS_ERROR_UNEXPECTED;
+    }
+    uint8_t *yuv420p_buffer = (uint8_t *)mVideoBuffer->data();
+    int32_t stride = mFrameInfo.mStride;
+    int32_t slice_height = mFrameInfo.mSliceHeight;
 
-  VideoData::YCbCrBuffer b;
-  b.mPlanes[0].mData = yuv420p_y;
-  b.mPlanes[0].mWidth = mFrameInfo.mWidth;
-  b.mPlanes[0].mHeight = mFrameInfo.mHeight;
-  b.mPlanes[0].mStride = stride;
-  b.mPlanes[0].mOffset = 0;
-  b.mPlanes[0].mSkip = 0;
+    // Converts to OMX_COLOR_FormatYUV420Planar
+    if (mFrameInfo.mColorFormat != OMX_COLOR_FormatYUV420Planar) {
+      ARect crop;
+      crop.top = 0;
+      crop.bottom = mFrameInfo.mHeight;
+      crop.left = 0;
+      crop.right = mFrameInfo.mWidth;
+      yuv420p_buffer = GetColorConverterBuffer(mFrameInfo.mWidth, mFrameInfo.mHeight);
+      if (mColorConverter.convertDecoderOutputToI420(mVideoBuffer->data(),
+          mFrameInfo.mWidth, mFrameInfo.mHeight, crop, yuv420p_buffer) != OK) {
+          ReleaseVideoBuffer();
+          ALOG("Color conversion failed!");
+          return NS_ERROR_UNEXPECTED;
+      }
+        stride = mFrameInfo.mWidth;
+        slice_height = mFrameInfo.mHeight;
+    }
 
-  b.mPlanes[1].mData = yuv420p_u;
-  b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
-  b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
-  b.mPlanes[1].mStride = (stride + 1) / 2;
-  b.mPlanes[1].mOffset = 0;
-  b.mPlanes[1].mSkip = 0;
+    size_t yuv420p_y_size = stride * slice_height;
+    size_t yuv420p_u_size = ((stride + 1) / 2) * ((slice_height + 1) / 2);
+    uint8_t *yuv420p_y = yuv420p_buffer;
+    uint8_t *yuv420p_u = yuv420p_y + yuv420p_y_size;
+    uint8_t *yuv420p_v = yuv420p_u + yuv420p_u_size;
+
+    // This is the approximate byte position in the stream.
+    int64_t pos = aStreamOffset;
+
+    VideoData::YCbCrBuffer b;
+    b.mPlanes[0].mData = yuv420p_y;
+    b.mPlanes[0].mWidth = mFrameInfo.mWidth;
+    b.mPlanes[0].mHeight = mFrameInfo.mHeight;
+    b.mPlanes[0].mStride = stride;
+    b.mPlanes[0].mOffset = 0;
+    b.mPlanes[0].mSkip = 0;
 
-  b.mPlanes[2].mData = yuv420p_v;
-  b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
-  b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
-  b.mPlanes[2].mStride = (stride + 1) / 2;
-  b.mPlanes[2].mOffset = 0;
-  b.mPlanes[2].mSkip = 0;
+    b.mPlanes[1].mData = yuv420p_u;
+    b.mPlanes[1].mWidth = (mFrameInfo.mWidth + 1) / 2;
+    b.mPlanes[1].mHeight = (mFrameInfo.mHeight + 1) / 2;
+    b.mPlanes[1].mStride = (stride + 1) / 2;
+    b.mPlanes[1].mOffset = 0;
+    b.mPlanes[1].mSkip = 0;
 
-  *v = VideoData::Create(
-      mInfo.mVideo,
-      mImageContainer,
-      pos,
-      timeUs,
-      1, // We don't know the duration.
-      b,
-      keyFrame,
-      -1,
-      picture);
-  ReleaseVideoBuffer();
+    b.mPlanes[2].mData = yuv420p_v;
+    b.mPlanes[2].mWidth =(mFrameInfo.mWidth + 1) / 2;
+    b.mPlanes[2].mHeight = (mFrameInfo.mHeight + 1) / 2;
+    b.mPlanes[2].mStride = (stride + 1) / 2;
+    b.mPlanes[2].mOffset = 0;
+    b.mPlanes[2].mSkip = 0;
+
+    *v = VideoData::Create(
+        mInfo.mVideo,
+        mImageContainer,
+        pos,
+        timeUs,
+        1, // We don't know the duration.
+        b,
+        keyFrame,
+        -1,
+        picture);
+    ReleaseVideoBuffer();
+  }
   return NS_OK;
 }
 
 bool
 GonkVideoDecoderManager::SetVideoFormat()
 {
   // read video metadata from MediaCodec
   sp<AMessage> codecFormat;
@@ -329,22 +369,18 @@ GonkVideoDecoderManager::Output(int64_t 
     }
   }
 
   return NS_OK;
 }
 
 void GonkVideoDecoderManager::ReleaseVideoBuffer() {
   if (mVideoBuffer) {
-    sp<MetaData> metaData = mVideoBuffer->meta_data();
-    int32_t index;
-    metaData->findInt32(android::MediaCodecProxy::kKeyBufferIndex, &index);
-    mVideoBuffer->release();
+    mDecoder->ReleaseMediaBuffer(mVideoBuffer);
     mVideoBuffer = nullptr;
-    mDecoder->releaseOutputBuffer(index);
   }
 }
 
 nsresult
 GonkVideoDecoderManager::Input(mp4_demuxer::MP4Sample* aSample)
 {
   if (mDecoder == nullptr) {
     ALOG("Decoder is not inited");
@@ -366,22 +402,26 @@ GonkVideoDecoderManager::Input(mp4_demux
   }
   return (rv == OK) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 void
 GonkVideoDecoderManager::codecReserved()
 {
   sp<AMessage> format = new AMessage;
+  sp<Surface> surface;
+
   // Fixed values
   format->setString("mime", "video/avc");
   format->setInt32("width", mVideoWidth);
   format->setInt32("height", mVideoHeight);
-
-  mDecoder->configure(format, nullptr, nullptr, 0);
+  if (mNativeWindow != nullptr) {
+    surface = new Surface(mNativeWindow->getBufferQueue());
+  }
+  status_t err = mDecoder->configure(format, surface, nullptr, 0);
   mDecoder->Prepare();
   SetVideoFormat();
 
   if (mHandler != nullptr) {
     // post kNotifyCodecReserved to Looper thread.
     sp<AMessage> notify = new AMessage(kNotifyCodecReserved, mHandler->id());
     notify->post();
   }
@@ -394,17 +434,17 @@ GonkVideoDecoderManager::codecCanceled()
   if (mHandler != nullptr) {
     // post kNotifyCodecCanceled to Looper thread.
     sp<AMessage> notify = new AMessage(kNotifyCodecCanceled, mHandler->id());
     notify->post();
   }
 
 }
 
-// Called on GonkVideoDecoderManager::mLooper thread.
+// Called on GonkVideoDecoderManager::mManagerLooper thread.
 void
 GonkVideoDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
 {
   switch (aMessage->what()) {
     case kNotifyCodecReserved:
     {
       // Our decode may have acquired the hardware resource that it needs
       // to start. Notify the state machine to resume loading metadata.
@@ -413,16 +453,22 @@ GonkVideoDecoderManager::onMessageReceiv
     }
 
     case kNotifyCodecCanceled:
     {
       mReaderCallback->ReleaseMediaResources();
       break;
     }
 
+    case kNotifyPostReleaseBuffer:
+    {
+      ReleaseAllPendingVideoBuffersLocked();
+      break;
+    }
+
     default:
       TRESPASS();
       break;
   }
 }
 
 GonkVideoDecoderManager::MessageHandler::MessageHandler(GonkVideoDecoderManager *aManager)
   : mManager(aManager)
@@ -479,9 +525,56 @@ GonkVideoDecoderManager::GetColorConvert
   if (mColorConverterBufferSize != yuv420p_size) {
     mColorConverterBuffer = nullptr; // release the previous buffer first
     mColorConverterBuffer = new uint8_t[yuv420p_size];
     mColorConverterBufferSize = yuv420p_size;
   }
   return mColorConverterBuffer.get();
 }
 
+/* static */
+void
+GonkVideoDecoderManager::RecycleCallback(TextureClient* aClient, void* aClosure)
+{
+  GonkVideoDecoderManager* videoManager = static_cast<GonkVideoDecoderManager*>(aClosure);
+  GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient);
+  aClient->ClearRecycleCallback();
+  videoManager->PostReleaseVideoBuffer(client->GetMediaBuffer());
+}
+
+void GonkVideoDecoderManager::PostReleaseVideoBuffer(
+                                android::MediaBuffer *aBuffer)
+{
+  {
+    MutexAutoLock autoLock(mPendingVideoBuffersLock);
+    if (aBuffer) {
+      mPendingVideoBuffers.append(aBuffer);
+    }
+  }
+  sp<AMessage> notify =
+            new AMessage(kNotifyPostReleaseBuffer, mHandler->id());
+  notify->post();
+
+}
+
+void GonkVideoDecoderManager::ReleaseAllPendingVideoBuffersLocked()
+{
+  Vector<android::MediaBuffer*> releasingVideoBuffers;
+  {
+    MutexAutoLock autoLock(mPendingVideoBuffersLock);
+    int size = mPendingVideoBuffers.length();
+    for (int i = 0; i < size; i++) {
+      releasingVideoBuffers.append(mPendingVideoBuffers[i]);
+    }
+    mPendingVideoBuffers.clear();
+  }
+  // Free all pending video buffers without holding mPendingVideoBuffersLock.
+  int size = releasingVideoBuffers.length();
+  for (int i = 0; i < size; i++) {
+    android::MediaBuffer *buffer;
+    buffer = releasingVideoBuffers[i];
+    mDecoder->ReleaseMediaBuffer(buffer);
+    buffer = nullptr;
+  }
+  releasingVideoBuffers.clear();
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/fmp4/gonk/GonkVideoDecoderManager.h
@@ -2,50 +2,60 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(GonkVideoDecoderManager_h_)
 #define GonkVideoDecoderManager_h_
 
+#include <set>
 #include "MP4Reader.h"
 #include "nsRect.h"
 #include "GonkMediaDataDecoder.h"
 #include "mozilla/RefPtr.h"
 #include "I420ColorConverterHelper.h"
 #include "MediaCodecProxy.h"
 #include <stagefright/foundation/AHandler.h>
+#include "GonkNativeWindow.h"
+#include "GonkNativeWindowClient.h"
 
 using namespace android;
 
 namespace android {
 struct MOZ_EXPORT ALooper;
 class MOZ_EXPORT MediaBuffer;
 struct MOZ_EXPORT AString;
+class GonkNativeWindow;
 } // namespace android
 
 namespace mozilla {
 
+namespace layers {
+class TextureClient;
+} // namespace mozilla::layers
+
 class GonkVideoDecoderManager : public GonkDecoderManager {
 typedef android::MediaCodecProxy MediaCodecProxy;
+typedef mozilla::layers::TextureClient TextureClient;
 
 public:
   GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
 		          const mp4_demuxer::VideoDecoderConfig& aConfig);
 
   ~GonkVideoDecoderManager();
 
   virtual android::sp<MediaCodecProxy> Init(MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
 
   virtual nsresult Output(int64_t aStreamOffset,
                           nsAutoPtr<MediaData>& aOutput) MOZ_OVERRIDE;
 
+  static void RecycleCallback(TextureClient* aClient, void* aClosure);
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
     int32_t mStride = 0;
     int32_t mSliceHeight = 0;
     int32_t mColorFormat = 0;
@@ -97,16 +107,19 @@ private:
   void ReleaseVideoBuffer();
   uint8_t* GetColorConverterBuffer(int32_t aWidth, int32_t aHeight);
 
   // For codec resource management
   void codecReserved();
   void codecCanceled();
   void onMessageReceived(const sp<AMessage> &aMessage);
 
+  void ReleaseAllPendingVideoBuffersLocked();
+  void PostReleaseVideoBuffer(android::MediaBuffer *aBuffer);
+
   const mp4_demuxer::VideoDecoderConfig& mConfig;
   uint32_t mVideoWidth;
   uint32_t mVideoHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
 
@@ -116,19 +129,32 @@ private:
 
   android::MediaBuffer* mVideoBuffer;
 
   MediaDataDecoderCallback*  mReaderCallback;
   MediaInfo mInfo;
   android::sp<VideoResourceListener> mVideoListener;
   android::sp<MessageHandler> mHandler;
   android::sp<ALooper> mLooper;
+  android::sp<ALooper> mManagerLooper;
   FrameInfo mFrameInfo;
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
   nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
   size_t mColorConverterBufferSize;
+
+  android::sp<android::GonkNativeWindow> mNativeWindow;
+  enum {
+    kNotifyPostReleaseBuffer = 'nprb',
+  };
+
+  // Hold video's MediaBuffers that are released.
+  // The holded MediaBuffers are released soon after flush.
+  Vector<android::MediaBuffer*> mPendingVideoBuffers;
+  // The lock protects mPendingVideoBuffers.
+  Mutex mPendingVideoBuffersLock;
+
 };
 
 } // namespace mozilla
 
 #endif // GonkVideoDecoderManager_h_
--- a/dom/media/omx/MediaCodecProxy.cpp
+++ b/dom/media/omx/MediaCodecProxy.cpp
@@ -11,16 +11,17 @@
 #include <stagefright/foundation/ADebug.h>
 #include <stagefright/MetaData.h>
 #include "stagefright/MediaErrors.h"
 
 #define LOG_TAG "MediaCodecProxy"
 #include <android/log.h>
 #define ALOG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
 #define TIMEOUT_DEQUEUE_INPUTBUFFER_MS 1000000ll
+
 namespace android {
 
 // General Template: MediaCodec::getOutputGraphicBufferFromIndex(...)
 template <typename T, bool InterfaceSupported>
 struct OutputGraphicBufferStub
 {
   static status_t GetOutputGraphicBuffer(T *aMediaCodec,
                                          size_t aIndex,
@@ -219,16 +220,17 @@ status_t
 MediaCodecProxy::start()
 {
   // Read Lock for mCodec
   RWLock::AutoRLock arl(mCodecLock);
 
   if (mCodec == nullptr) {
     return NO_INIT;
   }
+
   return mCodec->start();
 }
 
 status_t
 MediaCodecProxy::stop()
 {
   // Read Lock for mCodec
   RWLock::AutoRLock arl(mCodecLock);
@@ -555,18 +557,24 @@ status_t MediaCodecProxy::Output(MediaBu
   status_t err = dequeueOutputBuffer(&index, &offset, &size,
                                       &timeUs, &flags, aTimeoutUs);
   if (err != OK) {
     ALOG("Output returned %d", err);
     return err;
   }
 
   MediaBuffer *buffer;
+  sp<GraphicBuffer> graphicBuffer;
 
-  buffer = new MediaBuffer(mOutputBuffers.itemAt(index));
+  if (getOutputGraphicBufferFromIndex(index, &graphicBuffer) == OK &&
+      graphicBuffer != nullptr) {
+    buffer = new MediaBuffer(graphicBuffer);
+  } else {
+    buffer = new MediaBuffer(mOutputBuffers.itemAt(index));
+  }
   sp<MetaData> metaData = buffer->meta_data();
   metaData->setInt32(kKeyBufferIndex, index);
   metaData->setInt64(kKeyTime, timeUs);
   buffer->set_range(buffer->range_offset(), size);
   *aBuffer = buffer;
   if (flags & MediaCodec::BUFFER_FLAG_EOS) {
     return ERROR_END_OF_STREAM;
   }
@@ -587,9 +595,19 @@ void MediaCodecProxy::ReleaseMediaResour
 {
   if (mCodec.get()) {
     mCodec->stop();
     mCodec->release();
     mCodec.clear();
   }
 }
 
+void MediaCodecProxy::ReleaseMediaBuffer(MediaBuffer* aBuffer) {
+  if (aBuffer) {
+    sp<MetaData> metaData = aBuffer->meta_data();
+    int32_t index;
+    metaData->findInt32(kKeyBufferIndex, &index);
+    aBuffer->release();
+    releaseOutputBuffer(index);
+  }
+}
+
 } // namespace android
--- a/dom/media/omx/MediaCodecProxy.h
+++ b/dom/media/omx/MediaCodecProxy.h
@@ -3,21 +3,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MEDIA_CODEC_PROXY_H
 #define MEDIA_CODEC_PROXY_H
 
 #include <nsString.h>
-
 #include <stagefright/MediaCodec.h>
 #include <stagefright/MediaBuffer.h>
 #include <utils/threads.h>
-
 #include "MediaResourceHandler.h"
 
 namespace android {
 // This class is intended to be a proxy for MediaCodec with codec resource
 // management. Basically user can use it like MediaCodec, but need to handle
 // the listener when Codec is reserved for Async case. A good example is
 // MediaCodecReader.cpp. Another useage is to use configure(), Prepare(),
 // Input(), and Output(). It is used in GonkVideoDecoderManager.cpp which
@@ -130,16 +128,18 @@ public:
   status_t Input(const uint8_t* aData, uint32_t aDataSize,
                  int64_t aTimestampUsecs, uint64_t flags);
   status_t Output(MediaBuffer** aBuffer, int64_t aTimeoutUs);
   bool Prepare();
   bool IsWaitingResources();
   bool IsDormantNeeded();
   void ReleaseMediaResources();
 
+  void ReleaseMediaBuffer(MediaBuffer* abuffer);
+
 protected:
   virtual ~MediaCodecProxy();
 
   // MediaResourceHandler::EventListener::resourceReserved()
   virtual void resourceReserved();
   // MediaResourceHandler::EventListener::resourceCanceled()
   virtual void resourceCanceled();