Bug 1224889 - OpenMax IL video decoding for PDM. r=sotaro, r=nical
authorAlfredo Yang <ayang@mozilla.com>
Fri, 08 Jan 2016 06:24:00 -0500
changeset 279276 56062f372c4c7e5bb1f408d83d2b5cc2dc86cbe7
parent 279275 4214449db2db81174a087031ce298fc11b2b99d1
child 279277 31a86d5e5ffa5180a56414dba00573557e0533a6
push id29874
push userphilringnalda@gmail.com
push dateSun, 10 Jan 2016 23:37:38 +0000
treeherdermozilla-central@359f86fecbc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro, nical
bugs1224889
milestone46.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 1224889 - OpenMax IL video decoding for PDM. r=sotaro, r=nical
dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
dom/media/platforms/omx/GonkOmxPlatformLayer.h
dom/media/platforms/omx/OmxDataDecoder.cpp
dom/media/platforms/omx/OmxDataDecoder.h
dom/media/platforms/omx/OmxDecoderModule.cpp
dom/media/platforms/omx/OmxPlatformLayer.h
dom/media/platforms/omx/OmxPromiseLayer.cpp
dom/media/platforms/omx/OmxPromiseLayer.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/client/TextureClientRecycleAllocator.h
--- a/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
@@ -3,29 +3,40 @@
 /* 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/. */
 
 #include "OmxDataDecoder.h"
 #include "OmxPromiseLayer.h"
 #include "GonkOmxPlatformLayer.h"
 #include "MediaInfo.h"
+#include "ImageContainer.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/GrallocTextureClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
 #include <binder/MemoryDealer.h>
 #include <media/IOMX.h>
 #include <utils/List.h>
 #include <media/stagefright/OMXCodec.h>
 
 extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("GonkOmxPlatformLayer:: " arg, ##__VA_ARGS__))
 
+#define CHECK_ERR(err)                    \
+  if (err != OK)                       {  \
+    LOG("error %d at %s", err, __func__); \
+    return NS_ERROR_FAILURE;              \
+  }                                       \
+
 using namespace android;
 
 namespace mozilla {
 
 extern void GetPortIndex(nsTArray<uint32_t>& aPortIndex);
 
 bool IsSoftwareCodec(const char* aComponentName) {
   nsAutoCString str(aComponentName);
@@ -109,39 +120,221 @@ public:
     : mTaskQueue(aTaskQueue)
     , mPromiseLayer(aPromiseLayer)
     , mClient(aDataDecoder)
   {}
 
 protected:
   RefPtr<TaskQueue> mTaskQueue;
   // TODO:
-  //   we should combination both event handlers into one. And we should provide
+  //   we should combine both event handlers into one. And we should provide
   //   an unified way for event handling in OmxPlatforLayer class.
   RefPtr<OmxPromiseLayer> mPromiseLayer;
   RefPtr<OmxDataDecoder> mClient;
 };
 
-GonkBufferData::GonkBufferData(android::IOMX::buffer_id aId, bool aLiveInLocal, android::IMemory* aMemory)
-  : BufferData((OMX_BUFFERHEADERTYPE*)aId)
-  , mId(aId)
+// This class allocates Gralloc buffer and manages TextureClient's recycle.
+class GonkTextureClientRecycleHandler : public layers::ITextureClientRecycleAllocator
+{
+  typedef MozPromise<layers::TextureClient*, nsresult, /* IsExclusive = */ true> TextureClientRecyclePromise;
+
+public:
+  GonkTextureClientRecycleHandler(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
+    : ITextureClientRecycleAllocator()
+    , mMonitor("GonkTextureClientRecycleHandler")
+  {
+    // Allocate Gralloc texture memory.
+    layers::GrallocTextureData* textureData =
+      layers::GrallocTextureData::Create(gfx::IntSize(aDef.nFrameWidth, aDef.nFrameHeight),
+                                         aDef.eColorFormat,
+                                         gfx::BackendType::NONE,
+                                         GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_READ_OFTEN,
+                                         layers::ImageBridgeChild::GetSingleton());
+
+    mGraphBuffer = textureData->GetGraphicBuffer();
+    MOZ_ASSERT(mGraphBuffer.get());
+
+    mTextureClient =
+      layers::TextureClient::CreateWithData(textureData,
+                                            layers::TextureFlags::DEALLOCATE_CLIENT | layers::TextureFlags::RECYCLE,
+                                            layers::ImageBridgeChild::GetSingleton());
+    MOZ_ASSERT(mTextureClient);
+
+    mPromise.SetMonitor(&mMonitor);
+  }
+
+  RefPtr<TextureClientRecyclePromise> WaitforRecycle()
+  {
+    MonitorAutoLock lock(mMonitor);
+    MOZ_ASSERT(!!mGraphBuffer.get());
+
+    mTextureClient->SetRecycleAllocator(this);
+    return mPromise.Ensure(__func__);
+  }
+
+  // DO NOT use smart pointer to receive TextureClient; otherwise it will
+  // distrupt the reference count.
+  layers::TextureClient* GetTextureClient()
+  {
+    return mTextureClient;
+  }
+
+  GraphicBuffer* GetGraphicBuffer()
+  {
+    MonitorAutoLock lock(mMonitor);
+    return mGraphBuffer.get();
+  }
+
+  // This function is called from layers thread.
+  void RecycleTextureClient(layers::TextureClient* aClient) override
+  {
+    MOZ_ASSERT(mTextureClient == aClient);
+
+    // Clearing the recycle allocator drops a reference, so make sure we stay alive
+    // for the duration of this function.
+    RefPtr<GonkTextureClientRecycleHandler> kungFuDeathGrip(this);
+    aClient->SetRecycleAllocator(nullptr);
+
+    {
+      MonitorAutoLock lock(mMonitor);
+      mPromise.ResolveIfExists(mTextureClient, __func__);
+    }
+  }
+
+  void Shutdown()
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    mPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
+
+    // DO NOT clear TextureClient here.
+    // The ref count could be 1 and RecycleCallback will be called if we clear
+    // the ref count here. That breaks the whole mechanism. (RecycleCallback
+    // should be called from layers)
+    mGraphBuffer = nullptr;
+  }
+
+private:
+  // Because TextureClient calls RecycleCallbackl when ref count is 1, so we
+  // should hold only one reference here and use raw pointer when out of this
+  // class.
+  RefPtr<layers::TextureClient> mTextureClient;
+
+  // It is protected by mMonitor.
+  sp<android::GraphicBuffer> mGraphBuffer;
+
+  // It is protected by mMonitor.
+  MozPromiseHolder<TextureClientRecyclePromise> mPromise;
+
+  Monitor mMonitor;
+};
+
+GonkBufferData::GonkBufferData(bool aLiveInLocal,
+                               GonkOmxPlatformLayer* aGonkPlatformLayer)
+  : BufferData(nullptr)
+  , mId(0)
+  , mGonkPlatformLayer(aGonkPlatformLayer)
 {
   if (!aLiveInLocal) {
-    mLocalBuffer = new OMX_BUFFERHEADERTYPE;
-    PodZero(mLocalBuffer.get());
-    // aMemory is a IPC memory, it is safe to use it here.
-    mLocalBuffer->pBuffer = (OMX_U8*)aMemory->pointer();
-    mBuffer = mLocalBuffer.get();
+    mMirrorBuffer = new OMX_BUFFERHEADERTYPE;
+    PodZero(mMirrorBuffer.get());
+    mBuffer = mMirrorBuffer.get();
+  }
+}
+
+void
+GonkBufferData::ReleaseBuffer()
+{
+  if (mTextureClientRecycleHandler) {
+    mTextureClientRecycleHandler->Shutdown();
+    mTextureClientRecycleHandler = nullptr;
+  }
+}
+
+nsresult
+GonkBufferData::InitSharedMemory(android::IMemory* aMemory)
+{
+  MOZ_RELEASE_ASSERT(mMirrorBuffer.get());
+
+  // aMemory is a IPC memory, it is safe to use it here.
+  mBuffer->pBuffer = (OMX_U8*)aMemory->pointer();
+  mBuffer->nAllocLen = aMemory->size();
+  return NS_OK;
+}
+
+nsresult
+GonkBufferData::InitLocalBuffer(IOMX::buffer_id aId)
+{
+  MOZ_RELEASE_ASSERT(!mMirrorBuffer.get());
+
+  mBuffer = (OMX_BUFFERHEADERTYPE*)aId;
+  return NS_OK;
+}
+
+nsresult
+GonkBufferData::InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef)
+{
+  mTextureClientRecycleHandler = new GonkTextureClientRecycleHandler(aDef);
+
+  if (!mTextureClientRecycleHandler->GetGraphicBuffer()) {
+    return NS_ERROR_FAILURE;
   }
+
+  return NS_OK;
+}
+
+already_AddRefed<MediaData>
+GonkBufferData::GetPlatformMediaData()
+{
+  if (mGonkPlatformLayer->GetTrackInfo()->GetAsAudioInfo()) {
+    // This is audio decoding.
+    return nullptr;
+  }
+
+  MOZ_RELEASE_ASSERT(mTextureClientRecycleHandler);
+
+  VideoInfo info;
+  info.mDisplay = mGonkPlatformLayer->GetTrackInfo()->GetAsVideoInfo()->mDisplay;
+  info.mImage = mGonkPlatformLayer->GetTrackInfo()->GetAsVideoInfo()->mImage;
+  RefPtr<VideoData> data = VideoData::Create(info,
+                                             mGonkPlatformLayer->GetImageContainer(),
+                                             0,
+                                             mBuffer->nTimeStamp,
+                                             1,
+                                             mTextureClientRecycleHandler->GetTextureClient(),
+                                             false,
+                                             0,
+                                             info.mImage);
+  LOG("GetMediaData: %p, disp width %d, height %d, pic width %d, height %d, time %ld",
+      this, info.mDisplay.width, info.mDisplay.height,
+      info.mImage.width, info.mImage.height, mBuffer->nTimeStamp);
+
+  // Get TextureClient Promise here to wait for resolved.
+  RefPtr<GonkBufferData> self(this);
+  mTextureClientRecycleHandler->WaitforRecycle()
+    ->Then(mGonkPlatformLayer->GetTaskQueue(), __func__,
+           [self] () {
+             // Waiting for texture to be freed.
+             self->mTextureClientRecycleHandler->GetTextureClient()->WaitForBufferOwnership();
+             self->mPromise.ResolveIfExists(self, __func__);
+           },
+           [self] () {
+             OmxBufferFailureHolder failure(OMX_ErrorUndefined, self);
+             self->mPromise.RejectIfExists(failure, __func__);
+           });
+
+  return data.forget();
 }
 
 GonkOmxPlatformLayer::GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
                                            OmxPromiseLayer* aPromiseLayer,
-                                           TaskQueue* aTaskQueue)
+                                           TaskQueue* aTaskQueue,
+                                           layers::ImageContainer* aImageContainer)
   : mTaskQueue(aTaskQueue)
+  , mImageContainer(aImageContainer)
   , mNode(0)
   , mQuirks(0)
   , mUsingHardwareCodec(false)
 {
   mOmxObserver = new GonkOmxObserver(mTaskQueue, aPromiseLayer, aDataDecoder);
 }
 
 nsresult
@@ -164,66 +357,122 @@ GonkOmxPlatformLayer::AllocateOmxBuffer(
     if (err != OMX_ErrorNone) {
       return NS_ERROR_FAILURE;
     } else if (def.eDir == aType) {
       LOG("Get OMX_IndexParamPortDefinition: port: %d, type: %d", def.nPortIndex, def.eDir);
       break;
     }
   }
 
-  size_t t = def.nBufferCountActual * def.nBufferSize;
-  LOG("Buffer count %d, buffer size %d", def.nBufferCountActual, def.nBufferSize);
+  size_t t = 0;
+
+  // Configure video output GraphicBuffer for video decoding acceleration.
+  bool useGralloc = false;
+  if ((aType == OMX_DirOutput) &&
+      (mQuirks & OMXCodec::kRequiresAllocateBufferOnOutputPorts) &&
+      (def.eDomain == OMX_PortDomainVideo)) {
+    if (NS_FAILED(EnableOmxGraphicBufferPort(def))) {
+      return NS_ERROR_FAILURE;
+    }
+
+    LOG("Enable OMX GraphicBuffer port, number %d, width %d, height %d", def.nBufferCountActual,
+        def.format.video.nFrameWidth, def.format.video.nFrameHeight);
+
+    useGralloc = true;
+
+    t = 1024; // MemoryDealer doesn't like 0, it's just for MemoryDealer happy.
+  } else {
+    t = def.nBufferCountActual * def.nBufferSize;
+    LOG("Buffer count %d, buffer size %d", def.nBufferCountActual, def.nBufferSize);
+  }
 
   bool liveinlocal = mOmx->livesLocally(mNode, getpid());
 
   // MemoryDealer is a IPC buffer allocator in Gonk because IOMX is actually
   // lives in mediaserver.
   mMemoryDealer[aType] = new MemoryDealer(t, "Gecko-OMX");
   for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
-    sp<IMemory> mem = mMemoryDealer[aType]->allocate(def.nBufferSize);
-    MOZ_ASSERT(mem.get());
-
+    RefPtr<GonkBufferData> buffer;
     IOMX::buffer_id bufferID;
     status_t st;
+    nsresult rv;
 
-    if ((mQuirks & OMXCodec::kRequiresAllocateBufferOnInputPorts && aType == OMX_DirInput) ||
-        (mQuirks & OMXCodec::kRequiresAllocateBufferOnOutputPorts && aType == OMX_DirOutput)) {
-      st = mOmx->allocateBufferWithBackup(mNode, aType, mem, &bufferID);
+    buffer = new GonkBufferData(liveinlocal, this);
+    if (useGralloc) {
+      // Buffer is lived remotely. Use GraphicBuffer for decoded video frame display.
+      rv = buffer->InitGraphicBuffer(def.format.video);
+      NS_ENSURE_SUCCESS(rv, rv);
+      st = mOmx->useGraphicBuffer(mNode,
+                                  def.nPortIndex,
+                                  buffer->mTextureClientRecycleHandler->GetGraphicBuffer(),
+                                  &bufferID);
+      CHECK_ERR(st);
     } else {
-      st = mOmx->useBuffer(mNode, aType, mem, &bufferID);
+      sp<IMemory> mem = mMemoryDealer[aType]->allocate(def.nBufferSize);
+      MOZ_ASSERT(mem.get());
+
+      if ((mQuirks & OMXCodec::kRequiresAllocateBufferOnInputPorts && aType == OMX_DirInput) ||
+          (mQuirks & OMXCodec::kRequiresAllocateBufferOnOutputPorts && aType == OMX_DirOutput)) {
+        // Buffer is lived remotely. We allocate a local OMX_BUFFERHEADERTYPE
+        // as the mirror of the remote OMX_BUFFERHEADERTYPE.
+        st = mOmx->allocateBufferWithBackup(mNode, aType, mem, &bufferID);
+        CHECK_ERR(st);
+        rv = buffer->InitSharedMemory(mem.get());
+        NS_ENSURE_SUCCESS(rv, rv);
+      } else {
+        // Buffer is lived locally, bufferID is the actually OMX_BUFFERHEADERTYPE
+        // pointer.
+        st = mOmx->useBuffer(mNode, aType, mem, &bufferID);
+        CHECK_ERR(st);
+        rv = buffer->InitLocalBuffer(bufferID);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
     }
 
-    if (st != OK) {
-      return NS_ERROR_FAILURE;
-    }
+    rv = buffer->SetBufferId(bufferID);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    aBufferList->AppendElement(new GonkBufferData(bufferID, liveinlocal, mem.get()));
+    aBufferList->AppendElement(buffer);
   }
 
   return NS_OK;
 }
 
 nsresult
 GonkOmxPlatformLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType,
                                        BUFFERLIST* aBufferList)
 {
   status_t st;
-  for (uint32_t i = 0; i < aBufferList->Length(); i++) {
-    IOMX::buffer_id id = (OMX_BUFFERHEADERTYPE*) aBufferList->ElementAt(i)->ID();
+  uint32_t len = aBufferList->Length();
+  for (uint32_t i = 0; i < len; i++) {
+    GonkBufferData* buffer = static_cast<GonkBufferData*>(aBufferList->ElementAt(i).get());
+    IOMX::buffer_id id = (OMX_BUFFERHEADERTYPE*) buffer->ID();
     st = mOmx->freeBuffer(mNode, aType, id);
     if (st != OK) {
       return NS_ERROR_FAILURE;
     }
+    buffer->ReleaseBuffer();
   }
   aBufferList->Clear();
   mMemoryDealer[aType].clear();
 
   return NS_OK;
 }
 
+nsresult
+GonkOmxPlatformLayer::EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef)
+{
+  status_t st;
+
+  st = mOmx->enableGraphicBuffers(mNode, aDef.nPortIndex, OMX_TRUE);
+  CHECK_ERR(st);
+
+  return NS_OK;
+}
+
 OMX_ERRORTYPE
 GonkOmxPlatformLayer::GetState(OMX_STATETYPE* aType)
 {
   return (OMX_ERRORTYPE)mOmx->getState(mNode, aType);
 }
 
 OMX_ERRORTYPE
 GonkOmxPlatformLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
@@ -256,52 +505,54 @@ GonkOmxPlatformLayer::Shutdown()
   mOmxClient.disconnect();
 
   return NS_OK;
 }
 
 OMX_ERRORTYPE
 GonkOmxPlatformLayer::InitOmxToStateLoaded(const TrackInfo* aInfo)
 {
+  mInfo = aInfo;
   status_t err = mOmxClient.connect();
   if (err != OK) {
       return OMX_ErrorUndefined;
   }
   mOmx = mOmxClient.interface();
   if (!mOmx.get()) {
     return OMX_ErrorUndefined;
   }
 
-  // In Gonk, the software compoment name has prefix "OMX.google". It needs to
+  // In Gonk, the software component name has prefix "OMX.google". It needs to
   // have a way to use hardware codec first.
   android::Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
   const char* swcomponent = nullptr;
-  OMXCodec::findMatchingCodecs(aInfo->mMimeType.Data(),
+  OMXCodec::findMatchingCodecs(mInfo->mMimeType.Data(),
                                0,
                                nullptr,
                                0,
                                &matchingCodecs);
   for (uint32_t i = 0; i < matchingCodecs.size(); i++) {
     const char* componentName = matchingCodecs.itemAt(i).mName.string();
     if (IsSoftwareCodec(componentName)) {
       swcomponent = componentName;
     } else {
       // Try to use hardware codec first.
       if (LoadComponent(componentName)) {
         mUsingHardwareCodec = true;
         return OMX_ErrorNone;
       }
+      LOG("failed to load component %s", componentName);
     }
   }
 
   // TODO: in android ICS, the software codec is allocated in mediaserver by
-  //       default, it may be necessay to allocate it in local process.
+  //       default, it may be necessary to allocate it in local process.
   //
   // fallback to sw codec
-  if (LoadComponent(swcomponent)) {
+  if (swcomponent && LoadComponent(swcomponent)) {
     return OMX_ErrorNone;
   }
 
   LOG("no component is loaded");
   return OMX_ErrorUndefined;
 }
 
 OMX_ERRORTYPE
@@ -313,41 +564,53 @@ GonkOmxPlatformLayer::EmptyThisBuffer(Bu
                                           aData->mBuffer->nFilledLen,
                                           aData->mBuffer->nFlags,
                                           aData->mBuffer->nTimeStamp);
 }
 
 OMX_ERRORTYPE
 GonkOmxPlatformLayer::FillThisBuffer(BufferData* aData)
 {
-  return (OMX_ERRORTYPE)mOmx->fillBuffer(mNode, (IOMX::buffer_id)aData->mBuffer);
+  return (OMX_ERRORTYPE)mOmx->fillBuffer(mNode, (IOMX::buffer_id)aData->ID());
 }
 
 OMX_ERRORTYPE
 GonkOmxPlatformLayer::SendCommand(OMX_COMMANDTYPE aCmd,
                                   OMX_U32 aParam1,
                                   OMX_PTR aCmdData)
 {
   return  (OMX_ERRORTYPE)mOmx->sendCommand(mNode, aCmd, aParam1);
 }
 
+template<class T> void
+GonkOmxPlatformLayer::InitOmxParameter(T* aParam)
+{
+  PodZero(aParam);
+  aParam->nSize = sizeof(T);
+  aParam->nVersion.s.nVersionMajor = 1;
+}
+
 bool
 GonkOmxPlatformLayer::LoadComponent(const char* aName)
 {
   status_t err = mOmx->allocateNode(aName, mOmxObserver, &mNode);
   if (err == OK) {
     OMXCodec::findCodecQuirks(aName, &mQuirks);
     LOG("Load OpenMax component %s, quirks %x, live locally %d",
         aName, mQuirks, mOmx->livesLocally(mNode, getpid()));
     return true;
   }
   return false;
 }
 
-template<class T> void
-GonkOmxPlatformLayer::InitOmxParameter(T* aParam)
+layers::ImageContainer*
+GonkOmxPlatformLayer::GetImageContainer()
 {
-  PodZero(aParam);
-  aParam->nSize = sizeof(T);
-  aParam->nVersion.s.nVersionMajor = 1;
+  return mImageContainer;
+}
+
+const TrackInfo*
+GonkOmxPlatformLayer::GetTrackInfo()
+{
+  return mInfo;
 }
 
 } // mozilla
--- a/dom/media/platforms/omx/GonkOmxPlatformLayer.h
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.h
@@ -8,72 +8,112 @@
 #define GonkOmxPlatformLayer_h_
 
 #pragma GCC visibility push(default)
 
 #include "OmxPlatformLayer.h"
 #include "OMX_Component.h"
 #include <utils/RefBase.h>
 #include <media/stagefright/OMXClient.h>
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
 
 namespace android {
+class IMemory;
 class MemoryDealer;
-class IMemory;
 }
 
 namespace mozilla {
 
 class GonkOmxObserver;
+class GonkOmxPlatformLayer;
+class GonkTextureClientRecycleHandler;
 
 /*
  * Due to Android's omx node could live in local process (client) or remote
- * process (mediaserver).
+ * process (mediaserver). And there are 3 kinds of buffer in Android OMX.
  *
- * When it is in local process, the IOMX::buffer_id is OMX_BUFFERHEADERTYPE
+ * 1.
+ * When buffer is in local process, the IOMX::buffer_id is OMX_BUFFERHEADERTYPE
  * pointer actually, it is safe to use it directly.
  *
- * When it is in remote process, the OMX_BUFFERHEADERTYPE pointer is 'IN' the
+ * 2.
+ * When buffer is in remote process, the OMX_BUFFERHEADERTYPE pointer is 'IN' the
  * remote process. It can't be used in local process, so here it allocates a
- * local OMX_BUFFERHEADERTYPE.
+ * local OMX_BUFFERHEADERTYPE. The raw/decoded data is in the android shared
+ * memory, IMemory.
+ *
+ * 3.
+ * When buffer is in remote process for the display output port. It uses
+ * GraphicBuffer to accelerate the decoding and display.
+ *
  */
 class GonkBufferData : public OmxPromiseLayer::BufferData {
 protected:
   virtual ~GonkBufferData() {}
 
 public:
-  // aMemory is an IPC based memory which will be used as the pBuffer in
-  // mLocalBuffer.
-  GonkBufferData(android::IOMX::buffer_id aId, bool aLiveInLocal, android::IMemory* aMemory);
+  GonkBufferData(bool aLiveInLocal,
+                 GonkOmxPlatformLayer* aLayer);
 
   BufferID ID() override
   {
     return mId;
   }
 
+  already_AddRefed<MediaData> GetPlatformMediaData() override;
+
   bool IsLocalBuffer()
   {
-    return !!mLocalBuffer.get();
+    return !!mMirrorBuffer.get();
+  }
+
+  void ReleaseBuffer();
+
+  nsresult SetBufferId(android::IOMX::buffer_id aId)
+  {
+    mId = aId;
+    return NS_OK;
   }
 
+  // The mBuffer is in local process. And aId is actually the OMX_BUFFERHEADERTYPE
+  // pointer. It doesn't need a mirror buffer.
+  nsresult InitLocalBuffer(android::IOMX::buffer_id aId);
+
+  // aMemory is an IPC based memory which will be used as the pBuffer in
+  // mBuffer. And the mBuffer will be the mirror OMX_BUFFERHEADERTYPE
+  // of the one in the remote process.
+  nsresult InitSharedMemory(android::IMemory* aMemory);
+
+  // GraphicBuffer is for video decoding acceleration on output port.
+  // Then mBuffer is the mirror OMX_BUFFERHEADERTYPE of the one in the remote
+  // process.
+  nsresult InitGraphicBuffer(OMX_VIDEO_PORTDEFINITIONTYPE& aDef);
+
   // Android OMX uses this id to pass the buffer between OMX component and
   // client.
   android::IOMX::buffer_id mId;
 
-  // mLocalBuffer are used only when the omx node is in mediaserver.
+  // mMirrorBuffer are used only when the omx node is in mediaserver.
   // Due to IPC problem, the mId is the OMX_BUFFERHEADERTYPE address in mediaserver.
   // It can't mapping to client process, so we need a local OMX_BUFFERHEADERTYPE
-  // here.
-  nsAutoPtr<OMX_BUFFERHEADERTYPE> mLocalBuffer;
+  // here to mirror the remote OMX_BUFFERHEADERTYPE in mediaserver.
+  nsAutoPtr<OMX_BUFFERHEADERTYPE> mMirrorBuffer;
+
+  // It creates GraphicBuffer and manages TextureClient.
+  RefPtr<GonkTextureClientRecycleHandler> mTextureClientRecycleHandler;
+
+  GonkOmxPlatformLayer* mGonkPlatformLayer;
 };
 
 class GonkOmxPlatformLayer : public OmxPlatformLayer {
 public:
   GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
                        OmxPromiseLayer* aPromiseLayer,
-                       TaskQueue* aTaskQueue);
+                       TaskQueue* aTaskQueue,
+                       layers::ImageContainer* aImageContainer);
 
   nsresult AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) override;
 
   nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) override;
 
   OMX_ERRORTYPE GetState(OMX_STATETYPE* aType) override;
 
   OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
@@ -92,40 +132,57 @@ public:
 
   OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE aCmd,
                             OMX_U32 aParam1,
                             OMX_PTR aCmdData) override;
 
   nsresult Shutdown() override;
 
   // TODO:
-  // There is another InitOmxParameter in OmxDataDecoder. They need to combinate
+  // There is another InitOmxParameter in OmxDataDecoder. They need to combine
   // to one function.
   template<class T> void InitOmxParameter(T* aParam);
 
 protected:
+  friend GonkBufferData;
+
+  layers::ImageContainer* GetImageContainer();
+
+  const TrackInfo* GetTrackInfo();
+
+  TaskQueue* GetTaskQueue()
+  {
+    return mTaskQueue;
+  }
+
+  nsresult EnableOmxGraphicBufferPort(OMX_PARAM_PORTDEFINITIONTYPE& aDef);
+
   bool LoadComponent(const char* aName);
 
   friend class GonkOmxObserver;
 
   RefPtr<TaskQueue> mTaskQueue;
 
+  RefPtr<layers::ImageContainer> mImageContainer;
+
   // OMX_DirInput is 0, OMX_DirOutput is 1.
   android::sp<android::MemoryDealer> mMemoryDealer[2];
 
   android::sp<GonkOmxObserver> mOmxObserver;
 
   android::sp<android::IOMX> mOmx;
 
   android::IOMX::node_id mNode;
 
   android::OMXClient mOmxClient;
 
   uint32_t mQuirks;
 
   bool mUsingHardwareCodec;
+
+  const TrackInfo* mInfo;
 };
 
 }
 
 #pragma GCC visibility pop
 
 #endif // GonkOmxPlatformLayer_h_
--- a/dom/media/platforms/omx/OmxDataDecoder.cpp
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -56,41 +56,41 @@ StateTypeToStr(OMX_STATETYPE aType)
 
 // There should be 2 ports and port number start from 0.
 void GetPortIndex(nsTArray<uint32_t>& aPortIndex) {
   aPortIndex.AppendElement(0);
   aPortIndex.AppendElement(1);
 }
 
 OmxDataDecoder::OmxDataDecoder(const TrackInfo& aTrackInfo,
-                               MediaDataDecoderCallback* aCallback)
+                               MediaDataDecoderCallback* aCallback,
+                               layers::ImageContainer* aImageContainer)
   : mMonitor("OmxDataDecoder")
   , mOmxTaskQueue(CreateMediaDecodeTaskQueue())
   , mWatchManager(this, mOmxTaskQueue)
   , mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
   , mTrackInfo(aTrackInfo.Clone())
   , mFlushing(false)
   , mShuttingDown(false)
   , mCheckingInputExhausted(false)
   , mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
   , mAudioCompactor(mAudioQueue)
   , mCallback(aCallback)
 {
   LOG("(%p)", this);
-  mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this);
+  mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this, aImageContainer);
 
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(this, &OmxDataDecoder::InitializationTask);
   mOmxTaskQueue->Dispatch(r.forget());
 }
 
 OmxDataDecoder::~OmxDataDecoder()
 {
   LOG("(%p)", this);
-  mWatchManager.Shutdown();
 }
 
 void
 OmxDataDecoder::InitializationTask()
 {
   mWatchManager.Watch(mOmxState, &OmxDataDecoder::OmxStateRunner);
   mWatchManager.Watch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
 }
@@ -118,25 +118,21 @@ OmxDataDecoder::Init()
 
   RefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
   RefPtr<OmxDataDecoder> self = this;
 
   // TODO: it needs to get permission from resource manager before allocating
   //       Omx component.
   InvokeAsync(mOmxTaskQueue, mOmxLayer.get(), __func__, &OmxPromiseLayer::Init,
               mOmxTaskQueue, mTrackInfo.get())
-    ->Then(mReaderTaskQueue, __func__,
+    ->Then(mOmxTaskQueue, __func__,
       [self] () {
         // Omx state should be OMX_StateIdle.
-        nsCOMPtr<nsIRunnable> r =
-          NS_NewRunnableFunction([self] () {
-            self->mOmxState = self->mOmxLayer->GetState();
-            MOZ_ASSERT(self->mOmxState != OMX_StateIdle);
-          });
-        self->mOmxTaskQueue->Dispatch(r.forget());
+        self->mOmxState = self->mOmxLayer->GetState();
+        MOZ_ASSERT(self->mOmxState != OMX_StateIdle);
       },
       [self] () {
         self->RejectInitPromise(DecoderFailureReason::INIT_ERROR, __func__);
       });
 
   return p;
 }
 
@@ -186,19 +182,16 @@ OmxDataDecoder::Flush()
   return NS_OK;
 }
 
 nsresult
 OmxDataDecoder::Drain()
 {
   LOG("(%p)", this);
 
-  // TODO: For video decoding, it needs to copy the latest video frame to yuv
-  //       and output to layer again, because all video buffers will be released
-  //       later.
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(this, &OmxDataDecoder::SendEosBuffer);
   mOmxTaskQueue->Dispatch(r.forget());
 
   return NS_OK;
 }
 
 nsresult
@@ -228,17 +221,17 @@ OmxDataDecoder::Shutdown()
   return NS_OK;
 }
 
 void
 OmxDataDecoder::DoAsyncShutdown()
 {
   LOG("(%p)", this);
   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
-  MOZ_ASSERT(mFlushing);
+  MOZ_ASSERT(!mFlushing);
 
   mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
   mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
 
   // Flush to all ports, so all buffers can be returned from component.
   RefPtr<OmxDataDecoder> self = this;
   mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
     ->Then(mOmxTaskQueue, __func__,
@@ -250,94 +243,61 @@ OmxDataDecoder::DoAsyncShutdown()
              self->mOmxLayer->Shutdown();
            })
     ->CompletionPromise()
     ->Then(mOmxTaskQueue, __func__,
            [self] () -> RefPtr<OmxCommandPromise> {
              RefPtr<OmxCommandPromise> p =
                self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
 
-             LOG("DoAsyncShutdown: collecting buffers...");
-             self->CollectBufferPromises(OMX_DirMax)
-               ->Then(self->mOmxTaskQueue, __func__,
-                   [self] () {
-                   // According to spec 3.1.1.2.2.1:
-                   // OMX_StateLoaded needs to be sent before releasing buffers.
-                   // And state transition from OMX_StateIdle to OMX_StateLoaded
-                   // is completed when all of the buffers have been removed
-                   // from the component.
-                   // Here the buffer promises are not resolved due to displaying
-                   // in layer, it needs to wait before the layer returns the
-                   // buffers.
-                   LOG("DoAsyncShutdown: all buffers collected, releasing buffers...");
-                   self->ReleaseBuffers(OMX_DirInput);
-                   self->ReleaseBuffers(OMX_DirOutput);
-                   },
-                   [self] () {
-                     self->mOmxLayer->Shutdown();
-                   });
+             // According to spec 3.1.1.2.2.1:
+             // OMX_StateLoaded needs to be sent before releasing buffers.
+             // And state transition from OMX_StateIdle to OMX_StateLoaded
+             // is completed when all of the buffers have been removed
+             // from the component.
+             // Here the buffer promises are not resolved due to displaying
+             // in layer, it needs to wait before the layer returns the
+             // buffers.
+             LOG("DoAsyncShutdown: releasing buffers...");
+             self->ReleaseBuffers(OMX_DirInput);
+             self->ReleaseBuffers(OMX_DirOutput);
 
              return p;
            },
            [self] () {
              self->mOmxLayer->Shutdown();
            })
     ->CompletionPromise()
     ->Then(mOmxTaskQueue, __func__,
            [self] () {
              LOG("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
              self->mOmxLayer->Shutdown();
+             self->mWatchManager.Shutdown();
+             self->mOmxLayer = nullptr;
 
              MonitorAutoLock lock(self->mMonitor);
              self->mShuttingDown = false;
              self->mMonitor.Notify();
            },
            [self] () {
              self->mOmxLayer->Shutdown();
+             self->mWatchManager.Shutdown();
+             self->mOmxLayer = nullptr;
 
              MonitorAutoLock lock(self->mMonitor);
              self->mShuttingDown = false;
              self->mMonitor.Notify();
            });
 }
 
 void
-OmxDataDecoder::CheckIfInputExhausted()
-{
-  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
-  MOZ_ASSERT(!mCheckingInputExhausted);
-
-  mCheckingInputExhausted = false;
-
-  if (mMediaRawDatas.Length()) {
-    return;
-  }
-
-  // When all input buffers are not in omx component, it means all samples have
-  // been fed into OMX component.
-  for (auto buf : mInPortBuffers) {
-    if (buf->mStatus == BufferData::BufferStatus::OMX_COMPONENT) {
-      return;
-    }
-  }
-
-  // When all output buffers are held by component, it means client is waiting for output.
-  for (auto buf : mOutPortBuffers) {
-    if (buf->mStatus != BufferData::BufferStatus::OMX_COMPONENT) {
-      return;
-    }
-  }
-
-  LOG("Call InputExhausted()");
-  mCallback->InputExhausted();
-}
-
-void
 OmxDataDecoder::OutputAudio(BufferData* aBufferData)
 {
+  // TODO: it'd be better to move these code to BufferData::GetPlatformMediaData() or
+  //       some kind of abstract layer.
   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
   OMX_BUFFERHEADERTYPE* buf = aBufferData->mBuffer;
   AudioInfo* info = mTrackInfo->GetAsAudioInfo();
   if (buf->nFilledLen) {
     uint64_t offset = 0;
     uint32_t frames = buf->nFilledLen / (2 * info->mChannels);
     if (aBufferData->mRawData) {
       offset = aBufferData->mRawData->mOffset;
@@ -353,40 +313,89 @@ OmxDataDecoder::OutputAudio(BufferData* 
                                  info->mChannels));
     RefPtr<AudioData> audio = mAudioQueue.PopFront();
     mCallback->Output(audio);
   }
   aBufferData->mStatus = BufferData::BufferStatus::FREE;
 }
 
 void
+OmxDataDecoder::OutputVideo(BufferData* aBufferData)
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  RefPtr<MediaData> data = aBufferData->GetPlatformMediaData();
+  MOZ_RELEASE_ASSERT(data);
+
+  VideoData* video(data->As<VideoData>());
+  if (aBufferData->mRawData) {
+    video->mTime = aBufferData->mRawData->mTime;
+    video->mTimecode = aBufferData->mRawData->mTimecode;
+    video->mOffset = aBufferData->mRawData->mOffset;
+    video->mDuration = aBufferData->mRawData->mDuration;
+    video->mKeyframe = aBufferData->mRawData->mKeyframe;
+  }
+
+  aBufferData->mStatus = BufferData::BufferStatus::OMX_CLIENT_OUTPUT;
+
+  // TextureClient's recycle callback is called when reference count of
+  // TextureClient becomes 1. In most cases, the last reference count is held
+  // by ITextureClientRecycleAllocator.
+  // And then promise will be resolved in the callback.
+  // TODO:
+  //   Because it is gonk specific behaviour, it needs to find a way to
+  //   proper abstracting it.
+  MOZ_RELEASE_ASSERT(aBufferData->mPromise.IsEmpty());
+  RefPtr<OmxBufferPromise> p = aBufferData->mPromise.Ensure(__func__);
+
+  RefPtr<OmxDataDecoder> self = this;
+  RefPtr<BufferData> buffer = aBufferData;
+  p->Then(mOmxTaskQueue, __func__,
+          [self, buffer] () {
+            MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
+            buffer->mStatus = BufferData::BufferStatus::FREE;
+            self->FillAndEmptyBuffers();
+          },
+          [buffer] () {
+            MOZ_RELEASE_ASSERT(buffer->mStatus == BufferData::BufferStatus::OMX_CLIENT_OUTPUT);
+            buffer->mStatus = BufferData::BufferStatus::FREE;
+          });
+
+  mCallback->Output(video);
+}
+
+void
 OmxDataDecoder::FillBufferDone(BufferData* aData)
 {
   MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
 
-  if (mTrackInfo->IsAudio()) {
-    OutputAudio(aData);
-  } else {
-    MOZ_ASSERT(0);
+  // Don't output sample when flush or shutting down, especially for video
+  // decoded frame. Because video decoded frame has a promise in BufferData
+  // waiting for layer to resolve it via recycle callback on Gonk, if other
+  // module doesn't send it to layer, it will cause a unresolved promise and
+  // waiting for resolve infinitely.
+  if (mFlushing || mShuttingDown) {
+    LOG("mFlush or mShuttingDown, drop data");
+    aData->mStatus = BufferData::BufferStatus::FREE;
+    return;
   }
 
   if (aData->mBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
+    // Reach eos, it's an empty data so it doesn't need to output.
     EndOfStream();
+    aData->mStatus = BufferData::BufferStatus::FREE;
   } else {
+    if (mTrackInfo->IsAudio()) {
+      OutputAudio(aData);
+    } else if (mTrackInfo->IsVideo()) {
+      OutputVideo(aData);
+    } else {
+      MOZ_ASSERT(0);
+    }
     FillAndEmptyBuffers();
-
-    // If the latest decoded sample's MediaRawData is also the latest input
-    // sample, it means there is no input data in queue and component, calling
-    // CheckIfInputExhausted().
-    if (aData->mRawData == mLatestInputRawData && !mCheckingInputExhausted) {
-      mCheckingInputExhausted = true;
-      nsCOMPtr<nsIRunnable> r =
-        NS_NewRunnableMethod(this, &OmxDataDecoder::CheckIfInputExhausted);
-      mOmxTaskQueue->Dispatch(r.forget());
-    }
   }
 }
 
 void
 OmxDataDecoder::FillBufferFailure(OmxBufferFailureHolder aFailureHolder)
 {
   NotifyError(aFailureHolder.mError, __func__);
 }
@@ -394,16 +403,40 @@ OmxDataDecoder::FillBufferFailure(OmxBuf
 void
 OmxDataDecoder::EmptyBufferDone(BufferData* aData)
 {
   MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
 
   // Nothing to do when status of input buffer is OMX_CLIENT.
   aData->mStatus = BufferData::BufferStatus::FREE;
   FillAndEmptyBuffers();
+
+  // There is no way to know if component gets enough raw samples to generate
+  // output, especially for video decoding. So here it needs to request raw
+  // samples aggressively.
+  if (!mCheckingInputExhausted && !mMediaRawDatas.Length()) {
+    mCheckingInputExhausted = true;
+
+    RefPtr<OmxDataDecoder> self = this;
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableFunction([self] () {
+        MOZ_ASSERT(self->mOmxTaskQueue->IsCurrentThreadIn());
+
+        self->mCheckingInputExhausted = false;
+
+        if (self->mMediaRawDatas.Length()) {
+          return;
+        }
+
+        LOG("Call InputExhausted()");
+        self->mCallback->InputExhausted();
+      });
+
+    mOmxTaskQueue->Dispatch(r.forget());
+  }
 }
 
 void
 OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder)
 {
   NotifyError(aFailureHolder.mError, __func__);
 }
 
@@ -422,44 +455,45 @@ OmxDataDecoder::FillAndEmptyBuffers()
 
   // During the port setting changed, it is forbidden to do any buffer operation.
   if (mPortSettingsChanged != -1 || mShuttingDown || mFlushing) {
     return;
   }
 
   // Trigger input port.
   while (!!mMediaRawDatas.Length()) {
-    // input buffer must be usedi by component if there is data available.
+    // input buffer must be used by component if there is data available.
     RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
     if (!inbuf) {
       LOG("no input buffer!");
       break;
     }
 
     RefPtr<MediaRawData> data = mMediaRawDatas[0];
+    // Buffer size should large enough for raw data.
+    MOZ_RELEASE_ASSERT(inbuf->mBuffer->nAllocLen >= data->Size());
+
     memcpy(inbuf->mBuffer->pBuffer, data->Data(), data->Size());
     inbuf->mBuffer->nFilledLen = data->Size();
     inbuf->mBuffer->nOffset = 0;
-    // TODO: the frame size could larger than buffer size in video case.
     inbuf->mBuffer->nFlags = inbuf->mBuffer->nAllocLen > data->Size() ?
                              OMX_BUFFERFLAG_ENDOFFRAME : 0;
     inbuf->mBuffer->nTimeStamp = data->mTime;
     if (data->Size()) {
       inbuf->mRawData = mMediaRawDatas[0];
     } else {
        LOG("send EOS buffer");
       inbuf->mBuffer->nFlags |= OMX_BUFFERFLAG_EOS;
     }
 
     LOG("feed sample %p to omx component, len %d, flag %X", data.get(),
         inbuf->mBuffer->nFilledLen, inbuf->mBuffer->nFlags);
     mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
                                         &OmxDataDecoder::EmptyBufferDone,
                                         &OmxDataDecoder::EmptyBufferFailure);
-    mLatestInputRawData.swap(mMediaRawDatas[0]);
     mMediaRawDatas.RemoveElementAt(0);
   }
 
   // Trigger output port.
   while (true) {
     RefPtr<BufferData> outbuf = FindAvailableBuffer(OMX_DirOutput);
     if (!outbuf) {
       break;
@@ -546,19 +580,21 @@ OmxDataDecoder::OmxStateRunner()
   LOG("OMX state: %s", StateTypeToStr(mOmxState));
 
   // TODO: maybe it'd be better to use promise CompletionPromise() to replace
   //       this state machine.
   if (mOmxState == OMX_StateLoaded) {
     // Config codec parameters by minetype.
     if (mTrackInfo->IsAudio()) {
       ConfigAudioCodec();
+    } else if (mTrackInfo->IsVideo()) {
+      ConfigVideoCodec();
     }
 
-    // Send OpenMax state commane to OMX_StateIdle.
+    // Send OpenMax state command to OMX_StateIdle.
     RefPtr<OmxDataDecoder> self = this;
     mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr)
       ->Then(mOmxTaskQueue, __func__,
              [self] () {
                // Current state should be OMX_StateIdle.
                self->mOmxState = self->mOmxLayer->GetState();
                MOZ_ASSERT(self->mOmxState == OMX_StateIdle);
              },
@@ -584,17 +620,17 @@ OmxDataDecoder::OmxStateRunner()
                MOZ_ASSERT(self->mOmxState == OMX_StateExecuting);
 
                self->ResolveInitPromise(__func__);
              },
              [self] () {
                self->RejectInitPromise(DecoderFailureReason::INIT_ERROR, __func__);
              });
   } else if (mOmxState == OMX_StateExecuting) {
-    // Config codec once it gets OMX_StateExecuting state.
+    // Configure codec once it gets OMX_StateExecuting state.
     FillCodecConfigDataToOmx();
   } else {
     MOZ_ASSERT(0);
   }
 }
 
 void
 OmxDataDecoder::ConfigAudioCodec()
@@ -614,41 +650,96 @@ OmxDataDecoder::ConfigAudioCodec()
     err = mOmxLayer->SetParameter(OMX_IndexParamAudioAac, &aac_profile, sizeof(aac_profile));
     CHECK_OMX_ERR(err);
     LOG("Config OMX_IndexParamAudioAac, channel %d, sample rate %d, profile %d",
         audioInfo->mChannels, audioInfo->mRate, audioInfo->mProfile);
   }
 }
 
 void
+OmxDataDecoder::ConfigVideoCodec()
+{
+  OMX_ERRORTYPE err;
+  const VideoInfo* videoInfo = mTrackInfo->GetAsVideoInfo();
+
+  OMX_PARAM_PORTDEFINITIONTYPE def;
+
+  // Set up in/out port definition.
+  nsTArray<uint32_t> ports;
+  GetPortIndex(ports);
+  for (auto idx : ports) {
+    InitOmxParameter(&def);
+    def.nPortIndex = idx;
+    err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
+                                  &def,
+                                  sizeof(def));
+    if (err != OMX_ErrorNone) {
+      return;
+    }
+
+    def.format.video.nFrameWidth =  videoInfo->mDisplay.width;
+    def.format.video.nFrameHeight = videoInfo->mDisplay.height;
+    def.format.video.nStride = videoInfo->mImage.width;
+    def.format.video.nSliceHeight = videoInfo->mImage.height;
+
+    // TODO: it needs to add other formats like webm, mp4, h263... etc.
+    OMX_VIDEO_CODINGTYPE codetype;
+    if (videoInfo->mMimeType.EqualsLiteral("video/avc")) {
+      codetype = OMX_VIDEO_CodingAVC;
+    }
+
+    if (def.eDir == OMX_DirInput) {
+      def.format.video.eCompressionFormat = codetype;
+      def.format.video.eColorFormat = OMX_COLOR_FormatUnused;
+    } else {
+      def.format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+    }
+
+    err = mOmxLayer->SetParameter(OMX_IndexParamPortDefinition,
+                                  &def,
+                                  sizeof(def));
+    if (err != OMX_ErrorNone) {
+      return;
+    }
+  }
+}
+
+void
 OmxDataDecoder::FillCodecConfigDataToOmx()
 {
-  // Codec config data should be the first sample running on Omx TaskQueue.
+  // Codec configure data should be the first sample running on Omx TaskQueue.
   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
   MOZ_ASSERT(!mMediaRawDatas.Length());
   MOZ_ASSERT(mOmxState == OMX_StateIdle || mOmxState == OMX_StateExecuting);
 
 
   RefPtr<BufferData> inbuf = FindAvailableBuffer(OMX_DirInput);
+  RefPtr<MediaByteBuffer> csc;
   if (mTrackInfo->IsAudio()) {
-    AudioInfo* audio_info = mTrackInfo->GetAsAudioInfo();
+    csc = mTrackInfo->GetAsAudioInfo()->mCodecSpecificConfig;
+  } else if (mTrackInfo->IsVideo()) {
+    csc = mTrackInfo->GetAsVideoInfo()->mCodecSpecificConfig;
+  }
+
+  MOZ_RELEASE_ASSERT(csc);
+
+  // Some codecs like h264, its codec specific data is at the first packet, not in container.
+  if (csc->Length()) {
     memcpy(inbuf->mBuffer->pBuffer,
-           audio_info->mCodecSpecificConfig->Elements(),
-           audio_info->mCodecSpecificConfig->Length());
-    inbuf->mBuffer->nFilledLen = audio_info->mCodecSpecificConfig->Length();
+           csc->Elements(),
+           csc->Length());
+    inbuf->mBuffer->nFilledLen = csc->Length();
     inbuf->mBuffer->nOffset = 0;
     inbuf->mBuffer->nFlags = (OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG);
-  } else {
-    MOZ_ASSERT(0);
+
+    LOG("Feed codec configure data to OMX component");
+    mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
+                                        &OmxDataDecoder::EmptyBufferDone,
+                                        &OmxDataDecoder::EmptyBufferFailure);
   }
-
-  LOG("Feed codec configure data to OMX component");
-  mOmxLayer->EmptyBuffer(inbuf)->Then(mOmxTaskQueue, __func__, this,
-                                      &OmxDataDecoder::EmptyBufferDone,
-                                      &OmxDataDecoder::EmptyBufferFailure);
 }
 
 bool
 OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
 {
   MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
 
   if (mOmxLayer->Event(aEvent, aData1, aData2)) {
--- a/dom/media/platforms/omx/OmxDataDecoder.h
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -22,17 +22,17 @@ typedef OmxPromiseLayer::OmxCommandFailu
 typedef OmxPromiseLayer::BufferData BufferData;
 typedef OmxPromiseLayer::BUFFERLIST BUFFERLIST;
 
 /* OmxDataDecoder is the major class which performs followings:
  *   1. Translate PDM function into OMX commands.
  *   2. Keeping the buffers between client and component.
  *   3. Manage the OMX state.
  *
- * From the definiton in OpenMax spec. "2.2.1", there are 3 major roles in
+ * From the definition in OpenMax spec. "2.2.1", there are 3 major roles in
  * OpenMax IL.
  *
  * IL client:
  *   "The IL client may be a layer below the GUI application, such as GStreamer,
  *   or may be several layers below the GUI layer."
  *
  *   OmxDataDecoder acts as the IL client.
  *
@@ -49,17 +49,18 @@ typedef OmxPromiseLayer::BUFFERLIST BUFF
  *   OmxPlatformLayer acts as the OpenMAX IL core.
  */
 class OmxDataDecoder : public MediaDataDecoder {
 protected:
   virtual ~OmxDataDecoder();
 
 public:
   OmxDataDecoder(const TrackInfo& aTrackInfo,
-                 MediaDataDecoderCallback* aCallback);
+                 MediaDataDecoderCallback* aCallback,
+                 layers::ImageContainer* aImageContainer);
 
   RefPtr<InitPromise> Init() override;
 
   nsresult Input(MediaRawData* aSample) override;
 
   nsresult Flush() override;
 
   nsresult Drain() override;
@@ -85,40 +86,38 @@ protected:
   void FillBufferFailure(OmxBufferFailureHolder aFailureHolder);
 
   void EmptyBufferDone(BufferData* aData);
 
   void EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder);
 
   void NotifyError(OMX_ERRORTYPE aError, const char* aLine);
 
-  // Config audio codec.
+  // Configure audio codec.
   // Some codec may just ignore this and rely on codec specific data in
   // FillCodecConfigDataToOmx().
   void ConfigAudioCodec();
+  void ConfigVideoCodec();
 
   // Sending codec specific data to OMX component. OMX component could send a
   // OMX_EventPortSettingsChanged back to client. And then client needs to
   // disable port and reallocate buffer.
   void FillCodecConfigDataToOmx();
 
   void SendEosBuffer();
 
   void EndOfStream();
 
   // It could be called after codec specific data is sent and component found
   // the port format is changed due to different codec specific.
   void PortSettingsChanged();
 
   void OutputAudio(BufferData* aBufferData);
 
-  // Notify InputExhausted when:
-  //   1. all input buffers are not held by component.
-  //   2. all output buffers are waiting for filling complete.
-  void CheckIfInputExhausted();
+  void OutputVideo(BufferData* aBufferData);
 
   // Buffer can be released if its status is not OMX_COMPONENT or
   // OMX_CLIENT_OUTPUT.
   bool BuffersCanBeReleased(OMX_DIRTYPE aType);
 
   OMX_DIRTYPE GetPortDirection(uint32_t aPortIndex);
 
   void DoAsyncShutdown();
@@ -177,19 +176,16 @@ protected:
   //
   // Note: when port setting changed, there should be no buffer operations
   //       via EmptyBuffer or FillBuffer.
   Watchable<int32_t> mPortSettingsChanged;
 
   // It is access in Omx TaskQueue.
   nsTArray<RefPtr<MediaRawData>> mMediaRawDatas;
 
-  // It is access in Omx TaskQueue. The latest input MediaRawData.
-  RefPtr<MediaRawData> mLatestInputRawData;
-
   BUFFERLIST mInPortBuffers;
 
   BUFFERLIST mOutPortBuffers;
 
   // For audio output.
   // TODO: because this class is for both video and audio decoding, so there
   // should be some kind of abstract things to these members.
   MediaQueue<AudioData> mAudioQueue;
--- a/dom/media/platforms/omx/OmxDecoderModule.cpp
+++ b/dom/media/platforms/omx/OmxDecoderModule.cpp
@@ -11,25 +11,26 @@ namespace mozilla {
 
 already_AddRefed<MediaDataDecoder>
 OmxDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
                                      mozilla::layers::LayersBackend aLayersBackend,
                                      mozilla::layers::ImageContainer* aImageContainer,
                                      FlushableTaskQueue* aVideoTaskQueue,
                                      MediaDataDecoderCallback* aCallback)
 {
-  return nullptr;
+  RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback, aImageContainer);
+  return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 OmxDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
                                      FlushableTaskQueue* aAudioTaskQueue,
                                      MediaDataDecoderCallback* aCallback)
 {
-  RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback);
+  RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback, nullptr);
   return decoder.forget();
 }
 
 void
 OmxDecoderModule::Init()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
 }
@@ -38,12 +39,13 @@ PlatformDecoderModule::ConversionRequire
 OmxDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   return kNeedNone;
 }
 
 bool
 OmxDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
 {
-  return aMimeType.EqualsLiteral("audio/mp4a-latm");
+  return aMimeType.EqualsLiteral("audio/mp4a-latm") ||
+         aMimeType.EqualsLiteral("video/avc");
 }
 
 }
--- a/dom/media/platforms/omx/OmxPlatformLayer.h
+++ b/dom/media/platforms/omx/OmxPlatformLayer.h
@@ -11,16 +11,17 @@
 #include "OMX_Types.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/TaskQueue.h"
 #include "OmxPromiseLayer.h"
 
 namespace mozilla {
 
 class TrackInfo;
+class VideoData;
 
 /*
  * This class the the abstract layer of the platform OpenMax IL implementation.
  *
  * For some platform like andoird, it exposures its OpenMax IL via IOMX which 
  * is definitions are different comparing to standard.
  * For other platforms like Raspberry Pi, it will be easy to implement this layer
  * with the standard OpenMax IL api.
--- a/dom/media/platforms/omx/OmxPromiseLayer.cpp
+++ b/dom/media/platforms/omx/OmxPromiseLayer.cpp
@@ -3,37 +3,39 @@
 /* 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/. */
 
 #include "OmxPromiseLayer.h"
 #include "OmxPlatformLayer.h"
 #include "OmxDataDecoder.h"
 
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 21
+#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
 #include "GonkOmxPlatformLayer.h"
 #endif
 
 extern mozilla::LogModule* GetPDMLog();
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxPromiseLayer:: " arg, ##__VA_ARGS__))
 
 namespace mozilla {
 
 extern void GetPortIndex(nsTArray<uint32_t>& aPortIndex);
 
-OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder)
+OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue,
+                                 OmxDataDecoder* aDataDecoder,
+                                 layers::ImageContainer* aImageContainer)
   : mTaskQueue(aTaskQueue)
 {
-#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 21
-  mPlatformLayer = new GonkOmxPlatformLayer(aDataDecoder, this, aTaskQueue);
+#if defined(MOZ_WIDGET_GONK) && (ANDROID_VERSION == 20 || ANDROID_VERSION == 19)
+  mPlatformLayer = new GonkOmxPlatformLayer(aDataDecoder, this, aTaskQueue, aImageContainer);
 #endif
   MOZ_ASSERT(!!mPlatformLayer);
 }
 
 RefPtr<OmxPromiseLayer::OmxCommandPromise>
 OmxPromiseLayer::Init(TaskQueue* aTaskQueue, const TrackInfo* aInfo)
 {
   mTaskQueue = aTaskQueue;
@@ -177,52 +179,43 @@ OmxPromiseLayer::EmptyFillBufferDone(OMX
     aData->mPromise.Resolve(aData, __func__);
   }
 }
 
 void
 OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData::BufferID aID)
 {
   RefPtr<BufferData> holder = FindAndRemoveBufferHolder(aType, aID);
-  MOZ_ASSERT(!!holder);
-  LOG("EmptyFillBufferDone: type %d, buffer %p", aType, holder->mBuffer);
-  if (holder) {
-    if (aType == OMX_DirOutput) {
-      holder->mRawData = nullptr;
-      holder->mRawData = FindAndRemoveRawData(holder->mBuffer->nTimeStamp);
-    }
-    holder->mStatus = BufferData::BufferStatus::OMX_CLIENT;
-    holder->mPromise.Resolve(holder, __func__);
-  }
+  EmptyFillBufferDone(aType, holder);
 }
 
 RefPtr<OmxPromiseLayer::OmxCommandPromise>
 OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmdData)
 {
   if (aCmd == OMX_CommandFlush) {
     // It doesn't support another flush commands before previous one is completed.
     MOZ_RELEASE_ASSERT(!mFlushCommands.Length());
 
     // Some coomponents don't send event with OMX_ALL, they send flush complete
     // event with input port and another event for output port.
-    // In prupose of better compatibility, we inteprete the OMX_ALL to OMX_DirInput
+    // In prupose of better compatibility, we interpret the OMX_ALL to OMX_DirInput
     // and OMX_DirOutput flush separately.
     OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
     for(const auto type : types) {
       if ((aParam1 == type) || (aParam1 == OMX_ALL)) {
         mFlushCommands.AppendElement(FlushCommand({type, aCmdData}));
       }
 
       if (type == OMX_DirInput) {
         // Clear all buffered raw data.
         mRawDatas.Clear();
       }
     }
 
-    // Don't overlay more than one fush command, some components can't overlay flush commands.
+    // Don't overlay more than one flush command, some components can't overlay flush commands.
     // So here we send another flush after receiving the previous flush completed event.
     if (mFlushCommands.Length()) {
       OMX_ERRORTYPE err =
         mPlatformLayer->SendCommand(OMX_CommandFlush,
                                     mFlushCommands.ElementAt(0).type,
                                     mFlushCommands.ElementAt(0).cmd);
       if (err != OMX_ErrorNone) {
         OmxCommandFailureHolder failure(OMX_ErrorNotReady, OMX_CommandFlush);
@@ -301,16 +294,18 @@ OmxPromiseLayer::Event(OMX_EVENTTYPE aEv
         OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandFlush);
         mFlushPromise.Reject(failure, __func__);
       } else if (cmd == OMX_CommandPortDisable) {
         OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandPortDisable);
         mPortDisablePromise.Reject(failure, __func__);
       } else if (cmd == OMX_CommandPortEnable) {
         OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandPortEnable);
         mPortEnablePromise.Reject(failure, __func__);
+      } else {
+        return false;
       }
       break;
     }
     default:
     {
       return false;
     }
   }
--- a/dom/media/platforms/omx/OmxPromiseLayer.h
+++ b/dom/media/platforms/omx/OmxPromiseLayer.h
@@ -9,19 +9,20 @@
 
 #include "OMX_Core.h"
 #include "OMX_Types.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/TaskQueue.h"
 
 namespace mozilla {
 
-class TrackInfo;
 class OmxPlatformLayer;
 class OmxDataDecoder;
+class TrackInfo;
+class MediaData;
 
 /* This class acts as a middle layer between OmxDataDecoder and the underlying
  * OmxPlatformLayer.
  *
  * This class has two purposes:
  * 1. Using promise instead of OpenMax async callback function.
  *    For example, OmxCommandPromise is used for OpenMax IL SendCommand.
  * 2. Manage the buffer exchanged between client and component.
@@ -33,17 +34,19 @@ class OmxDataDecoder;
  */
 class OmxPromiseLayer {
 protected:
   virtual ~OmxPromiseLayer() {}
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OmxPromiseLayer)
 
-  OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder);
+  OmxPromiseLayer(TaskQueue* aTaskQueue,
+                  OmxDataDecoder* aDataDecoder,
+                  layers::ImageContainer* aImageContainer);
 
   class BufferData;
 
   typedef nsTArray<RefPtr<BufferData>> BUFFERLIST;
 
   class OmxBufferFailureHolder {
   public:
     OmxBufferFailureHolder(OMX_ERRORTYPE aError, BufferData* aBuffer)
@@ -120,16 +123,25 @@ public:
 
     // In most cases, the ID of this buffer is the pointer address of mBuffer.
     // However, in platform like gonk, it is another value.
     virtual BufferID ID()
     {
       return mBuffer;
     }
 
+    // Return the platform dependent MediaData().
+    // For example, it returns the MediaData with Gralloc texture.
+    // If it returns nullptr, then caller uses the normal way to
+    // create MediaData().
+    virtual already_AddRefed<MediaData> GetPlatformMediaData()
+    {
+      return nullptr;
+    }
+
     // The buffer could be used by several objects. And only one object owns the
     // buffer the same time.
     //   FREE:
     //     nobody uses it.
     //
     //   OMX_COMPONENT:
     //     buffer is used by OMX component (OmxPlatformLayer).
     //
@@ -157,34 +169,34 @@ public:
 
     bool mEos;
 
     // The raw keeps in OmxPromiseLayer after EmptyBuffer and then passing to
     // output decoded buffer in EmptyFillBufferDone. It is used to keep the
     // records of the original data from demuxer, like duration, stream offset...etc.
     RefPtr<MediaRawData> mRawData;
 
-    // Because OMX buffer works acorssing threads, so it uses a promise
+    // Because OMX buffer works across threads, so it uses a promise
     // for each buffer when the buffer is used by Omx component.
     MozPromiseHolder<OmxBufferPromise> mPromise;
     BufferStatus mStatus;
     OMX_BUFFERHEADERTYPE* mBuffer;
   };
 
   void EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData::BufferID aID);
 
   void EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData* aData);
 
   already_AddRefed<BufferData>
   FindBufferById(OMX_DIRTYPE aType, BufferData::BufferID aId);
 
   already_AddRefed<BufferData>
   FindAndRemoveBufferHolder(OMX_DIRTYPE aType, BufferData::BufferID aId);
 
-  // Return truen if event is handled.
+  // Return true if event is handled.
   bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
 
 protected:
   struct FlushCommand {
     OMX_DIRTYPE type;
     OMX_PTR cmd;
   };
 
@@ -203,19 +215,19 @@ protected:
   MozPromiseHolder<OmxCommandPromise> mFlushPromise;
 
   nsTArray<FlushCommand> mFlushCommands;
 
   nsAutoPtr<OmxPlatformLayer> mPlatformLayer;
 
 private:
   // Elements are added to holders when FillBuffer() or FillBuffer(). And
-  // removing elelments when the promise is resolved. Buffers in these lists
+  // removing element when the promise is resolved. Buffers in these lists
   // should NOT be used by other component; for example, output it to audio
-  // output. These list should be empty when engine is about to shutdown.
+  // output. These lists should be empty when engine is about to shutdown.
   //
   // Note:
   //      There bufferlist should not be used by other class directly.
   BUFFERLIST mInbufferHolders;
 
   BUFFERLIST mOutbufferHolders;
 
   nsTArray<RefPtr<MediaRawData>> mRawDatas;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -655,17 +655,17 @@ TextureClient::SetAddedToCompositableCli
 /* static */ void
 TextureClient::TextureClientRecycleCallback(TextureClient* aClient, void* aClosure)
 {
   MOZ_ASSERT(aClient->GetRecycleAllocator());
   aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
 }
 
 void
-TextureClient::SetRecycleAllocator(TextureClientRecycleAllocator* aAllocator)
+TextureClient::SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator)
 {
   mRecycleAllocator = aAllocator;
   if (aAllocator) {
     SetRecycleCallback(TextureClientRecycleCallback, nullptr);
   } else {
     ClearRecycleCallback();
   }
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -52,17 +52,17 @@ class CompositableClient;
 struct PlanarYCbCrData;
 class Image;
 class PTextureChild;
 class TextureChild;
 class TextureData;
 struct RawTextureBuffer;
 class RawYCbCrTextureBuffer;
 class TextureClient;
-class TextureClientRecycleAllocator;
+class ITextureClientRecycleAllocator;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
 class TextureClientPool;
 #endif
 class KeepAlive;
 
 /**
  * TextureClient is the abstraction that allows us to share data between the
  * content and the compositor side.
@@ -564,18 +564,18 @@ public:
   virtual void SetReadbackSink(TextureReadbackSink* aReadbackSink) {
     mReadbackSink = aReadbackSink;
   }
 
   void SyncWithObject(SyncObject* aFence) { mData->SyncWithObject(aFence); }
 
   ISurfaceAllocator* GetAllocator() { return mAllocator; }
 
-  TextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
-  void SetRecycleAllocator(TextureClientRecycleAllocator* aAllocator);
+  ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
+  void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
 
   /// If you add new code that uses this method, you are probably doing something wrong.
   TextureData* GetInternalData() { return mData; }
   const TextureData* GetInternalData() const { return mData; }
 
 private:
   static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
 
@@ -599,17 +599,17 @@ protected:
    * or never constructing a TextureHost with aDescriptor may result in a memory
    * leak (see TextureClientD3D9 for example).
    */
   bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
 
 
   RefPtr<ISurfaceAllocator> mAllocator;
   RefPtr<TextureChild> mActor;
-  RefPtr<TextureClientRecycleAllocator> mRecycleAllocator;
+  RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
   RefPtr<AsyncTransactionWaiter> mRemoveFromCompositableWaiter;
 
   TextureData* mData;
   RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
 
   TextureFlags mFlags;
   FenceHandle mReleaseFenceHandle;
   FenceHandle mAcquireFenceHandle;
--- a/gfx/layers/client/TextureClientRecycleAllocator.h
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -13,16 +13,29 @@
 #include "TextureClient.h"
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
 namespace layers {
 
 class TextureClientHolder;
 
+class ITextureClientRecycleAllocator
+{
+protected:
+  virtual ~ITextureClientRecycleAllocator() {}
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ITextureClientRecycleAllocator)
+
+protected:
+  friend class TextureClient;
+  virtual void RecycleTextureClient(TextureClient* aClient) = 0;
+};
+
 class ITextureClientAllocationHelper
 {
 public:
   ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
                                  gfx::IntSize aSize,
                                  BackendSelector aSelector,
                                  TextureFlags aTextureFlags,
                                  TextureAllocationFlags aAllocationFlags)
@@ -47,24 +60,22 @@ public:
  * TextureClientRecycleAllocator provides TextureClients allocation and
  * recycling capabilities. It expects allocations of same sizes and
  * attributres. If a recycled TextureClient is different from
  * requested one, the recycled one is dropped and new TextureClient is allocated.
  *
  * By default this uses TextureClient::CreateForDrawing to allocate new texture
  * clients.
  */
-class TextureClientRecycleAllocator
+class TextureClientRecycleAllocator : public ITextureClientRecycleAllocator
 {
 protected:
   virtual ~TextureClientRecycleAllocator();
 
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientRecycleAllocator)
-
   explicit TextureClientRecycleAllocator(CompositableForwarder* aAllocator);
 
   void SetMaxPoolSize(uint32_t aMax);
 
   // Creates and allocates a TextureClient.
   already_AddRefed<TextureClient>
   CreateOrRecycle(gfx::SurfaceFormat aFormat,
                   gfx::IntSize aSize,
@@ -80,20 +91,18 @@ protected:
   Allocate(gfx::SurfaceFormat aFormat,
            gfx::IntSize aSize,
            BackendSelector aSelector,
            TextureFlags aTextureFlags,
            TextureAllocationFlags aAllocFlags);
 
   RefPtr<CompositableForwarder> mSurfaceAllocator;
 
-private:
-  friend class TextureClient;
   friend class DefaultTextureClientAllocationHelper;
-  void RecycleTextureClient(TextureClient* aClient);
+  void RecycleTextureClient(TextureClient* aClient) override;
 
   static const uint32_t kMaxPooledSized = 2;
   uint32_t mMaxPooledSize;
 
   std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
 
   // On b2g gonk, std::queue might be a better choice.
   // On ICS, fence wait happens implicitly before drawing.