Bug 911046 - Part 4: Add external H.264 HW video codec implementation for B2G. r=jesup
authorJohn Lin <jolin@mozilla.com>
Mon, 21 Apr 2014 23:42:00 +0200
changeset 198004 cda06a015aead1efb08f36f54b465aa14cecb2c1
parent 198003 091d1a5fd0aa7544c46c67d4810d8ee58ab11704
child 198005 344e6ea026898f7b7e0eadbfeb2bfb12734156f4
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs911046
milestone31.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 911046 - Part 4: Add external H.264 HW video codec implementation for B2G. r=jesup
build/gyp.mozbuild
content/media/omx/OMXCodecWrapper.h
media/webrtc/signaling/signaling.gyp
media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
media/webrtc/signaling/src/media-conduit/OMXVideoCodec.cpp
media/webrtc/signaling/src/media-conduit/OMXVideoCodec.h
media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp
media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h
widget/gonk/nativewindow/moz.build
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -30,16 +30,17 @@ gyp_vars = {
     # saves 4MB when webrtc_trace is off
     'enable_lazy_trace_alloc': 1,
 
      # turn off mandatory use of NEON and instead use NEON detection
     'arm_neon': 0,
     'arm_neon_optional': 1,
 
     'moz_widget_toolkit_gonk': 0,
+    'moz_omx_encoder': 0,
 
     # (for vp8) chromium sets to 0 also
     'use_temporal_layers': 0,
     # Creates AEC internal sample dump files in current directory
     # 'aec_debug_dump': 1,
 
     # codec enable/disables:
     'include_g711': 1,
@@ -56,16 +57,18 @@ if os == 'WINNT':
     gyp_vars.update(
         MSVS_VERSION=CONFIG['_MSVS_VERSION'],
         MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_OS'] else 32,
     )
 elif os == 'Android':
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         gyp_vars['build_with_gonk'] = 1
         gyp_vars['moz_widget_toolkit_gonk'] = 1
+        if CONFIG['MOZ_OMX_ENCODER']:
+          gyp_vars['moz_omx_encoder'] = 1
     else:
         gyp_vars.update(
             gtest_target_type='executable',
             android_toolchain=CONFIG['ANDROID_TOOLCHAIN'],
         )
 
 flavors = {
     'WINNT': 'win',
--- a/content/media/omx/OMXCodecWrapper.h
+++ b/content/media/omx/OMXCodecWrapper.h
@@ -236,16 +236,17 @@ private:
   int64_t mSampleDuration;
 };
 
 /**
  * Video encoder.
  */
 class OMXVideoEncoder MOZ_FINAL : public OMXCodecWrapper
 {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OMXVideoEncoder)
 public:
   // Types of output blob format.
   enum BlobFormat {
     AVC_MP4, // MP4 file config descripter (defined in ISO/IEC 14496-15 5.2.4.1.1)
     AVC_NAL  // NAL (Network Abstract Layer) (defined in ITU-T H.264 7.4.1)
   };
 
   /**
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -52,16 +52,17 @@
         './src/sipcc/cpr/include',
         '../../../ipc/chromium/src',
         '../../../ipc/chromium/src/base/third_party/nspr',
         '../../../xpcom/base',
         '../../../dom/base',
         '../../../content/media',
         '../../../media/mtransport',
         '../trunk',
+        '../trunk/webrtc',
         '../trunk/webrtc/video_engine/include',
         '../trunk/webrtc/voice_engine/include',
         '../trunk/webrtc/modules/interface',
         '../trunk/webrtc/peerconnection',
         '../../libyuv/include',
         '../../../netwerk/srtp/src/include',
         '../../../netwerk/srtp/src/crypto/include',
         '../../../ipc/chromium/src',
@@ -186,16 +187,36 @@
         '$(NSS_CFLAGS)',
         '$(MOZ_PIXMAN_CFLAGS)',
       ],
 
       #
       # Conditionals
       #
       'conditions': [
+        ['moz_omx_encoder==1', {
+          'sources': [
+            './src/media-conduit/WebrtcOMXH264VideoCodec.cpp',
+            './src/media-conduit/OMXVideoCodec.cpp',
+          ],
+          'include_dirs': [
+            '../../../content/media/omx',
+            '../../../gfx/layers/client',
+          ],
+          'cflags_mozilla': [
+            '-I$(ANDROID_SOURCE)/frameworks/av/include/media/stagefright',
+            '-I$(ANDROID_SOURCE)/frameworks/av/include',
+            '-I$(ANDROID_SOURCE)/frameworks/native/include/media/openmax',
+            '-I$(ANDROID_SOURCE)/frameworks/native/include',
+            '-I$(ANDROID_SOURCE)/frameworks/native/opengl/include',
+          ],
+          'defines' : [
+            'MOZ_OMX_ENCODER'
+          ],
+        }],
         ['build_for_test==0', {
           'defines' : [
             'MOZILLA_INTERNAL_API'
           ],
           'sources': [
             './src/peerconnection/WebrtcGlobalInformation.cpp',
             './src/peerconnection/WebrtcGlobalInformation.h',
           ],
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -183,26 +183,22 @@ public:
 
 };
 
 // Abstract base classes for external encoder/decoder.
 class VideoEncoder
 {
 public:
   virtual ~VideoEncoder() {};
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoEncoder);
 };
 
 class VideoDecoder
 {
 public:
   virtual ~VideoDecoder() {};
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoder);
 };
 
 /**
  * MediaSessionConduit for video
  * Refer to the comments on MediaSessionConduit above for overall
  * information
  */
 class VideoSessionConduit : public MediaSessionConduit
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/OMXVideoCodec.cpp
@@ -0,0 +1,30 @@
+/* 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 "OMXVideoCodec.h"
+
+#ifdef WEBRTC_GONK
+#include "WebrtcOMXH264VideoCodec.h"
+#endif
+
+namespace mozilla {
+
+VideoEncoder*
+OMXVideoCodec::CreateEncoder(CodecType aCodecType)
+{
+  if (aCodecType == CODEC_H264) {
+    return new WebrtcOMXH264VideoEncoder();
+  }
+  return nullptr;
+}
+
+VideoDecoder*
+OMXVideoCodec::CreateDecoder(CodecType aCodecType) {
+  if (aCodecType == CODEC_H264) {
+    return new WebrtcOMXH264VideoDecoder();
+  }
+  return nullptr;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/OMXVideoCodec.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef OMX_VIDEO_CODEC_H_
+#define OMX_VIDEO_CODEC_H_
+
+#include "MediaConduitInterface.h"
+
+namespace mozilla {
+class OMXVideoCodec {
+ public:
+  enum CodecType {
+    CODEC_H264,
+  };
+
+  /**
+   * Create encoder object for codec type |aCodecType|. Return |nullptr| when
+   * failed.
+   */
+  static VideoEncoder* CreateEncoder(CodecType aCodecType);
+
+  /**
+   * Create decoder object for codec type |aCodecType|. Return |nullptr| when
+   * failed.
+   */
+  static VideoDecoder* CreateDecoder(CodecType aCodecType);
+};
+
+}
+
+#endif // OMX_VIDEO_CODEC_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp
@@ -0,0 +1,875 @@
+/* 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 "CSFLog.h"
+
+#include "WebrtcOMXH264VideoCodec.h"
+
+// Android/Stagefright
+#include <avc_utils.h>
+#include <binder/ProcessState.h>
+#include <foundation/ABuffer.h>
+#include <foundation/AMessage.h>
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <MediaCodec.h>
+#include <MediaDefs.h>
+#include <MediaErrors.h>
+#include <MetaData.h>
+#include <OMX_Component.h>
+using namespace android;
+
+// WebRTC
+#include "common_video/interface/texture_video_frame.h"
+#include "video_engine/include/vie_external_codec.h"
+
+// Gecko
+#include "GonkNativeWindow.h"
+#include "GonkNativeWindowClient.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+#include "nsThreadUtils.h"
+#include "OMXCodecWrapper.h"
+#include "TextureClient.h"
+
+#define DEQUEUE_BUFFER_TIMEOUT_US (100 * 1000ll) // 100ms.
+#define START_DEQUEUE_BUFFER_TIMEOUT_US (10 * DEQUEUE_BUFFER_TIMEOUT_US) // 1s.
+#define DRAIN_THREAD_TIMEOUT_US  (1000 * 1000ll) // 1s.
+
+#define LOG_TAG "WebrtcOMXH264VideoCodec"
+#define CODEC_LOGV(...) CSFLogInfo(LOG_TAG, __VA_ARGS__)
+#define CODEC_LOGD(...) CSFLogDebug(LOG_TAG, __VA_ARGS__)
+#define CODEC_LOGI(...) CSFLogInfo(LOG_TAG, __VA_ARGS__)
+#define CODEC_LOGW(...) CSFLogWarn(LOG_TAG, __VA_ARGS__)
+#define CODEC_LOGE(...) CSFLogError(LOG_TAG, __VA_ARGS__)
+
+namespace mozilla {
+
+// NS_INLINE_DECL_THREADSAFE_REFCOUNTING() cannot be used directly in
+// ImageNativeHandle below because the return type of webrtc::NativeHandle
+// AddRef()/Release() conflicts with those defined in macro. To avoid another
+// copy/paste of ref-counting implementation here, this dummy base class
+// is created to proivde another level of indirection.
+class DummyRefCountBase {
+public:
+  // Use the name of real class for logging.
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageNativeHandle)
+  // To make sure subclass will be deleted/destructed properly.
+  virtual ~DummyRefCountBase() {}
+};
+
+// This function implements 2 interafces:
+// 1. webrtc::NativeHandle: to wrap layers::Image object so decoded frames can
+//    be passed through WebRTC rendering pipeline using TextureVideoFrame.
+// 2. ImageHandle: for renderer to get the image object inside without knowledge
+//    about webrtc::NativeHandle.
+class ImageNativeHandle MOZ_FINAL
+  : public webrtc::NativeHandle
+  , public DummyRefCountBase
+{
+public:
+  ImageNativeHandle(layers::Image* aImage)
+    : mImage(aImage)
+  {}
+
+  // Implement webrtc::NativeHandle.
+  virtual void* GetHandle() MOZ_OVERRIDE { return mImage.get(); }
+
+  virtual int AddRef() MOZ_OVERRIDE
+  {
+    return DummyRefCountBase::AddRef();
+  }
+
+  virtual int Release() MOZ_OVERRIDE
+  {
+    return DummyRefCountBase::Release();
+  }
+
+private:
+  RefPtr<layers::Image> mImage;
+};
+
+// Graphic buffer lifecycle management.
+// Return buffer to OMX codec when renderer is done with it.
+class RecycleCallback
+{
+public:
+  RecycleCallback(const sp<MediaCodec>& aOmx, uint32_t aBufferIndex)
+    : mOmx(aOmx)
+    , mBufferIndex(aBufferIndex)
+  {}
+  typedef void* CallbackPtr;
+  static void ReturnOMXBuffer(layers::TextureClient* aClient, CallbackPtr aClosure)
+  {
+    aClient->ClearRecycleCallback();
+    RecycleCallback* self = static_cast<RecycleCallback*>(aClosure);
+    self->mOmx->releaseOutputBuffer(self->mBufferIndex);
+    delete self;
+  }
+
+private:
+  sp<MediaCodec> mOmx;
+  uint32_t mBufferIndex;
+};
+
+struct EncodedFrame
+{
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mTimestamp;
+  int64_t mRenderTimeMs;
+};
+
+// Base runnable class to repeatly pull OMX output buffers in seperate thread.
+// How to use:
+// - implementing DrainOutput() to get output. Remember to return false to tell
+//   drain not to pop input queue.
+// - call QueueInput() to schedule a run to drain output. The input, aFrame,
+//   should contains corresponding info such as image size and timestamps for
+//   DrainOutput() implementation to construct data needed by encoded/decoded
+//   callbacks.
+// TODO: Bug 997110 - Revisit queue/drain logic. Current design assumes that
+//       encoder only generate one output buffer per input frame and won't work
+//       if encoder drops frames or generates multiple output per input.
+class OMXOutputDrain : public nsRunnable
+{
+public:
+  void Start() {
+    MonitorAutoLock lock(mMonitor);
+    if (mThread == nullptr) {
+      NS_NewNamedThread("OMXOutputDrain", getter_AddRefs(mThread));
+    }
+    CODEC_LOGD("OMXOutputDrain started");
+    mEnding = false;
+    mThread->Dispatch(this, NS_DISPATCH_NORMAL);
+  }
+
+  void Stop() {
+    MonitorAutoLock lock(mMonitor);
+    mEnding = true;
+    lock.NotifyAll(); // In case Run() is waiting.
+
+    if (mThread != nullptr) {
+      mThread->Shutdown();
+      mThread = nullptr;
+    }
+    CODEC_LOGD("OMXOutputDrain stopped");
+  }
+
+  void QueueInput(const EncodedFrame& aFrame)
+  {
+    MonitorAutoLock lock(mMonitor);
+
+    MOZ_ASSERT(mThread);
+
+    mInputFrames.push(aFrame);
+    // Notify Run() about queued input and it can start working.
+    lock.NotifyAll();
+  }
+
+  NS_IMETHODIMP Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(mThread);
+
+    MonitorAutoLock lock(mMonitor);
+    while (true) {
+      if (mInputFrames.empty()) {
+        ALOGE("Waiting OMXOutputDrain");
+        // Wait for new input.
+        lock.Wait();
+      }
+
+      if (mEnding) {
+        ALOGE("Ending OMXOutputDrain");
+        // Stop draining.
+        break;
+      }
+
+      MOZ_ASSERT(!mInputFrames.empty());
+      EncodedFrame frame = mInputFrames.front();
+      bool shouldPop = false;
+      {
+        // Release monitor while draining because it's blocking.
+        MonitorAutoUnlock unlock(mMonitor);
+        // |frame| provides size and time of corresponding input.
+        shouldPop = DrainOutput(frame);
+      }
+      if (shouldPop) {
+        mInputFrames.pop();
+      }
+    }
+
+    CODEC_LOGD("OMXOutputDrain Ended");
+    return NS_OK;
+  }
+
+protected:
+  OMXOutputDrain()
+    : mMonitor("OMXOutputDrain monitor")
+    , mEnding(false)
+  {}
+
+  // Drain output buffer for input frame aFrame.
+  // aFrame contains info such as size and time of the input frame and can be
+  // used to construct data for encoded/decoded callbacks if needed.
+  // Return true to indicate we should pop input queue, and return false to
+  // indicate aFrame should not be removed from input queue (either output is
+  // not ready yet and should try again later, or the drained output is SPS/PPS
+  // NALUs that has no corresponding input in queue).
+  virtual bool DrainOutput(const EncodedFrame& aFrame) = 0;
+
+private:
+  // This monitor protects all things below it, and is also used to
+  // wait/notify queued input.
+  Monitor mMonitor;
+  nsCOMPtr<nsIThread> mThread;
+  std::queue<EncodedFrame> mInputFrames;
+  bool mEnding;
+};
+
+// H.264 decoder using stagefright.
+class WebrtcOMXDecoder MOZ_FINAL
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcOMXDecoder)
+public:
+  WebrtcOMXDecoder(const char* aMimeType)
+    : mWidth(0)
+    , mHeight(0)
+    , mStarted(false)
+  {
+    // Create binder thread pool required by stagefright.
+    android::ProcessState::self()->startThreadPool();
+
+    mLooper = new ALooper;
+    mLooper->start();
+    mCodec = MediaCodec::CreateByType(mLooper, aMimeType, false /* encoder */);
+  }
+
+  virtual ~WebrtcOMXDecoder()
+  {
+    if (mStarted) {
+      Stop();
+    }
+    if (mCodec != nullptr) {
+      mCodec->release();
+      mCodec.clear();
+    }
+    mLooper.clear();
+  }
+
+  // Parse SPS/PPS NALUs.
+  static sp<MetaData> ParseParamSets(sp<ABuffer>& aParamSets)
+  {
+    return MakeAVCCodecSpecificData(aParamSets);
+  }
+
+  // Configure decoder using data returned by ParseParamSets().
+  status_t ConfigureWithParamSets(const sp<MetaData>& aParamSets)
+  {
+    MOZ_ASSERT(mCodec != nullptr);
+    if (mCodec == nullptr) {
+      return INVALID_OPERATION;
+    }
+
+    int32_t width = 0;
+    bool ok = aParamSets->findInt32(kKeyWidth, &width);
+    MOZ_ASSERT(ok && width > 0);
+    int32_t height = 0;
+    ok = aParamSets->findInt32(kKeyHeight, &height);
+    MOZ_ASSERT(ok && height > 0);
+    CODEC_LOGD("OMX:%p decoder config width:%d height:%d", this, width, height);
+
+    sp<AMessage> config = new AMessage();
+    config->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
+    config->setInt32("width", width);
+    config->setInt32("height", height);
+    mWidth = width;
+    mHeight = height;
+
+    sp<Surface> surface = nullptr;
+    mNativeWindow = new GonkNativeWindow();
+    if (mNativeWindow.get()) {
+      mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue());
+      if (mNativeWindowClient.get()) {
+        surface = new Surface(mNativeWindowClient->getIGraphicBufferProducer());
+      }
+    }
+    status_t result = mCodec->configure(config, surface, nullptr, 0);
+    if (result == OK) {
+      result = Start();
+    }
+    return result;
+  }
+
+  status_t
+  FillInput(const webrtc::EncodedImage& aEncoded, bool aIsFirstFrame,
+            int64_t& aRenderTimeMs, webrtc::DecodedImageCallback* aCallback)
+  {
+    MOZ_ASSERT(mCodec != nullptr);
+    if (mCodec == nullptr) {
+      return INVALID_OPERATION;
+    }
+
+    size_t index;
+    status_t err = mCodec->dequeueInputBuffer(&index,
+      aIsFirstFrame ? START_DEQUEUE_BUFFER_TIMEOUT_US : DEQUEUE_BUFFER_TIMEOUT_US);
+    if (err != OK) {
+      CODEC_LOGE("decode dequeue input buffer error:%d", err);
+      return err;
+    }
+
+    uint32_t flags = 0;
+    if (aEncoded._frameType == webrtc::kKeyFrame) {
+      flags = aIsFirstFrame ? MediaCodec::BUFFER_FLAG_CODECCONFIG : MediaCodec::BUFFER_FLAG_SYNCFRAME;
+    }
+    size_t size = aEncoded._length;
+    MOZ_ASSERT(size);
+    const sp<ABuffer>& omxIn = mInputBuffers.itemAt(index);
+    MOZ_ASSERT(omxIn->capacity() >= size);
+    omxIn->setRange(0, size);
+    // Copying is needed because MediaCodec API doesn't support externallay
+    // allocated buffer as input.
+    memcpy(omxIn->data(), aEncoded._buffer, size);
+    int64_t inputTimeUs = aEncoded._timeStamp * 1000 / 90; // 90kHz -> us.
+    err = mCodec->queueInputBuffer(index, 0, size, inputTimeUs, flags);
+    if (err == OK && !(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) {
+      if (mOutputDrain == nullptr) {
+        mOutputDrain = new OutputDrain(this, aCallback);
+        mOutputDrain->Start();
+      }
+      EncodedFrame frame;
+      frame.mWidth = mWidth;
+      frame.mHeight = mHeight;
+      frame.mTimestamp = aEncoded._timeStamp;
+      frame.mRenderTimeMs = aRenderTimeMs;
+      mOutputDrain->QueueInput(frame);
+    }
+
+    return err;
+  }
+
+  status_t
+  DrainOutput(const EncodedFrame& aFrame, webrtc::DecodedImageCallback* aCallback)
+  {
+    MOZ_ASSERT(mCodec != nullptr);
+    if (mCodec == nullptr) {
+      return INVALID_OPERATION;
+    }
+
+    size_t index = 0;
+    size_t outOffset = 0;
+    size_t outSize = 0;
+    int64_t outTime = -1ll;
+    uint32_t outFlags = 0;
+    status_t err = mCodec->dequeueOutputBuffer(&index, &outOffset, &outSize,
+                                               &outTime, &outFlags,
+                                               DRAIN_THREAD_TIMEOUT_US);
+    switch (err) {
+      case OK:
+        break;
+      case -EAGAIN:
+        // Not an error: output not available yet. Try later.
+        CODEC_LOGI("decode dequeue OMX output buffer timed out. Try later.");
+        return err;
+      case INFO_FORMAT_CHANGED:
+        // Not an error: will get this value when OMX output buffer is enabled,
+        // or when input size changed.
+        CODEC_LOGD("decode dequeue OMX output buffer format change");
+        return err;
+      case INFO_OUTPUT_BUFFERS_CHANGED:
+        // Not an error: will get this value when OMX output buffer changed
+        // (probably because of input size change).
+        CODEC_LOGD("decode dequeue OMX output buffer change");
+        err = mCodec->getOutputBuffers(&mOutputBuffers);
+        MOZ_ASSERT(err == OK);
+        return INFO_OUTPUT_BUFFERS_CHANGED;
+      default:
+        CODEC_LOGE("decode dequeue OMX output buffer error:%d", err);
+        // Return OK to instruct OutputDrain to drop input from queue.
+        return OK;
+    }
+
+    sp<ABuffer> omxOut = mOutputBuffers.itemAt(index);
+    nsAutoPtr<webrtc::I420VideoFrame> videoFrame(GenerateVideoFrame(aFrame,
+                                                                    index,
+                                                                    omxOut));
+    if (videoFrame == nullptr) {
+      mCodec->releaseOutputBuffer(index);
+    } else if (aCallback) {
+      aCallback->Decoded(*videoFrame);
+      // OMX buffer will be released by RecycleCallback after rendered.
+    }
+
+    return err;
+  }
+
+private:
+  class OutputDrain : public OMXOutputDrain
+  {
+  public:
+    OutputDrain(WebrtcOMXDecoder* aOMX, webrtc::DecodedImageCallback* aCallback)
+      : OMXOutputDrain()
+      , mOMX(aOMX)
+      , mCallback(aCallback)
+    {}
+
+  protected:
+    virtual bool DrainOutput(const EncodedFrame& aFrame) MOZ_OVERRIDE
+    {
+      return (mOMX->DrainOutput(aFrame, mCallback) == OK);
+    }
+
+  private:
+    WebrtcOMXDecoder* mOMX;
+    webrtc::DecodedImageCallback* mCallback;
+  };
+
+  status_t Start()
+  {
+    MOZ_ASSERT(!mStarted);
+    if (mStarted) {
+      return OK;
+    }
+
+    status_t err = mCodec->start();
+    if (err == OK) {
+      mStarted = true;
+      mCodec->getInputBuffers(&mInputBuffers);
+      mCodec->getOutputBuffers(&mOutputBuffers);
+    }
+
+    return err;
+  }
+
+  status_t Stop()
+  {
+    MOZ_ASSERT(mStarted);
+    if (!mStarted) {
+      return OK;
+    }
+    if (mOutputDrain != nullptr) {
+      mOutputDrain->Stop();
+      mOutputDrain = nullptr;
+    }
+
+    status_t err = mCodec->stop();
+    if (err == OK) {
+      mInputBuffers.clear();
+      mOutputBuffers.clear();
+      mStarted = false;
+    } else {
+      MOZ_ASSERT(false);
+    }
+
+    return err;
+  }
+
+  webrtc::I420VideoFrame*
+  GenerateVideoFrame(const EncodedFrame& aEncoded, uint32_t aBufferIndex,
+                     const sp<ABuffer>& aOMXBuffer)
+  {
+    // TODO: Get decoded frame buffer through native window to obsolete
+    //       changes to stagefright code.
+    sp<RefBase> obj;
+    bool hasGraphicBuffer = aOMXBuffer->meta()->findObject("graphic-buffer", &obj);
+    if (!hasGraphicBuffer) {
+      MOZ_ASSERT(false, "Decoder doesn't produce graphic buffer");
+      // Nothing to render.
+      return nullptr;
+    }
+
+    sp<GraphicBuffer> gb = static_cast<GraphicBuffer*>(obj.get());
+    if (!gb.get()) {
+      MOZ_ASSERT(false, "Null graphic buffer");
+      return nullptr;
+    }
+
+    RefPtr<mozilla::layers::TextureClient> textureClient =
+      mNativeWindow->getTextureClientFromBuffer(gb.get());
+    textureClient->SetRecycleCallback(RecycleCallback::ReturnOMXBuffer,
+                                      new RecycleCallback(mCodec, aBufferIndex));
+
+    int width = gb->getWidth();
+    int height = gb->getHeight();
+    layers::GrallocImage::GrallocData grallocData;
+    grallocData.mPicSize = gfx::IntSize(width, height);
+    grallocData.mGraphicBuffer = textureClient;
+
+    layers::GrallocImage* grallocImage = new layers::GrallocImage();
+    grallocImage->SetData(grallocData);
+
+    nsAutoPtr<webrtc::I420VideoFrame> videoFrame(
+      new webrtc::TextureVideoFrame(new ImageNativeHandle(grallocImage),
+                                    width, height,
+                                    aEncoded.mTimestamp,
+                                    aEncoded.mRenderTimeMs));
+
+    return videoFrame.forget();
+  }
+
+  sp<ALooper> mLooper;
+  sp<MediaCodec> mCodec; // OMXCodec
+  int mWidth;
+  int mHeight;
+  android::Vector<sp<ABuffer> > mInputBuffers;
+  android::Vector<sp<ABuffer> > mOutputBuffers;
+  bool mStarted;
+
+  sp<GonkNativeWindow> mNativeWindow;
+  sp<GonkNativeWindowClient> mNativeWindowClient;
+
+  RefPtr<OutputDrain> mOutputDrain;
+};
+
+class EncOutputDrain : public OMXOutputDrain
+{
+public:
+  EncOutputDrain(OMXVideoEncoder* aOMX, webrtc::EncodedImageCallback* aCallback)
+    : OMXOutputDrain()
+    , mOMX(aOMX)
+    , mCallback(aCallback)
+    , mIsPrevOutputParamSets(false)
+  {}
+
+protected:
+  virtual bool DrainOutput(const EncodedFrame& aInputFrame) MOZ_OVERRIDE
+  {
+    nsTArray<uint8_t> output;
+    int64_t timeUs = -1ll;
+    int flags = 0;
+    nsresult rv = mOMX->GetNextEncodedFrame(&output, &timeUs, &flags,
+                                            DRAIN_THREAD_TIMEOUT_US);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      // Fail to get encoded frame. The corresponding input frame should be
+      // removed.
+      return true;
+    }
+
+    if (output.Length() == 0) {
+      // No encoded data yet. Try later.
+      CODEC_LOGD("OMX:%p (encode no output available this time)", mOMX);
+      return false;
+    }
+
+    bool isParamSets = (flags & MediaCodec::BUFFER_FLAG_CODECCONFIG);
+    bool isIFrame = (flags & MediaCodec::BUFFER_FLAG_SYNCFRAME);
+    // Should not be parameter sets and I-frame at the same time.
+    MOZ_ASSERT(!(isParamSets && isIFrame));
+
+    if (mCallback) {
+      // Implementation here assumes encoder output to be a buffer containing
+      // parameter sets(SPS + PPS) followed by a series of buffers, each for
+      // one input frame.
+      // TODO: handle output violating this assumpton in bug 997110.
+      webrtc::EncodedImage encoded(output.Elements(), output.Length(),
+                                   output.Capacity());
+      encoded._frameType = (isParamSets || isIFrame) ?
+                           webrtc::kKeyFrame : webrtc::kDeltaFrame;
+      encoded._encodedWidth = aInputFrame.mWidth;
+      encoded._encodedHeight = aInputFrame.mHeight;
+      encoded._timeStamp = aInputFrame.mTimestamp;
+      encoded.capture_time_ms_ = aInputFrame.mRenderTimeMs;
+      encoded._completeFrame = true;
+
+      ALOGE("OMX:%p encode frame type:%d size:%u", mOMX, encoded._frameType, encoded._length);
+
+      // Prepend SPS/PPS to I-frames unless they were sent last time.
+      SendEncodedDataToCallback(encoded, isIFrame && !mIsPrevOutputParamSets);
+      mIsPrevOutputParamSets = isParamSets;
+    }
+
+    // Tell base class not to pop input for parameter sets blob because they
+    // don't have corresponding input.
+    return !isParamSets;
+  }
+
+private:
+  // Send encoded data to callback.The data will be broken into individual NALUs
+  // if necessary and sent to callback one by one. This function can also insert
+  // SPS/PPS NALUs in front of input data if requested.
+  void SendEncodedDataToCallback(webrtc::EncodedImage& aEncodedImage,
+                                 bool aPrependParamSets)
+  {
+    // Individual NALU inherits metadata from input encoded data.
+    webrtc::EncodedImage nalu(aEncodedImage);
+
+    if (aPrependParamSets) {
+      // Insert current parameter sets in front of the input encoded data.
+      nsTArray<uint8_t> paramSets;
+      mOMX->GetCodecConfig(&paramSets);
+      MOZ_ASSERT(paramSets.Length() > 4); // Start code + ...
+      // Set buffer range.
+      nalu._buffer = paramSets.Elements();
+      nalu._length = paramSets.Length();
+      // Break into NALUs and send.
+      SendEncodedDataToCallback(nalu, false);
+    }
+
+    // Break input encoded data into NALUs and send each one to callback.
+    const uint8_t* data = aEncodedImage._buffer;
+    size_t size = aEncodedImage._length;
+    const uint8_t* nalStart = nullptr;
+    size_t nalSize = 0;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+      nalu._buffer = const_cast<uint8_t*>(nalStart);
+      nalu._length = nalSize;
+      mCallback->Encoded(nalu, nullptr, nullptr);
+    }
+  }
+
+  OMXVideoEncoder* mOMX;
+  webrtc::EncodedImageCallback* mCallback;
+  bool mIsPrevOutputParamSets;
+};
+
+// Encoder.
+WebrtcOMXH264VideoEncoder::WebrtcOMXH264VideoEncoder()
+  : mOMX(nullptr)
+  , mCallback(nullptr)
+  , mWidth(0)
+  , mHeight(0)
+  , mFrameRate(0)
+  , mOMXConfigured(false)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p constructed", this);
+}
+
+int32_t
+WebrtcOMXH264VideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                                      int32_t aNumOfCores,
+                                      uint32_t aMaxPayloadSize)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p init", this);
+
+  if (mOMX == nullptr) {
+    nsAutoPtr<OMXVideoEncoder> omx(OMXCodecWrapper::CreateAVCEncoder());
+    if (NS_WARN_IF(omx == nullptr)) {
+      return WEBRTC_VIDEO_CODEC_ERROR;
+    }
+    mOMX = omx.forget();
+  }
+
+  // Defer configuration until 1st frame is received because this function will
+  // be called more than once, and unfortunately with incorrect setting values
+  // at first.
+  mWidth = aCodecSettings->width;
+  mHeight = aCodecSettings->height;
+  mFrameRate = aCodecSettings->maxFramerate;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcOMXH264VideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
+                                  const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                                  const std::vector<webrtc::VideoFrameType>* aFrameTypes)
+{
+  MOZ_ASSERT(mOMX != nullptr);
+  if (mOMX == nullptr) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  if (!mOMXConfigured) {
+    mOMX->Configure(mWidth, mHeight, mFrameRate,
+                    OMXVideoEncoder::BlobFormat::AVC_NAL);
+    mOMXConfigured = true;
+    CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p start OMX with image size:%ux%u",
+               this, mWidth, mHeight);
+  }
+
+  // Wrap I420VideoFrame input with PlanarYCbCrImage for OMXVideoEncoder.
+  layers::PlanarYCbCrData yuvData;
+  yuvData.mYChannel = const_cast<uint8_t*>(aInputImage.buffer(webrtc::kYPlane));
+  yuvData.mYSize = gfx::IntSize(aInputImage.width(), aInputImage.height());
+  yuvData.mYStride = aInputImage.stride(webrtc::kYPlane);
+  MOZ_ASSERT(aInputImage.stride(webrtc::kUPlane) == aInputImage.stride(webrtc::kVPlane));
+  yuvData.mCbCrStride = aInputImage.stride(webrtc::kUPlane);
+  yuvData.mCbChannel = const_cast<uint8_t*>(aInputImage.buffer(webrtc::kUPlane));
+  yuvData.mCrChannel = const_cast<uint8_t*>(aInputImage.buffer(webrtc::kVPlane));
+  yuvData.mCbCrSize = gfx::IntSize((yuvData.mYSize.width + 1) / 2,
+                                   (yuvData.mYSize.height + 1) / 2);
+  yuvData.mPicSize = yuvData.mYSize;
+  yuvData.mStereoMode = StereoMode::MONO;
+  layers::PlanarYCbCrImage img(nullptr);
+  img.SetDataNoCopy(yuvData);
+
+  nsresult rv = mOMX->Encode(&img,
+                             yuvData.mYSize.width,
+                             yuvData.mYSize.height,
+                             aInputImage.timestamp() * 1000 / 90, // 90kHz -> us.
+                             0);
+  if (rv == NS_OK) {
+    if (mOutputDrain == nullptr) {
+      mOutputDrain = new EncOutputDrain(mOMX, mCallback);
+      mOutputDrain->Start();
+    }
+    EncodedFrame frame;
+    frame.mWidth = mWidth;
+    frame.mHeight = mHeight;
+    frame.mTimestamp = aInputImage.timestamp();
+    frame.mRenderTimeMs = aInputImage.render_time_ms();
+    mOutputDrain->QueueInput(frame);
+  }
+
+  return (rv == NS_OK) ? WEBRTC_VIDEO_CODEC_OK : WEBRTC_VIDEO_CODEC_ERROR;
+}
+
+int32_t
+WebrtcOMXH264VideoEncoder::RegisterEncodeCompleteCallback(
+    webrtc::EncodedImageCallback* aCallback)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p set callback:%p", this, aCallback);
+  MOZ_ASSERT(aCallback);
+  mCallback = aCallback;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcOMXH264VideoEncoder::Release()
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p will be released", this);
+
+  if (mOutputDrain != nullptr) {
+    mOutputDrain->Stop();
+    mOutputDrain = nullptr;
+  }
+
+  mOMX = nullptr;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebrtcOMXH264VideoEncoder::~WebrtcOMXH264VideoEncoder()
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p will be destructed", this);
+
+  Release();
+}
+
+// Inform the encoder of the new packet loss rate and the round-trip time of
+// the network. aPacketLossRate is fraction lost and can be 0~255
+// (255 means 100% lost).
+// Note: stagefright doesn't handle these parameters.
+int32_t
+WebrtcOMXH264VideoEncoder::SetChannelParameters(uint32_t aPacketLossRate,
+                                                int aRoundTripTimeMs)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p set channel packet loss:%u, rtt:%d",
+             this, aPacketLossRate, aRoundTripTimeMs);
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+// TODO: Bug 997567. Find the way to support frame rate change.
+int32_t
+WebrtcOMXH264VideoEncoder::SetRates(uint32_t aBitRate, uint32_t aFrameRate)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoEncoder:%p set bitrate:%u, frame rate:%u)",
+             this, aBitRate, aFrameRate);
+  MOZ_ASSERT(mOMX != nullptr);
+  if (mOMX == nullptr) {
+    return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+  }
+
+  mOMX->SetBitrate(aBitRate);
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+// Decoder.
+WebrtcOMXH264VideoDecoder::WebrtcOMXH264VideoDecoder()
+  : mCallback(nullptr)
+  , mOMX(nullptr)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p will be constructed", this);
+}
+
+int32_t
+WebrtcOMXH264VideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
+                                      int32_t aNumOfCores)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p init OMX:%p", this, mOMX.get());
+
+  // Defer configuration until SPS/PPS NALUs (where actual decoder config
+  // values can be extracted) are received.
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcOMXH264VideoDecoder::Decode(const webrtc::EncodedImage& aInputImage,
+                                  bool aMissingFrames,
+                                  const webrtc::RTPFragmentationHeader* aFragmentation,
+                                  const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                                  int64_t aRenderTimeMs)
+{
+  if (aInputImage._length== 0 || !aInputImage._buffer) {
+    return WEBRTC_VIDEO_CODEC_ERROR;
+  }
+
+  ALOGE("WebrtcOMXH264VideoDecoder:%p will decode", this);
+
+  bool configured = !!mOMX;
+  if (!configured) {
+    // Search for SPS/PPS NALUs in input to get decoder config.
+    sp<ABuffer> input = new ABuffer(aInputImage._buffer, aInputImage._length);
+    sp<MetaData> paramSets = WebrtcOMXDecoder::ParseParamSets(input);
+    if (NS_WARN_IF(paramSets == nullptr)) {
+      // Cannot config decoder because SPS/PPS NALUs haven't been seen.
+      return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+    }
+    RefPtr<WebrtcOMXDecoder> omx = new WebrtcOMXDecoder(MEDIA_MIMETYPE_VIDEO_AVC);
+    status_t result = omx->ConfigureWithParamSets(paramSets);
+    if (NS_WARN_IF(result != OK)) {
+      return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
+    }
+    CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p start OMX", this);
+    mOMX = omx;
+  }
+
+  bool feedFrame = true;
+  while (feedFrame) {
+    int64_t timeUs;
+    status_t err = mOMX->FillInput(aInputImage, !configured, aRenderTimeMs, mCallback);
+    feedFrame = (err == -EAGAIN); // No input buffer available. Try again.
+  }
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcOMXH264VideoDecoder::RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* aCallback)
+{
+  CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p set callback:%p", this, aCallback);
+  MOZ_ASSERT(aCallback);
+  mCallback = aCallback;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t
+WebrtcOMXH264VideoDecoder::Release()
+{
+  CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p will be released", this);
+
+  mOMX = nullptr;
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+WebrtcOMXH264VideoDecoder::~WebrtcOMXH264VideoDecoder()
+{
+  CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p will be destructed", this);
+  Release();
+}
+
+int32_t
+WebrtcOMXH264VideoDecoder::Reset()
+{
+  CODEC_LOGW("WebrtcOMXH264VideoDecoder::Reset() will NOT reset decoder");
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.h
@@ -0,0 +1,88 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBRTC_GONK
+#pragma error WebrtcOMXH264VideoCodec works only on B2G.
+#endif
+
+#ifndef WEBRTC_OMX_H264_CODEC_H_
+#define WEBRTC_OMX_H264_CODEC_H_
+
+#include "AudioConduit.h"
+#include "VideoConduit.h"
+
+namespace android {
+  class OMXVideoEncoder;
+}
+
+namespace mozilla {
+
+class WebrtcOMXDecoder;
+class OMXOutputDrain;
+
+class WebrtcOMXH264VideoEncoder : public WebrtcVideoEncoder
+{
+public:
+  WebrtcOMXH264VideoEncoder();
+
+  virtual ~WebrtcOMXH264VideoEncoder();
+
+  // Implement VideoEncoder interface.
+  virtual int32_t InitEncode(const webrtc::VideoCodec* aCodecSettings,
+                             int32_t aNumOfCores,
+                             uint32_t aMaxPayloadSize) MOZ_OVERRIDE;
+
+  virtual int32_t Encode(const webrtc::I420VideoFrame& aInputImage,
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
+                         const std::vector<webrtc::VideoFrameType>* aFrameTypes) MOZ_OVERRIDE;
+
+  virtual int32_t RegisterEncodeCompleteCallback(webrtc::EncodedImageCallback* aCallback) MOZ_OVERRIDE;
+
+  virtual int32_t Release() MOZ_OVERRIDE;
+
+  virtual int32_t SetChannelParameters(uint32_t aPacketLossRate,
+                                       int aRoundTripTimeMs) MOZ_OVERRIDE;
+
+  virtual int32_t SetRates(uint32_t aBitRate, uint32_t aFrameRate) MOZ_OVERRIDE;
+
+private:
+  RefPtr<android::OMXVideoEncoder> mOMX;
+  webrtc::EncodedImageCallback* mCallback;
+  RefPtr<OMXOutputDrain> mOutputDrain;
+  uint32_t mWidth;
+  uint32_t mHeight;
+  uint32_t mFrameRate;
+  bool mOMXConfigured;
+  webrtc::EncodedImage mEncodedImage;
+};
+
+class WebrtcOMXH264VideoDecoder : public WebrtcVideoDecoder
+{
+public:
+  WebrtcOMXH264VideoDecoder();
+
+  virtual ~WebrtcOMXH264VideoDecoder();
+
+  // Implement VideoDecoder interface.
+  virtual int32_t InitDecode(const webrtc::VideoCodec* aCodecSettings,
+                             int32_t aNumOfCores) MOZ_OVERRIDE;
+  virtual int32_t Decode(const webrtc::EncodedImage& aInputImage,
+                         bool aMissingFrames,
+                         const webrtc::RTPFragmentationHeader* aFragmentation,
+                         const webrtc::CodecSpecificInfo* aCodecSpecificInfo = nullptr,
+                         int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
+  virtual int32_t RegisterDecodeCompleteCallback(webrtc::DecodedImageCallback* callback) MOZ_OVERRIDE;
+
+  virtual int32_t Release() MOZ_OVERRIDE;
+
+  virtual int32_t Reset() MOZ_OVERRIDE;
+
+private:
+  webrtc::DecodedImageCallback* mCallback;
+  RefPtr<WebrtcOMXDecoder> mOMX;
+};
+
+}
+
+#endif // WEBRTC_OMX_H264_CODEC_H_
--- a/widget/gonk/nativewindow/moz.build
+++ b/widget/gonk/nativewindow/moz.build
@@ -36,17 +36,17 @@ elif CONFIG['ANDROID_VERSION'] in ('17',
         'GonkNativeWindowJB.h',
     ]
 elif CONFIG['ANDROID_VERSION'] == '15':
     EXPORTS += [
         'GonkNativeWindowClientICS.h',
         'GonkNativeWindowICS.h',
     ]
 
-if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER']:
+if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']:
     if CONFIG['ANDROID_VERSION'] == '19':
         SOURCES += [
             'GonkBufferQueueKK.cpp',
             'GonkConsumerBaseKK.cpp',
             'GonkNativeWindowClientKK.cpp',
             'GonkNativeWindowKK.cpp',
             'IGonkGraphicBufferConsumer.cpp',
         ]