Bug 1257777 - Part 6: Implement remote data decoders and enable/disable them with pref. r=snorp draft
authorJohn Lin <jolin@mozilla.com>
Fri, 05 Aug 2016 15:24:46 +0800
changeset 397139 2266eb1fb66e8729ad7e4fd209c1745f254f157a
parent 397138 d4026f4e3bcde89c9a2ab8b7e98b60980f4bced4
child 527382 82f2b50f53cbf4252e783d5cb1167057c18cb58e
push id25213
push userbmo:jolin@mozilla.com
push dateFri, 05 Aug 2016 08:21:05 +0000
reviewerssnorp
bugs1257777
milestone51.0a1
Bug 1257777 - Part 6: Implement remote data decoders and enable/disable them with pref. r=snorp MozReview-Commit-ID: 54ZEckQHsBI
dom/media/platforms/android/AndroidDecoderModule.cpp
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
dom/media/platforms/moz.build
modules/libpref/init/all.js
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -1,20 +1,22 @@
 /* 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 "AndroidDecoderModule.h"
 #include "AndroidBridge.h"
 
 #include "MediaCodecDataDecoder.h"
+#include "RemoteDataDecoder.h"
 
 #include "MediaInfo.h"
 #include "VPXDecoder.h"
 
+#include "mozilla/Preferences.h"
 #include "nsPromiseFlatString.h"
 #include "nsIGfxInfo.h"
 
 #include "prlog.h"
 
 #include <jni.h>
 
 #undef LOG
@@ -22,16 +24,17 @@
     mozilla::LogLevel::Debug, ("AndroidDecoderModule(%p)::%s: " arg, \
       this, __func__, ##__VA_ARGS__))
 
 using namespace mozilla;
 using namespace mozilla::gl;
 using namespace mozilla::java::sdk;
 using media::TimeUnit;
 
+#define PREF_KEY_ENABLE_REMOTE "media.android.remote-decoder.enabled"
 namespace mozilla {
 
 mozilla::LazyLogModule sAndroidDecoderModuleLog("AndroidDecoderModule");
 
 static const char*
 TranslateMimeType(const nsACString& aMimeType)
 {
   if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
@@ -97,21 +100,26 @@ AndroidDecoderModule::CreateVideoDecoder
 
   const VideoInfo& config = aParams.VideoConfig();
   NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
       TranslateMimeType(config.mMimeType),
       config.mDisplay.width,
       config.mDisplay.height,
       &format), nullptr);
 
-  RefPtr<MediaDataDecoder> decoder =
-    MediaCodecDataDecoder::CreateVideoDecoder(config,
-                                              format,
-                                              aParams.mCallback,
-                                              aParams.mImageContainer);
+  bool remote = Preferences::GetBool(PREF_KEY_ENABLE_REMOTE);
+  RefPtr<MediaDataDecoder> decoder = remote ?
+      RemoteDataDecoder::CreateVideoDecoder(config,
+                                            format,
+                                            aParams.mCallback,
+                                            aParams.mImageContainer) :
+      MediaCodecDataDecoder::CreateVideoDecoder(config,
+                                                format,
+                                                aParams.mCallback,
+                                                aParams.mImageContainer);
 
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
 {
   const AudioInfo& config = aParams.AudioConfig();
@@ -123,18 +131,20 @@ AndroidDecoderModule::CreateAudioDecoder
       config.mMimeType.Data(), config.mRate, config.mChannels);
 
   NS_ENSURE_SUCCESS(MediaFormat::CreateAudioFormat(
       config.mMimeType,
       config.mRate,
       config.mChannels,
       &format), nullptr);
 
-  RefPtr<MediaDataDecoder> decoder =
-    MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
+  bool remote = Preferences::GetBool(PREF_KEY_ENABLE_REMOTE);
+  RefPtr<MediaDataDecoder> decoder = remote ?
+      RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback) :
+      MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
 
   return decoder.forget();
 }
 
 PlatformDecoderModule::ConversionRequired
 AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   if (aConfig.IsVideo()) {
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -0,0 +1,457 @@
+/* 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 "AndroidDecoderModule.h"
+#include "AndroidBridge.h"
+#include "AndroidSurfaceTexture.h"
+#include "GeneratedJNINatives.h"
+#include "GLImages.h"
+
+#include "MediaData.h"
+#include "MediaInfo.h"
+#include "VPXDecoder.h"
+
+#include "nsThreadUtils.h"
+#include "nsPromiseFlatString.h"
+#include "nsIGfxInfo.h"
+
+#include "prlog.h"
+
+#include <jni.h>
+
+#include <deque>
+
+#undef LOG
+#define LOG(arg, ...) MOZ_LOG(sAndroidDecoderModuleLog, \
+    mozilla::LogLevel::Debug, ("RemoteDataDecoder(%p)::%s: " arg, \
+      this, __func__, ##__VA_ARGS__))
+
+using namespace mozilla;
+using namespace mozilla::gl;
+using namespace mozilla::java;
+using namespace mozilla::java::sdk;
+using media::TimeUnit;
+
+namespace mozilla {
+
+class JavaCallbacksSupport
+  : public CodecProxy::NativeCallbacks::Natives<JavaCallbacksSupport>
+{
+public:
+  typedef CodecProxy::NativeCallbacks::Natives<JavaCallbacksSupport> Base;
+  using Base::AttachNative;
+
+  JavaCallbacksSupport(MediaDataDecoderCallback* aDecoderCallback)
+    : mDecoderCallback(aDecoderCallback)
+  {
+    MOZ_ASSERT(aDecoderCallback);
+  }
+
+  virtual ~JavaCallbacksSupport() {}
+
+  void OnInputExhausted()
+  {
+    if (mDecoderCallback) {
+      mDecoderCallback->InputExhausted();
+    }
+  }
+
+  virtual void HandleOutput(jni::ByteArray::Param aBytes, BufferInfo::Param aInfo) = 0;
+
+  void OnOutput(jni::ByteArray::Param aBytes, jni::Object::Param aInfo)
+  {
+    if (mDecoderCallback) {
+      HandleOutput(aBytes, BufferInfo::Ref::From(aInfo));
+    }
+  }
+
+  virtual void HandleOutputFormatChanged(MediaFormat::Param aFormat) {};
+
+  void OnOutputFormatChanged(jni::Object::Param aFormat)
+  {
+    if (mDecoderCallback) {
+      HandleOutputFormatChanged(MediaFormat::Ref::From(aFormat));
+    }
+  }
+
+  void OnError(bool aIsFatal)
+  {
+    if (mDecoderCallback) {
+      mDecoderCallback->Error(aIsFatal ?
+        MediaDataDecoderError::FATAL_ERROR :
+        MediaDataDecoderError::DECODE_ERROR);
+    }
+  }
+
+  void DisposeNative()
+  {
+    // TODO
+  }
+
+  void Cancel()
+  {
+    mDecoderCallback = nullptr;
+  }
+
+protected:
+  MediaDataDecoderCallback* mDecoderCallback;
+};
+
+struct SampleTime final
+{
+  SampleTime(int64_t aStart, int64_t aDuration)
+    : mStart(aStart)
+    , mDuration(aDuration)
+  {}
+
+  int64_t mStart;
+  int64_t mDuration;
+};
+
+
+class RemoteVideoDecoder final : public RemoteDataDecoder
+{
+public:
+  class CallbacksSupport final : public JavaCallbacksSupport
+  {
+  public:
+    CallbacksSupport(RemoteVideoDecoder* aDecoder, MediaDataDecoderCallback* aCallback)
+      : JavaCallbacksSupport(aCallback)
+      , mDecoder(aDecoder)
+    {}
+
+    virtual ~CallbacksSupport() {}
+
+    void HandleOutput(jni::ByteArray::Param aBytes, BufferInfo::Param aInfo) override
+    {
+      Maybe<int64_t> durationUs = mDecoder->mInputDurations.Get();
+      if (!durationUs) {
+        return;
+      }
+
+      int32_t flags;
+      bool ok = NS_SUCCEEDED(aInfo->Flags(&flags));
+      MOZ_ASSERT(ok);
+
+      int32_t offset;
+      ok |= NS_SUCCEEDED(aInfo->Offset(&offset));
+      MOZ_ASSERT(ok);
+
+      int64_t presentationTimeUs;
+      ok |= NS_SUCCEEDED(aInfo->PresentationTimeUs(&presentationTimeUs));
+      MOZ_ASSERT(ok);
+
+      int32_t size;
+      ok |= NS_SUCCEEDED(aInfo->Size(&size));
+      MOZ_ASSERT(ok);
+
+      NS_ENSURE_TRUE_VOID(ok);
+
+      if (size > 0) {
+        RefPtr<layers::Image> img =
+          new SurfaceTextureImage(mDecoder->mSurfaceTexture.get(), mDecoder->mConfig.mDisplay,
+                                  gl::OriginPos::BottomLeft);
+
+        RefPtr<VideoData> v =
+          VideoData::CreateFromImage(mDecoder->mConfig,
+                                    mDecoder->mImageContainer,
+                                    offset,
+                                    presentationTimeUs,
+                                    durationUs.value(),
+                                    img,
+                                    !!(flags & MediaCodec::BUFFER_FLAG_SYNC_FRAME),
+                                    presentationTimeUs,
+                                    gfx::IntRect(0, 0,
+                                                  mDecoder->mConfig.mDisplay.width,
+                                                  mDecoder->mConfig.mDisplay.height));
+
+        mDecoderCallback->Output(v);
+      }
+
+      if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
+        mDecoderCallback->DrainComplete();
+        return;
+      }
+    }
+
+    friend class RemoteDataDecoder;
+
+  private:
+    RemoteVideoDecoder* mDecoder;
+  };
+
+  RemoteVideoDecoder(const VideoInfo& aConfig,
+                   MediaFormat::Param aFormat,
+                   MediaDataDecoderCallback* aCallback,
+                   layers::ImageContainer* aImageContainer)
+    : RemoteDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
+                        aFormat, aCallback)
+    , mImageContainer(aImageContainer)
+    , mConfig(aConfig)
+  {
+  }
+
+  RefPtr<InitPromise> InitDecoder() override
+  {
+    mSurfaceTexture = AndroidSurfaceTexture::Create();
+    if (!mSurfaceTexture) {
+      NS_WARNING("Failed to create SurfaceTexture for video decode\n");
+      return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
+    }
+
+    // Register native methods.
+    JavaCallbacksSupport::Init();
+
+    mJavaCallbacks = CodecProxy::NativeCallbacks::New();
+    JavaCallbacksSupport::AttachNative(mJavaCallbacks,
+                                       mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
+
+    mJavaDecoder = CodecProxy::Create(mFormat, mSurfaceTexture->JavaSurface(), mJavaCallbacks);
+    if (mJavaDecoder == nullptr) {
+      return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
+    }
+
+    return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+  }
+
+private:
+  layers::ImageContainer* mImageContainer;
+  const VideoInfo& mConfig;
+  RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
+};
+
+class RemoteAudioDecoder final : public RemoteDataDecoder
+{
+public:
+  RemoteAudioDecoder(const AudioInfo& aConfig,
+                   MediaFormat::Param aFormat,
+                   MediaDataDecoderCallback* aCallback)
+    : RemoteDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
+                        aFormat, aCallback)
+    , mConfig(aConfig)
+  {
+    JNIEnv* const env = jni::GetEnvForThread();
+
+    bool formatHasCSD = false;
+    NS_ENSURE_SUCCESS_VOID(aFormat->ContainsKey(NS_LITERAL_STRING("csd-0"), &formatHasCSD));
+
+    if (!formatHasCSD && aConfig.mCodecSpecificConfig->Length() >= 2) {
+      jni::ByteBuffer::LocalRef buffer(env);
+      buffer = jni::ByteBuffer::New(
+          aConfig.mCodecSpecificConfig->Elements(),
+          aConfig.mCodecSpecificConfig->Length());
+      NS_ENSURE_SUCCESS_VOID(aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"),
+                                                    buffer));
+    }
+  }
+
+  RefPtr<InitPromise> InitDecoder() override
+  {
+    // Register native methods.
+    JavaCallbacksSupport::Init();
+
+    mJavaCallbacks = CodecProxy::NativeCallbacks::New();
+    JavaCallbacksSupport::AttachNative(mJavaCallbacks,
+                                       mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
+
+    mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks);
+    if (mJavaDecoder == nullptr) {
+      return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
+    }
+
+    return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
+  }
+
+private:
+  class CallbacksSupport final : public JavaCallbacksSupport
+  {
+  public:
+    CallbacksSupport(RemoteAudioDecoder* aDecoder, MediaDataDecoderCallback* aCallback)
+      : JavaCallbacksSupport(aCallback)
+      , mDecoder(aDecoder)
+    {}
+
+    virtual ~CallbacksSupport() {}
+
+    void HandleOutput(jni::ByteArray::Param aBytes, BufferInfo::Param aInfo) override
+    {
+      Maybe<int64_t> durationUs = mDecoder->mInputDurations.Get();
+      if (!durationUs) {
+        return;
+      }
+
+      int32_t flags;
+      bool ok = NS_SUCCEEDED(aInfo->Flags(&flags));
+      MOZ_ASSERT(ok);
+
+      int32_t offset;
+      ok |= NS_SUCCEEDED(aInfo->Offset(&offset));
+      MOZ_ASSERT(ok);
+
+      int64_t presentationTimeUs;
+      ok |= NS_SUCCEEDED(aInfo->PresentationTimeUs(&presentationTimeUs));
+      MOZ_ASSERT(ok);
+
+      int32_t size;
+      ok |= NS_SUCCEEDED(aInfo->Size(&size));
+      MOZ_ASSERT(ok);
+
+      NS_ENSURE_TRUE_VOID(ok);
+
+      if (size > 0) {
+#ifdef MOZ_SAMPLE_TYPE_S16
+        const int32_t numSamples = size / 2;
+#else
+#error We only support 16-bit integer PCM
+#endif
+
+        const int32_t numFrames = numSamples / mOutputChannels;
+        AlignedAudioBuffer audio(numSamples);
+        if (!audio) {
+          return;
+        }
+
+        JNIEnv* const env = jni::GetEnvForThread();
+        jbyteArray bytes = aBytes.Get();
+        env->GetByteArrayRegion(bytes, offset, size,
+                                reinterpret_cast<jbyte*>(audio.get()));
+
+        RefPtr<AudioData> data = new AudioData(0, presentationTimeUs,
+                                              durationUs.value(),
+                                              numFrames,
+                                              Move(audio),
+                                              mOutputChannels,
+                                              mOutputSampleRate);
+
+        mDecoderCallback->Output(data);
+      }
+
+      if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
+        mDecoderCallback->DrainComplete();
+        return;
+      }
+    }
+
+    void HandleOutputFormatChanged(MediaFormat::Param aFormat) override
+    {
+      aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &mOutputChannels);
+      AudioConfig::ChannelLayout layout(mOutputChannels);
+      if (!layout.IsValid()) {
+        mDecoderCallback->Error(MediaDataDecoderError::FATAL_ERROR);
+        return;
+      }
+      aFormat->GetInteger(NS_LITERAL_STRING("sample-rate"), &mOutputSampleRate);
+      LOG("Audio output format changed: channels:%d sample rate:%d", mOutputChannels, mOutputSampleRate);
+    }
+
+  private:
+    RemoteAudioDecoder* mDecoder;
+    int32_t mOutputChannels;
+    int32_t mOutputSampleRate;
+  };
+
+  const AudioInfo& mConfig;
+};
+
+MediaDataDecoder*
+RemoteDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
+                                          MediaFormat::Param aFormat,
+                                          MediaDataDecoderCallback* aCallback)
+{
+  return new RemoteAudioDecoder(aConfig, aFormat, aCallback);
+}
+
+MediaDataDecoder*
+RemoteDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
+                                          MediaFormat::Param aFormat,
+                                          MediaDataDecoderCallback* aCallback,
+                                          layers::ImageContainer* aImageContainer)
+{
+  return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer);
+}
+
+RemoteDataDecoder::RemoteDataDecoder(MediaData::Type aType,
+                                     const nsACString& aMimeType,
+                                     MediaFormat::Param aFormat,
+                                     MediaDataDecoderCallback* aCallback)
+  : mType(aType)
+  , mMimeType(aMimeType)
+  , mFormat(aFormat)
+  , mCallback(aCallback)
+{
+}
+
+RefPtr<MediaDataDecoder::InitPromise>
+RemoteDataDecoder::Init()
+{
+  mInputDurations.Clear();
+
+  return InitDecoder();
+}
+
+nsresult
+RemoteDataDecoder::Flush()
+{
+  mInputDurations.Clear();
+  mJavaDecoder->Flush();
+  return NS_OK;
+}
+
+nsresult
+RemoteDataDecoder::Drain()
+{
+  BufferInfo::LocalRef bufferInfo;
+  nsresult rv = BufferInfo::New(&bufferInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+  bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
+
+  mJavaDecoder->Input(nullptr, bufferInfo);
+  return NS_ERROR_FAILURE;
+}
+
+nsresult
+RemoteDataDecoder::Shutdown()
+{
+  LOG("");
+  MOZ_ASSERT(mJavaDecoder && mJavaCallbacks);
+
+  mJavaDecoder->Release();
+  mJavaDecoder = nullptr;
+
+  JavaCallbacksSupport::GetNative(mJavaCallbacks)->Cancel();
+  mJavaCallbacks = nullptr;
+
+  mFormat = nullptr;
+
+  return NS_OK;
+}
+
+nsresult
+RemoteDataDecoder::Input(MediaRawData* aSample)
+{
+  MOZ_ASSERT(aSample != nullptr);
+
+  mInputDurations.Put(aSample->mDuration);
+
+  JNIEnv* const env = jni::GetEnvForThread();
+
+  // Copy sample data into Java byte array.
+  uint32_t length = aSample->Size();
+  jbyteArray data = env->NewByteArray(length);
+  env->SetByteArrayRegion(data, 0, length, reinterpret_cast<const jbyte*>(aSample->Data()));
+
+  jni::ByteArray::LocalRef bytes(env);
+  bytes = jni::Object::LocalRef::Adopt(env, data);
+
+  BufferInfo::LocalRef bufferInfo;
+  nsresult rv = BufferInfo::New(&bufferInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+  bufferInfo->Set(0, aSample->Size(), aSample->mTime, 0);
+
+  mJavaDecoder->Input(bytes, bufferInfo);
+
+  return NS_OK;
+}
+
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -0,0 +1,96 @@
+/* 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 RemoteDataDecoder_h_
+#define RemoteDataDecoder_h_
+
+#include "AndroidDecoderModule.h"
+
+#include "GeneratedJNIWrappers.h"
+
+#include "SurfaceTexture.h"
+#include "TimeUnits.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Maybe.h"
+
+#include <deque>
+
+namespace mozilla {
+
+class DurationQueue {
+public:
+
+  void Clear()
+  {
+    mValues.clear();
+  }
+
+  void Put(int64_t aDurationUs)
+  {
+    mValues.emplace_back(aDurationUs);
+  }
+
+  Maybe<int64_t> Get()
+  {
+    if (mValues.empty()) {
+      return Nothing();
+    }
+
+    auto value = Some(mValues.front());
+    mValues.pop_front();
+
+    return value;
+  }
+
+private:
+  std::deque<int64_t> mValues;
+};
+
+class RemoteDataDecoder : public MediaDataDecoder {
+public:
+  static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
+                                              java::sdk::MediaFormat::Param aFormat,
+                                              MediaDataDecoderCallback* aCallback);
+
+  static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
+                                              java::sdk::MediaFormat::Param aFormat,
+                                              MediaDataDecoderCallback* aCallback,
+                                              layers::ImageContainer* aImageContainer);
+
+  virtual ~RemoteDataDecoder() {}
+
+  RefPtr<MediaDataDecoder::InitPromise> Init() override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
+  nsresult Input(MediaRawData* aSample) override;
+  const char* GetDescriptionName() const override
+  {
+    return "android remote decoder";
+  }
+
+protected:
+  RemoteDataDecoder(MediaData::Type aType,
+                    const nsACString& aMimeType,
+                    java::sdk::MediaFormat::Param aFormat,
+                    MediaDataDecoderCallback* aCallback);
+
+  virtual RefPtr<MediaDataDecoder::InitPromise> InitDecoder() = 0;
+
+  MediaData::Type mType;
+
+  nsAutoCString mMimeType;
+  java::sdk::MediaFormat::GlobalRef mFormat;
+
+  MediaDataDecoderCallback* mCallback;
+
+  java::CodecProxy::GlobalRef mJavaDecoder;
+  java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
+
+  DurationQueue mInputDurations;
+};
+
+} // namespace mozilla
+
+#endif
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -80,14 +80,15 @@ include('/ipc/chromium/chromium-config.m
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [
         'android/AndroidDecoderModule.h',
     ]
     UNIFIED_SOURCES += [
         'android/AndroidDecoderModule.cpp',
         'android/MediaCodecDataDecoder.cpp',
+        'android/RemoteDataDecoder.cpp',
     ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5495,8 +5495,13 @@ pref("media.seekToNextFrame.enabled", tr
 #if !defined(RELEASE_BUILD)
 pref("osfile.reset_worker_delay", 30000);
 #endif
 
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 pref("dom.webkitBlink.dirPicker.enabled", true);
 pref("dom.webkitBlink.filesystem.enabled", true);
 #endif
+
+// Run decoder in seperate process.
+#ifdef MOZ_WIDGET_ANDROID
+pref("media.android.remote-decoder.enabled", false);
+#endif