Bug 1224887 - new PlatformDecoderModule based on OpenMax IL. r=jya, sotaro
☠☠ backed out by 859f581be109 ☠ ☠
authorAlfredo Yang <ayang@mozilla.com>
Tue, 01 Dec 2015 06:12:00 +0100
changeset 275097 1e400c0266e54f1b4e73b05b5ded051526f01a31
parent 275096 0bc40a9a58044c65e5b3630e4f0b296d5df06662
child 275098 d38f8cb67db321d8f1f53b3b51fef1ebd847149f
push id68762
push usercbook@mozilla.com
push dateWed, 02 Dec 2015 07:32:51 +0000
treeherdermozilla-inbound@1e400c0266e5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, sotaro
bugs1224887
milestone45.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 1224887 - new PlatformDecoderModule based on OpenMax IL. r=jya, sotaro
dom/media/platforms/moz.build
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/OmxDecoderModule.h
dom/media/platforms/omx/OmxPlatformLayer.h
dom/media/platforms/omx/OmxPromiseLayer.cpp
dom/media/platforms/omx/OmxPromiseLayer.h
dom/media/platforms/omx/moz.build
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -22,17 +22,20 @@ UNIFIED_SOURCES += [
     'agnostic/VorbisDecoder.cpp',
     'agnostic/VPXDecoder.cpp',
     'PDMFactory.cpp',
     'PlatformDecoderModule.cpp',
     'wrappers/FuzzingWrapper.cpp',
     'wrappers/H264Converter.cpp'
 ]
 
-DIRS += ['agnostic/gmp']
+DIRS += [
+    'agnostic/gmp',
+    'omx'
+]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_EME']:
     DIRS += ['agnostic/eme']
 
 if CONFIG['MOZ_FFMPEG']:
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.cpp
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "OmxDataDecoder.h"
+#include "OmxPromiseLayer.h"
+#include "GonkOmxPlatformLayer.h"
+#include "MediaInfo.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__))
+
+using namespace android;
+
+namespace mozilla {
+
+extern void GetPortIndex(nsTArray<uint32_t>& aPortIndex);
+
+bool IsSoftwareCodec(const char* aComponentName) {
+  nsAutoCString str(aComponentName);
+  return (str.Find(NS_LITERAL_CSTRING("OMX.google.")) == -1 ? false : true);
+}
+
+class GonkOmxObserver : public BnOMXObserver {
+public:
+  void onMessage(const omx_message& aMsg)
+  {
+    switch (aMsg.type) {
+      case omx_message::EVENT:
+      {
+        sp<GonkOmxObserver> self = this;
+        nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
+          if (self->mClient && self->mClient->Event(aMsg.u.event_data.event,
+                                                    aMsg.u.event_data.data1,
+                                                    aMsg.u.event_data.data2))
+          {
+            return;
+          }
+        });
+        mTaskQueue->Dispatch(r.forget());
+        break;
+      }
+      case omx_message::EMPTY_BUFFER_DONE:
+      {
+        sp<GonkOmxObserver> self = this;
+        nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
+          if (!self->mPromiseLayer) {
+            return;
+          }
+          BufferData::BufferID id = (BufferData::BufferID)aMsg.u.buffer_data.buffer;
+          self->mPromiseLayer->EmptyFillBufferDone(OMX_DirInput, id);
+        });
+        mTaskQueue->Dispatch(r.forget());
+        break;
+      }
+      case omx_message::FILL_BUFFER_DONE:
+      {
+        sp<GonkOmxObserver> self = this;
+        nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aMsg] () {
+          if (!self->mPromiseLayer) {
+            return;
+          }
+
+          // TODO: these codes look a little ugly, it'd be better to improve them.
+          RefPtr<BufferData> buf;
+          BufferData::BufferID id = (BufferData::BufferID)aMsg.u.extended_buffer_data.buffer;
+          buf = self->mPromiseLayer->FindAndRemoveBufferHolder(OMX_DirOutput, id);
+          MOZ_RELEASE_ASSERT(buf);
+          GonkBufferData* gonkBuffer = static_cast<GonkBufferData*>(buf.get());
+
+          // Copy the critical information to local buffer.
+          if (gonkBuffer->IsLocalBuffer()) {
+            gonkBuffer->mBuffer->nOffset = aMsg.u.extended_buffer_data.range_offset;
+            gonkBuffer->mBuffer->nFilledLen = aMsg.u.extended_buffer_data.range_length;
+            gonkBuffer->mBuffer->nFlags = aMsg.u.extended_buffer_data.flags;
+            gonkBuffer->mBuffer->nTimeStamp = aMsg.u.extended_buffer_data.timestamp;
+          }
+          self->mPromiseLayer->EmptyFillBufferDone(OMX_DirOutput, buf);
+        });
+        mTaskQueue->Dispatch(r.forget());
+        break;
+      }
+      default:
+      {
+        LOG("Unhandle event %d", aMsg.type);
+      }
+    }
+  }
+
+  void Shutdown()
+  {
+    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+    mPromiseLayer = nullptr;
+    mClient = nullptr;
+  }
+
+  GonkOmxObserver(TaskQueue* aTaskQueue, OmxPromiseLayer* aPromiseLayer, OmxDataDecoder* aDataDecoder)
+    : mTaskQueue(aTaskQueue)
+    , mPromiseLayer(aPromiseLayer)
+    , mClient(aDataDecoder)
+  {}
+
+protected:
+  RefPtr<TaskQueue> mTaskQueue;
+  // TODO:
+  //   we should combination 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)
+{
+  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();
+  }
+}
+
+GonkOmxPlatformLayer::GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
+                                           OmxPromiseLayer* aPromiseLayer,
+                                           TaskQueue* aTaskQueue)
+  : mTaskQueue(aTaskQueue)
+  , mNode(0)
+  , mQuirks(0)
+  , mUsingHardwareCodec(false)
+{
+  mOmxObserver = new GonkOmxObserver(mTaskQueue, aPromiseLayer, aDataDecoder);
+}
+
+nsresult
+GonkOmxPlatformLayer::AllocateOmxBuffer(OMX_DIRTYPE aType,
+                                        BUFFERLIST* aBufferList)
+{
+  MOZ_ASSERT(!mMemoryDealer[aType].get());
+
+  // Get port definition.
+  OMX_PARAM_PORTDEFINITIONTYPE def;
+  nsTArray<uint32_t> portindex;
+  GetPortIndex(portindex);
+  for (auto idx : portindex) {
+    InitOmxParameter(&def);
+    def.nPortIndex = idx;
+
+    OMX_ERRORTYPE err = GetParameter(OMX_IndexParamPortDefinition,
+                                     &def,
+                                     sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+    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);
+
+  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());
+
+    IOMX::buffer_id bufferID;
+    status_t st;
+
+    if ((mQuirks & OMXCodec::kRequiresAllocateBufferOnInputPorts && aType == OMX_DirInput) ||
+        (mQuirks & OMXCodec::kRequiresAllocateBufferOnOutputPorts && aType == OMX_DirOutput)) {
+      st = mOmx->allocateBufferWithBackup(mNode, aType, mem, &bufferID);
+    } else {
+      st = mOmx->useBuffer(mNode, aType, mem, &bufferID);
+    }
+
+    if (st != OK) {
+      return NS_ERROR_FAILURE;
+    }
+
+    aBufferList->AppendElement(new GonkBufferData(bufferID, liveinlocal, mem.get()));
+  }
+
+  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();
+    st = mOmx->freeBuffer(mNode, aType, id);
+    if (st != OK) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  aBufferList->Clear();
+  mMemoryDealer[aType].clear();
+
+  return NS_OK;
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::GetState(OMX_STATETYPE* aType)
+{
+  return (OMX_ERRORTYPE)mOmx->getState(mNode, aType);
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
+                                   OMX_PTR aComponentParameterStructure,
+                                   OMX_U32 aComponentParameterSize)
+{
+  return (OMX_ERRORTYPE)mOmx->getParameter(mNode,
+                                           aParamIndex,
+                                           aComponentParameterStructure,
+                                           aComponentParameterSize);
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
+                                   OMX_PTR aComponentParameterStructure,
+                                   OMX_U32 aComponentParameterSize)
+{
+  return (OMX_ERRORTYPE)mOmx->setParameter(mNode,
+                                           aParamIndex,
+                                           aComponentParameterStructure,
+                                           aComponentParameterSize);
+}
+
+nsresult
+GonkOmxPlatformLayer::Shutdown()
+{
+  mOmx->freeNode(mNode);
+  mOmxObserver->Shutdown();
+  mOmxObserver = nullptr;
+  mOmxClient.disconnect();
+
+  return NS_OK;
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::InitOmxToStateLoaded(const TrackInfo* 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
+  // have a way to use hardware codec first.
+  android::Vector<OMXCodec::CodecNameAndQuirks> matchingCodecs;
+  const char* swcomponent = nullptr;
+  OMXCodec::findMatchingCodecs(aInfo->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;
+      }
+    }
+  }
+
+  // TODO: in android ICS, the software codec is allocated in mediaserver by
+  //       default, it may be necessay to allocate it in local process.
+  //
+  // fallback to sw codec
+  if (LoadComponent(swcomponent)) {
+    return OMX_ErrorNone;
+  }
+
+  LOG("no component is loaded");
+  return OMX_ErrorUndefined;
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::EmptyThisBuffer(BufferData* aData)
+{
+  return (OMX_ERRORTYPE)mOmx->emptyBuffer(mNode,
+                                          (IOMX::buffer_id)aData->ID(),
+                                          aData->mBuffer->nOffset,
+                                          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);
+}
+
+OMX_ERRORTYPE
+GonkOmxPlatformLayer::SendCommand(OMX_COMMANDTYPE aCmd,
+                                  OMX_U32 aParam1,
+                                  OMX_PTR aCmdData)
+{
+  return  (OMX_ERRORTYPE)mOmx->sendCommand(mNode, aCmd, aParam1);
+}
+
+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)
+{
+  PodZero(aParam);
+  aParam->nSize = sizeof(T);
+  aParam->nVersion.s.nVersionMajor = 1;
+}
+
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/GonkOmxPlatformLayer.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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(GonkOmxPlatformLayer_h_)
+#define GonkOmxPlatformLayer_h_
+
+#pragma GCC visibility push(default)
+
+#include "OmxPlatformLayer.h"
+#include "OMX_Component.h"
+#include <utils/RefBase.h>
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+class MemoryDealer;
+class IMemory;
+}
+
+namespace mozilla {
+
+class GonkOmxObserver;
+
+/*
+ * Due to Android's omx node could live in local process (client) or remote
+ * process (mediaserver).
+ *
+ * When it 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
+ * remote process. It can't be used in local process, so here it allocates a
+ * local OMX_BUFFERHEADERTYPE.
+ */
+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);
+
+  BufferID ID() override
+  {
+    return mId;
+  }
+
+  bool IsLocalBuffer()
+  {
+    return !!mLocalBuffer.get();
+  }
+
+  // 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.
+  // 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;
+};
+
+class GonkOmxPlatformLayer : public OmxPlatformLayer {
+public:
+  GonkOmxPlatformLayer(OmxDataDecoder* aDataDecoder,
+                       OmxPromiseLayer* aPromiseLayer,
+                       TaskQueue* aTaskQueue);
+
+  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,
+                             OMX_PTR aComponentParameterStructure,
+                             OMX_U32 aComponentParameterSize) override;
+
+  OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
+                             OMX_PTR aComponentParameterStructure,
+                             OMX_U32 aComponentParameterSize) override;
+
+  OMX_ERRORTYPE InitOmxToStateLoaded(const TrackInfo* aInfo) override;
+
+  OMX_ERRORTYPE EmptyThisBuffer(BufferData* aData) override;
+
+  OMX_ERRORTYPE FillThisBuffer(BufferData* aData) override;
+
+  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
+  // to one function.
+  template<class T> void InitOmxParameter(T* aParam);
+
+protected:
+  bool LoadComponent(const char* aName);
+
+  friend class GonkOmxObserver;
+
+  RefPtr<TaskQueue> mTaskQueue;
+
+  // 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;
+};
+
+}
+
+#pragma GCC visibility pop
+
+#endif // GonkOmxPlatformLayer_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDataDecoder.cpp
@@ -0,0 +1,861 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "OmxDataDecoder.h"
+#include "OMX_Types.h"
+#include "OMX_Component.h"
+#include "OMX_Audio.h"
+
+extern mozilla::LogModule* GetPDMLog();
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#define LOG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, ("OmxDataDecoder::%s: " arg, __func__, ##__VA_ARGS__))
+
+#define CHECK_OMX_ERR(err)     \
+  if (err != OMX_ErrorNone) {  \
+    NotifyError(err, __func__);\
+    return;                    \
+  }                            \
+
+
+namespace mozilla {
+
+static const char*
+StateTypeToStr(OMX_STATETYPE aType)
+{
+  MOZ_ASSERT(aType == OMX_StateLoaded ||
+             aType == OMX_StateIdle ||
+             aType == OMX_StateExecuting ||
+             aType == OMX_StatePause ||
+             aType == OMX_StateWaitForResources ||
+             aType == OMX_StateInvalid);
+
+  switch (aType) {
+    case OMX_StateLoaded:
+      return "OMX_StateLoaded";
+    case OMX_StateIdle:
+      return "OMX_StateIdle";
+    case OMX_StateExecuting:
+      return "OMX_StateExecuting";
+    case OMX_StatePause:
+      return "OMX_StatePause";
+    case OMX_StateWaitForResources:
+      return "OMX_StateWaitForResources";
+    case OMX_StateInvalid:
+      return "OMX_StateInvalid";
+    default:
+      return "Unknown";
+  }
+}
+
+// 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)
+  : mMonitor("OmxDataDecoder")
+  , mOmxTaskQueue(CreateMediaDecodeTaskQueue())
+  , mWatchManager(this, mOmxTaskQueue)
+  , mOmxState(OMX_STATETYPE::OMX_StateInvalid, "OmxDataDecoder::mOmxState")
+  , mTrackInfo(aTrackInfo.Clone())
+  , mFlushing(false)
+  , mShutdown(false)
+  , mCheckingInputExhausted(false)
+  , mPortSettingsChanged(-1, "OmxDataDecoder::mPortSettingsChanged")
+  , mAudioCompactor(mAudioQueue)
+  , mCallback(aCallback)
+{
+  LOG("(%p)", this);
+  mOmxLayer = new OmxPromiseLayer(mOmxTaskQueue, this);
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableMethod(this, &OmxDataDecoder::InitializationTask);
+  mOmxTaskQueue->Dispatch(r.forget());
+}
+
+OmxDataDecoder::~OmxDataDecoder()
+{
+  LOG("(%p)", this);
+  mWatchManager.Shutdown();
+  mOmxTaskQueue->AwaitShutdownAndIdle();
+}
+
+void
+OmxDataDecoder::InitializationTask()
+{
+  mWatchManager.Watch(mOmxState, &OmxDataDecoder::OmxStateRunner);
+  mWatchManager.Watch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
+}
+
+void
+OmxDataDecoder::EndOfStream()
+{
+  LOG("(%p)", this);
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  RefPtr<OmxDataDecoder> self = this;
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction([self] () {
+      self->mCallback->DrainComplete();
+    });
+  mReaderTaskQueue->Dispatch(r.forget());
+}
+
+RefPtr<MediaDataDecoder::InitPromise>
+OmxDataDecoder::Init()
+{
+  LOG("(%p)", this);
+  mReaderTaskQueue = AbstractThread::GetCurrent()->AsTaskQueue();
+  MOZ_ASSERT(mReaderTaskQueue);
+
+  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__,
+      [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] () {
+        self->RejectInitPromise(DecoderFailureReason::INIT_ERROR, __func__);
+      });
+
+  return p;
+}
+
+nsresult
+OmxDataDecoder::Input(MediaRawData* aSample)
+{
+  LOG("(%p) sample %p", this, aSample);
+  MOZ_ASSERT(mInitPromise.IsEmpty());
+
+  RefPtr<OmxDataDecoder> self = this;
+  RefPtr<MediaRawData> sample = aSample;
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction([self, sample] () {
+      self->mMediaRawDatas.AppendElement(sample);
+
+      // Start to fill/empty buffers.
+      if (self->mOmxState == OMX_StateIdle ||
+          self->mOmxState == OMX_StateExecuting) {
+        self->FillAndEmptyBuffers();
+      }
+    });
+  mOmxTaskQueue->Dispatch(r.forget());
+
+  return NS_OK;
+}
+
+nsresult
+OmxDataDecoder::Flush()
+{
+  LOG("(%p)", this);
+
+  mFlushing = true;
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableMethod(this, &OmxDataDecoder::DoFlush);
+  mOmxTaskQueue->Dispatch(r.forget());
+
+  // According to the definition of Flush() in PDM:
+  // "the decoder must be ready to accept new input for decoding".
+  // So it needs to wait for the Omx to complete the flush command.
+  MonitorAutoLock lock(mMonitor);
+  while (mFlushing) {
+    lock.Wait();
+  }
+
+  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
+OmxDataDecoder::Shutdown()
+{
+  LOG("(%p)", this);
+
+  mShutdown = true;
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableMethod(this, &OmxDataDecoder::DoAsyncShutdown);
+  mOmxTaskQueue->Dispatch(r.forget());
+
+  return NS_OK;
+}
+
+void
+OmxDataDecoder::DoAsyncShutdown()
+{
+  LOG("(%p)", this);
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+  MOZ_ASSERT(mFlushing);
+
+  mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
+  mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
+
+  // Do flush so all port can be returned to client.
+  RefPtr<OmxDataDecoder> self = this;
+  mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
+    ->Then(mOmxTaskQueue, __func__,
+           [self] () -> RefPtr<OmxCommandPromise> {
+             LOG("DoAsyncShutdown: flush complete, collecting buffers...");
+             self->CollectBufferPromises(OMX_DirMax)
+               ->Then(self->mOmxTaskQueue, __func__,
+                   [self] () {
+                     LOG("DoAsyncShutdown: releasing all buffers.");
+                     self->ReleaseBuffers(OMX_DirInput);
+                     self->ReleaseBuffers(OMX_DirOutput);
+                   },
+                   [self] () {
+                     self->mOmxLayer->Shutdown();
+                   });
+
+             return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
+           },
+           [self] () {
+             self->mOmxLayer->Shutdown();
+           })
+    ->CompletionPromise()
+    ->Then(mOmxTaskQueue, __func__,
+           [self] () -> RefPtr<OmxCommandPromise> {
+             LOG("DoAsyncShutdown: OMX_StateIdle");
+             return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
+           },
+           [self] () {
+             self->mOmxLayer->Shutdown();
+           })
+    ->CompletionPromise()
+    ->Then(mOmxTaskQueue, __func__,
+           [self] () {
+             LOG("DoAsyncShutdown: OMX_StateLoaded, it is safe to shutdown omx");
+             self->mOmxLayer->Shutdown();
+           },
+           [self] () {
+             self->mOmxLayer->Shutdown();
+           });
+}
+
+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)
+{
+  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;
+    }
+    typedef AudioCompactor::NativeCopy OmxCopy;
+    mAudioCompactor.Push(offset,
+                         buf->nTimeStamp,
+                         info->mRate,
+                         frames,
+                         info->mChannels,
+                         OmxCopy(buf->pBuffer + buf->nOffset,
+                                 buf->nFilledLen,
+                                 info->mChannels));
+    RefPtr<AudioData> audio = mAudioQueue.PopFront();
+    mCallback->Output(audio);
+  }
+  aBufferData->mStatus = BufferData::BufferStatus::FREE;
+}
+
+void
+OmxDataDecoder::FillBufferDone(BufferData* aData)
+{
+  MOZ_ASSERT(!aData || aData->mStatus == BufferData::BufferStatus::OMX_CLIENT);
+
+  if (mTrackInfo->IsAudio()) {
+    OutputAudio(aData);
+  } else {
+    MOZ_ASSERT(0);
+  }
+
+  if (aData->mBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
+    EndOfStream();
+  } else {
+    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__);
+}
+
+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();
+}
+
+void
+OmxDataDecoder::EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder)
+{
+  NotifyError(aFailureHolder.mError, __func__);
+}
+
+void
+OmxDataDecoder::NotifyError(OMX_ERRORTYPE aError, const char* aLine)
+{
+  LOG("NotifyError %d at %s", aError, aLine);
+  mCallback->Error();
+}
+
+void
+OmxDataDecoder::FillAndEmptyBuffers()
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+  MOZ_ASSERT(mOmxState == OMX_StateExecuting);
+
+  // During the port setting changed, it is forbided to do any buffer operations.
+  if (mPortSettingsChanged != -1 || mShutdown) {
+    return;
+  }
+
+  if (mFlushing) {
+    return;
+  }
+
+  // Trigger input port.
+  while (!!mMediaRawDatas.Length()) {
+    // input buffer must be usedi 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];
+    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;
+    }
+
+    mOmxLayer->FillBuffer(outbuf)->Then(mOmxTaskQueue, __func__, this,
+                                        &OmxDataDecoder::FillBufferDone,
+                                        &OmxDataDecoder::FillBufferFailure);
+  }
+}
+
+OmxPromiseLayer::BufferData*
+OmxDataDecoder::FindAvailableBuffer(OMX_DIRTYPE aType)
+{
+  BUFFERLIST* buffers = GetBuffers(aType);
+
+  for (uint32_t i = 0; i < buffers->Length(); i++) {
+    BufferData* buf = buffers->ElementAt(i);
+    if (buf->mStatus == BufferData::BufferStatus::FREE) {
+      return buf;
+    }
+  }
+
+  return nullptr;
+}
+
+nsresult
+OmxDataDecoder::AllocateBuffers(OMX_DIRTYPE aType)
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  return mOmxLayer->AllocateOmxBuffer(aType, GetBuffers(aType));
+}
+
+nsresult
+OmxDataDecoder::ReleaseBuffers(OMX_DIRTYPE aType)
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  return mOmxLayer->ReleaseOmxBuffer(aType, GetBuffers(aType));
+}
+
+nsTArray<RefPtr<OmxPromiseLayer::BufferData>>*
+OmxDataDecoder::GetBuffers(OMX_DIRTYPE aType)
+{
+  MOZ_ASSERT(aType == OMX_DIRTYPE::OMX_DirInput ||
+             aType == OMX_DIRTYPE::OMX_DirOutput);
+
+  if (aType == OMX_DIRTYPE::OMX_DirInput) {
+    return &mInPortBuffers;
+  }
+  return &mOutPortBuffers;
+}
+
+void
+OmxDataDecoder::ResolveInitPromise(const char* aMethodName)
+{
+  LOG("Resolved InitPromise");
+  RefPtr<OmxDataDecoder> self = this;
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction([self, aMethodName] () {
+      MOZ_ASSERT(self->mReaderTaskQueue->IsCurrentThreadIn());
+      self->mInitPromise.ResolveIfExists(self->mTrackInfo->GetType(), aMethodName);
+    });
+  mReaderTaskQueue->Dispatch(r.forget());
+}
+
+void
+OmxDataDecoder::RejectInitPromise(DecoderFailureReason aReason, const char* aMethodName)
+{
+  RefPtr<OmxDataDecoder> self = this;
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction([self, aReason, aMethodName] () {
+      MOZ_ASSERT(self->mReaderTaskQueue->IsCurrentThreadIn());
+      self->mInitPromise.RejectIfExists(aReason, aMethodName);
+    });
+  mReaderTaskQueue->Dispatch(r.forget());
+}
+
+void
+OmxDataDecoder::OmxStateRunner()
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+  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();
+    }
+
+    // Send OpenMax state commane 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);
+             },
+             [self] () {
+               self->RejectInitPromise(DecoderFailureReason::INIT_ERROR, __func__);
+             });
+
+    // Allocate input and output buffers.
+    OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
+    for(const auto id : types) {
+      if (NS_FAILED(AllocateBuffers(id))) {
+        LOG("Failed to allocate buffer on port %d", id);
+        RejectInitPromise(DecoderFailureReason::INIT_ERROR, __func__);
+        break;
+      }
+    }
+  } else if (mOmxState == OMX_StateIdle) {
+    RefPtr<OmxDataDecoder> self = this;
+    mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateExecuting, nullptr)
+      ->Then(mOmxTaskQueue, __func__,
+             [self] () {
+               self->mOmxState = self->mOmxLayer->GetState();
+               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.
+    FillCodecConfigDataToOmx();
+  } else {
+    MOZ_ASSERT(0);
+  }
+}
+
+void
+OmxDataDecoder::ConfigAudioCodec()
+{
+  const AudioInfo* audioInfo = mTrackInfo->GetAsAudioInfo();
+  OMX_ERRORTYPE err;
+
+  // TODO: it needs to handle other formats like mp3, amr-nb...etc.
+  if (audioInfo->mMimeType.EqualsLiteral("audio/mp4a-latm")) {
+    OMX_AUDIO_PARAM_AACPROFILETYPE aac_profile;
+    InitOmxParameter(&aac_profile);
+    err = mOmxLayer->GetParameter(OMX_IndexParamAudioAac, &aac_profile, sizeof(aac_profile));
+    CHECK_OMX_ERR(err);
+    aac_profile.nSampleRate = audioInfo->mRate;
+    aac_profile.nChannels = audioInfo->mChannels;
+    aac_profile.eAACProfile = (OMX_AUDIO_AACPROFILETYPE)audioInfo->mProfile;
+    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::FillCodecConfigDataToOmx()
+{
+  // Codec config 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);
+  if (mTrackInfo->IsAudio()) {
+    AudioInfo* audio_info = mTrackInfo->GetAsAudioInfo();
+    memcpy(inbuf->mBuffer->pBuffer,
+           audio_info->mCodecSpecificConfig->Elements(),
+           audio_info->mCodecSpecificConfig->Length());
+    inbuf->mBuffer->nFilledLen = audio_info->mCodecSpecificConfig->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);
+}
+
+bool
+OmxDataDecoder::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  if (mOmxLayer->Event(aEvent, aData1, aData2)) {
+    return true;
+  }
+
+  switch (aEvent) {
+    case OMX_EventPortSettingsChanged:
+    {
+      // According to spec: "To prevent the loss of any input data, the
+      // component issuing the OMX_EventPortSettingsChanged event on its input
+      // port should buffer all input port data that arrives between the
+      // emission of the OMX_EventPortSettingsChanged event and the arrival of
+      // the command to disable the input port."
+      //
+      // So client needs to disable port and reallocate buffers.
+      MOZ_ASSERT(mPortSettingsChanged == -1);
+      mPortSettingsChanged = aData1;
+      LOG("Got OMX_EventPortSettingsChanged event");
+      break;
+    }
+    default:
+    {
+      LOG("WARNING: got none handle event: %d, aData1: %d, aData2: %d",
+          aEvent, aData1, aData2);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+template<class T> void
+OmxDataDecoder::InitOmxParameter(T* aParam)
+{
+  PodZero(aParam);
+  aParam->nSize = sizeof(T);
+  aParam->nVersion.s.nVersionMajor = 1;
+}
+
+bool
+OmxDataDecoder::BuffersCanBeReleased(OMX_DIRTYPE aType)
+{
+  BUFFERLIST* buffers = GetBuffers(aType);
+  uint32_t len = buffers->Length();
+  for (uint32_t i = 0; i < len; i++) {
+    BufferData::BufferStatus buf_status = buffers->ElementAt(i)->mStatus;
+    if (buf_status == BufferData::BufferStatus::OMX_COMPONENT ||
+        buf_status == BufferData::BufferStatus::OMX_CLIENT_OUTPUT) {
+      return false;
+    }
+  }
+  return true;
+}
+
+OMX_DIRTYPE
+OmxDataDecoder::GetPortDirection(uint32_t aPortIndex)
+{
+  OMX_PARAM_PORTDEFINITIONTYPE def;
+  InitOmxParameter(&def);
+  def.nPortIndex = mPortSettingsChanged;
+
+  OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
+                                              &def,
+                                              sizeof(def));
+  if (err != OMX_ErrorNone) {
+    return OMX_DirMax;
+  }
+  return def.eDir;
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
+OmxDataDecoder::CollectBufferPromises(OMX_DIRTYPE aType)
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  nsTArray<RefPtr<OmxBufferPromise>> promises;
+  OMX_DIRTYPE types[] = {OMX_DIRTYPE::OMX_DirInput, OMX_DIRTYPE::OMX_DirOutput};
+  for (const auto type : types) {
+    if ((aType == type) || (aType == OMX_DirMax)) {
+      // find the buffer which has promise.
+      BUFFERLIST* buffers = GetBuffers(type);
+
+      for (uint32_t i = 0; i < buffers->Length(); i++) {
+        BufferData* buf = buffers->ElementAt(i);
+        if (!buf->mPromise.IsEmpty()) {
+          // OmxBufferPromise is not exclusive, it can be multiple "Then"s, so it
+          // is safe to call "Ensure" here.
+          promises.AppendElement(buf->mPromise.Ensure(__func__));
+        }
+      }
+    }
+  }
+
+  LOG("CollectBufferPromises: type %d, total %d promiese", aType, promises.Length());
+  if (promises.Length()) {
+    return OmxBufferPromise::All(mOmxTaskQueue, promises);
+  }
+
+  nsTArray<BufferData*> headers;
+  return OmxBufferPromise::AllPromiseType::CreateAndResolve(headers, __func__);
+}
+
+void
+OmxDataDecoder::PortSettingsChanged()
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  if (mPortSettingsChanged == -1 || mOmxState == OMX_STATETYPE::OMX_StateInvalid) {
+    return;
+  }
+
+  // The PortSettingsChanged algorithm:
+  //
+  //   1. disable port.
+  //   2. wait for port buffers return to client and then release these buffers.
+  //   3. enable port.
+  //   4. allocate port buffers.
+  //
+
+  // Disable port. Get port definition if the target port is enable.
+  OMX_PARAM_PORTDEFINITIONTYPE def;
+  InitOmxParameter(&def);
+  def.nPortIndex = mPortSettingsChanged;
+
+  OMX_ERRORTYPE err = mOmxLayer->GetParameter(OMX_IndexParamPortDefinition,
+                                              &def,
+                                              sizeof(def));
+  CHECK_OMX_ERR(err);
+
+  RefPtr<OmxDataDecoder> self = this;
+  if (def.bEnabled) {
+    // 1. disable port.
+    LOG("PortSettingsChanged: disable port %d", def.nPortIndex);
+    mOmxLayer->SendCommand(OMX_CommandPortDisable, mPortSettingsChanged, nullptr)
+      ->Then(mOmxTaskQueue, __func__,
+             [self, def] () -> RefPtr<OmxCommandPromise> {
+               // 3. enable port.
+               // Send enable port command.
+               RefPtr<OmxCommandPromise> p =
+                 self->mOmxLayer->SendCommand(OMX_CommandPortEnable,
+                                              self->mPortSettingsChanged,
+                                              nullptr);
+
+               // 4. allocate port buffers.
+               // Allocate new port buffers.
+               nsresult rv = self->AllocateBuffers(def.eDir);
+               if (NS_FAILED(rv)) {
+                 self->NotifyError(OMX_ErrorUndefined, __func__);
+               }
+
+               return p;
+             },
+             [self] () {
+               self->NotifyError(OMX_ErrorUndefined, __func__);
+             })
+      ->CompletionPromise()
+      ->Then(mOmxTaskQueue, __func__,
+             [self] () {
+               LOG("PortSettingsChanged: port settings changed complete");
+               // finish port setting changed.
+               self->mPortSettingsChanged = -1;
+               self->FillAndEmptyBuffers();
+             },
+             [self] () {
+               self->NotifyError(OMX_ErrorUndefined, __func__);
+             });
+
+    // 2. wait for port buffers return to client and then release these buffers.
+    //
+    // Port buffers will be returned to client soon once OMX_CommandPortDisable
+    // command is sent. Then releasing these buffers.
+    CollectBufferPromises(def.eDir)
+      ->Then(mOmxTaskQueue, __func__,
+          [self, def] () {
+            MOZ_ASSERT(self->BuffersCanBeReleased(def.eDir));
+            nsresult rv = self->ReleaseBuffers(def.eDir);
+            if (NS_FAILED(rv)) {
+              MOZ_RELEASE_ASSERT(0);
+              self->NotifyError(OMX_ErrorUndefined, __func__);
+            }
+          },
+          [self] () {
+            self->NotifyError(OMX_ErrorUndefined, __func__);
+          });
+  }
+}
+
+void
+OmxDataDecoder::SendEosBuffer()
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  // There is no 'Drain' API in OpenMax, so it needs to wait for output sample
+  // with EOS flag. However, MediaRawData doesn't provide EOS information,
+  // so here it generates an empty BufferData with eos OMX_BUFFERFLAG_EOS in queue.
+  // This behaviour should be compliant with spec, I think...
+  RefPtr<MediaRawData> eos_data = new MediaRawData();
+  mMediaRawDatas.AppendElement(eos_data);
+  FillAndEmptyBuffers();
+}
+
+void
+OmxDataDecoder::DoFlush()
+{
+  MOZ_ASSERT(mOmxTaskQueue->IsCurrentThreadIn());
+
+  // 1. Call OMX command OMX_CommandFlush in Omx TaskQueue.
+  // 2. Remove all elements in mMediaRawDatas when flush is completed.
+  RefPtr<OmxDataDecoder> self = this;
+  mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
+    ->Then(mOmxTaskQueue, __func__, this,
+           &OmxDataDecoder::FlushComplete,
+           &OmxDataDecoder::FlushFailure);
+}
+
+void
+OmxDataDecoder::FlushComplete(OMX_COMMANDTYPE aCommandType)
+{
+  mMediaRawDatas.Clear();
+  mFlushing = false;
+
+  MonitorAutoLock lock(mMonitor);
+  mMonitor.Notify();
+  LOG("Flush complete");
+}
+
+void OmxDataDecoder::FlushFailure(OmxCommandFailureHolder aFailureHolder)
+{
+  NotifyError(OMX_ErrorUndefined, __func__);
+  mFlushing = false;
+
+  MonitorAutoLock lock(mMonitor);
+  mMonitor.Notify();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDataDecoder.h
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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(OmxDataDecoder_h_)
+#define OmxDataDecoder_h_
+
+#include "mozilla/Monitor.h"
+#include "PlatformDecoderModule.h"
+#include "OmxPromiseLayer.h"
+#include "MediaInfo.h"
+#include "AudioCompactor.h"
+
+namespace mozilla {
+
+typedef OmxPromiseLayer::OmxCommandPromise OmxCommandPromise;
+typedef OmxPromiseLayer::OmxBufferPromise OmxBufferPromise;
+typedef OmxPromiseLayer::OmxBufferFailureHolder OmxBufferFailureHolder;
+typedef OmxPromiseLayer::OmxCommandFailureHolder OmxCommandFailureHolder;
+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
+ * 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.
+ *
+ * OpenMAX IL component:
+ *   "A component that is intended to wrap functionality that is required in the
+ *   target system."
+ *
+ *   OmxPromiseLayer acts as the OpenMAX IL component.
+ *
+ * OpenMAX IL core:
+ *   "Platform-specific code that has the functionality necessary to locate and
+ *   then load an OpenMAX IL component into main memory."
+ *
+ *   OmxPlatformLayer acts as the OpenMAX IL core.
+ */
+class OmxDataDecoder : public MediaDataDecoder {
+protected:
+  virtual ~OmxDataDecoder();
+
+public:
+  OmxDataDecoder(const TrackInfo& aTrackInfo,
+                 MediaDataDecoderCallback* aCallback);
+
+  RefPtr<InitPromise> Init() override;
+
+  nsresult Input(MediaRawData* aSample) override;
+
+  nsresult Flush() override;
+
+  nsresult Drain() override;
+
+  nsresult Shutdown() override;
+
+  // Return true if event is handled.
+  bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
+
+protected:
+  void InitializationTask();
+
+  void ResolveInitPromise(const char* aMethodName);
+
+  void RejectInitPromise(DecoderFailureReason aReason, const char* aMethodName);
+
+  void OmxStateRunner();
+
+  void FillAndEmptyBuffers();
+
+  void FillBufferDone(BufferData* aData);
+
+  void FillBufferFailure(OmxBufferFailureHolder aFailureHolder);
+
+  void EmptyBufferDone(BufferData* aData);
+
+  void EmptyBufferFailure(OmxBufferFailureHolder aFailureHolder);
+
+  void NotifyError(OMX_ERRORTYPE aError, const char* aLine);
+
+  // Config audio codec.
+  // Some codec may just ignore this and rely on codec specific data in
+  // FillCodecConfigDataToOmx().
+  void ConfigAudioCodec();
+
+  // 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();
+
+  // 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();
+
+  void DoFlush();
+
+  void FlushComplete(OMX_COMMANDTYPE aCommandType);
+
+  void FlushFailure(OmxCommandFailureHolder aFailureHolder);
+
+  BUFFERLIST* GetBuffers(OMX_DIRTYPE aType);
+
+  nsresult AllocateBuffers(OMX_DIRTYPE aType);
+
+  nsresult ReleaseBuffers(OMX_DIRTYPE aType);
+
+  BufferData* FindAvailableBuffer(OMX_DIRTYPE aType);
+
+  template<class T> void InitOmxParameter(T* aParam);
+
+  // aType could be OMX_DirMax for all types.
+  RefPtr<OmxPromiseLayer::OmxBufferPromise::AllPromiseType>
+  CollectBufferPromises(OMX_DIRTYPE aType);
+
+  Monitor mMonitor;
+
+  // The Omx TaskQueue.
+  RefPtr<TaskQueue> mOmxTaskQueue;
+
+  RefPtr<TaskQueue> mReaderTaskQueue;
+
+  WatchManager<OmxDataDecoder> mWatchManager;
+
+  // It is accessed in omx TaskQueue.
+  Watchable<OMX_STATETYPE> mOmxState;
+
+  RefPtr<OmxPromiseLayer> mOmxLayer;
+
+  UniquePtr<TrackInfo> mTrackInfo;
+
+  // It is accessed in both omx and reader TaskQueue.
+  Atomic<bool> mFlushing;
+
+  // It is accessed in Omx/reader TaskQeueu.
+  Atomic<bool> mShutdown;
+
+  // It is accessed in Omx TaskQeueu.
+  bool mCheckingInputExhausted;
+
+  // It is accessed in reader TaskQueue.
+  MozPromiseHolder<InitPromise> mInitPromise;
+
+  // It is written in Omx TaskQeueu. Read in Omx TaskQueue.
+  // It value means the port index which port settings is changed.
+  // -1 means no port setting changed.
+  //
+  // 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;
+
+  AudioCompactor mAudioCompactor;
+
+  MediaDataDecoderCallback* mCallback;
+};
+
+}
+
+#endif /* OmxDataDecoder_h_ */
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDecoderModule.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "OmxDecoderModule.h"
+#include "OmxDataDecoder.h"
+
+namespace mozilla {
+
+already_AddRefed<MediaDataDecoder>
+OmxDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
+                                     mozilla::layers::LayersBackend aLayersBackend,
+                                     mozilla::layers::ImageContainer* aImageContainer,
+                                     FlushableTaskQueue* aVideoTaskQueue,
+                                     MediaDataDecoderCallback* aCallback)
+{
+  return nullptr;
+}
+
+already_AddRefed<MediaDataDecoder>
+OmxDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
+                                     FlushableTaskQueue* aAudioTaskQueue,
+                                     MediaDataDecoderCallback* aCallback)
+{
+  RefPtr<OmxDataDecoder> decoder = new OmxDataDecoder(aConfig, aCallback);
+  return decoder.forget();
+}
+
+void
+OmxDecoderModule::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
+}
+
+PlatformDecoderModule::ConversionRequired
+OmxDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
+{
+  return kNeedNone;
+}
+
+bool
+OmxDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
+{
+  return aMimeType.EqualsLiteral("audio/mp4a-latm");
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxDecoderModule.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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(OmxDecoderModule_h_)
+#define OmxDecoderModule_h_
+
+#include "PlatformDecoderModule.h"
+
+namespace mozilla {
+
+class OmxDecoderModule : public PlatformDecoderModule {
+public:
+  already_AddRefed<MediaDataDecoder>
+  CreateVideoDecoder(const VideoInfo& aConfig,
+                     mozilla::layers::LayersBackend aLayersBackend,
+                     mozilla::layers::ImageContainer* aImageContainer,
+                     FlushableTaskQueue* aVideoTaskQueue,
+                     MediaDataDecoderCallback* aCallback) override;
+
+  already_AddRefed<MediaDataDecoder>
+  CreateAudioDecoder(const AudioInfo& aConfig,
+                     FlushableTaskQueue* aAudioTaskQueue,
+                     MediaDataDecoderCallback* aCallback) override;
+
+  static void Init();
+
+  bool SupportsMimeType(const nsACString& aMimeType) const override;
+
+  ConversionRequired DecoderNeedsConversion(const TrackInfo& aConfig) const override;
+};
+
+} // namespace mozilla
+
+#endif // OmxDecoderModule_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPlatformLayer.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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(OmxPlatformLayer_h_)
+#define OmxPlatformLayer_h_
+
+#include "OMX_Core.h"
+#include "OMX_Types.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/TaskQueue.h"
+#include "OmxPromiseLayer.h"
+
+namespace mozilla {
+
+class TrackInfo;
+
+/*
+ * 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.
+ */
+class OmxPlatformLayer {
+public:
+  typedef OmxPromiseLayer::BUFFERLIST BUFFERLIST;
+  typedef OmxPromiseLayer::BufferData BufferData;
+
+  virtual OMX_ERRORTYPE InitOmxToStateLoaded(const TrackInfo* aInfo) = 0;
+
+  virtual OMX_ERRORTYPE EmptyThisBuffer(BufferData* aData) = 0;
+
+  virtual OMX_ERRORTYPE FillThisBuffer(BufferData* aData) = 0;
+
+  virtual OMX_ERRORTYPE SendCommand(OMX_COMMANDTYPE aCmd,
+                                    OMX_U32 aParam1,
+                                    OMX_PTR aCmdData) = 0;
+
+  // Buffer could be platform dependent; for example, video decoding needs gralloc
+  // on Gonk. Therefore, derived class needs to implement its owned buffer
+  // allocate/release API according to its platform type.
+  virtual nsresult AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) = 0;
+
+  virtual nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBufferList) = 0;
+
+  virtual OMX_ERRORTYPE GetState(OMX_STATETYPE* aType) = 0;
+
+  virtual OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
+                                     OMX_PTR aComponentParameterStructure,
+                                     OMX_U32 aComponentParameterSize) = 0;
+
+  virtual OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
+                                     OMX_PTR aComponentParameterStructure,
+                                     OMX_U32 aComponentParameterSize) = 0;
+
+  virtual nsresult Shutdown() = 0;
+
+  virtual ~OmxPlatformLayer() {}
+};
+
+}
+
+#endif // OmxPlatformLayer_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPromiseLayer.cpp
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "OmxPromiseLayer.h"
+#include "OmxPlatformLayer.h"
+#include "OmxDataDecoder.h"
+
+#ifdef MOZ_WIDGET_GONK
+#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 {
+
+OmxPromiseLayer::OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder)
+  : mTaskQueue(aTaskQueue)
+  , mFlushPortIndex(0)
+{
+#ifdef MOZ_WIDGET_GONK
+  mPlatformLayer = new GonkOmxPlatformLayer(aDataDecoder, this, aTaskQueue);
+#endif
+  MOZ_ASSERT(!!mPlatformLayer);
+}
+
+RefPtr<OmxPromiseLayer::OmxCommandPromise>
+OmxPromiseLayer::Init(TaskQueue* aTaskQueue, const TrackInfo* aInfo)
+{
+  mTaskQueue = aTaskQueue;
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+  OMX_ERRORTYPE err = mPlatformLayer->InitOmxToStateLoaded(aInfo);
+  if (err != OMX_ErrorNone) {
+    OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
+    return OmxCommandPromise::CreateAndReject(failure, __func__);
+  }
+
+  OMX_STATETYPE state = GetState();
+  if (state ==  OMX_StateLoaded) {
+    return OmxCommandPromise::CreateAndResolve(OMX_CommandStateSet, __func__);
+  } if (state == OMX_StateIdle) {
+    return SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
+  }
+
+  OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
+  return OmxCommandPromise::CreateAndReject(failure, __func__);
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise>
+OmxPromiseLayer::FillBuffer(BufferData* aData)
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  LOG("FillBuffer: buffer %p", aData->mBuffer);
+
+  RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
+
+  OMX_ERRORTYPE err = mPlatformLayer->FillThisBuffer(aData);
+
+  if (err != OMX_ErrorNone) {
+    OmxBufferFailureHolder failure(err, aData);
+    aData->mPromise.Reject(Move(failure), __func__);
+  } else {
+    aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT;
+    GetBufferHolders(OMX_DirOutput)->AppendElement(aData);
+  }
+
+  return p;
+}
+
+RefPtr<OmxPromiseLayer::OmxBufferPromise>
+OmxPromiseLayer::EmptyBuffer(BufferData* aData)
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  LOG("EmptyBuffer: buffer %p, size %d", aData->mBuffer, aData->mBuffer->nFilledLen);
+
+  RefPtr<OmxBufferPromise> p = aData->mPromise.Ensure(__func__);
+
+  OMX_ERRORTYPE err = mPlatformLayer->EmptyThisBuffer(aData);
+
+  if (err != OMX_ErrorNone) {
+    OmxBufferFailureHolder failure(err, aData);
+    aData->mPromise.Reject(Move(failure), __func__);
+  } else {
+    if (aData->mRawData) {
+      mRawDatas.AppendElement(Move(aData->mRawData));
+    }
+    aData->mStatus = BufferData::BufferStatus::OMX_COMPONENT;
+    GetBufferHolders(OMX_DirInput)->AppendElement(aData);
+  }
+
+  return p;
+}
+
+OmxPromiseLayer::BUFFERLIST*
+OmxPromiseLayer::GetBufferHolders(OMX_DIRTYPE aType)
+{
+  MOZ_ASSERT(aType == OMX_DirInput || aType == OMX_DirOutput);
+
+  if (aType == OMX_DirInput) {
+    return &mInbufferHolders;
+  }
+
+  return &mOutbufferHolders;
+}
+
+already_AddRefed<MediaRawData>
+OmxPromiseLayer::FindAndRemoveRawData(OMX_TICKS aTimecode)
+{
+  for (auto raw : mRawDatas) {
+    if (raw->mTimecode == aTimecode) {
+      mRawDatas.RemoveElement(raw);
+      return raw.forget();
+    }
+  }
+  return nullptr;
+}
+
+already_AddRefed<BufferData>
+OmxPromiseLayer::FindAndRemoveBufferHolder(OMX_DIRTYPE aType,
+                                           BufferData::BufferID aId)
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+  RefPtr<BufferData> holder;
+  BUFFERLIST* holders = GetBufferHolders(aType);
+
+  for (uint32_t i = 0; i < holders->Length(); i++) {
+    if (holders->ElementAt(i)->ID() == aId) {
+      holder = holders->ElementAt(i);
+      holders->RemoveElementAt(i);
+      return holder.forget();
+    }
+  }
+
+  return nullptr;
+}
+
+already_AddRefed<BufferData>
+OmxPromiseLayer::FindBufferById(OMX_DIRTYPE aType, BufferData::BufferID aId)
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
+  RefPtr<BufferData> holder;
+  BUFFERLIST* holders = GetBufferHolders(aType);
+
+  for (uint32_t i = 0; i < holders->Length(); i++) {
+    if (holders->ElementAt(i)->ID() == aId) {
+      holder = holders->ElementAt(i);
+      return holder.forget();
+    }
+  }
+
+  return nullptr;
+}
+
+void
+OmxPromiseLayer::EmptyFillBufferDone(OMX_DIRTYPE aType, BufferData* aData)
+{
+  MOZ_ASSERT(!!aData);
+  LOG("EmptyFillBufferDone: type %d, buffer %p", aType, aData->mBuffer);
+  if (aData) {
+    if (aType == OMX_DirOutput) {
+      aData->mRawData = nullptr;
+      aData->mRawData = FindAndRemoveRawData(aData->mBuffer->nTimeStamp);
+    }
+    aData->mStatus = BufferData::BufferStatus::OMX_CLIENT;
+    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__);
+  }
+}
+
+RefPtr<OmxPromiseLayer::OmxCommandPromise>
+OmxPromiseLayer::SendCommand(OMX_COMMANDTYPE aCmd, OMX_U32 aParam1, OMX_PTR aCmdData)
+{
+  // No need to issue flush because of buffers are in client already.
+  //
+  // Some components fail to respond flush event when all of buffers are in
+  // client.
+  if (aCmd == OMX_CommandFlush) {
+    bool needFlush = false;
+    if ((aParam1 & OMX_DirInput && mInbufferHolders.Length()) ||
+        (aParam1 & OMX_DirOutput && mOutbufferHolders.Length())) {
+      needFlush = true;
+    }
+    if (!needFlush) {
+      LOG("SendCommand: buffers are in client already, no need to flush");
+      mRawDatas.Clear();
+      return OmxCommandPromise::CreateAndResolve(OMX_CommandFlush, __func__);
+    }
+  }
+
+  OMX_ERRORTYPE err = mPlatformLayer->SendCommand(aCmd, aParam1, aCmdData);
+  if (err != OMX_ErrorNone) {
+    OmxCommandFailureHolder failure(OMX_ErrorNotReady, aCmd);
+    return OmxCommandPromise::CreateAndReject(failure, __func__);
+  }
+
+  RefPtr<OmxCommandPromise> p;
+  if (aCmd == OMX_CommandStateSet) {
+    p = mCommandStatePromise.Ensure(__func__);
+  } else if (aCmd == OMX_CommandFlush) {
+    p = mFlushPromise.Ensure(__func__);
+    mFlushPortIndex = aParam1;
+    // Clear all buffered raw data.
+    mRawDatas.Clear();
+  } else if (aCmd == OMX_CommandPortEnable) {
+    p = mPortEnablePromise.Ensure(__func__);
+  } else if (aCmd == OMX_CommandPortDisable) {
+    p = mPortDisablePromise.Ensure(__func__);
+  } else {
+    LOG("SendCommand: error unsupport command");
+    MOZ_ASSERT(0);
+  }
+
+  return p;
+}
+
+bool
+OmxPromiseLayer::Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2)
+{
+  OMX_COMMANDTYPE cmd = (OMX_COMMANDTYPE) aData1;
+  switch (aEvent) {
+    case OMX_EventCmdComplete:
+    {
+      if (cmd == OMX_CommandStateSet) {
+        mCommandStatePromise.Resolve(OMX_CommandStateSet, __func__);
+      } else if (cmd == OMX_CommandFlush && mFlushPortIndex == aData2) {
+        mFlushPromise.Resolve(OMX_CommandFlush, __func__);
+      } else if (cmd == OMX_CommandPortDisable) {
+        mPortDisablePromise.Resolve(OMX_CommandPortDisable, __func__);
+      } else if (cmd == OMX_CommandPortEnable) {
+        mPortEnablePromise.Resolve(OMX_CommandPortEnable, __func__);
+      }
+      break;
+    }
+    case OMX_EventError:
+    {
+      if (cmd == OMX_CommandStateSet) {
+        OmxCommandFailureHolder failure(OMX_ErrorUndefined, OMX_CommandStateSet);
+        mCommandStatePromise.Reject(failure, __func__);
+      } else if (cmd == OMX_CommandFlush && mFlushPortIndex == aData2) {
+        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__);
+      }
+      break;
+    }
+    default:
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
+nsresult
+OmxPromiseLayer::AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers)
+{
+  return mPlatformLayer->AllocateOmxBuffer(aType, aBuffers);
+}
+
+nsresult
+OmxPromiseLayer::ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers)
+{
+  return mPlatformLayer->ReleaseOmxBuffer(aType, aBuffers);
+}
+
+OMX_STATETYPE
+OmxPromiseLayer::GetState()
+{
+  OMX_STATETYPE state;
+  OMX_ERRORTYPE err = mPlatformLayer->GetState(&state);
+  return err == OMX_ErrorNone ? state : OMX_StateInvalid;
+}
+
+OMX_ERRORTYPE
+OmxPromiseLayer::GetParameter(OMX_INDEXTYPE aParamIndex,
+                              OMX_PTR aComponentParameterStructure,
+                              OMX_U32 aComponentParameterSize)
+{
+  return mPlatformLayer->GetParameter(aParamIndex,
+                                      aComponentParameterStructure,
+                                      aComponentParameterSize);
+}
+
+OMX_ERRORTYPE
+OmxPromiseLayer::SetParameter(OMX_INDEXTYPE aParamIndex,
+                              OMX_PTR aComponentParameterStructure,
+                              OMX_U32 aComponentParameterSize)
+{
+  return mPlatformLayer->SetParameter(aParamIndex,
+                                      aComponentParameterStructure,
+                                      aComponentParameterSize);
+}
+
+nsresult
+OmxPromiseLayer::Shutdown()
+{
+  LOG("Shutdown");
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  MOZ_ASSERT(!GetBufferHolders(OMX_DirInput)->Length());
+  MOZ_ASSERT(!GetBufferHolders(OMX_DirOutput)->Length());
+  return mPlatformLayer->Shutdown();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/OmxPromiseLayer.h
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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(OmxPromiseLayer_h_)
+#define OmxPromiseLayer_h_
+
+#include "OMX_Core.h"
+#include "OMX_Types.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/TaskQueue.h"
+
+namespace mozilla {
+
+class TrackInfo;
+class OmxPlatformLayer;
+class OmxDataDecoder;
+
+/* 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.
+ *    Because omx buffer works crossing threads, so each omx buffer has its own
+ *    promise, it is defined in BufferData.
+ *
+ * All of functions and members in this class should be run in the same
+ * TaskQueue.
+ */
+class OmxPromiseLayer {
+protected:
+  virtual ~OmxPromiseLayer() {}
+
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OmxPromiseLayer)
+
+  OmxPromiseLayer(TaskQueue* aTaskQueue, OmxDataDecoder* aDataDecoder);
+
+  class BufferData;
+
+  typedef nsTArray<RefPtr<BufferData>> BUFFERLIST;
+
+  class OmxBufferFailureHolder {
+  public:
+    OmxBufferFailureHolder(OMX_ERRORTYPE aError, BufferData* aBuffer)
+      : mError(aError)
+      , mBuffer(aBuffer)
+    {}
+
+    OMX_ERRORTYPE mError;
+    BufferData* mBuffer;
+  };
+
+  typedef MozPromise<BufferData*, OmxBufferFailureHolder, /* IsExclusive = */ false> OmxBufferPromise;
+
+  class OmxCommandFailureHolder {
+  public:
+    OmxCommandFailureHolder(OMX_ERRORTYPE aErrorType,
+                            OMX_COMMANDTYPE aCommandType)
+      : mErrorType(aErrorType)
+      , mCommandType(aCommandType)
+    {}
+
+    OMX_ERRORTYPE mErrorType;
+    OMX_COMMANDTYPE mCommandType;
+  };
+
+  typedef MozPromise<OMX_COMMANDTYPE, OmxCommandFailureHolder, /* IsExclusive = */ true> OmxCommandPromise;
+
+  typedef MozPromise<uint32_t, bool, /* IsExclusive = */ true> OmxPortConfigPromise;
+
+  // TODO: maybe a generic promise is good enough for this case?
+  RefPtr<OmxCommandPromise> Init(TaskQueue* aQueue, const TrackInfo* aInfo);
+
+  RefPtr<OmxBufferPromise> FillBuffer(BufferData* aData);
+
+  RefPtr<OmxBufferPromise> EmptyBuffer(BufferData* aData);
+
+  RefPtr<OmxCommandPromise> SendCommand(OMX_COMMANDTYPE aCmd,
+                                        OMX_U32 aParam1,
+                                        OMX_PTR aCmdData);
+
+  nsresult AllocateOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers);
+
+  nsresult ReleaseOmxBuffer(OMX_DIRTYPE aType, BUFFERLIST* aBuffers);
+
+  OMX_STATETYPE GetState();
+
+  OMX_ERRORTYPE GetParameter(OMX_INDEXTYPE aParamIndex,
+                             OMX_PTR aComponentParameterStructure,
+                             OMX_U32 aComponentParameterSize);
+
+  OMX_ERRORTYPE SetParameter(OMX_INDEXTYPE nIndex,
+                             OMX_PTR aComponentParameterStructure,
+                             OMX_U32 aComponentParameterSize);
+
+  nsresult Shutdown();
+
+  // BufferData maintains the status of OMX buffer (OMX_BUFFERHEADERTYPE).
+  // mStatus tracks the buffer owner.
+  // And a promise because OMX buffer working among different threads.
+  class BufferData {
+  protected:
+    virtual ~BufferData() {}
+
+  public:
+    explicit BufferData(OMX_BUFFERHEADERTYPE* aBuffer)
+      : mEos(false)
+      , mStatus(BufferStatus::FREE)
+      , mBuffer(aBuffer)
+    {}
+
+    typedef void* BufferID;
+
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BufferData)
+
+    // 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;
+    }
+
+    // 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).
+    //
+    //   OMX_CLIENT:
+    //     buffer is used by client which is wait for audio/video playing
+    //     (OmxDataDecoder)
+    //
+    //   OMX_CLIENT_OUTPUT:
+    //     used by client to output decoded data (for example, Gecko layer in
+    //     this case)
+    //
+    // For output port buffer, the status transition is:
+    // FREE -> OMX_COMPONENT -> OMX_CLIENT -> OMX_CLIENT_OUTPUT -> FREE
+    //
+    // For input port buffer, the status transition is:
+    // FREE -> OMX_COMPONENT -> OMX_CLIENT -> FREE
+    //
+    enum BufferStatus {
+      FREE,
+      OMX_COMPONENT,
+      OMX_CLIENT,
+      OMX_CLIENT_OUTPUT,
+      INVALID
+    };
+
+    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
+    // 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.
+  bool Event(OMX_EVENTTYPE aEvent, OMX_U32 aData1, OMX_U32 aData2);
+
+protected:
+  BUFFERLIST* GetBufferHolders(OMX_DIRTYPE aType);
+
+  already_AddRefed<MediaRawData> FindAndRemoveRawData(OMX_TICKS aTimecode);
+
+  RefPtr<TaskQueue> mTaskQueue;
+
+  MozPromiseHolder<OmxCommandPromise> mCommandStatePromise;
+
+  MozPromiseHolder<OmxCommandPromise> mPortDisablePromise;
+
+  MozPromiseHolder<OmxCommandPromise> mPortEnablePromise;
+
+  MozPromiseHolder<OmxCommandPromise> mFlushPromise;
+
+  OMX_U32 mFlushPortIndex;
+
+  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
+  // 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.
+  //
+  // Note:
+  //      There bufferlist should not be used by other class directly.
+  BUFFERLIST mInbufferHolders;
+
+  BUFFERLIST mOutbufferHolders;
+
+  nsTArray<RefPtr<MediaRawData>> mRawDatas;
+};
+
+}
+
+#endif /* OmxPromiseLayer_h_ */
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/omx/moz.build
@@ -0,0 +1,46 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+    'OmxDecoderModule.h',
+]
+
+UNIFIED_SOURCES += [
+    'OmxDataDecoder.cpp',
+    'OmxDecoderModule.cpp',
+    'OmxPromiseLayer.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/media/openmax_il/il112',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    # Suppress some GCC/clang warnings being treated as errors:
+    #  - about attributes on forward declarations for types that are already
+    #    defined, which complains about an important MOZ_EXPORT for android::AString
+    #  - about multi-character constants which are used in codec-related code
+    if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']:
+        CXXFLAGS += [
+          '-Wno-error=attributes',
+          '-Wno-error=multichar'
+        ]
+    CXXFLAGS += [
+        '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
+            'frameworks/base/include/binder',
+            'frameworks/base/include/utils',
+        ]
+    ]
+    UNIFIED_SOURCES += [
+        'GonkOmxPlatformLayer.cpp',
+    ]
+    EXTRA_DSO_LDOPTS += [
+        '-libbinder',
+    ]
+
+FINAL_LIBRARY = 'xul'