LCOV - code coverage report
Current view: top level - dom/media/gmp - ChromiumCDMParent.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 554 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 52 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "ChromiumCDMParent.h"
       7             : 
       8             : #include "ChromiumCDMProxy.h"
       9             : #include "content_decryption_module.h"
      10             : #include "GMPContentChild.h"
      11             : #include "GMPContentParent.h"
      12             : #include "GMPLog.h"
      13             : #include "GMPUtils.h"
      14             : #include "MediaPrefs.h"
      15             : #include "mozilla/dom/MediaKeyMessageEventBinding.h"
      16             : #include "mozilla/gmp/GMPTypes.h"
      17             : #include "mozilla/Telemetry.h"
      18             : #include "mozilla/Unused.h"
      19             : #include "mp4_demuxer/AnnexB.h"
      20             : #include "mp4_demuxer/H264.h"
      21             : 
      22             : namespace mozilla {
      23             : namespace gmp {
      24             : 
      25             : using namespace eme;
      26             : 
      27           0 : ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
      28           0 :                                      uint32_t aPluginId)
      29             :   : mPluginId(aPluginId)
      30             :   , mContentParent(aContentParent)
      31           0 :   , mVideoShmemLimit(MediaPrefs::EMEChromiumAPIVideoShmemCount())
      32             : {
      33           0 :   GMP_LOG(
      34             :     "ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
      35             :     this,
      36             :     aContentParent,
      37             :     aPluginId);
      38           0 : }
      39             : 
      40             : bool
      41           0 : ChromiumCDMParent::Init(ChromiumCDMProxy* aProxy,
      42             :                         bool aAllowDistinctiveIdentifier,
      43             :                         bool aAllowPersistentState)
      44             : {
      45           0 :   GMP_LOG("ChromiumCDMParent::Init(this=%p)", this);
      46           0 :   if (!aProxy) {
      47           0 :     return false;
      48             :   }
      49           0 :   mProxy = aProxy;
      50           0 :   return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
      51             : }
      52             : 
      53             : void
      54           0 : ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
      55             :                                  uint32_t aSessionType,
      56             :                                  uint32_t aInitDataType,
      57             :                                  uint32_t aPromiseId,
      58             :                                  const nsTArray<uint8_t>& aInitData)
      59             : {
      60           0 :   GMP_LOG("ChromiumCDMParent::CreateSession(this=%p)", this);
      61           0 :   if (mIsShutdown) {
      62           0 :     RejectPromise(aPromiseId,
      63             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
      64           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
      65           0 :     return;
      66             :   }
      67           0 :   if (!SendCreateSessionAndGenerateRequest(
      68             :         aPromiseId, aSessionType, aInitDataType, aInitData)) {
      69           0 :     RejectPromise(
      70             :       aPromiseId,
      71             :       NS_ERROR_DOM_INVALID_STATE_ERR,
      72           0 :       NS_LITERAL_CSTRING("Failed to send generateRequest to CDM process."));
      73           0 :     return;
      74             :   }
      75           0 :   mPromiseToCreateSessionToken.Put(aPromiseId, aCreateSessionToken);
      76             : }
      77             : 
      78             : void
      79           0 : ChromiumCDMParent::LoadSession(uint32_t aPromiseId,
      80             :                                uint32_t aSessionType,
      81             :                                nsString aSessionId)
      82             : {
      83           0 :   GMP_LOG("ChromiumCDMParent::LoadSession(this=%p, pid=%u, type=%u, sid=%s)",
      84             :           this,
      85             :           aPromiseId,
      86             :           aSessionType,
      87             :           NS_ConvertUTF16toUTF8(aSessionId).get());
      88           0 :   if (mIsShutdown) {
      89           0 :     RejectPromise(aPromiseId,
      90             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
      91           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
      92           0 :     return;
      93             :   }
      94           0 :   if (!SendLoadSession(
      95           0 :         aPromiseId, aSessionType, NS_ConvertUTF16toUTF8(aSessionId))) {
      96           0 :     RejectPromise(
      97             :       aPromiseId,
      98             :       NS_ERROR_DOM_INVALID_STATE_ERR,
      99           0 :       NS_LITERAL_CSTRING("Failed to send loadSession to CDM process."));
     100           0 :     return;
     101             :   }
     102             : }
     103             : 
     104             : void
     105           0 : ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId,
     106             :                                         const nsTArray<uint8_t>& aCert)
     107             : {
     108           0 :   GMP_LOG("ChromiumCDMParent::SetServerCertificate(this=%p)", this);
     109           0 :   if (mIsShutdown) {
     110           0 :     RejectPromise(aPromiseId,
     111             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
     112           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
     113           0 :     return;
     114             :   }
     115           0 :   if (!SendSetServerCertificate(aPromiseId, aCert)) {
     116           0 :     RejectPromise(
     117             :       aPromiseId,
     118             :       NS_ERROR_DOM_INVALID_STATE_ERR,
     119           0 :       NS_LITERAL_CSTRING("Failed to send setServerCertificate to CDM process"));
     120             :   }
     121             : }
     122             : 
     123             : void
     124           0 : ChromiumCDMParent::UpdateSession(const nsCString& aSessionId,
     125             :                                  uint32_t aPromiseId,
     126             :                                  const nsTArray<uint8_t>& aResponse)
     127             : {
     128           0 :   GMP_LOG("ChromiumCDMParent::UpdateSession(this=%p)", this);
     129           0 :   if (mIsShutdown) {
     130           0 :     RejectPromise(aPromiseId,
     131             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
     132           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
     133           0 :     return;
     134             :   }
     135           0 :   if (!SendUpdateSession(aPromiseId, aSessionId, aResponse)) {
     136           0 :     RejectPromise(
     137             :       aPromiseId,
     138             :       NS_ERROR_DOM_INVALID_STATE_ERR,
     139           0 :       NS_LITERAL_CSTRING("Failed to send updateSession to CDM process"));
     140             :   }
     141             : }
     142             : 
     143             : void
     144           0 : ChromiumCDMParent::CloseSession(const nsCString& aSessionId,
     145             :                                 uint32_t aPromiseId)
     146             : {
     147           0 :   GMP_LOG("ChromiumCDMParent::CloseSession(this=%p)", this);
     148           0 :   if (mIsShutdown) {
     149           0 :     RejectPromise(aPromiseId,
     150             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
     151           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
     152           0 :     return;
     153             :   }
     154           0 :   if (!SendCloseSession(aPromiseId, aSessionId)) {
     155           0 :     RejectPromise(
     156             :       aPromiseId,
     157             :       NS_ERROR_DOM_INVALID_STATE_ERR,
     158           0 :       NS_LITERAL_CSTRING("Failed to send closeSession to CDM process"));
     159             :   }
     160             : }
     161             : 
     162             : void
     163           0 : ChromiumCDMParent::RemoveSession(const nsCString& aSessionId,
     164             :                                  uint32_t aPromiseId)
     165             : {
     166           0 :   GMP_LOG("ChromiumCDMParent::RemoveSession(this=%p)", this);
     167           0 :   if (mIsShutdown) {
     168           0 :     RejectPromise(aPromiseId,
     169             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
     170           0 :                   NS_LITERAL_CSTRING("CDM is shutdown."));
     171           0 :     return;
     172             :   }
     173           0 :   if (!SendRemoveSession(aPromiseId, aSessionId)) {
     174           0 :     RejectPromise(
     175             :       aPromiseId,
     176             :       NS_ERROR_DOM_INVALID_STATE_ERR,
     177           0 :       NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
     178             :   }
     179             : }
     180             : 
     181             : bool
     182           0 : ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
     183             :                                       MediaRawData* aSample)
     184             : {
     185           0 :   const CryptoSample& crypto = aSample->mCrypto;
     186           0 :   if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
     187           0 :     GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
     188           0 :     return false;
     189             :   }
     190             : 
     191           0 :   Shmem shmem;
     192           0 :   if (!AllocShmem(aSample->Size(), Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
     193           0 :     return false;
     194             :   }
     195           0 :   memcpy(shmem.get<uint8_t>(), aSample->Data(), aSample->Size());
     196             : 
     197           0 :   aBuffer = gmp::CDMInputBuffer(shmem,
     198             :                                 crypto.mKeyId,
     199             :                                 crypto.mIV,
     200           0 :                                 aSample->mTime.ToMicroseconds(),
     201           0 :                                 aSample->mDuration.ToMicroseconds(),
     202             :                                 crypto.mPlainSizes,
     203             :                                 crypto.mEncryptedSizes,
     204           0 :                                 crypto.mValid);
     205           0 :   return true;
     206             : }
     207             : 
     208             : bool
     209           0 : ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes)
     210             : {
     211           0 :   Shmem shmem;
     212           0 :   if (!AllocShmem(aSizeInBytes, Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
     213           0 :     return false;
     214             :   }
     215           0 :   if (!SendGiveBuffer(shmem)) {
     216           0 :     DeallocShmem(shmem);
     217           0 :     return false;
     218             :   }
     219           0 :   return true;
     220             : }
     221             : 
     222             : RefPtr<DecryptPromise>
     223           0 : ChromiumCDMParent::Decrypt(MediaRawData* aSample)
     224             : {
     225           0 :   if (mIsShutdown) {
     226           0 :     return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
     227           0 :                                            __func__);
     228             :   }
     229           0 :   CDMInputBuffer buffer;
     230           0 :   if (!InitCDMInputBuffer(buffer, aSample)) {
     231           0 :     return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
     232           0 :                                            __func__);
     233             :   }
     234             :   // Send a buffer to the CDM to store the output. The CDM will either fill
     235             :   // it with the decrypted sample and return it, or deallocate it on failure.
     236           0 :   if (!SendBufferToCDM(aSample->Size())) {
     237           0 :     DeallocShmem(buffer.mData());
     238           0 :     return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
     239           0 :                                            __func__);
     240             :   }
     241             : 
     242           0 :   RefPtr<DecryptJob> job = new DecryptJob(aSample);
     243           0 :   if (!SendDecrypt(job->mId, buffer)) {
     244           0 :     GMP_LOG(
     245             :       "ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message",
     246             :       this);
     247           0 :     DeallocShmem(buffer.mData());
     248           0 :     return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
     249           0 :                                            __func__);
     250             :   }
     251           0 :   RefPtr<DecryptPromise> promise = job->Ensure();
     252           0 :   mDecrypts.AppendElement(job);
     253           0 :   return promise;
     254             : }
     255             : 
     256             : ipc::IPCResult
     257           0 : ChromiumCDMParent::Recv__delete__()
     258             : {
     259           0 :   MOZ_ASSERT(mIsShutdown);
     260           0 :   GMP_LOG("ChromiumCDMParent::Recv__delete__(this=%p)", this);
     261           0 :   if (mContentParent) {
     262           0 :     mContentParent->ChromiumCDMDestroyed(this);
     263           0 :     mContentParent = nullptr;
     264             :   }
     265           0 :   return IPC_OK();
     266             : }
     267             : 
     268             : ipc::IPCResult
     269           0 : ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
     270             :                                                   const nsCString& aSessionId)
     271             : {
     272           0 :   GMP_LOG("ChromiumCDMParent::RecvOnResolveNewSessionPromise(this=%p, pid=%u, "
     273             :           "sid=%s)",
     274             :           this,
     275             :           aPromiseId,
     276             :           aSessionId.get());
     277           0 :   if (!mProxy || mIsShutdown) {
     278           0 :     return IPC_OK();
     279             :   }
     280             : 
     281           0 :   Maybe<uint32_t> token = mPromiseToCreateSessionToken.GetAndRemove(aPromiseId);
     282           0 :   if (token.isNothing()) {
     283           0 :     RejectPromise(aPromiseId,
     284             :                   NS_ERROR_DOM_INVALID_STATE_ERR,
     285           0 :                   NS_LITERAL_CSTRING("Lost session token for new session."));
     286           0 :     return IPC_OK();
     287             :   }
     288             : 
     289             :   RefPtr<Runnable> task =
     290           0 :     NewRunnableMethod<uint32_t, nsString>("ChromiumCDMProxy::OnSetSessionId",
     291             :                                           mProxy,
     292             :                                           &ChromiumCDMProxy::OnSetSessionId,
     293           0 :                                           token.value(),
     294           0 :                                           NS_ConvertUTF8toUTF16(aSessionId));
     295           0 :   NS_DispatchToMainThread(task);
     296             : 
     297           0 :   ResolvePromise(aPromiseId);
     298             : 
     299           0 :   return IPC_OK();
     300             : }
     301             : 
     302             : ipc::IPCResult
     303           0 : ChromiumCDMParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
     304             :                                                  const bool& aSuccessful)
     305             : {
     306           0 :   GMP_LOG("ChromiumCDMParent::RecvResolveLoadSessionPromise(this=%p, pid=%u, "
     307             :           "successful=%d)",
     308             :           this,
     309             :           aPromiseId,
     310             :           aSuccessful);
     311           0 :   if (!mProxy || mIsShutdown) {
     312           0 :     return IPC_OK();
     313             :   }
     314             : 
     315           0 :   NS_DispatchToMainThread(NewRunnableMethod<uint32_t, bool>(
     316             :     "ChromiumCDMProxy::OnResolveLoadSessionPromise",
     317             :     mProxy,
     318             :     &ChromiumCDMProxy::OnResolveLoadSessionPromise,
     319             :     aPromiseId,
     320           0 :     aSuccessful));
     321           0 :   return IPC_OK();
     322             : }
     323             : void
     324           0 : ChromiumCDMParent::ResolvePromise(uint32_t aPromiseId)
     325             : {
     326           0 :   GMP_LOG(
     327             :     "ChromiumCDMParent::ResolvePromise(this=%p, pid=%u)", this, aPromiseId);
     328             : 
     329             :   // Note: The MediaKeys rejects all pending DOM promises when it
     330             :   // initiates shutdown.
     331           0 :   if (!mProxy || mIsShutdown) {
     332           0 :     return;
     333             :   }
     334           0 :   NS_DispatchToMainThread(
     335           0 :     NewRunnableMethod<uint32_t>("ChromiumCDMProxy::ResolvePromise",
     336             :                                 mProxy,
     337             :                                 &ChromiumCDMProxy::ResolvePromise,
     338           0 :                                 aPromiseId));
     339             : }
     340             : 
     341             : ipc::IPCResult
     342           0 : ChromiumCDMParent::RecvOnResolvePromise(const uint32_t& aPromiseId)
     343             : {
     344           0 :   ResolvePromise(aPromiseId);
     345           0 :   return IPC_OK();
     346             : }
     347             : 
     348             : static nsresult
     349           0 : ToNsresult(uint32_t aError)
     350             : {
     351           0 :   switch (static_cast<cdm::Error>(aError)) {
     352             :     case cdm::kNotSupportedError:
     353           0 :       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     354             :     case cdm::kInvalidStateError:
     355           0 :       return NS_ERROR_DOM_INVALID_STATE_ERR;
     356             :     case cdm::kInvalidAccessError:
     357             :       // Note: Chrome converts kInvalidAccessError to TypeError, since the
     358             :       // Chromium CDM API doesn't have a type error enum value. The EME spec
     359             :       // requires TypeError in some places, so we do the same conversion.
     360             :       // See bug 1313202.
     361           0 :       return NS_ERROR_DOM_TYPE_ERR;
     362             :     case cdm::kQuotaExceededError:
     363           0 :       return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
     364             :     case cdm::kUnknownError:
     365           0 :       return NS_ERROR_DOM_UNKNOWN_ERR; // Note: Unique placeholder.
     366             :     case cdm::kClientError:
     367           0 :       return NS_ERROR_DOM_ABORT_ERR; // Note: Unique placeholder.
     368             :     case cdm::kOutputError:
     369           0 :       return NS_ERROR_DOM_SECURITY_ERR; // Note: Unique placeholder.
     370             :   };
     371           0 :   MOZ_ASSERT_UNREACHABLE("Invalid cdm::Error enum value.");
     372             :   return NS_ERROR_DOM_TIMEOUT_ERR; // Note: Unique placeholder.
     373             : }
     374             : 
     375             : void
     376           0 : ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
     377             :                                  nsresult aError,
     378             :                                  const nsCString& aErrorMessage)
     379             : {
     380           0 :   GMP_LOG(
     381             :     "ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId);
     382             :   // Note: The MediaKeys rejects all pending DOM promises when it
     383             :   // initiates shutdown.
     384           0 :   if (!mProxy || mIsShutdown) {
     385           0 :     return;
     386             :   }
     387           0 :   NS_DispatchToMainThread(NewRunnableMethod<uint32_t, nsresult, nsCString>(
     388             :     "ChromiumCDMProxy::RejectPromise",
     389             :     mProxy,
     390             :     &ChromiumCDMProxy::RejectPromise,
     391             :     aPromiseId,
     392             :     aError,
     393           0 :     aErrorMessage));
     394             : }
     395             : 
     396             : ipc::IPCResult
     397           0 : ChromiumCDMParent::RecvOnRejectPromise(const uint32_t& aPromiseId,
     398             :                                        const uint32_t& aError,
     399             :                                        const uint32_t& aSystemCode,
     400             :                                        const nsCString& aErrorMessage)
     401             : {
     402           0 :   RejectPromise(aPromiseId, ToNsresult(aError), aErrorMessage);
     403           0 :   return IPC_OK();
     404             : }
     405             : 
     406             : static dom::MediaKeyMessageType
     407           0 : ToDOMMessageType(uint32_t aMessageType)
     408             : {
     409           0 :   switch (static_cast<cdm::MessageType>(aMessageType)) {
     410             :     case cdm::kLicenseRequest:
     411           0 :       return dom::MediaKeyMessageType::License_request;
     412             :     case cdm::kLicenseRenewal:
     413           0 :       return dom::MediaKeyMessageType::License_renewal;
     414             :     case cdm::kLicenseRelease:
     415           0 :       return dom::MediaKeyMessageType::License_release;
     416             :   }
     417           0 :   MOZ_ASSERT_UNREACHABLE("Invalid cdm::MessageType enum value.");
     418             :   return dom::MediaKeyMessageType::License_request;
     419             : }
     420             : 
     421             : ipc::IPCResult
     422           0 : ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId,
     423             :                                         const uint32_t& aMessageType,
     424             :                                         nsTArray<uint8_t>&& aMessage)
     425             : {
     426           0 :   GMP_LOG("ChromiumCDMParent::RecvOnSessionMessage(this=%p, sid=%s)",
     427             :           this,
     428             :           aSessionId.get());
     429           0 :   if (!mProxy || mIsShutdown) {
     430           0 :     return IPC_OK();
     431             :   }
     432           0 :   RefPtr<CDMProxy> proxy = mProxy;
     433           0 :   nsString sid = NS_ConvertUTF8toUTF16(aSessionId);
     434           0 :   dom::MediaKeyMessageType messageType = ToDOMMessageType(aMessageType);
     435           0 :   nsTArray<uint8_t> msg(Move(aMessage));
     436           0 :   NS_DispatchToMainThread(
     437           0 :     NS_NewRunnableFunction("gmp::ChromiumCDMParent::RecvOnSessionMessage",
     438           0 :                            [proxy, sid, messageType, msg]() mutable {
     439           0 :                              proxy->OnSessionMessage(sid, messageType, msg);
     440           0 :                            }));
     441           0 :   return IPC_OK();
     442             : }
     443             : 
     444             : static dom::MediaKeyStatus
     445           0 : ToDOMMediaKeyStatus(uint32_t aStatus)
     446             : {
     447           0 :   switch (static_cast<cdm::KeyStatus>(aStatus)) {
     448             :     case cdm::kUsable:
     449           0 :       return dom::MediaKeyStatus::Usable;
     450             :     case cdm::kInternalError:
     451           0 :       return dom::MediaKeyStatus::Internal_error;
     452             :     case cdm::kExpired:
     453           0 :       return dom::MediaKeyStatus::Expired;
     454             :     case cdm::kOutputRestricted:
     455           0 :       return dom::MediaKeyStatus::Output_restricted;
     456             :     case cdm::kOutputDownscaled:
     457           0 :       return dom::MediaKeyStatus::Output_downscaled;
     458             :     case cdm::kStatusPending:
     459           0 :       return dom::MediaKeyStatus::Status_pending;
     460             :     case cdm::kReleased:
     461           0 :       return dom::MediaKeyStatus::Released;
     462             :   }
     463           0 :   MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
     464             :   return dom::MediaKeyStatus::Internal_error;
     465             : }
     466             : 
     467             : ipc::IPCResult
     468           0 : ChromiumCDMParent::RecvOnSessionKeysChange(
     469             :   const nsCString& aSessionId,
     470             :   nsTArray<CDMKeyInformation>&& aKeysInfo)
     471             : {
     472           0 :   GMP_LOG("ChromiumCDMParent::RecvOnSessionKeysChange(this=%p)", this);
     473           0 :   if (!mProxy || mIsShutdown) {
     474           0 :     return IPC_OK();
     475             :   }
     476           0 :   bool keyStatusesChange = false;
     477             :   {
     478           0 :     CDMCaps::AutoLock caps(mProxy->Capabilites());
     479           0 :     for (size_t i = 0; i < aKeysInfo.Length(); i++) {
     480           0 :       keyStatusesChange |=
     481           0 :         caps.SetKeyStatus(aKeysInfo[i].mKeyId(),
     482           0 :                           NS_ConvertUTF8toUTF16(aSessionId),
     483           0 :                           dom::Optional<dom::MediaKeyStatus>(
     484           0 :                             ToDOMMediaKeyStatus(aKeysInfo[i].mStatus())));
     485             :     }
     486             :   }
     487           0 :   if (keyStatusesChange) {
     488           0 :     NS_DispatchToMainThread(
     489           0 :       NewRunnableMethod<nsString>("ChromiumCDMProxy::OnKeyStatusesChange",
     490             :                                   mProxy,
     491             :                                   &ChromiumCDMProxy::OnKeyStatusesChange,
     492           0 :                                   NS_ConvertUTF8toUTF16(aSessionId)));
     493             :   }
     494           0 :   return IPC_OK();
     495             : }
     496             : 
     497             : ipc::IPCResult
     498           0 : ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId,
     499             :                                           const double& aSecondsSinceEpoch)
     500             : {
     501           0 :   GMP_LOG("ChromiumCDMParent::RecvOnExpirationChange(this=%p) time=%lf",
     502             :           this,
     503             :           aSecondsSinceEpoch);
     504           0 :   if (!mProxy || mIsShutdown) {
     505           0 :     return IPC_OK();
     506             :   }
     507           0 :   NS_DispatchToMainThread(NewRunnableMethod<nsString, UnixTime>(
     508             :     "ChromiumCDMProxy::OnExpirationChange",
     509             :     mProxy,
     510             :     &ChromiumCDMProxy::OnExpirationChange,
     511           0 :     NS_ConvertUTF8toUTF16(aSessionId),
     512           0 :     GMPTimestamp(aSecondsSinceEpoch * 1000)));
     513           0 :   return IPC_OK();
     514             : }
     515             : 
     516             : ipc::IPCResult
     517           0 : ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId)
     518             : {
     519           0 :   GMP_LOG("ChromiumCDMParent::RecvOnSessionClosed(this=%p)", this);
     520           0 :   if (!mProxy || mIsShutdown) {
     521           0 :     return IPC_OK();
     522             :   }
     523           0 :   NS_DispatchToMainThread(
     524           0 :     NewRunnableMethod<nsString>("ChromiumCDMProxy::OnSessionClosed",
     525             :                                 mProxy,
     526             :                                 &ChromiumCDMProxy::OnSessionClosed,
     527           0 :                                 NS_ConvertUTF8toUTF16(aSessionId)));
     528           0 :   return IPC_OK();
     529             : }
     530             : 
     531             : ipc::IPCResult
     532           0 : ChromiumCDMParent::RecvOnLegacySessionError(const nsCString& aSessionId,
     533             :                                             const uint32_t& aError,
     534             :                                             const uint32_t& aSystemCode,
     535             :                                             const nsCString& aMessage)
     536             : {
     537           0 :   GMP_LOG("ChromiumCDMParent::RecvOnLegacySessionError(this=%p)", this);
     538           0 :   if (!mProxy || mIsShutdown) {
     539           0 :     return IPC_OK();
     540             :   }
     541           0 :   NS_DispatchToMainThread(
     542           0 :     NewRunnableMethod<nsString, nsresult, uint32_t, nsString>(
     543             :       "ChromiumCDMProxy::OnSessionError",
     544             :       mProxy,
     545             :       &ChromiumCDMProxy::OnSessionError,
     546           0 :       NS_ConvertUTF8toUTF16(aSessionId),
     547           0 :       ToNsresult(aError),
     548             :       aSystemCode,
     549           0 :       NS_ConvertUTF8toUTF16(aMessage)));
     550           0 :   return IPC_OK();
     551             : }
     552             : 
     553             : DecryptStatus
     554           0 : ToDecryptStatus(uint32_t aError)
     555             : {
     556           0 :   switch (static_cast<cdm::Status>(aError)) {
     557             :     case cdm::kSuccess:
     558           0 :       return DecryptStatus::Ok;
     559             :     case cdm::kNoKey:
     560           0 :       return DecryptStatus::NoKeyErr;
     561             :     default:
     562           0 :       return DecryptStatus::GenericErr;
     563             :   }
     564             : }
     565             : 
     566             : ipc::IPCResult
     567           0 : ChromiumCDMParent::RecvDecryptFailed(const uint32_t& aId,
     568             :                                      const uint32_t& aStatus)
     569             : {
     570           0 :   GMP_LOG("ChromiumCDMParent::RecvDecryptFailed(this=%p, id=%u, status=%u)",
     571             :           this,
     572             :           aId,
     573             :           aStatus);
     574             : 
     575           0 :   if (mIsShutdown) {
     576           0 :     MOZ_ASSERT(mDecrypts.IsEmpty());
     577           0 :     return IPC_OK();
     578             :   }
     579             : 
     580           0 :   for (size_t i = 0; i < mDecrypts.Length(); i++) {
     581           0 :     if (mDecrypts[i]->mId == aId) {
     582           0 :       mDecrypts[i]->PostResult(ToDecryptStatus(aStatus));
     583           0 :       mDecrypts.RemoveElementAt(i);
     584           0 :       break;
     585             :     }
     586             :   }
     587           0 :   return IPC_OK();
     588             : }
     589             : 
     590             : ipc::IPCResult
     591           0 : ChromiumCDMParent::RecvDecrypted(const uint32_t& aId,
     592             :                                  const uint32_t& aStatus,
     593             :                                  ipc::Shmem&& aShmem)
     594             : {
     595           0 :   GMP_LOG("ChromiumCDMParent::RecvDecrypted(this=%p, id=%u, status=%u)",
     596             :           this,
     597             :           aId,
     598             :           aStatus);
     599             : 
     600             :   // We must deallocate the shmem once we've copied the result out of it
     601             :   // in PostResult below.
     602           0 :   auto autoDeallocateShmem = MakeScopeExit([&, this] { DeallocShmem(aShmem); });
     603             : 
     604           0 :   if (mIsShutdown) {
     605           0 :     MOZ_ASSERT(mDecrypts.IsEmpty());
     606           0 :     return IPC_OK();
     607             :   }
     608           0 :   for (size_t i = 0; i < mDecrypts.Length(); i++) {
     609           0 :     if (mDecrypts[i]->mId == aId) {
     610           0 :       mDecrypts[i]->PostResult(
     611             :         ToDecryptStatus(aStatus),
     612           0 :         MakeSpan<const uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()));
     613           0 :       mDecrypts.RemoveElementAt(i);
     614           0 :       break;
     615             :     }
     616             :   }
     617           0 :   return IPC_OK();
     618             : }
     619             : 
     620             : bool
     621           0 : ChromiumCDMParent::PurgeShmems()
     622             : {
     623           0 :   GMP_LOG("ChromiumCDMParent::PurgeShmems(this=%p) frame_size=%" PRIuSIZE
     624             :           " limit=%" PRIu32 " active=%" PRIu32,
     625             :           this,
     626             :           mVideoFrameBufferSize,
     627             :           mVideoShmemLimit,
     628             :           mVideoShmemsActive);
     629             : 
     630           0 :   if (mVideoShmemsActive == 0) {
     631             :     // We haven't allocated any shmems, nothing to do here.
     632           0 :     return true;
     633             :   }
     634           0 :   if (!SendPurgeShmems()) {
     635           0 :     return false;
     636             :   }
     637           0 :   mVideoShmemsActive = 0;
     638           0 :   return true;
     639             : }
     640             : 
     641             : bool
     642           0 : ChromiumCDMParent::EnsureSufficientShmems(size_t aVideoFrameSize)
     643             : {
     644           0 :   GMP_LOG("ChromiumCDMParent::EnsureSufficientShmems(this=%p) "
     645             :           "size=%" PRIuSIZE " expected_size=%" PRIuSIZE " limit=%" PRIu32
     646             :           " active=%" PRIu32,
     647             :           this,
     648             :           aVideoFrameSize,
     649             :           mVideoFrameBufferSize,
     650             :           mVideoShmemLimit,
     651             :           mVideoShmemsActive);
     652             : 
     653             :   // The Chromium CDM API requires us to implement a synchronous
     654             :   // interface to supply buffers to the CDM for it to write decrypted samples
     655             :   // into. We want our buffers to be backed by shmems, in order to reduce
     656             :   // the overhead of transferring decoded frames. However due to sandboxing
     657             :   // restrictions, the CDM process cannot allocate shmems itself.
     658             :   // We don't want to be doing synchronous IPC to request shmems from the
     659             :   // content process, nor do we want to have to do intr IPC or make async
     660             :   // IPC conform to the sync allocation interface. So instead we have the
     661             :   // content process pre-allocate a set of shmems and give them to the CDM
     662             :   // process in advance of them being needed.
     663             :   //
     664             :   // When the CDM needs to allocate a buffer for storing a decoded video
     665             :   // frame, the CDM host gives it one of these shmems' buffers. When this
     666             :   // is sent back to the content process, we upload it to a GPU surface,
     667             :   // and send the shmem back to the CDM process so it can reuse it.
     668             :   //
     669             :   // Normally the CDM won't allocate more than one buffer at once, but
     670             :   // we've seen cases where it allocates multiple buffers, returns one and
     671             :   // holds onto the rest. So we need to ensure we have several extra
     672             :   // shmems pre-allocated for the CDM. This threshold is set by the pref
     673             :   // media.eme.chromium-api.video-shmems.
     674             :   //
     675             :   // We also have a failure recovery mechanism; if the CDM asks for more
     676             :   // buffers than we have shmem's available, ChromiumCDMChild gives the
     677             :   // CDM a non-shared memory buffer, and returns the frame to the parent
     678             :   // in an nsTArray<uint8_t> instead of a shmem. Every time this happens,
     679             :   // the parent sends an extra shmem to the CDM process for it to add to the
     680             :   // set of shmems with which to return output. Via this mechanism we should
     681             :   // recover from incorrectly predicting how many shmems to pre-allocate.
     682             :   //
     683             :   // At decoder start up, we guess how big the shmems need to be based on
     684             :   // the video frame dimensions. If we guess wrong, the CDM will follow
     685             :   // the non-shmem path, and we'll re-create the shmems of the correct size.
     686             :   // This meanns we can recover from guessing the shmem size wrong.
     687             :   // We must re-take this path after every decoder de-init/re-init, as the
     688             :   // frame sizes should change every time we switch video stream.
     689             : 
     690           0 :   if (mVideoFrameBufferSize < aVideoFrameSize) {
     691           0 :     if (!PurgeShmems()) {
     692           0 :       return false;
     693             :     }
     694           0 :     mVideoFrameBufferSize = aVideoFrameSize;
     695             :   } else {
     696             :     // Put an upper limit on the number of shmems we tolerate the CDM asking
     697             :     // for, to prevent a memory blow-out. In practice, we expect the CDM to
     698             :     // need less than 5, but some encodings require more.
     699             :     // We'd expect CDMs to not have video frames larger than 720p-1080p
     700             :     // (due to DRM robustness requirements), which is about 1.5MB-3MB per
     701             :     // frame.
     702           0 :     if (mVideoShmemLimit > 50) {
     703           0 :       return false;
     704             :     }
     705           0 :     mVideoShmemLimit++;
     706             :   }
     707             : 
     708           0 :   while (mVideoShmemsActive < mVideoShmemLimit) {
     709           0 :     if (!SendBufferToCDM(mVideoFrameBufferSize)) {
     710           0 :       return false;
     711             :     }
     712           0 :     mVideoShmemsActive++;
     713             :   }
     714             : 
     715           0 :   mMaxVideoShmemsActive =
     716           0 :     std::max(mMaxVideoShmemsActive, mVideoShmemsActive);
     717             : 
     718           0 :   return true;
     719             : }
     720             : 
     721             : ipc::IPCResult
     722           0 : ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
     723             :                                    nsTArray<uint8_t>&& aData)
     724             : {
     725           0 :   GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p)", this);
     726             : 
     727           0 :   if (mIsShutdown || mDecodePromise.IsEmpty()) {
     728           0 :     return IPC_OK();
     729             :   }
     730             : 
     731           0 :   if (!EnsureSufficientShmems(aData.Length())) {
     732           0 :     mDecodePromise.RejectIfExists(
     733           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     734           0 :                   RESULT_DETAIL("Failled to ensure CDM has enough shmems.")),
     735           0 :       __func__);
     736           0 :     return IPC_OK();
     737             :   }
     738             : 
     739           0 :   RefPtr<VideoData> v = CreateVideoFrame(aFrame, aData);
     740           0 :   if (!v) {
     741           0 :     mDecodePromise.RejectIfExists(
     742           0 :       MediaResult(NS_ERROR_OUT_OF_MEMORY,
     743           0 :                   RESULT_DETAIL("Can't create VideoData")),
     744           0 :       __func__);
     745           0 :     return IPC_OK();
     746             :   }
     747             : 
     748           0 :   ReorderAndReturnOutput(Move(v));
     749             : 
     750           0 :   return IPC_OK();
     751             : }
     752             : 
     753             : ipc::IPCResult
     754           0 : ChromiumCDMParent::RecvDecodedShmem(const CDMVideoFrame& aFrame,
     755             :                                     ipc::Shmem&& aShmem)
     756             : {
     757           0 :   GMP_LOG("ChromiumCDMParent::RecvDecodedShmem(this=%p) time=%" PRId64
     758             :           " duration=%" PRId64,
     759             :           this,
     760             :           aFrame.mTimestamp(),
     761             :           aFrame.mDuration());
     762             : 
     763             :   // On failure we need to deallocate the shmem we're to return to the
     764             :   // CDM. On success we return it to the CDM to be reused.
     765             :   auto autoDeallocateShmem =
     766           0 :     MakeScopeExit([&, this] { this->DeallocShmem(aShmem); });
     767             : 
     768           0 :   if (mIsShutdown || mDecodePromise.IsEmpty()) {
     769           0 :     return IPC_OK();
     770             :   }
     771             : 
     772           0 :   RefPtr<VideoData> v = CreateVideoFrame(
     773           0 :     aFrame, MakeSpan<uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()));
     774           0 :   if (!v) {
     775           0 :     mDecodePromise.RejectIfExists(
     776           0 :       MediaResult(NS_ERROR_OUT_OF_MEMORY,
     777           0 :                   RESULT_DETAIL("Can't create VideoData")),
     778           0 :       __func__);
     779           0 :     return IPC_OK();
     780             :   }
     781             : 
     782             :   // Return the shmem to the CDM so the shmem can be reused to send us
     783             :   // another frame.
     784           0 :   if (!SendGiveBuffer(aShmem)) {
     785           0 :     mDecodePromise.RejectIfExists(
     786           0 :       MediaResult(NS_ERROR_OUT_OF_MEMORY,
     787           0 :                   RESULT_DETAIL("Can't return shmem to CDM process")),
     788           0 :       __func__);
     789           0 :     return IPC_OK();
     790             :   }
     791             : 
     792             :   // Don't need to deallocate the shmem since the CDM process is responsible
     793             :   // for it again.
     794           0 :   autoDeallocateShmem.release();
     795             : 
     796           0 :   ReorderAndReturnOutput(Move(v));
     797             : 
     798           0 :   return IPC_OK();
     799             : }
     800             : 
     801             : void
     802           0 : ChromiumCDMParent::ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame)
     803             : {
     804           0 :   if (mMaxRefFrames == 0) {
     805           0 :     mDecodePromise.ResolveIfExists({ Move(aFrame) }, __func__);
     806           0 :     return;
     807             :   }
     808           0 :   mReorderQueue.Push(Move(aFrame));
     809           0 :   MediaDataDecoder::DecodedData results;
     810           0 :   while (mReorderQueue.Length() > mMaxRefFrames) {
     811           0 :     results.AppendElement(mReorderQueue.Pop());
     812             :   }
     813           0 :   mDecodePromise.Resolve(Move(results), __func__);
     814             : }
     815             : 
     816             : already_AddRefed<VideoData>
     817           0 : ChromiumCDMParent::CreateVideoFrame(const CDMVideoFrame& aFrame,
     818             :                                     Span<uint8_t> aData)
     819             : {
     820           0 :   VideoData::YCbCrBuffer b;
     821           0 :   MOZ_ASSERT(aData.Length() > 0);
     822             : 
     823           0 :   b.mPlanes[0].mData = aData.Elements();
     824           0 :   b.mPlanes[0].mWidth = aFrame.mImageWidth();
     825           0 :   b.mPlanes[0].mHeight = aFrame.mImageHeight();
     826           0 :   b.mPlanes[0].mStride = aFrame.mYPlane().mStride();
     827           0 :   b.mPlanes[0].mOffset = aFrame.mYPlane().mPlaneOffset();
     828           0 :   b.mPlanes[0].mSkip = 0;
     829             : 
     830           0 :   b.mPlanes[1].mData = aData.Elements();
     831           0 :   b.mPlanes[1].mWidth = (aFrame.mImageWidth() + 1) / 2;
     832           0 :   b.mPlanes[1].mHeight = (aFrame.mImageHeight() + 1) / 2;
     833           0 :   b.mPlanes[1].mStride = aFrame.mUPlane().mStride();
     834           0 :   b.mPlanes[1].mOffset = aFrame.mUPlane().mPlaneOffset();
     835           0 :   b.mPlanes[1].mSkip = 0;
     836             : 
     837           0 :   b.mPlanes[2].mData = aData.Elements();
     838           0 :   b.mPlanes[2].mWidth = (aFrame.mImageWidth() + 1) / 2;
     839           0 :   b.mPlanes[2].mHeight = (aFrame.mImageHeight() + 1) / 2;
     840           0 :   b.mPlanes[2].mStride = aFrame.mVPlane().mStride();
     841           0 :   b.mPlanes[2].mOffset = aFrame.mVPlane().mPlaneOffset();
     842           0 :   b.mPlanes[2].mSkip = 0;
     843             : 
     844           0 :   gfx::IntRect pictureRegion(0, 0, aFrame.mImageWidth(), aFrame.mImageHeight());
     845           0 :   RefPtr<VideoData> v = VideoData::CreateAndCopyData(
     846             :     mVideoInfo,
     847             :     mImageContainer,
     848           0 :     mLastStreamOffset,
     849           0 :     media::TimeUnit::FromMicroseconds(aFrame.mTimestamp()),
     850           0 :     media::TimeUnit::FromMicroseconds(aFrame.mDuration()),
     851             :     b,
     852             :     false,
     853           0 :     media::TimeUnit::FromMicroseconds(-1),
     854           0 :     pictureRegion);
     855             : 
     856           0 :   return v.forget();
     857             : }
     858             : 
     859             : ipc::IPCResult
     860           0 : ChromiumCDMParent::RecvDecodeFailed(const uint32_t& aStatus)
     861             : {
     862           0 :   if (mIsShutdown) {
     863           0 :     MOZ_ASSERT(mDecodePromise.IsEmpty());
     864           0 :     return IPC_OK();
     865             :   }
     866             : 
     867           0 :   if (aStatus == cdm::kNeedMoreData) {
     868           0 :     mDecodePromise.ResolveIfExists(nsTArray<RefPtr<MediaData>>(), __func__);
     869           0 :     return IPC_OK();
     870             :   }
     871             : 
     872           0 :   mDecodePromise.RejectIfExists(
     873           0 :     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     874           0 :                 RESULT_DETAIL("ChromiumCDMParent::RecvDecodeFailed")),
     875           0 :     __func__);
     876           0 :   return IPC_OK();
     877             : }
     878             : 
     879             : ipc::IPCResult
     880           0 : ChromiumCDMParent::RecvShutdown()
     881             : {
     882           0 :   GMP_LOG("ChromiumCDMParent::RecvShutdown(this=%p)", this);
     883           0 :   Shutdown();
     884           0 :   return IPC_OK();
     885             : }
     886             : 
     887             : void
     888           0 : ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy)
     889             : {
     890           0 :   GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy);
     891           0 :   MOZ_ASSERT(!mActorDestroyed);
     892           0 :   mActorDestroyed = true;
     893             :   // Shutdown() will clear mProxy, so let's keep a reference for later use.
     894           0 :   RefPtr<ChromiumCDMProxy> proxy = mProxy;
     895           0 :   if (!mIsShutdown) {
     896             :     // Plugin crash.
     897           0 :     MOZ_ASSERT(aWhy == AbnormalShutdown);
     898           0 :     Shutdown();
     899             :   }
     900           0 :   MOZ_ASSERT(mIsShutdown);
     901           0 :   RefPtr<ChromiumCDMParent> kungFuDeathGrip(this);
     902           0 :   if (mContentParent) {
     903           0 :     mContentParent->ChromiumCDMDestroyed(this);
     904           0 :     mContentParent = nullptr;
     905             :   }
     906           0 :   bool abnormalShutdown = (aWhy == AbnormalShutdown);
     907           0 :   if (abnormalShutdown && proxy) {
     908           0 :     RefPtr<Runnable> task = NewRunnableMethod(
     909           0 :       "ChromiumCDMProxy::Terminated", proxy, &ChromiumCDMProxy::Terminated);
     910           0 :     NS_DispatchToMainThread(task);
     911             :   }
     912           0 :   MaybeDisconnect(abnormalShutdown);
     913           0 : }
     914             : 
     915             : RefPtr<MediaDataDecoder::InitPromise>
     916           0 : ChromiumCDMParent::InitializeVideoDecoder(
     917             :   const gmp::CDMVideoDecoderConfig& aConfig,
     918             :   const VideoInfo& aInfo,
     919             :   RefPtr<layers::ImageContainer> aImageContainer)
     920             : {
     921           0 :   if (mIsShutdown) {
     922             :     return MediaDataDecoder::InitPromise::CreateAndReject(
     923           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     924           0 :                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
     925           0 :       __func__);
     926             :   }
     927             : 
     928             :   const size_t bufferSize =
     929           0 :     I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
     930           0 :   if (bufferSize <= 0) {
     931             :     return MediaDataDecoder::InitPromise::CreateAndReject(
     932           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     933           0 :                   RESULT_DETAIL("Video frame buffer size is invalid.")),
     934           0 :       __func__);
     935             :   }
     936             : 
     937           0 :   if (!EnsureSufficientShmems(bufferSize)) {
     938             :     return MediaDataDecoder::InitPromise::CreateAndReject(
     939           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     940           0 :                   RESULT_DETAIL("Failed to init shmems for video decoder")),
     941           0 :       __func__);
     942             :   }
     943             : 
     944           0 :   if (!SendInitializeVideoDecoder(aConfig)) {
     945             :     return MediaDataDecoder::InitPromise::CreateAndReject(
     946           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     947           0 :                   RESULT_DETAIL("Failed to send init video decoder to CDM")),
     948           0 :       __func__);
     949             :   }
     950             : 
     951           0 :   mMaxRefFrames =
     952           0 :     (aConfig.mCodec() == cdm::VideoDecoderConfig::kCodecH264)
     953           0 :       ? mp4_demuxer::H264::HasSPS(aInfo.mExtraData)
     954           0 :           ? mp4_demuxer::H264::ComputeMaxRefFrames(aInfo.mExtraData)
     955             :           : 16
     956             :       : 0;
     957             : 
     958           0 :   mVideoDecoderInitialized = true;
     959           0 :   mImageContainer = aImageContainer;
     960           0 :   mVideoInfo = aInfo;
     961           0 :   mVideoFrameBufferSize = bufferSize;
     962             : 
     963           0 :   return mInitVideoDecoderPromise.Ensure(__func__);
     964             : }
     965             : 
     966             : ipc::IPCResult
     967           0 : ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus)
     968             : {
     969           0 :   GMP_LOG("ChromiumCDMParent::RecvOnDecoderInitDone(this=%p, status=%u)",
     970             :           this,
     971             :           aStatus);
     972           0 :   if (mIsShutdown) {
     973           0 :     MOZ_ASSERT(mInitVideoDecoderPromise.IsEmpty());
     974           0 :     return IPC_OK();
     975             :   }
     976           0 :   if (aStatus == static_cast<uint32_t>(cdm::kSuccess)) {
     977           0 :     mInitVideoDecoderPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
     978             :   } else {
     979           0 :     mVideoDecoderInitialized = false;
     980           0 :     mInitVideoDecoderPromise.RejectIfExists(
     981           0 :       MediaResult(
     982             :         NS_ERROR_DOM_MEDIA_FATAL_ERR,
     983           0 :         RESULT_DETAIL("CDM init decode failed with %" PRIu32, aStatus)),
     984           0 :       __func__);
     985             :   }
     986           0 :   return IPC_OK();
     987             : }
     988             : 
     989             : RefPtr<MediaDataDecoder::DecodePromise>
     990           0 : ChromiumCDMParent::DecryptAndDecodeFrame(MediaRawData* aSample)
     991             : {
     992           0 :   if (mIsShutdown) {
     993             :     return MediaDataDecoder::DecodePromise::CreateAndReject(
     994           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
     995           0 :                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
     996           0 :       __func__);
     997             :   }
     998             : 
     999           0 :   GMP_LOG("ChromiumCDMParent::DecryptAndDecodeFrame t=%" PRId64,
    1000             :           aSample->mTime.ToMicroseconds());
    1001             : 
    1002           0 :   CDMInputBuffer buffer;
    1003             : 
    1004           0 :   if (!InitCDMInputBuffer(buffer, aSample)) {
    1005             :     return MediaDataDecoder::DecodePromise::CreateAndReject(
    1006           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to init CDM buffer."),
    1007           0 :       __func__);
    1008             :   }
    1009             : 
    1010           0 :   mLastStreamOffset = aSample->mOffset;
    1011             : 
    1012           0 :   if (!SendDecryptAndDecodeFrame(buffer)) {
    1013           0 :     GMP_LOG(
    1014             :       "ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message.",
    1015             :       this);
    1016           0 :     DeallocShmem(buffer.mData());
    1017             :     return MediaDataDecoder::DecodePromise::CreateAndReject(
    1018           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1019             :                   "Failed to send decrypt to CDM process."),
    1020           0 :       __func__);
    1021             :   }
    1022             : 
    1023           0 :   return mDecodePromise.Ensure(__func__);
    1024             : }
    1025             : 
    1026             : RefPtr<MediaDataDecoder::FlushPromise>
    1027           0 : ChromiumCDMParent::FlushVideoDecoder()
    1028             : {
    1029           0 :   if (mIsShutdown) {
    1030           0 :     MOZ_ASSERT(mReorderQueue.IsEmpty());
    1031             :     return MediaDataDecoder::FlushPromise::CreateAndReject(
    1032           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1033           0 :                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
    1034           0 :       __func__);
    1035             :   }
    1036             : 
    1037           0 :   mReorderQueue.Clear();
    1038             : 
    1039           0 :   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    1040           0 :   if (!SendResetVideoDecoder()) {
    1041             :     return MediaDataDecoder::FlushPromise::CreateAndReject(
    1042           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to send flush to CDM."),
    1043           0 :       __func__);
    1044             :   }
    1045           0 :   return mFlushDecoderPromise.Ensure(__func__);
    1046             : }
    1047             : 
    1048             : ipc::IPCResult
    1049           0 : ChromiumCDMParent::RecvResetVideoDecoderComplete()
    1050             : {
    1051           0 :   MOZ_ASSERT(mReorderQueue.IsEmpty());
    1052           0 :   if (mIsShutdown) {
    1053           0 :     MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
    1054           0 :     return IPC_OK();
    1055             :   }
    1056           0 :   mFlushDecoderPromise.ResolveIfExists(true, __func__);
    1057           0 :   return IPC_OK();
    1058             : }
    1059             : 
    1060             : RefPtr<MediaDataDecoder::DecodePromise>
    1061           0 : ChromiumCDMParent::Drain()
    1062             : {
    1063           0 :   MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
    1064           0 :   if (mIsShutdown) {
    1065             :     return MediaDataDecoder::DecodePromise::CreateAndReject(
    1066           0 :       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1067           0 :                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
    1068           0 :       __func__);
    1069             :   }
    1070             : 
    1071           0 :   RefPtr<MediaDataDecoder::DecodePromise> p = mDecodePromise.Ensure(__func__);
    1072           0 :   if (!SendDrain()) {
    1073           0 :     mDecodePromise.Resolve(MediaDataDecoder::DecodedData(), __func__);
    1074             :   }
    1075           0 :   return p;
    1076             : }
    1077             : 
    1078             : ipc::IPCResult
    1079           0 : ChromiumCDMParent::RecvDrainComplete()
    1080             : {
    1081           0 :   if (mIsShutdown) {
    1082           0 :     MOZ_ASSERT(mDecodePromise.IsEmpty());
    1083           0 :     return IPC_OK();
    1084             :   }
    1085             : 
    1086           0 :   MediaDataDecoder::DecodedData samples;
    1087           0 :   while (!mReorderQueue.IsEmpty()) {
    1088           0 :     samples.AppendElement(Move(mReorderQueue.Pop()));
    1089             :   }
    1090             : 
    1091           0 :   mDecodePromise.ResolveIfExists(Move(samples), __func__);
    1092           0 :   return IPC_OK();
    1093             : }
    1094             : RefPtr<ShutdownPromise>
    1095           0 : ChromiumCDMParent::ShutdownVideoDecoder()
    1096             : {
    1097           0 :   if (mIsShutdown || !mVideoDecoderInitialized) {
    1098           0 :     return ShutdownPromise::CreateAndResolve(true, __func__);
    1099             :   }
    1100           0 :   mInitVideoDecoderPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED,
    1101           0 :                                           __func__);
    1102           0 :   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    1103           0 :   MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
    1104           0 :   if (!SendDeinitializeVideoDecoder()) {
    1105           0 :     return ShutdownPromise::CreateAndResolve(true, __func__);
    1106             :   }
    1107           0 :   mVideoDecoderInitialized = false;
    1108             : 
    1109           0 :   GMP_LOG("ChromiumCDMParent::~ShutdownVideoDecoder(this=%p) "
    1110             :           "VIDEO_CHROMIUM_CDM_MAX_SHMEMS=%u",
    1111             :           this,
    1112             :           mMaxVideoShmemsActive);
    1113           0 :   Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_CHROMIUM_CDM_MAX_SHMEMS,
    1114           0 :                         mMaxVideoShmemsActive);
    1115             : 
    1116             :   // The ChromiumCDMChild will purge its shmems, so if the decoder is
    1117             :   // reinitialized the shmems need to be re-allocated, and they may need
    1118             :   // to be a different size.
    1119           0 :   mVideoShmemsActive = 0;
    1120           0 :   mVideoFrameBufferSize = 0;
    1121           0 :   return ShutdownPromise::CreateAndResolve(true, __func__);
    1122             : }
    1123             : 
    1124             : void
    1125           0 : ChromiumCDMParent::Shutdown()
    1126             : {
    1127           0 :   GMP_LOG("ChromiumCDMParent::Shutdown(this=%p)", this);
    1128             : 
    1129           0 :   if (mIsShutdown) {
    1130           0 :     return;
    1131             :   }
    1132           0 :   mIsShutdown = true;
    1133             : 
    1134             :   // If we're shutting down due to the plugin shutting down due to application
    1135             :   // shutdown, we should tell the CDM proxy to also shutdown. Otherwise the
    1136             :   // proxy will shutdown when the owning MediaKeys is destroyed during cycle
    1137             :   // collection, and that will not shut down cleanly as the GMP thread will be
    1138             :   // shutdown by then.
    1139           0 :   if (mProxy) {
    1140           0 :     RefPtr<Runnable> task = NewRunnableMethod(
    1141           0 :       "ChromiumCDMProxy::Shutdown", mProxy, &ChromiumCDMProxy::Shutdown);
    1142           0 :     NS_DispatchToMainThread(task.forget());
    1143             :   }
    1144             : 
    1145             :   // We may be called from a task holding the last reference to the proxy, so
    1146             :   // let's clear our local weak pointer to ensure it will not be used afterward
    1147             :   // (including from an already-queued task, e.g.: ActorDestroy).
    1148           0 :   mProxy = nullptr;
    1149             : 
    1150           0 :   mReorderQueue.Clear();
    1151             : 
    1152           0 :   for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
    1153           0 :     decrypt->PostResult(eme::AbortedErr);
    1154             :   }
    1155           0 :   mDecrypts.Clear();
    1156             : 
    1157           0 :   if (mVideoDecoderInitialized && !mActorDestroyed) {
    1158           0 :     Unused << SendDeinitializeVideoDecoder();
    1159           0 :     mVideoDecoderInitialized = false;
    1160             :   }
    1161             : 
    1162             :   // Note: MediaKeys rejects all outstanding promises when it initiates shutdown.
    1163           0 :   mPromiseToCreateSessionToken.Clear();
    1164             : 
    1165           0 :   mInitVideoDecoderPromise.RejectIfExists(
    1166           0 :     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1167           0 :                 RESULT_DETAIL("ChromiumCDMParent is shutdown")),
    1168           0 :     __func__);
    1169           0 :   mDecodePromise.RejectIfExists(
    1170           0 :     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1171           0 :                 RESULT_DETAIL("ChromiumCDMParent is shutdown")),
    1172           0 :     __func__);
    1173           0 :   mFlushDecoderPromise.RejectIfExists(
    1174           0 :     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    1175           0 :                 RESULT_DETAIL("ChromiumCDMParent is shutdown")),
    1176           0 :     __func__);
    1177             : 
    1178           0 :   if (!mActorDestroyed) {
    1179           0 :     Unused << SendDestroy();
    1180             :   }
    1181             : }
    1182             : 
    1183             : } // namespace gmp
    1184             : } // namespace mozilla

Generated by: LCOV version 1.13