Bug 1315850 - Initialize video decoder. r=jya draft
authorChris Pearce <cpearce@mozilla.com>
Thu, 09 Mar 2017 18:17:50 +1300
changeset 504172 b6d499cafef2d6a6558b0db703b60320dea67803
parent 504171 aa854e9d88965d7da60231d6f6a3912bf6ad2eeb
child 504173 b7f162515a1a32b2c344c11d0fa5c7004cec2e15
push id50748
push userbmo:cpearce@mozilla.com
push dateFri, 24 Mar 2017 01:10:17 +0000
reviewersjya
bugs1315850
milestone55.0a1
Bug 1315850 - Initialize video decoder. r=jya MozReview-Commit-ID: 559SP0ECldq
dom/media/gmp/ChromiumCDMChild.cpp
dom/media/gmp/ChromiumCDMChild.h
dom/media/gmp/ChromiumCDMParent.cpp
dom/media/gmp/ChromiumCDMParent.h
dom/media/gmp/PChromiumCDM.ipdl
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -13,85 +13,94 @@
 #include "base/time.h"
 
 namespace mozilla {
 namespace gmp {
 
 ChromiumCDMChild::ChromiumCDMChild(GMPContentChild* aPlugin)
   : mPlugin(aPlugin)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
 }
 
 void
 ChromiumCDMChild::Init(cdm::ContentDecryptionModule_8* aCDM)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   mCDM = aCDM;
   MOZ_ASSERT(mCDM);
 }
 
 void
 ChromiumCDMChild::TimerExpired(void* aContext)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
   if (mCDM) {
     mCDM->TimerExpired(aContext);
   }
 }
 
 cdm::Buffer*
 ChromiumCDMChild::Allocate(uint32_t aCapacity)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::Allocate(capacity=%" PRIu32 ")", aCapacity);
   return new WidevineBuffer(aCapacity);
 }
 
 void
 ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::SetTimer(delay=%" PRId64 ", context=0x%p)",
           aDelayMs,
           aContext);
   RefPtr<ChromiumCDMChild> self(this);
   SetTimerOnMainThread(NewGMPTask([self, aContext]() {
     self->TimerExpired(aContext);
   }), aDelayMs);
 }
 
 cdm::Time
 ChromiumCDMChild::GetCurrentWallTime()
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   return base::Time::Now().ToDoubleT();
 }
 
 void
 ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
                                              const char* aSessionId,
                                              uint32_t aSessionIdSize)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
           ", sid=%s)",
           aPromiseId,
           aSessionId);
   Unused << SendOnResolveNewSessionPromise(aPromiseId,
                                            nsCString(aSessionId, aSessionIdSize));
 }
 
 void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
   Unused << SendOnResolvePromise(aPromiseId);
 }
 
 void
 ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
                                   cdm::Error aError,
                                   uint32_t aSystemCode,
                                   const char* aErrorMessage,
                                   uint32_t aErrorMessageSize)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32 ", err=%" PRIu32
           " code=%" PRIu32 ", msg='%s')",
           aPromiseId,
           aError,
           aSystemCode,
           aErrorMessage);
   Unused << SendOnRejectPromise(aPromiseId,
                                 static_cast<uint32_t>(aError),
@@ -103,16 +112,17 @@ void
 ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
                                    uint32_t aSessionIdSize,
                                    cdm::MessageType aMessageType,
                                    const char* aMessage,
                                    uint32_t aMessageSize,
                                    const char* aLegacyDestinationUrl,
                                    uint32_t aLegacyDestinationUrlLength)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
           " size=%" PRIu32 ")",
           aSessionId,
           aMessageType,
           aMessageSize);
   nsTArray<uint8_t> message;
   message.AppendElements(aMessage, aMessageSize);
   Unused << SendOnSessionMessage(nsCString(aSessionId, aSessionIdSize),
@@ -142,16 +152,17 @@ ToString(const cdm::KeyInformation* aKey
 
 void
 ChromiumCDMChild::OnSessionKeysChange(const char *aSessionId,
                                       uint32_t aSessionIdSize,
                                       bool aHasAdditionalUsableKey,
                                       const cdm::KeyInformation* aKeysInfo,
                                       uint32_t aKeysInfoCount)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
           aSessionId,
           ToString(aKeysInfo, aKeysInfoCount).get());
 
   nsTArray<CDMKeyInformation> keys;
   keys.SetCapacity(aKeysInfoCount);
   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     const cdm::KeyInformation& key = aKeysInfo[i];
@@ -163,76 +174,88 @@ ChromiumCDMChild::OnSessionKeysChange(co
                                     keys);
 }
 
 void
 ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
                                      uint32_t aSessionIdSize,
                                      cdm::Time aNewExpiryTime)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
           aSessionId,
           aNewExpiryTime);
   Unused << SendOnExpirationChange(nsCString(aSessionId, aSessionIdSize),
                                    aNewExpiryTime);
 }
 
 void
 ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
                                   uint32_t aSessionIdSize)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
   Unused << SendOnSessionClosed(nsCString(aSessionId, aSessionIdSize));
 }
 
 void
 ChromiumCDMChild::OnLegacySessionError(const char* aSessionId,
                                        uint32_t aSessionIdLength,
                                        cdm::Error aError,
                                        uint32_t aSystemCode,
                                        const char* aErrorMessage,
                                        uint32_t aErrorMessageLength)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnLegacySessionError(sid=%s, error=%" PRIu32
           " msg='%s')",
           aSessionId,
           aError,
           aErrorMessage);
   Unused << SendOnLegacySessionError(
     nsCString(aSessionId, aSessionIdLength),
     static_cast<uint32_t>(aError),
     aSystemCode,
     nsCString(aErrorMessage, aErrorMessageLength));
 }
 
 cdm::FileIO*
 ChromiumCDMChild::CreateFileIO(cdm::FileIOClient * aClient)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::CreateFileIO()");
   return nullptr;
 }
 
+bool
+ChromiumCDMChild::IsOnMessageLoopThread()
+{
+  return mPlugin && mPlugin->GMPMessageLoop() == MessageLoop::current();
+}
+
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
                            const bool& aAllowPersistentState)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%d, persistentState=%d)",
           aAllowDistinctiveIdentifier,
           aAllowPersistentState);
   if (mCDM) {
     mCDM->Initialize(aAllowDistinctiveIdentifier, aAllowPersistentState);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
                                            nsTArray<uint8_t>&& aServerCert)
 
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvSetServerCertificate() certlen=%zu",
           aServerCert.Length());
   if (mCDM) {
     mCDM->SetServerCertificate(aPromiseId,
                                aServerCert.Elements(),
                                aServerCert.Length());
   }
   return IPC_OK();
@@ -240,16 +263,17 @@ ChromiumCDMChild::RecvSetServerCertifica
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvCreateSessionAndGenerateRequest(
   const uint32_t& aPromiseId,
   const uint32_t& aSessionType,
   const uint32_t& aInitDataType,
   nsTArray<uint8_t>&& aInitData)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvCreateSessionAndGenerateRequest("
           "pid=%" PRIu32 ", sessionType=%" PRIu32 ", initDataType=%" PRIu32
           ") initDataLen=%zu",
           aPromiseId,
           aSessionType,
           aInitDataType,
           aInitData.Length());
   MOZ_ASSERT(aSessionType <= cdm::SessionType::kPersistentKeyRelease);
@@ -264,16 +288,17 @@ ChromiumCDMChild::RecvCreateSessionAndGe
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvUpdateSession(const uint32_t& aPromiseId,
                                     const nsCString& aSessionId,
                                     nsTArray<uint8_t>&& aResponse)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
           ", sid=%s) responseLen=%zu",
           aPromiseId,
           aSessionId.get(),
           aResponse.Length());
   if (mCDM) {
     mCDM->UpdateSession(aPromiseId,
                         aSessionId.get(),
@@ -283,41 +308,44 @@ ChromiumCDMChild::RecvUpdateSession(cons
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvCloseSession(const uint32_t& aPromiseId,
                                    const nsCString& aSessionId)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvCloseSession(pid=%" PRIu32 ", sid=%s)",
           aPromiseId,
           aSessionId.get());
   if (mCDM) {
     mCDM->CloseSession(aPromiseId, aSessionId.get(), aSessionId.Length());
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvRemoveSession(const uint32_t& aPromiseId,
                                     const nsCString& aSessionId)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvRemoveSession(pid=%" PRIu32 ", sid=%s)",
           aPromiseId,
           aSessionId.get());
   if (mCDM) {
     mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
   }
   return IPC_OK();
 }
 
 void
 ChromiumCDMChild::DecryptFailed(uint32_t aId, cdm::Status aStatus)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   Unused << SendDecrypted(aId, aStatus, nsTArray<uint8_t>());
 }
 
 static void
 InitInputBuffer(const CDMInputBuffer& aBuffer,
                 nsTArray<cdm::SubsampleEntry>& aSubSamples,
                 cdm::InputBuffer& aInputBuffer)
 {
@@ -341,16 +369,17 @@ InitInputBuffer(const CDMInputBuffer& aB
   }
   aInputBuffer.timestamp = aBuffer.mTimestamp();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDecrypt(const uint32_t& aId,
                               const CDMInputBuffer& aBuffer)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDecrypt()");
   if (!mCDM) {
     GMP_LOG("ChromiumCDMChild::RecvDecrypt() no CDM");
     DecryptFailed(aId, cdm::kDecryptError);
     return IPC_OK();
   }
   if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
     GMP_LOG("ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
@@ -384,46 +413,76 @@ ChromiumCDMChild::RecvDecrypt(const uint
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvInitializeVideoDecoder(
   const CDMVideoDecoderConfig& aConfig)
 {
-  GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder()");
+  MOZ_ASSERT(IsOnMessageLoopThread());
+  MOZ_ASSERT(!mDecoderInitialized);
+  cdm::VideoDecoderConfig config;
+  config.codec =
+    static_cast<cdm::VideoDecoderConfig::VideoCodec>(aConfig.mCodec());
+  config.profile =
+    static_cast<cdm::VideoDecoderConfig::VideoCodecProfile>(aConfig.mProfile());
+  config.format = static_cast<cdm::VideoFormat>(aConfig.mFormat());
+  config.coded_size = { aConfig.mImageWidth(), aConfig.mImageHeight() };
+  nsTArray<uint8_t> extraData(aConfig.mExtraData());
+  config.extra_data = extraData.Elements();
+  config.extra_data_size = extraData.Length();
+  cdm::Status status = mCDM->InitializeVideoDecoder(config);
+  GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() status=%u", status);
+  Unused << SendOnDecoderInitDone(status);
+  mDecoderInitialized = status == cdm::kSuccess;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDeinitializeVideoDecoder()
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
+  MOZ_ASSERT(mDecoderInitialized);
+  if (mDecoderInitialized) {
+    mDecoderInitialized = false;
+    mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
+  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvResetVideoDecoder()
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvResetVideoDecoder()");
+  if (mDecoderInitialized) {
+    mCDM->ResetDecoder(cdm::kStreamTypeVideo);
+  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame()");
+  MOZ_ASSERT(mDecoderInitialized);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvDestroy()
 {
+  MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvDestroy()");
 
+  MOZ_ASSERT(!mDecoderInitialized);
+
   if (mCDM) {
     mCDM->Destroy();
     mCDM = nullptr;
   }
 
   Unused << Send__delete__(this);
 
   return IPC_OK();
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -69,16 +69,18 @@ public:
   void EnableOutputProtection(uint32_t aDesiredProtectionMask) override {}
   void QueryOutputProtectionStatus() override {}
   void OnDeferredInitializationDone(cdm::StreamType aStreamType,
                                     cdm::Status aDecoderStatus) override {}
   cdm::FileIO* CreateFileIO(cdm::FileIOClient* aClient) override;
 protected:
   ~ChromiumCDMChild() {}
 
+  bool IsOnMessageLoopThread();
+
   ipc::IPCResult RecvInit(const bool& aAllowDistinctiveIdentifier,
                           const bool& aAllowPersistentState) override;
   ipc::IPCResult RecvSetServerCertificate(
     const uint32_t& aPromiseId,
     nsTArray<uint8_t>&& aServerCert) override;
   ipc::IPCResult RecvCreateSessionAndGenerateRequest(
     const uint32_t& aPromiseId,
     const uint32_t& aSessionType,
@@ -100,14 +102,16 @@ protected:
   ipc::IPCResult RecvDecryptAndDecodeFrame(
     const CDMInputBuffer& aBuffer) override;
   ipc::IPCResult RecvDestroy() override;
 
   void DecryptFailed(uint32_t aId, cdm::Status aStatus);
 
   GMPContentChild* mPlugin = nullptr;
   cdm::ContentDecryptionModule_8* mCDM = nullptr;
+
+  bool mDecoderInitialized = false;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // ChromiumCDMChild_h_
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -463,10 +463,42 @@ ChromiumCDMParent::RecvShutdown()
 }
 
 void
 ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy);
 }
 
+RefPtr<MediaDataDecoder::InitPromise>
+ChromiumCDMParent::InitializeVideoDecoder(
+  const gmp::CDMVideoDecoderConfig& aConfig)
+{
+  if (!SendInitializeVideoDecoder(aConfig)) {
+    return MediaDataDecoder::InitPromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                  RESULT_DETAIL("Failed to send init video decoder to CDM")),
+      __func__);
+  }
+
+  return mInitVideoDecoderPromise.Ensure(__func__);
+}
+
+ipc::IPCResult
+ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus)
+{
+  GMP_LOG("ChromiumCDMParent::RecvOnDecoderInitDone(this=%p, status=%u)",
+          this,
+          aStatus);
+  if (aStatus == static_cast<uint32_t>(cdm::kSuccess)) {
+    mInitVideoDecoderPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
+  } else {
+    mInitVideoDecoderPromise.RejectIfExists(
+      MediaResult(
+        NS_ERROR_DOM_MEDIA_FATAL_ERR,
+        RESULT_DETAIL("CDM init decode failed with %" PRIu32, aStatus)),
+      __func__);
+  }
+  return IPC_OK();
+}
+
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -1,23 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 ChromiumCDMParent_h_
 #define ChromiumCDMParent_h_
 
+#include "DecryptJob.h"
 #include "GMPCrashHelper.h"
 #include "GMPCrashHelperHolder.h"
 #include "GMPMessageUtils.h"
 #include "mozilla/gmp/PChromiumCDMParent.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
-#include "DecryptJob.h"
+#include "PlatformDecoderModule.h"
 
 namespace mozilla {
 
 class MediaRawData;
 class ChromiumCDMProxy;
 
 namespace gmp {
 
@@ -54,16 +55,18 @@ public:
   void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
   void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
   RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
 
   // TODO: Add functions for clients to send data to CDM, and
   // a Close() function.
+  RefPtr<MediaDataDecoder::InitPromise> InitializeVideoDecoder(
+    const gmp::CDMVideoDecoderConfig& aConfig);
 
 protected:
   ~ChromiumCDMParent() {}
 
   ipc::IPCResult Recv__delete__() override;
   ipc::IPCResult RecvOnResolveNewSessionPromise(
     const uint32_t& aPromiseId,
     const nsCString& aSessionId) override;
@@ -84,16 +87,17 @@ protected:
   ipc::IPCResult RecvOnSessionClosed(const nsCString& aSessionId) override;
   ipc::IPCResult RecvOnLegacySessionError(const nsCString& aSessionId,
                                           const uint32_t& aError,
                                           const uint32_t& aSystemCode,
                                           const nsCString& aMessage) override;
   ipc::IPCResult RecvDecrypted(const uint32_t& aId,
                                const uint32_t& aStatus,
                                nsTArray<uint8_t>&& aData) override;
+  ipc::IPCResult RecvOnDecoderInitDone(const uint32_t& aStatus) override;
   ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
   ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
   ipc::IPCResult RecvShutdown() override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage);
@@ -103,14 +107,16 @@ protected:
   const uint32_t mPluginId;
   GMPContentParent* mContentParent;
   // Note: this pointer is a weak reference because otherwise it would cause
   // a cycle, as ChromiumCDMProxy has a strong reference to the
   // ChromiumCDMParent.
   ChromiumCDMProxy* mProxy = nullptr;
   nsDataHashtable<nsUint32HashKey, uint32_t> mPromiseToCreateSessionToken;
   nsTArray<RefPtr<DecryptJob>> mDecrypts;
+
+  MozPromiseHolder<MediaDataDecoder::InitPromise> mInitVideoDecoderPromise;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // ChromiumCDMParent_h_
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -76,16 +76,18 @@ parent:
   async OnLegacySessionError(nsCString aSessionId,
                              uint32_t aError,
                              uint32_t aSystemCode,
                              nsCString aMessage);
 
   // Return values of cdm::ContentDecryptionModule8::Decrypt
   async Decrypted(uint32_t aId, uint32_t aStatus, uint8_t[] aData);
 
+  async OnDecoderInitDone(uint32_t aStatus);
+
   // Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
   async Decoded(CDMVideoFrame aFrame);
   async DecodeFailed(uint32_t aStatus);
 
   async Shutdown();
 };
 
 } // namespace gmp
--- a/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
@@ -1,38 +1,95 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ChromiumCDMVideoDecoder.h"
+#include "ChromiumCDMProxy.h"
+#include "content_decryption_module.h"
 #include "GMPService.h"
 #include "GMPVideoDecoder.h"
+#include "MP4Decoder.h"
+#include "VPXDecoder.h"
 
 namespace mozilla {
 
 ChromiumCDMVideoDecoder::ChromiumCDMVideoDecoder(
   const GMPVideoDecoderParams& aParams,
   CDMProxy* aCDMProxy)
-  : mConfig(aParams.mConfig)
+  : mCDMParent(aCDMProxy->AsChromiumCDMProxy()->GetCDMParent())
+  , mConfig(aParams.mConfig)
   , mCrashHelper(aParams.mCrashHelper)
   , mGMPThread(GetGMPAbstractThread())
   , mImageContainer(aParams.mImageContainer)
 {
 }
 
 ChromiumCDMVideoDecoder::~ChromiumCDMVideoDecoder()
 {
 }
 
+static uint32_t
+ToCDMH264Profile(uint8_t aProfile)
+{
+  switch (aProfile) {
+    case 66:
+      return cdm::VideoDecoderConfig::kH264ProfileBaseline;
+    case 77:
+      return cdm::VideoDecoderConfig::kH264ProfileMain;
+    case 88:
+      return cdm::VideoDecoderConfig::kH264ProfileExtended;
+    case 100:
+      return cdm::VideoDecoderConfig::kH264ProfileHigh;
+    case 110:
+      return cdm::VideoDecoderConfig::kH264ProfileHigh10;
+    case 122:
+      return cdm::VideoDecoderConfig::kH264ProfileHigh422;
+    case 144:
+      return cdm::VideoDecoderConfig::kH264ProfileHigh444Predictive;
+  }
+  return cdm::VideoDecoderConfig::kUnknownVideoCodecProfile;
+}
+
 RefPtr<MediaDataDecoder::InitPromise>
 ChromiumCDMVideoDecoder::Init()
 {
-  return InitPromise::CreateAndResolve(TrackInfo::kUndefinedTrack, __func__);
+  if (!mCDMParent) {
+    // Must have failed to get the CDMParent from the ChromiumCDMProxy
+    // in our constructor; the MediaKeys must have shut down the CDM
+    // before we had a chance to start up the decoder.
+    return InitPromise::CreateAndReject(
+      NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+  }
+
+  gmp::CDMVideoDecoderConfig config;
+  if (MP4Decoder::IsH264(mConfig.mMimeType)) {
+    config.mCodec() = cdm::VideoDecoderConfig::kCodecH264;
+    config.mProfile() =
+      ToCDMH264Profile(mConfig.mExtraData->SafeElementAt(1, 0));
+    config.mExtraData() = *mConfig.mExtraData;
+  } else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
+    config.mCodec() = cdm::VideoDecoderConfig::kCodecVp8;
+    config.mProfile() = cdm::VideoDecoderConfig::kProfileNotNeeded;
+  } else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
+    config.mCodec() = cdm::VideoDecoderConfig::kCodecVp9;
+    config.mProfile() = cdm::VideoDecoderConfig::kProfileNotNeeded;
+  } else {
+    return MediaDataDecoder::InitPromise::CreateAndReject(
+      NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
+  }
+  config.mImageWidth() = mConfig.mImage.width;
+  config.mImageHeight() = mConfig.mImage.height;
+
+  RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
+  return InvokeAsync(mGMPThread, __func__, [cdm, config]() {
+    return cdm->InitializeVideoDecoder(config);
+  });
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 ChromiumCDMVideoDecoder::Decode(MediaRawData* aSample)
 {
   return DecodePromise::CreateAndReject(
     MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, RESULT_DETAIL("Unimplemented")),
     __func__);
--- a/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
+++ b/dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
@@ -3,16 +3,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/. */
 
 #ifndef ChromiumCDMVideoDecoder_h_
 #define ChromiumCDMVideoDecoder_h_
 
 #include "PlatformDecoderModule.h"
+#include "ChromiumCDMParent.h"
 
 namespace mozilla {
 
 class CDMProxy;
 struct GMPVideoDecoderParams;
 
 class ChromiumCDMVideoDecoder : public MediaDataDecoder
 {
@@ -28,16 +29,17 @@ public:
   const char* GetDescriptionName() const override
   {
     return "Chromium CDM video decoder";
   }
 
 private:
   ~ChromiumCDMVideoDecoder();
 
+  RefPtr<gmp::ChromiumCDMParent> mCDMParent;
   const VideoInfo mConfig;
   RefPtr<GMPCrashHelper> mCrashHelper;
   RefPtr<AbstractThread> mGMPThread;
   RefPtr<layers::ImageContainer> mImageContainer;
   MozPromiseHolder<InitPromise> mInitPromise;
 };
 
 } // mozilla