Bug 1307818-[P2] Setup MediaCrypto for both in-process and out-of-process decode. draft
authorKilik Kuo <kikuo@mozilla.com>
Wed, 16 Nov 2016 22:10:01 +0800
changeset 439723 3ba77475e01f76d293cfd35d98e57a9842742cb0
parent 439722 9b1d2838ef48e0f05a957f2dbc4f42fe13c537d4
child 439724 8032a4a22cb21744f918891a3b74a0a60a983700
push id36071
push userkikuo@mozilla.com
push dateWed, 16 Nov 2016 14:19:26 +0000
bugs1307818
milestone52.0a1
Bug 1307818-[P2] Setup MediaCrypto for both in-process and out-of-process decode. MozReview-Commit-ID: Ehbm2u8LeLg
dom/media/platforms/PDMFactory.cpp
dom/media/platforms/android/AndroidDecoderModule.cpp
dom/media/platforms/android/AndroidDecoderModule.h
dom/media/platforms/android/MediaCodecDataDecoder.cpp
dom/media/platforms/android/MediaCodecDataDecoder.h
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
mobile/android/base/java/org/mozilla/gecko/media/Codec.java
mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
widget/android/bindings/MediaCodec-classes.txt
widget/android/fennec/FennecJNIWrappers.cpp
widget/android/fennec/FennecJNIWrappers.h
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -464,13 +464,21 @@ PDMFactory::GetDecoder(const TrackInfo& 
     }
   }
   return pdm.forget();
 }
 
 void
 PDMFactory::SetCDMProxy(CDMProxy* aProxy)
 {
+  MOZ_ASSERT(aProxy);
+
+#ifdef MOZ_WIDGET_ANDROID
+  if (IsWidevineKeySystem(aProxy->KeySystem())) {
+    mEMEPDM = new AndroidDecoderModule(aProxy);
+    return;
+  }
+#endif
   RefPtr<PDMFactory> m = new PDMFactory();
   mEMEPDM = new EMEDecoderModule(aProxy, m);
 }
 
 }  // namespace mozilla
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -114,16 +114,21 @@ GetCryptoInfoFromSample(const MediaRawDa
                   numBytesOfEncryptedData,
                   keyId,
                   iv,
                   MediaCodec::CRYPTO_MODE_AES_CTR);
 
   return cryptoInfo;
 }
 
+AndroidDecoderModule::AndroidDecoderModule(CDMProxy* aProxy)
+{
+  mProxy = static_cast<MediaDrmCDMProxy*>(aProxy);
+}
+
 bool
 AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                        DecoderDoctorDiagnostics* aDiagnostics) const
 {
   if (!AndroidBridge::Bridge() ||
       AndroidBridge::Bridge()->GetAPIVersion() < 16) {
     return false;
   }
@@ -169,26 +174,34 @@ AndroidDecoderModule::CreateVideoDecoder
 
   const VideoInfo& config = aParams.VideoConfig();
   NS_ENSURE_SUCCESS(MediaFormat::CreateVideoFormat(
       TranslateMimeType(config.mMimeType),
       config.mDisplay.width,
       config.mDisplay.height,
       &format), nullptr);
 
+  nsString drmStubId;
+  if (mProxy) {
+    drmStubId = mProxy->GetMediaDrmStubId();
+  }
+
   RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
-      RemoteDataDecoder::CreateVideoDecoder(config,
-                                            format,
-                                            aParams.mCallback,
-                                            aParams.mImageContainer) :
-      MediaCodecDataDecoder::CreateVideoDecoder(config,
-                                                format,
-                                                aParams.mCallback,
-                                                aParams.mImageContainer);
-
+    RemoteDataDecoder::CreateVideoDecoder(config,
+                                          format,
+                                          aParams.mCallback,
+                                          aParams.mImageContainer,
+                                          drmStubId) :
+    MediaCodecDataDecoder::CreateVideoDecoder(config,
+                                              format,
+                                              aParams.mCallback,
+                                              aParams.mImageContainer,
+                                              drmStubId,
+                                              mProxy,
+                                              aParams.mTaskQueue);
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AndroidDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
 {
   const AudioInfo& config = aParams.AudioConfig();
   MOZ_ASSERT(config.mBitDepth == 16, "We only handle 16-bit audio!");
@@ -199,20 +212,28 @@ AndroidDecoderModule::CreateAudioDecoder
       config.mMimeType.Data(), config.mRate, config.mChannels);
 
   NS_ENSURE_SUCCESS(MediaFormat::CreateAudioFormat(
       config.mMimeType,
       config.mRate,
       config.mChannels,
       &format), nullptr);
 
+  nsString drmStubId;
+  if (mProxy) {
+    drmStubId = mProxy->GetMediaDrmStubId();
+  }
   RefPtr<MediaDataDecoder> decoder = MediaPrefs::PDMAndroidRemoteCodecEnabled() ?
-      RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback) :
-      MediaCodecDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback);
-
+      RemoteDataDecoder::CreateAudioDecoder(config, format, aParams.mCallback, drmStubId) :
+      MediaCodecDataDecoder::CreateAudioDecoder(config,
+                                                format,
+                                                aParams.mCallback,
+                                                drmStubId,
+                                                mProxy,
+                                                aParams.mTaskQueue);
   return decoder.forget();
 }
 
 PlatformDecoderModule::ConversionRequired
 AndroidDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   if (aConfig.IsVideo()) {
     return ConversionRequired::kNeedAnnexB;
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -1,34 +1,38 @@
 /* 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 AndroidDecoderModule_h_
 #define AndroidDecoderModule_h_
 
 #include "PlatformDecoderModule.h"
+#include "mozilla/MediaDrmCDMProxy.h"
 
 namespace mozilla {
 
 class AndroidDecoderModule : public PlatformDecoderModule {
 public:
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const CreateDecoderParams& aParams) override;
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const CreateDecoderParams& aParams) override;
 
-  AndroidDecoderModule() {}
+  AndroidDecoderModule(CDMProxy* aProxy = nullptr);
   virtual ~AndroidDecoderModule() {}
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
+
+private:
+  RefPtr<MediaDrmCDMProxy> mProxy;
 };
 
 extern LazyLogModule sAndroidDecoderModuleLog;
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/android/MediaCodecDataDecoder.cpp
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.cpp
@@ -51,19 +51,20 @@ CreateDecoder(const nsACString& aMimeTyp
 }
 
 class VideoDataDecoder : public MediaCodecDataDecoder
 {
 public:
   VideoDataDecoder(const VideoInfo& aConfig,
                    MediaFormat::Param aFormat,
                    MediaDataDecoderCallback* aCallback,
-                   layers::ImageContainer* aImageContainer)
+                   layers::ImageContainer* aImageContainer,
+                   const nsString& aDrmStubId)
     : MediaCodecDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
-                            aFormat, aCallback)
+                            aFormat, aCallback, aDrmStubId)
     , mImageContainer(aImageContainer)
     , mConfig(aConfig)
   {
 
   }
 
   const char* GetDescriptionName() const override
   {
@@ -76,17 +77,16 @@ public:
     if (!mSurfaceTexture) {
       NS_WARNING("Failed to create SurfaceTexture for video decode\n");
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
 
     if (NS_FAILED(InitDecoder(mSurfaceTexture->JavaSurface()))) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
-
     return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
   }
 
   void Cleanup() override
   {
   }
 
   nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
@@ -122,25 +122,69 @@ public:
     INVOKE_CALLBACK(Output, v);
     return NS_OK;
   }
 
 protected:
   layers::ImageContainer* mImageContainer;
   const VideoInfo& mConfig;
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
+  RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
 };
 
+
+
+class EMEVideoDataDecoder : public VideoDataDecoder {
+public:
+  EMEVideoDataDecoder(const VideoInfo& aConfig,
+                      MediaFormat::Param aFormat,
+                      MediaDataDecoderCallback* aCallback,
+                      layers::ImageContainer* aImageContainer,
+                      const nsString& aDrmStubId,
+                      CDMProxy* aProxy,
+                      TaskQueue* aTaskQueue)
+   : VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId)
+   , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
+                                                    aTaskQueue, aProxy))
+  {
+  }
+
+  void Input(MediaRawData* aSample) override;
+  void Shutdown() override;
+
+private:
+  RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+};
+
+void
+EMEVideoDataDecoder::Input(MediaRawData* aSample)
+{
+  if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+    return;
+  }
+  VideoDataDecoder::Input(aSample);
+}
+
+void
+EMEVideoDataDecoder::Shutdown()
+{
+  VideoDataDecoder::Shutdown();
+
+  mSamplesWaitingForKey->BreakCycles();
+  mSamplesWaitingForKey = nullptr;
+}
+
 class AudioDataDecoder : public MediaCodecDataDecoder
 {
 public:
   AudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
-                   MediaDataDecoderCallback* aCallback)
+                   MediaDataDecoderCallback* aCallback,
+                   const nsString& aDrmStubId)
     : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
-                            aFormat, aCallback)
+                            aFormat, aCallback, aDrmStubId)
   {
     JNIEnv* const env = jni::GetEnvForThread();
 
     jni::ByteBuffer::LocalRef buffer(env);
     NS_ENSURE_SUCCESS_VOID(aFormat->GetByteBuffer(NS_LITERAL_STRING("csd-0"),
                                                   &buffer));
 
     if (!buffer && aConfig.mCodecSpecificConfig->Length() >= 2) {
@@ -205,47 +249,109 @@ public:
                                            Move(audio),
                                            numChannels,
                                            sampleRate);
     INVOKE_CALLBACK(Output, data);
     return NS_OK;
   }
 };
 
+class EMEAudioDataDecoder : public AudioDataDecoder {
+public:
+  EMEAudioDataDecoder(const AudioInfo& aConfig, MediaFormat::Param aFormat,
+                      MediaDataDecoderCallback* aCallback, const nsString& aDrmStubId,
+                      CDMProxy* aProxy, TaskQueue* aTaskQueue)
+   : AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId)
+   , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aCallback,
+                                                    aTaskQueue, aProxy))
+  {
+  }
+
+  void Input(MediaRawData* aSample) override;
+  void Shutdown() override;
+
+private:
+  RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
+};
+
+void
+EMEAudioDataDecoder::Input(MediaRawData* aSample)
+{
+  if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
+    return;
+  }
+  AudioDataDecoder::Input(aSample);
+}
+
+void
+EMEAudioDataDecoder::Shutdown()
+{
+  AudioDataDecoder::Shutdown();
+
+  mSamplesWaitingForKey->BreakCycles();
+  mSamplesWaitingForKey = nullptr;
+}
+
 MediaDataDecoder*
 MediaCodecDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
-                                          MediaFormat::Param aFormat,
-                                          MediaDataDecoderCallback* aCallback)
+                                          java::sdk::MediaFormat::Param aFormat,
+                                          MediaDataDecoderCallback* aCallback,
+                                          const nsString& aDrmStubId,
+                                          CDMProxy* aProxy,
+                                          TaskQueue* aTaskQueue)
 {
-  return new AudioDataDecoder(aConfig, aFormat, aCallback);
+  if (!aProxy) {
+    return new AudioDataDecoder(aConfig, aFormat, aCallback, aDrmStubId);
+  } else {
+    return new EMEAudioDataDecoder(aConfig,
+                                   aFormat,
+                                   aCallback,
+                                   aDrmStubId,
+                                   aProxy,
+                                   aTaskQueue);
+  }
 }
 
 MediaDataDecoder*
 MediaCodecDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
-                                          MediaFormat::Param aFormat,
+                                          java::sdk::MediaFormat::Param aFormat,
                                           MediaDataDecoderCallback* aCallback,
-                                          layers::ImageContainer* aImageContainer)
+                                          layers::ImageContainer* aImageContainer,
+                                          const nsString& aDrmStubId,
+                                          CDMProxy* aProxy,
+                                          TaskQueue* aTaskQueue)
 {
-  return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer);
+  if (!aProxy) {
+    return new VideoDataDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
+  } else {
+    return new EMEVideoDataDecoder(aConfig,
+                                   aFormat,
+                                   aCallback,
+                                   aImageContainer,
+                                   aDrmStubId,
+                                   aProxy,
+                                   aTaskQueue);
+  }
 }
 
 MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
                                              const nsACString& aMimeType,
                                              MediaFormat::Param aFormat,
-                                             MediaDataDecoderCallback* aCallback)
+                                             MediaDataDecoderCallback* aCallback,
+                                             const nsString& aDrmStubId)
   : mType(aType)
   , mMimeType(aMimeType)
   , mFormat(aFormat)
   , mCallback(aCallback)
   , mInputBuffers(nullptr)
   , mOutputBuffers(nullptr)
   , mMonitor("MediaCodecDataDecoder::mMonitor")
   , mState(ModuleState::kDecoding)
+  , mDrmStubId(aDrmStubId)
 {
-
 }
 
 MediaCodecDataDecoder::~MediaCodecDataDecoder()
 {
   Shutdown();
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
@@ -268,18 +374,21 @@ MediaCodecDataDecoder::InitDecoder(Surfa
 {
   mDecoder = CreateDecoder(mMimeType);
   if (!mDecoder) {
     INVOKE_CALLBACK(Error,
                     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__));
     return NS_ERROR_FAILURE;
   }
 
+  MediaCrypto::LocalRef crypto = MediaDrmProxy::GetMediaCrypto(mDrmStubId);
+  bool hascrypto = !!crypto;
+  LOG("Has(%d) MediaCrypto (%s)", hascrypto, NS_ConvertUTF16toUTF8(mDrmStubId).get());
   nsresult rv;
-  NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, nullptr, 0), rv);
+  NS_ENSURE_SUCCESS(rv = mDecoder->Configure(mFormat, aSurface, crypto, 0), rv);
   NS_ENSURE_SUCCESS(rv = mDecoder->Start(), rv);
 
   NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv);
   NS_ENSURE_SUCCESS(rv = ResetOutputBuffers(), rv);
 
   nsCOMPtr<nsIRunnable> r = NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop);
   rv = NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread), r);
 
--- a/dom/media/platforms/android/MediaCodecDataDecoder.h
+++ b/dom/media/platforms/android/MediaCodecDataDecoder.h
@@ -18,22 +18,28 @@
 namespace mozilla {
 
 typedef std::deque<RefPtr<MediaRawData>> SampleQueue;
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
   static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
                                               java::sdk::MediaFormat::Param aFormat,
-                                              MediaDataDecoderCallback* aCallback);
+                                              MediaDataDecoderCallback* aCallback,
+                                              const nsString& aDrmStubId,
+                                              CDMProxy* aProxy,
+                                              TaskQueue* aTaskQueue);
 
   static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
                                               java::sdk::MediaFormat::Param aFormat,
                                               MediaDataDecoderCallback* aCallback,
-                                              layers::ImageContainer* aImageContainer);
+                                              layers::ImageContainer* aImageContainer,
+                                              const nsString& aDrmStubId,
+                                              CDMProxy* aProxy,
+                                              TaskQueue* aTaskQueue);
 
   virtual ~MediaCodecDataDecoder();
 
   RefPtr<MediaDataDecoder::InitPromise> Init() override;
   void Flush() override;
   void Drain() override;
   void Shutdown() override;
   void Input(MediaRawData* aSample) override;
@@ -53,17 +59,18 @@ protected:
     kShutdown
   };
 
   friend class AndroidDecoderModule;
 
   MediaCodecDataDecoder(MediaData::Type aType,
                         const nsACString& aMimeType,
                         java::sdk::MediaFormat::Param aFormat,
-                        MediaDataDecoderCallback* aCallback);
+                        MediaDataDecoderCallback* aCallback,
+                        const nsString& aDrmStubId);
 
   static const char* ModuleStateStr(ModuleState aState);
 
   virtual nsresult InitDecoder(java::sdk::Surface::Param aSurface);
 
   virtual nsresult Output(java::sdk::BufferInfo::Param aInfo, void* aBuffer,
       java::sdk::MediaFormat::Param aFormat, const media::TimeUnit& aDuration)
   {
@@ -114,13 +121,15 @@ protected:
   // Only these members are protected by mMonitor.
   Monitor mMonitor;
 
   ModuleState mState;
 
   SampleQueue mQueue;
   // Durations are stored in microseconds.
   std::deque<media::TimeUnit> mDurations;
+
+  nsString mDrmStubId;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -180,19 +180,20 @@ public:
 
   private:
     RemoteVideoDecoder* mDecoder;
   };
 
   RemoteVideoDecoder(const VideoInfo& aConfig,
                    MediaFormat::Param aFormat,
                    MediaDataDecoderCallback* aCallback,
-                   layers::ImageContainer* aImageContainer)
+                   layers::ImageContainer* aImageContainer,
+                   const nsString& aDrmStubId)
     : RemoteDataDecoder(MediaData::Type::VIDEO_DATA, aConfig.mMimeType,
-                        aFormat, aCallback)
+                        aFormat, aCallback, aDrmStubId)
     , mImageContainer(aImageContainer)
     , mConfig(aConfig)
   {
   }
 
   RefPtr<InitPromise> Init() override
   {
     mSurfaceTexture = AndroidSurfaceTexture::Create();
@@ -208,17 +209,20 @@ public:
 
     // Register native methods.
     JavaCallbacksSupport::Init();
 
     mJavaCallbacks = CodecProxy::NativeCallbacks::New();
     JavaCallbacksSupport::AttachNative(mJavaCallbacks,
                                        mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
 
-    mJavaDecoder = CodecProxy::Create(mFormat, mSurfaceTexture->JavaSurface(), mJavaCallbacks);
+    mJavaDecoder = CodecProxy::Create(mFormat,
+                                      mSurfaceTexture->JavaSurface(),
+                                      mJavaCallbacks,
+                                      mDrmStubId);
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
 
     mInputDurations.Clear();
 
     return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
   }
@@ -276,20 +280,21 @@ private:
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
   DurationQueue mInputDurations;
 };
 
 class RemoteAudioDecoder final : public RemoteDataDecoder
 {
 public:
   RemoteAudioDecoder(const AudioInfo& aConfig,
-                   MediaFormat::Param aFormat,
-                   MediaDataDecoderCallback* aCallback)
+                     MediaFormat::Param aFormat,
+                     MediaDataDecoderCallback* aCallback,
+                     const nsString& aDrmStubId)
     : RemoteDataDecoder(MediaData::Type::AUDIO_DATA, aConfig.mMimeType,
-                        aFormat, aCallback)
+                        aFormat, aCallback, aDrmStubId)
     , 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) {
@@ -306,17 +311,17 @@ public:
   {
     // Register native methods.
     JavaCallbacksSupport::Init();
 
     mJavaCallbacks = CodecProxy::NativeCallbacks::New();
     JavaCallbacksSupport::AttachNative(mJavaCallbacks,
                                        mozilla::MakeUnique<CallbacksSupport>(this, mCallback));
 
-    mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks);
+    mJavaDecoder = CodecProxy::Create(mFormat, nullptr, mJavaCallbacks, mDrmStubId);
     if (mJavaDecoder == nullptr) {
       return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
     }
 
     return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__);
   }
 
 private:
@@ -405,38 +410,42 @@ private:
   };
 
   const AudioInfo& mConfig;
 };
 
 MediaDataDecoder*
 RemoteDataDecoder::CreateAudioDecoder(const AudioInfo& aConfig,
                                           MediaFormat::Param aFormat,
-                                          MediaDataDecoderCallback* aCallback)
+                                          MediaDataDecoderCallback* aCallback,
+                                          const nsString& aDrmStubId)
 {
-  return new RemoteAudioDecoder(aConfig, aFormat, aCallback);
+  return new RemoteAudioDecoder(aConfig, aFormat, aCallback, aDrmStubId);
 }
 
 MediaDataDecoder*
 RemoteDataDecoder::CreateVideoDecoder(const VideoInfo& aConfig,
                                           MediaFormat::Param aFormat,
                                           MediaDataDecoderCallback* aCallback,
-                                          layers::ImageContainer* aImageContainer)
+                                          layers::ImageContainer* aImageContainer,
+                                          const nsString& aDrmStubId)
 {
-  return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer);
+  return new RemoteVideoDecoder(aConfig, aFormat, aCallback, aImageContainer, aDrmStubId);
 }
 
 RemoteDataDecoder::RemoteDataDecoder(MediaData::Type aType,
                                      const nsACString& aMimeType,
                                      MediaFormat::Param aFormat,
-                                     MediaDataDecoderCallback* aCallback)
+                                     MediaDataDecoderCallback* aCallback,
+                                     const nsString& aDrmStubId)
   : mType(aType)
   , mMimeType(aMimeType)
   , mFormat(aFormat)
   , mCallback(aCallback)
+  , mDrmStubId(aDrmStubId)
 {
 }
 
 void
 RemoteDataDecoder::Flush()
 {
   mJavaDecoder->Flush();
 }
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -17,46 +17,50 @@
 #include <deque>
 
 namespace mozilla {
 
 class RemoteDataDecoder : public MediaDataDecoder {
 public:
   static MediaDataDecoder* CreateAudioDecoder(const AudioInfo& aConfig,
                                               java::sdk::MediaFormat::Param aFormat,
-                                              MediaDataDecoderCallback* aCallback);
+                                              MediaDataDecoderCallback* aCallback,
+                                              const nsString& aDrmStubId);
 
   static MediaDataDecoder* CreateVideoDecoder(const VideoInfo& aConfig,
                                               java::sdk::MediaFormat::Param aFormat,
                                               MediaDataDecoderCallback* aCallback,
-                                              layers::ImageContainer* aImageContainer);
+                                              layers::ImageContainer* aImageContainer,
+                                              const nsString& aDrmStubId);
 
   virtual ~RemoteDataDecoder() {}
 
   void Flush() override;
   void Drain() override;
   void Shutdown() override;
   void 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);
+                    MediaDataDecoderCallback* aCallback,
+                    const nsString& aDrmStubId);
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
   java::sdk::MediaFormat::GlobalRef mFormat;
 
   MediaDataDecoderCallback* mCallback;
 
   java::CodecProxy::GlobalRef mJavaDecoder;
   java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
+  nsString mDrmStubId;
 };
 
 } // namespace mozilla
 
 #endif
--- a/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
+++ b/mobile/android/base/aidl/org/mozilla/gecko/media/ICodec.aidl
@@ -8,17 +8,17 @@ package org.mozilla.gecko.media;
 import android.os.Bundle;
 import android.view.Surface;
 import org.mozilla.gecko.media.FormatParam;
 import org.mozilla.gecko.media.ICodecCallbacks;
 import org.mozilla.gecko.media.Sample;
 
 interface ICodec {
     void setCallbacks(in ICodecCallbacks callbacks);
-    boolean configure(in FormatParam format, inout Surface surface, int flags);
+    boolean configure(in FormatParam format, inout Surface surface, int flags, in String drmStubId);
     oneway void start();
     oneway void stop();
     oneway void flush();
     oneway void release();
 
     Sample dequeueInput(int size);
     oneway void queueInput(in Sample sample);
 
--- a/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -1,15 +1,17 @@
 /* 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/. */
 
 package org.mozilla.gecko.media;
 
 import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CryptoInfo;
+import android.media.MediaCrypto;
 import android.media.MediaFormat;
 import android.os.Handler;
 import android.view.Surface;
 
 import java.nio.ByteBuffer;
 
 // A wrapper interface that mimics the new {@link android.media.MediaCodec}
 // asynchronous mode API in Lollipop.
@@ -17,18 +19,19 @@ public interface AsyncCodec {
     public interface Callbacks {
         void onInputBufferAvailable(AsyncCodec codec, int index);
         void onOutputBufferAvailable(AsyncCodec codec, int index, BufferInfo info);
         void onError(AsyncCodec codec, int error);
         void onOutputFormatChanged(AsyncCodec codec, MediaFormat format);
     }
 
     public abstract void setCallbacks(Callbacks callbacks, Handler handler);
-    public abstract void configure(MediaFormat format, Surface surface, int flags);
+    public abstract void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags);
     public abstract void start();
     public abstract void stop();
     public abstract void flush();
     public abstract void release();
     public abstract ByteBuffer getInputBuffer(int index);
     public abstract ByteBuffer getOutputBuffer(int index);
     public abstract void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
+    public abstract void queueSecureInputBuffer(int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
     public abstract void releaseOutputBuffer(int index, boolean render);
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
@@ -2,16 +2,17 @@
  * 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/. */
 
 package org.mozilla.gecko.media;
 
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
+import android.media.MediaCrypto;
 import android.media.MediaFormat;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
 import android.util.Log;
 import android.view.Surface;
 
 import java.io.IOException;
@@ -162,30 +163,36 @@ import java.util.concurrent.ConcurrentLi
 
         private void feedSampleToBuffer() {
             while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) {
                 int index = mAvailableInputBuffers.poll();
                 int len = 0;
                 Sample sample = mInputSamples.poll();
                 long pts = sample.info.presentationTimeUs;
                 int flags = sample.info.flags;
+                MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
                 if (!sample.isEOS() && sample.buffer != null) {
                     len = sample.info.size;
                     ByteBuffer buf = mCodec.getInputBuffer(index);
                     try {
                         sample.writeToByteBuffer(buf);
                         mCallbacks.onInputExhausted();
                     } catch (IOException e) {
                         e.printStackTrace();
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
                     mSamplePool.recycleInput(sample);
                 }
-                mCodec.queueInputBuffer(index, 0, len, pts, flags);
+
+                if (cryptoInfo != null) {
+                    mCodec.queueSecureInputBuffer(index, 0, cryptoInfo, pts, flags);
+                } else {
+                    mCodec.queueInputBuffer(index, 0, len, pts, flags);
+                }
             }
         }
 
         private synchronized void reset() {
             mInputSamples.clear();
             mAvailableInputBuffers.clear();
         }
    }
@@ -209,17 +216,20 @@ import java.util.concurrent.ConcurrentLi
         try {
             release();
         } catch (RemoteException e) {
             // Nowhere to report the error.
         }
     }
 
     @Override
-    public synchronized boolean configure(FormatParam format, Surface surface, int flags) throws RemoteException {
+    public synchronized boolean configure(FormatParam format,
+                                          Surface surface,
+                                          int flags,
+                                          String drmStubId) throws RemoteException {
         if (mCallbacks == null) {
             Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()");
             return false;
         }
 
         if (mCodec != null) {
             if (DEBUG) Log.d(LOGTAG, "release existing codec: " + mCodec);
             releaseCodec();
@@ -231,18 +241,25 @@ import java.util.concurrent.ConcurrentLi
         String codecName = getDecoderForFormat(fmt);
         if (codecName == null) {
             Log.e(LOGTAG, "FAIL: cannot find codec");
             return false;
         }
 
         try {
             AsyncCodec codec = AsyncCodecFactory.create(codecName);
+
+            MediaCrypto crypto = RemoteMediaDrmBridgeStub.getMediaCrypto(drmStubId);
+            if (DEBUG) {
+                boolean hasCrypto = crypto != null;
+                Log.d(LOGTAG, "configure mediacodec with crypto(" + hasCrypto + ") / Id :" + drmStubId);
+            }
+
             codec.setCallbacks(new Callbacks(mCallbacks), null);
-            codec.configure(fmt, surface, flags);
+            codec.configure(fmt, surface, crypto, flags);
             mCodec = codec;
             mInputProcessor = new InputProcessor();
             mSamplePool = new SamplePool(codecName);
             if (DEBUG) Log.d(LOGTAG, codec.toString() + " created");
             return true;
         } catch (Exception e) {
             if (DEBUG) Log.d(LOGTAG, "FAIL: cannot create codec -- " + codecName);
             e.printStackTrace();
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -23,16 +23,17 @@ import java.nio.ByteBuffer;
 public final class CodecProxy {
     private static final String LOGTAG = "GeckoRemoteCodecProxy";
     private static final boolean DEBUG = false;
 
     private ICodec mRemote;
     private FormatParam mFormat;
     private Surface mOutputSurface;
     private CallbacksForwarder mCallbacks;
+    private String mRemoteDrmStubId;
 
     public interface Callbacks {
         void onInputExhausted();
         void onOutputFormatChanged(MediaFormat format);
         void onOutput(Sample output);
         void onError(boolean fatal);
     }
 
@@ -77,34 +78,41 @@ public final class CodecProxy {
         }
 
         public void reportError(boolean fatal) {
             mCallbacks.onError(fatal);
         }
     }
 
     @WrapForJNI
-    public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
-        return RemoteManager.getInstance().createCodec(format, surface, callbacks);
+    public static CodecProxy create(MediaFormat format,
+                                    Surface surface,
+                                    Callbacks callbacks,
+                                    String drmStubId) {
+        return RemoteManager.getInstance().createCodec(format, surface, callbacks, drmStubId);
     }
 
-    public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
-        return new CodecProxy(format, surface, callbacks);
+    public static CodecProxy createCodecProxy(MediaFormat format,
+                                              Surface surface,
+                                              Callbacks callbacks,
+                                              String drmStubId) {
+        return new CodecProxy(format, surface, callbacks, drmStubId);
     }
 
-    private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
+    private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks, String drmStubId) {
         mFormat = new FormatParam(format);
         mOutputSurface = surface;
+        mRemoteDrmStubId = drmStubId;
         mCallbacks = new CallbacksForwarder(callbacks);
     }
 
     boolean init(ICodec remote) {
         try {
             remote.setCallbacks(mCallbacks);
-            remote.configure(mFormat, mOutputSurface, 0);
+            remote.configure(mFormat, mOutputSurface, 0, mRemoteDrmStubId);
             remote.start();
         } catch (RemoteException e) {
             e.printStackTrace();
             return false;
         }
 
         mRemote = remote;
         return true;
@@ -123,17 +131,16 @@ public final class CodecProxy {
     }
 
     @WrapForJNI
     public synchronized boolean input(ByteBuffer bytes, BufferInfo info, CryptoInfo cryptoInfo) {
         if (mRemote == null) {
             Log.e(LOGTAG, "cannot send input to an ended codec");
             return false;
         }
-
         try {
             Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ?
                     Sample.EOS : mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);
             mRemote.queueInput(sample);
             sample.dispose();
         } catch (IOException e) {
             e.printStackTrace();
             return false;
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -1,15 +1,16 @@
 /* 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/. */
 
 package org.mozilla.gecko.media;
 
 import android.media.MediaCodec;
+import android.media.MediaCrypto;
 import android.media.MediaFormat;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
 import android.view.Surface;
 
@@ -289,20 +290,20 @@ final class JellyBeanAsyncCodec implemen
             // This thread has no looper. Use poller thread.
             looper = mBufferPoller.getLooper();
         }
         mCallbackSender = new CallbackSender(looper, callbacks);
         if (DEBUG) Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender);
     }
 
     @Override
-    public void configure(MediaFormat format, Surface surface, int flags) {
+    public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
         assertCallbacks();
 
-        mCodec.configure(format, surface, null, flags);
+        mCodec.configure(format, surface, crypto, flags);
     }
 
     private void assertCallbacks() {
         if (mCallbackSender == null) {
             throw new IllegalStateException(LOGTAG + ": callback must be supplied with setCallbacks().");
         }
     }
 
@@ -332,16 +333,38 @@ final class JellyBeanAsyncCodec implemen
             return;
         }
 
         mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
         mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
     }
 
     @Override
+    public final void queueSecureInputBuffer(int index,
+                                             int offset,
+                                             MediaCodec.CryptoInfo cryptoInfo,
+                                             long presentationTimeUs,
+                                             int flags) {
+        assertCallbacks();
+
+        mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+
+        try {
+            mCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+            mCallbackSender.notifyError(ERROR_CODEC);
+            return;
+        }
+
+        mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
+        mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
+    }
+
+    @Override
     public final void releaseOutputBuffer(int index, boolean render) {
         assertCallbacks();
 
         mCodec.releaseOutputBuffer(index, render);
     }
 
     @Override
     public final ByteBuffer getInputBuffer(int index) {
--- a/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/RemoteManager.java
@@ -115,24 +115,25 @@ public final class RemoteManager impleme
         }
         mConnectionLatch = null;
 
         return ok;
     }
 
     public synchronized CodecProxy createCodec(MediaFormat format,
                                                Surface surface,
-                                               CodecProxy.Callbacks callbacks) {
+                                               CodecProxy.Callbacks callbacks,
+                                               String drmStubId) {
         if (mRemote == null) {
             if (DEBUG) Log.d(LOGTAG, "createCodec failed due to not initialize");
             return null;
         }
         try {
             ICodec remote = mRemote.createCodec();
-            CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks);
+            CodecProxy proxy = CodecProxy.createCodecProxy(format, surface, callbacks, drmStubId);
             if (proxy.init(remote)) {
                 mProxies.add(proxy);
                 return proxy;
             } else {
                 return null;
             }
         } catch (RemoteException e) {
             e.printStackTrace();
--- a/widget/android/bindings/MediaCodec-classes.txt
+++ b/widget/android/bindings/MediaCodec-classes.txt
@@ -1,5 +1,6 @@
 android.media.MediaCodec
 android.media.MediaCodec$BufferInfo
 android.media.MediaCodec$CryptoInfo
+android.media.MediaCrypto
 android.media.MediaDrm$KeyStatus
 android.media.MediaFormat
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -188,19 +188,19 @@ auto AudioFocusAgent::NotifyStoppedPlayi
 }
 
 const char CodecProxy::name[] =
         "org/mozilla/gecko/media/CodecProxy";
 
 constexpr char CodecProxy::Create_t::name[];
 constexpr char CodecProxy::Create_t::signature[];
 
-auto CodecProxy::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2) -> CodecProxy::LocalRef
+auto CodecProxy::Create(mozilla::jni::Object::Param a0, mozilla::jni::Object::Param a1, mozilla::jni::Object::Param a2, mozilla::jni::String::Param a3) -> CodecProxy::LocalRef
 {
-    return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2);
+    return mozilla::jni::Method<Create_t>::Call(CodecProxy::Context(), nullptr, a0, a1, a2, a3);
 }
 
 constexpr char CodecProxy::Flush_t::name[];
 constexpr char CodecProxy::Flush_t::signature[];
 
 auto CodecProxy::Flush() const -> bool
 {
     return mozilla::jni::Method<Flush_t>::Call(CodecProxy::mCtx, nullptr);
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -717,30 +717,31 @@ public:
 
     struct Create_t {
         typedef CodecProxy Owner;
         typedef CodecProxy::LocalRef ReturnType;
         typedef CodecProxy::Param SetterType;
         typedef mozilla::jni::Args<
                 mozilla::jni::Object::Param,
                 mozilla::jni::Object::Param,
-                mozilla::jni::Object::Param> Args;
+                mozilla::jni::Object::Param,
+                mozilla::jni::String::Param> Args;
         static constexpr char name[] = "create";
         static constexpr char signature[] =
-                "(Landroid/media/MediaFormat;Landroid/view/Surface;Lorg/mozilla/gecko/media/CodecProxy$Callbacks;)Lorg/mozilla/gecko/media/CodecProxy;";
+                "(Landroid/media/MediaFormat;Landroid/view/Surface;Lorg/mozilla/gecko/media/CodecProxy$Callbacks;Ljava/lang/String;)Lorg/mozilla/gecko/media/CodecProxy;";
         static const bool isStatic = true;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
-    static auto Create(mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::Object::Param) -> CodecProxy::LocalRef;
+    static auto Create(mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::Object::Param, mozilla::jni::String::Param) -> CodecProxy::LocalRef;
 
     struct Flush_t {
         typedef CodecProxy Owner;
         typedef bool ReturnType;
         typedef bool SetterType;
         typedef mozilla::jni::Args<> Args;
         static constexpr char name[] = "flush";
         static constexpr char signature[] =