LCOV - code coverage report
Current view: top level - dom/media/gmp - ChromiumCDMChild.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 400 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 67 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 "ChromiumCDMChild.h"
       7             : #include "GMPContentChild.h"
       8             : #include "WidevineUtils.h"
       9             : #include "WidevineFileIO.h"
      10             : #include "WidevineVideoFrame.h"
      11             : #include "GMPLog.h"
      12             : #include "GMPPlatform.h"
      13             : #include "mozilla/Unused.h"
      14             : #include "nsPrintfCString.h"
      15             : #include "base/time.h"
      16             : #include "GMPUtils.h"
      17             : #include "mozilla/ScopeExit.h"
      18             : #include "mozilla/SizePrintfMacros.h"
      19             : 
      20             : namespace mozilla {
      21             : namespace gmp {
      22             : 
      23           0 : ChromiumCDMChild::ChromiumCDMChild(GMPContentChild* aPlugin)
      24           0 :   : mPlugin(aPlugin)
      25             : {
      26           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
      27           0 :   GMP_LOG("ChromiumCDMChild:: ctor this=%p", this);
      28           0 : }
      29             : 
      30             : void
      31           0 : ChromiumCDMChild::Init(cdm::ContentDecryptionModule_8* aCDM)
      32             : {
      33           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
      34           0 :   mCDM = aCDM;
      35           0 :   MOZ_ASSERT(mCDM);
      36           0 : }
      37             : 
      38             : void
      39           0 : ChromiumCDMChild::TimerExpired(void* aContext)
      40             : {
      41           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
      42           0 :   GMP_LOG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
      43           0 :   if (mCDM) {
      44           0 :     mCDM->TimerExpired(aContext);
      45             :   }
      46           0 : }
      47             : 
      48             : class CDMShmemBuffer : public CDMBuffer
      49             : {
      50             : public:
      51           0 :   CDMShmemBuffer(ChromiumCDMChild* aProtocol, ipc::Shmem aShmem)
      52           0 :     : mProtocol(aProtocol)
      53           0 :     , mSize(aShmem.Size<uint8_t>())
      54           0 :     , mShmem(aShmem)
      55             :   {
      56           0 :     CDM_LOG("CDMShmemBuffer(size=%" PRIu32 ") created", Size());
      57             :     // Note: Chrome initializes the size of a buffer to it capacity. We do the same.
      58           0 :   }
      59             : 
      60             :   CDMShmemBuffer(ChromiumCDMChild* aProtocol,
      61             :                  ipc::Shmem aShmem,
      62             :                  WidevineBuffer* aLocalBuffer)
      63             :     : CDMShmemBuffer(aProtocol, aShmem)
      64             :   {
      65             :     MOZ_ASSERT(aLocalBuffer->Size() == Size());
      66             :     memcpy(Data(),
      67             :            aLocalBuffer->Data(),
      68             :            std::min<uint32_t>(aLocalBuffer->Size(), Size()));
      69             :   }
      70             : 
      71           0 :   ~CDMShmemBuffer() override
      72           0 :   {
      73           0 :     CDM_LOG("CDMShmemBuffer(size=%" PRIu32 ") destructed writable=%d",
      74             :             Size(),
      75             :             mShmem.IsWritable());
      76           0 :     if (mShmem.IsWritable()) {
      77             :       // The shmem wasn't extracted to send its data back up to the parent process,
      78             :       // so we can reuse the shmem.
      79           0 :       mProtocol->GiveBuffer(Move(mShmem));
      80             :     }
      81           0 :   }
      82             : 
      83           0 :   void Destroy() override
      84             :   {
      85           0 :     CDM_LOG("CDMShmemBuffer::Destroy(size=%" PRIu32 ")", Size());
      86           0 :     delete this;
      87           0 :   }
      88           0 :   uint32_t Capacity() const override { return mShmem.Size<uint8_t>(); }
      89             : 
      90           0 :   uint8_t* Data() override { return mShmem.get<uint8_t>(); }
      91             : 
      92           0 :   void SetSize(uint32_t aSize) override
      93             :   {
      94           0 :     MOZ_ASSERT(aSize <= Capacity());
      95             :     // Note: We can't use the shmem's size member after ExtractShmem(),
      96             :     // has been called, so we track the size exlicitly so that we can use
      97             :     // Size() in logging after we've called ExtractShmem().
      98           0 :     CDM_LOG("CDMShmemBuffer::SetSize(size=%" PRIu32 ")", Size());
      99           0 :     mSize = aSize;
     100           0 :   }
     101             : 
     102           0 :   uint32_t Size() const override { return mSize; }
     103             : 
     104           0 :   ipc::Shmem ExtractShmem()
     105             :   {
     106           0 :     ipc::Shmem shmem = mShmem;
     107           0 :     mShmem = ipc::Shmem();
     108           0 :     return shmem;
     109             :   }
     110             : 
     111           0 :   CDMShmemBuffer* AsShmemBuffer() override { return this; }
     112             : 
     113             : private:
     114             :   RefPtr<ChromiumCDMChild> mProtocol;
     115             :   uint32_t mSize;
     116             :   mozilla::ipc::Shmem mShmem;
     117             :   CDMShmemBuffer(const CDMShmemBuffer&);
     118             :   void operator=(const CDMShmemBuffer&);
     119             : };
     120             : 
     121             : static nsCString
     122           0 : ToString(const nsTArray<ipc::Shmem>& aBuffers)
     123             : {
     124           0 :   nsCString s;
     125           0 :   for (const ipc::Shmem& shmem : aBuffers) {
     126           0 :     if (!s.IsEmpty()) {
     127           0 :       s.AppendLiteral(",");
     128             :     }
     129           0 :     s.AppendInt(static_cast<uint32_t>(shmem.Size<uint8_t>()));
     130             :   }
     131           0 :   return s;
     132             : }
     133             : 
     134             : cdm::Buffer*
     135           0 : ChromiumCDMChild::Allocate(uint32_t aCapacity)
     136             : {
     137           0 :   GMP_LOG("ChromiumCDMChild::Allocate(capacity=%" PRIu32 ") bufferSizes={%s}",
     138             :           aCapacity,
     139             :           ToString(mBuffers).get());
     140           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     141             : 
     142             :   // Find the shmem with the least amount of wasted space if we were to
     143             :   // select it for this sized allocation. We need to do this because shmems
     144             :   // for decrypted audio as well as video frames are both stored in this
     145             :   // list, and we don't want to use the video frame shmems for audio samples.
     146           0 :   const size_t invalid = std::numeric_limits<size_t>::max();
     147           0 :   size_t best = invalid;
     148           0 :   auto wastedSpace = [this, aCapacity](size_t index) {
     149           0 :     return mBuffers[index].Size<uint8_t>() - aCapacity;
     150           0 :   };
     151           0 :   for (size_t i = 0; i < mBuffers.Length(); i++) {
     152           0 :     if (mBuffers[i].Size<uint8_t>() >= aCapacity &&
     153           0 :         (best == invalid || wastedSpace(i) < wastedSpace(best))) {
     154           0 :       best = i;
     155             :     }
     156             :   }
     157           0 :   if (best == invalid) {
     158             :     // The parent process should have bestowed upon us a shmem of appropriate
     159             :     // size, but did not! Do a "dive and catch", and create an non-shared
     160             :     // memory buffer. The parent will detect this and send us an extra shmem
     161             :     // so future frames can be in shmems, i.e. returned on the fast path.
     162           0 :     return new WidevineBuffer(aCapacity);
     163             :   }
     164           0 :   ipc::Shmem shmem = mBuffers[best];
     165           0 :   mBuffers.RemoveElementAt(best);
     166           0 :   return new CDMShmemBuffer(this, shmem);
     167             : }
     168             : 
     169             : void
     170           0 : ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext)
     171             : {
     172           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     173           0 :   GMP_LOG("ChromiumCDMChild::SetTimer(delay=%" PRId64 ", context=0x%p)",
     174             :           aDelayMs,
     175             :           aContext);
     176           0 :   RefPtr<ChromiumCDMChild> self(this);
     177           0 :   SetTimerOnMainThread(NewGMPTask([self, aContext]() {
     178           0 :     self->TimerExpired(aContext);
     179           0 :   }), aDelayMs);
     180           0 : }
     181             : 
     182             : cdm::Time
     183           0 : ChromiumCDMChild::GetCurrentWallTime()
     184             : {
     185           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     186           0 :   return base::Time::Now().ToDoubleT();
     187             : }
     188             : 
     189             : void
     190           0 : ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
     191             :                                              const char* aSessionId,
     192             :                                              uint32_t aSessionIdSize)
     193             : {
     194           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     195           0 :   GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
     196             :           ", sid=%s)",
     197             :           aPromiseId,
     198             :           aSessionId);
     199             : 
     200           0 :   if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
     201             :     // As laid out in the Chromium CDM API, if the CDM fails to load
     202             :     // a session it calls OnResolveNewSessionPromise with nullptr as the sessionId.
     203             :     // We can safely assume this means that we have failed to load a session
     204             :     // as the other methods specify calling 'OnRejectPromise' when they fail.
     205           0 :     bool loadSuccessful = aSessionId != nullptr;
     206           0 :     GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
     207             :             "resolving %s load session ",
     208             :             aPromiseId,
     209             :             aSessionId,
     210             :             (loadSuccessful ? "successful" : "failed"));
     211           0 :     Unused << SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
     212           0 :     mLoadSessionPromiseIds.RemoveElement(aPromiseId);
     213           0 :     return;
     214             :   }
     215             : 
     216           0 :   Unused << SendOnResolveNewSessionPromise(aPromiseId,
     217           0 :                                            nsCString(aSessionId, aSessionIdSize));
     218             : }
     219             : 
     220           0 : void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
     221             : {
     222           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     223           0 :   GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
     224           0 :   Unused << SendOnResolvePromise(aPromiseId);
     225           0 : }
     226             : 
     227             : void
     228           0 : ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
     229             :                                   cdm::Error aError,
     230             :                                   uint32_t aSystemCode,
     231             :                                   const char* aErrorMessage,
     232             :                                   uint32_t aErrorMessageSize)
     233             : {
     234           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     235           0 :   GMP_LOG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32 ", err=%" PRIu32
     236             :           " code=%" PRIu32 ", msg='%s')",
     237             :           aPromiseId,
     238             :           aError,
     239             :           aSystemCode,
     240             :           aErrorMessage);
     241           0 :   Unused << SendOnRejectPromise(aPromiseId,
     242           0 :                                 static_cast<uint32_t>(aError),
     243             :                                 aSystemCode,
     244           0 :                                 nsCString(aErrorMessage, aErrorMessageSize));
     245           0 : }
     246             : 
     247             : void
     248           0 : ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
     249             :                                    uint32_t aSessionIdSize,
     250             :                                    cdm::MessageType aMessageType,
     251             :                                    const char* aMessage,
     252             :                                    uint32_t aMessageSize,
     253             :                                    const char* aLegacyDestinationUrl,
     254             :                                    uint32_t aLegacyDestinationUrlLength)
     255             : {
     256           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     257           0 :   GMP_LOG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
     258             :           " size=%" PRIu32 ")",
     259             :           aSessionId,
     260             :           aMessageType,
     261             :           aMessageSize);
     262           0 :   nsTArray<uint8_t> message;
     263           0 :   message.AppendElements(aMessage, aMessageSize);
     264           0 :   Unused << SendOnSessionMessage(nsCString(aSessionId, aSessionIdSize),
     265           0 :                                  static_cast<uint32_t>(aMessageType),
     266             :                                  message);
     267           0 : }
     268             : 
     269             : static nsCString
     270           0 : ToString(const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount)
     271             : {
     272           0 :   nsCString str;
     273           0 :   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     274           0 :     if (!str.IsEmpty()) {
     275           0 :       str.AppendLiteral(",");
     276             :     }
     277           0 :     const cdm::KeyInformation& key = aKeysInfo[i];
     278           0 :     str.Append(ToHexString(key.key_id, key.key_id_size));
     279           0 :     str.AppendLiteral("=");
     280           0 :     str.AppendInt(key.status);
     281             :   }
     282           0 :   return str;
     283             : }
     284             : 
     285             : void
     286           0 : ChromiumCDMChild::OnSessionKeysChange(const char *aSessionId,
     287             :                                       uint32_t aSessionIdSize,
     288             :                                       bool aHasAdditionalUsableKey,
     289             :                                       const cdm::KeyInformation* aKeysInfo,
     290             :                                       uint32_t aKeysInfoCount)
     291             : {
     292           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     293           0 :   GMP_LOG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
     294             :           aSessionId,
     295             :           ToString(aKeysInfo, aKeysInfoCount).get());
     296             : 
     297           0 :   nsTArray<CDMKeyInformation> keys;
     298           0 :   keys.SetCapacity(aKeysInfoCount);
     299           0 :   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     300           0 :     const cdm::KeyInformation& key = aKeysInfo[i];
     301           0 :     nsTArray<uint8_t> kid;
     302           0 :     kid.AppendElements(key.key_id, key.key_id_size);
     303           0 :     keys.AppendElement(CDMKeyInformation(kid, key.status, key.system_code));
     304             :   }
     305           0 :   Unused << SendOnSessionKeysChange(nsCString(aSessionId, aSessionIdSize),
     306             :                                     keys);
     307           0 : }
     308             : 
     309             : void
     310           0 : ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
     311             :                                      uint32_t aSessionIdSize,
     312             :                                      cdm::Time aNewExpiryTime)
     313             : {
     314           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     315           0 :   GMP_LOG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
     316             :           aSessionId,
     317             :           aNewExpiryTime);
     318           0 :   Unused << SendOnExpirationChange(nsCString(aSessionId, aSessionIdSize),
     319             :                                    aNewExpiryTime);
     320           0 : }
     321             : 
     322             : void
     323           0 : ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
     324             :                                   uint32_t aSessionIdSize)
     325             : {
     326           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     327           0 :   GMP_LOG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
     328           0 :   Unused << SendOnSessionClosed(nsCString(aSessionId, aSessionIdSize));
     329           0 : }
     330             : 
     331             : void
     332           0 : ChromiumCDMChild::OnLegacySessionError(const char* aSessionId,
     333             :                                        uint32_t aSessionIdLength,
     334             :                                        cdm::Error aError,
     335             :                                        uint32_t aSystemCode,
     336             :                                        const char* aErrorMessage,
     337             :                                        uint32_t aErrorMessageLength)
     338             : {
     339           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     340           0 :   GMP_LOG("ChromiumCDMChild::OnLegacySessionError(sid=%s, error=%" PRIu32
     341             :           " msg='%s')",
     342             :           aSessionId,
     343             :           aError,
     344             :           aErrorMessage);
     345           0 :   Unused << SendOnLegacySessionError(
     346           0 :     nsCString(aSessionId, aSessionIdLength),
     347           0 :     static_cast<uint32_t>(aError),
     348             :     aSystemCode,
     349           0 :     nsCString(aErrorMessage, aErrorMessageLength));
     350           0 : }
     351             : 
     352             : cdm::FileIO*
     353           0 : ChromiumCDMChild::CreateFileIO(cdm::FileIOClient * aClient)
     354             : {
     355           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     356           0 :   GMP_LOG("ChromiumCDMChild::CreateFileIO()");
     357           0 :   if (!mPersistentStateAllowed) {
     358           0 :     return nullptr;
     359             :   }
     360           0 :   return new WidevineFileIO(aClient);
     361             : }
     362             : 
     363           0 : ChromiumCDMChild::~ChromiumCDMChild()
     364             : {
     365           0 :   GMP_LOG("ChromiumCDMChild:: dtor this=%p", this);
     366           0 : }
     367             : 
     368             : bool
     369           0 : ChromiumCDMChild::IsOnMessageLoopThread()
     370             : {
     371           0 :   return mPlugin && mPlugin->GMPMessageLoop() == MessageLoop::current();
     372             : }
     373             : 
     374             : void
     375           0 : ChromiumCDMChild::PurgeShmems()
     376             : {
     377           0 :   for (ipc::Shmem& shmem : mBuffers) {
     378           0 :     DeallocShmem(shmem);
     379             :   }
     380           0 :   mBuffers.Clear();
     381           0 : }
     382             : 
     383             : ipc::IPCResult
     384           0 : ChromiumCDMChild::RecvPurgeShmems()
     385             : {
     386           0 :   PurgeShmems();
     387           0 :   return IPC_OK();
     388             : }
     389             : 
     390             : mozilla::ipc::IPCResult
     391           0 : ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
     392             :                            const bool& aAllowPersistentState)
     393             : {
     394           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     395           0 :   GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%d, persistentState=%d)",
     396             :           aAllowDistinctiveIdentifier,
     397             :           aAllowPersistentState);
     398           0 :   mPersistentStateAllowed = aAllowPersistentState;
     399           0 :   if (mCDM) {
     400           0 :     mCDM->Initialize(aAllowDistinctiveIdentifier, aAllowPersistentState);
     401             :   }
     402           0 :   return IPC_OK();
     403             : }
     404             : 
     405             : mozilla::ipc::IPCResult
     406           0 : ChromiumCDMChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
     407             :                                            nsTArray<uint8_t>&& aServerCert)
     408             : 
     409             : {
     410           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     411           0 :   GMP_LOG("ChromiumCDMChild::RecvSetServerCertificate() certlen=%zu",
     412             :           aServerCert.Length());
     413           0 :   if (mCDM) {
     414           0 :     mCDM->SetServerCertificate(aPromiseId,
     415           0 :                                aServerCert.Elements(),
     416           0 :                                aServerCert.Length());
     417             :   }
     418           0 :   return IPC_OK();
     419             : }
     420             : 
     421             : mozilla::ipc::IPCResult
     422           0 : ChromiumCDMChild::RecvCreateSessionAndGenerateRequest(
     423             :   const uint32_t& aPromiseId,
     424             :   const uint32_t& aSessionType,
     425             :   const uint32_t& aInitDataType,
     426             :   nsTArray<uint8_t>&& aInitData)
     427             : {
     428           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     429           0 :   GMP_LOG("ChromiumCDMChild::RecvCreateSessionAndGenerateRequest("
     430             :           "pid=%" PRIu32 ", sessionType=%" PRIu32 ", initDataType=%" PRIu32
     431             :           ") initDataLen=%zu",
     432             :           aPromiseId,
     433             :           aSessionType,
     434             :           aInitDataType,
     435             :           aInitData.Length());
     436           0 :   MOZ_ASSERT(aSessionType <= cdm::SessionType::kPersistentKeyRelease);
     437           0 :   MOZ_ASSERT(aInitDataType <= cdm::InitDataType::kWebM);
     438           0 :   if (mCDM) {
     439           0 :     mCDM->CreateSessionAndGenerateRequest(aPromiseId,
     440             :                                           static_cast<cdm::SessionType>(aSessionType),
     441             :                                           static_cast<cdm::InitDataType>(aInitDataType),
     442           0 :                                           aInitData.Elements(),
     443           0 :                                           aInitData.Length());
     444             :   }
     445           0 :   return IPC_OK();
     446             : }
     447             : 
     448             : mozilla::ipc::IPCResult
     449           0 : ChromiumCDMChild::RecvLoadSession(const uint32_t& aPromiseId,
     450             :                                   const uint32_t& aSessionType,
     451             :                                   const nsCString& aSessionId)
     452             : {
     453           0 :   GMP_LOG("ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
     454             :           aPromiseId,
     455             :           aSessionType,
     456             :           aSessionId.get());
     457           0 :   if (mCDM) {
     458           0 :     mLoadSessionPromiseIds.AppendElement(aPromiseId);
     459           0 :     mCDM->LoadSession(aPromiseId,
     460             :                       static_cast<cdm::SessionType>(aSessionType),
     461             :                       aSessionId.get(),
     462           0 :                       aSessionId.Length());
     463             :   }
     464           0 :   return IPC_OK();
     465             : }
     466             : 
     467             : mozilla::ipc::IPCResult
     468           0 : ChromiumCDMChild::RecvUpdateSession(const uint32_t& aPromiseId,
     469             :                                     const nsCString& aSessionId,
     470             :                                     nsTArray<uint8_t>&& aResponse)
     471             : {
     472           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     473           0 :   GMP_LOG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
     474             :           ", sid=%s) responseLen=%zu",
     475             :           aPromiseId,
     476             :           aSessionId.get(),
     477             :           aResponse.Length());
     478           0 :   if (mCDM) {
     479           0 :     mCDM->UpdateSession(aPromiseId,
     480             :                         aSessionId.get(),
     481             :                         aSessionId.Length(),
     482           0 :                         aResponse.Elements(),
     483           0 :                         aResponse.Length());
     484             :   }
     485           0 :   return IPC_OK();
     486             : }
     487             : 
     488             : mozilla::ipc::IPCResult
     489           0 : ChromiumCDMChild::RecvCloseSession(const uint32_t& aPromiseId,
     490             :                                    const nsCString& aSessionId)
     491             : {
     492           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     493           0 :   GMP_LOG("ChromiumCDMChild::RecvCloseSession(pid=%" PRIu32 ", sid=%s)",
     494             :           aPromiseId,
     495             :           aSessionId.get());
     496           0 :   if (mCDM) {
     497           0 :     mCDM->CloseSession(aPromiseId, aSessionId.get(), aSessionId.Length());
     498             :   }
     499           0 :   return IPC_OK();
     500             : }
     501             : 
     502             : mozilla::ipc::IPCResult
     503           0 : ChromiumCDMChild::RecvRemoveSession(const uint32_t& aPromiseId,
     504             :                                     const nsCString& aSessionId)
     505             : {
     506           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     507           0 :   GMP_LOG("ChromiumCDMChild::RecvRemoveSession(pid=%" PRIu32 ", sid=%s)",
     508             :           aPromiseId,
     509             :           aSessionId.get());
     510           0 :   if (mCDM) {
     511           0 :     mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
     512             :   }
     513           0 :   return IPC_OK();
     514             : }
     515             : 
     516             : static void
     517           0 : InitInputBuffer(const CDMInputBuffer& aBuffer,
     518             :                 nsTArray<cdm::SubsampleEntry>& aSubSamples,
     519             :                 cdm::InputBuffer& aInputBuffer)
     520             : {
     521           0 :   aInputBuffer.data = aBuffer.mData().get<uint8_t>();
     522           0 :   aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
     523             : 
     524           0 :   if (aBuffer.mIsEncrypted()) {
     525           0 :     aInputBuffer.key_id = aBuffer.mKeyId().Elements();
     526           0 :     aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
     527             : 
     528           0 :     aInputBuffer.iv = aBuffer.mIV().Elements();
     529           0 :     aInputBuffer.iv_size = aBuffer.mIV().Length();
     530             : 
     531           0 :     aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
     532           0 :     for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
     533           0 :       aSubSamples.AppendElement(cdm::SubsampleEntry(aBuffer.mClearBytes()[i],
     534           0 :                                                     aBuffer.mCipherBytes()[i]));
     535             :     }
     536           0 :     aInputBuffer.subsamples = aSubSamples.Elements();
     537           0 :     aInputBuffer.num_subsamples = aSubSamples.Length();
     538             :   }
     539           0 :   aInputBuffer.timestamp = aBuffer.mTimestamp();
     540           0 : }
     541             : 
     542             : bool
     543           0 : ChromiumCDMChild::HasShmemOfSize(size_t aSize) const
     544             : {
     545           0 :   for (const ipc::Shmem& shmem : mBuffers) {
     546           0 :     if (shmem.Size<uint8_t>() == aSize) {
     547           0 :       return true;
     548             :     }
     549             :   }
     550           0 :   return false;
     551             : }
     552             : 
     553             : mozilla::ipc::IPCResult
     554           0 : ChromiumCDMChild::RecvDecrypt(const uint32_t& aId,
     555             :                               const CDMInputBuffer& aBuffer)
     556             : {
     557           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     558           0 :   GMP_LOG("ChromiumCDMChild::RecvDecrypt()");
     559             : 
     560             :   // Parent should have already gifted us a shmem to use as output.
     561           0 :   size_t outputShmemSize = aBuffer.mData().Size<uint8_t>();
     562           0 :   MOZ_ASSERT(HasShmemOfSize(outputShmemSize));
     563             : 
     564             :   // Ensure we deallocate the shmem used to send input.
     565           0 :   RefPtr<ChromiumCDMChild> self = this;
     566             :   auto autoDeallocateInputShmem =
     567           0 :     MakeScopeExit([&, self] { self->DeallocShmem(aBuffer.mData()); });
     568             : 
     569             :   // On failure, we need to ensure that the shmem that the parent sent
     570             :   // for the CDM to use to return output back to the parent is deallocated.
     571             :   // Otherwise, it will leak.
     572           0 :   auto autoDeallocateOutputShmem = MakeScopeExit([self, outputShmemSize] {
     573           0 :     self->mBuffers.RemoveElementsBy([outputShmemSize, self](ipc::Shmem& aShmem) {
     574           0 :       if (aShmem.Size<uint8_t>() != outputShmemSize) {
     575           0 :         return false;
     576             :       }
     577           0 :       self->DeallocShmem(aShmem);
     578           0 :       return true;
     579           0 :     });
     580           0 :   });
     581             : 
     582           0 :   if (!mCDM) {
     583           0 :     GMP_LOG("ChromiumCDMChild::RecvDecrypt() no CDM");
     584           0 :     Unused << SendDecryptFailed(aId, cdm::kDecryptError);
     585           0 :     return IPC_OK();
     586             :   }
     587           0 :   if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
     588           0 :     GMP_LOG("ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
     589             :             "match");
     590           0 :     Unused << SendDecryptFailed(aId, cdm::kDecryptError);
     591           0 :     return IPC_OK();
     592             :   }
     593             : 
     594           0 :   cdm::InputBuffer input;
     595           0 :   nsTArray<cdm::SubsampleEntry> subsamples;
     596           0 :   InitInputBuffer(aBuffer, subsamples, input);
     597             : 
     598           0 :   WidevineDecryptedBlock output;
     599           0 :   cdm::Status status = mCDM->Decrypt(input, &output);
     600             : 
     601             :   // CDM should have allocated a cdm::Buffer for output.
     602             :   CDMShmemBuffer* buffer =
     603           0 :     output.DecryptedBuffer()
     604           0 :       ? static_cast<CDMShmemBuffer*>(output.DecryptedBuffer())
     605           0 :       : nullptr;
     606           0 :   MOZ_ASSERT_IF(buffer, buffer->AsShmemBuffer());
     607           0 :   if (status != cdm::kSuccess || !buffer) {
     608           0 :     Unused << SendDecryptFailed(aId, status);
     609           0 :     return IPC_OK();
     610             :   }
     611             : 
     612             :   // Success! Return the decrypted sample to parent.
     613           0 :   MOZ_ASSERT(!HasShmemOfSize(outputShmemSize));
     614           0 :   ipc::Shmem shmem = buffer->ExtractShmem();
     615           0 :   if (SendDecrypted(aId, cdm::kSuccess, shmem)) {
     616             :     // No need to deallocate the output shmem; it should have been returned
     617             :     // to the content process.
     618           0 :     autoDeallocateOutputShmem.release();
     619             :   }
     620             : 
     621           0 :   return IPC_OK();
     622             : }
     623             : 
     624             : mozilla::ipc::IPCResult
     625           0 : ChromiumCDMChild::RecvInitializeVideoDecoder(
     626             :   const CDMVideoDecoderConfig& aConfig)
     627             : {
     628           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     629           0 :   MOZ_ASSERT(!mDecoderInitialized);
     630           0 :   cdm::VideoDecoderConfig config;
     631           0 :   config.codec =
     632           0 :     static_cast<cdm::VideoDecoderConfig::VideoCodec>(aConfig.mCodec());
     633           0 :   config.profile =
     634           0 :     static_cast<cdm::VideoDecoderConfig::VideoCodecProfile>(aConfig.mProfile());
     635           0 :   config.format = static_cast<cdm::VideoFormat>(aConfig.mFormat());
     636           0 :   config.coded_size =
     637           0 :     mCodedSize = { aConfig.mImageWidth(), aConfig.mImageHeight() };
     638           0 :   nsTArray<uint8_t> extraData(aConfig.mExtraData());
     639           0 :   config.extra_data = extraData.Elements();
     640           0 :   config.extra_data_size = extraData.Length();
     641           0 :   cdm::Status status = mCDM->InitializeVideoDecoder(config);
     642           0 :   GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() status=%u", status);
     643           0 :   Unused << SendOnDecoderInitDone(status);
     644           0 :   mDecoderInitialized = status == cdm::kSuccess;
     645           0 :   return IPC_OK();
     646             : }
     647             : 
     648             : mozilla::ipc::IPCResult
     649           0 : ChromiumCDMChild::RecvDeinitializeVideoDecoder()
     650             : {
     651           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     652           0 :   GMP_LOG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
     653           0 :   MOZ_ASSERT(mDecoderInitialized);
     654           0 :   if (mDecoderInitialized) {
     655           0 :     mDecoderInitialized = false;
     656           0 :     mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
     657             :   }
     658           0 :   PurgeShmems();
     659           0 :   return IPC_OK();
     660             : }
     661             : 
     662             : mozilla::ipc::IPCResult
     663           0 : ChromiumCDMChild::RecvResetVideoDecoder()
     664             : {
     665           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     666           0 :   GMP_LOG("ChromiumCDMChild::RecvResetVideoDecoder()");
     667           0 :   if (mDecoderInitialized) {
     668           0 :     mCDM->ResetDecoder(cdm::kStreamTypeVideo);
     669             :   }
     670           0 :   Unused << SendResetVideoDecoderComplete();
     671           0 :   return IPC_OK();
     672             : }
     673             : 
     674             : mozilla::ipc::IPCResult
     675           0 : ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
     676             : {
     677           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     678           0 :   GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64 ")",
     679             :           aBuffer.mTimestamp());
     680           0 :   MOZ_ASSERT(mDecoderInitialized);
     681             : 
     682           0 :   RefPtr<ChromiumCDMChild> self = this;
     683           0 :   auto autoDeallocateShmem = MakeScopeExit([&, self] {
     684           0 :     self->DeallocShmem(aBuffer.mData());
     685           0 :   });
     686             : 
     687             :   // The output frame may not have the same timestamp as the frame we put in.
     688             :   // We may need to input a number of frames before we receive output. The
     689             :   // CDM's decoder reorders to ensure frames output are in presentation order.
     690             :   // So we need to store the durations of the frames input, and retrieve them
     691             :   // on output.
     692           0 :   mFrameDurations.Insert(aBuffer.mTimestamp(), aBuffer.mDuration());
     693             : 
     694           0 :   cdm::InputBuffer input;
     695           0 :   nsTArray<cdm::SubsampleEntry> subsamples;
     696           0 :   InitInputBuffer(aBuffer, subsamples, input);
     697             : 
     698           0 :   WidevineVideoFrame frame;
     699           0 :   cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
     700           0 :   GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64
     701             :           " CDM decoder rv=%d",
     702             :           aBuffer.mTimestamp(),
     703             :           rv);
     704             : 
     705           0 :   switch (rv) {
     706             :     case cdm::kNeedMoreData:
     707           0 :       Unused << SendDecodeFailed(rv);
     708           0 :       break;
     709             :     case cdm::kNoKey:
     710           0 :       GMP_LOG("NoKey for sample at time=%" PRId64 "!", input.timestamp);
     711             :       // Somehow our key became unusable. Typically this would happen when
     712             :       // a stream requires output protection, and the configuration changed
     713             :       // such that output protection is no longer available. For example, a
     714             :       // non-compliant monitor was attached. The JS player should notice the
     715             :       // key status changing to "output-restricted", and is supposed to switch
     716             :       // to a stream that doesn't require OP. In order to keep the playback
     717             :       // pipeline rolling, just output a black frame. See bug 1343140.
     718           0 :       if (!frame.InitToBlack(mCodedSize.width, mCodedSize.height,
     719             :                              input.timestamp)) {
     720           0 :         Unused << SendDecodeFailed(cdm::kDecodeError);
     721           0 :         break;
     722             :       }
     723             :       MOZ_FALLTHROUGH;
     724             :     case cdm::kSuccess:
     725           0 :       if (frame.FrameBuffer()) {
     726           0 :         ReturnOutput(frame);
     727           0 :         break;
     728             :       }
     729             :       // CDM didn't set a frame buffer on the sample, report it as an error.
     730             :       MOZ_FALLTHROUGH;
     731             :     default:
     732           0 :       Unused << SendDecodeFailed(rv);
     733           0 :       break;
     734             :   }
     735             : 
     736           0 :   return IPC_OK();
     737             : }
     738             : 
     739             : void
     740           0 : ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame)
     741             : {
     742           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     743           0 :   MOZ_ASSERT(aFrame.FrameBuffer());
     744           0 :   gmp::CDMVideoFrame output;
     745           0 :   output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
     746           0 :   output.mImageWidth() = aFrame.Size().width;
     747           0 :   output.mImageHeight() = aFrame.Size().height;
     748           0 :   output.mYPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kYPlane),
     749           0 :                        aFrame.Stride(cdm::VideoFrame::kYPlane) };
     750           0 :   output.mUPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kUPlane),
     751           0 :                        aFrame.Stride(cdm::VideoFrame::kUPlane) };
     752           0 :   output.mVPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kVPlane),
     753           0 :                        aFrame.Stride(cdm::VideoFrame::kVPlane) };
     754           0 :   output.mTimestamp() = aFrame.Timestamp();
     755             : 
     756           0 :   uint64_t duration = 0;
     757           0 :   if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
     758           0 :     output.mDuration() = duration;
     759             :   }
     760             : 
     761           0 :   CDMBuffer* base = reinterpret_cast<CDMBuffer*>(aFrame.FrameBuffer());
     762           0 :   if (base->AsShmemBuffer()) {
     763           0 :     ipc::Shmem shmem = base->AsShmemBuffer()->ExtractShmem();
     764           0 :     Unused << SendDecodedShmem(output, shmem);
     765             :   } else {
     766           0 :     MOZ_ASSERT(base->AsArrayBuffer());
     767           0 :     Unused << SendDecodedData(output, base->AsArrayBuffer()->ExtractBuffer());
     768             :   }
     769           0 : }
     770             : 
     771             : mozilla::ipc::IPCResult
     772           0 : ChromiumCDMChild::RecvDrain()
     773             : {
     774           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     775           0 :   WidevineVideoFrame frame;
     776           0 :   cdm::InputBuffer sample;
     777           0 :   cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
     778           0 :   CDM_LOG("ChromiumCDMChild::RecvDrain();  DecryptAndDecodeFrame() rv=%d", rv);
     779           0 :   if (rv == cdm::kSuccess) {
     780           0 :     MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
     781           0 :     ReturnOutput(frame);
     782             :   } else {
     783           0 :     Unused << SendDrainComplete();
     784             :   }
     785           0 :   return IPC_OK();
     786             : }
     787             : 
     788             : mozilla::ipc::IPCResult
     789           0 : ChromiumCDMChild::RecvDestroy()
     790             : {
     791           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     792           0 :   GMP_LOG("ChromiumCDMChild::RecvDestroy()");
     793             : 
     794           0 :   MOZ_ASSERT(!mDecoderInitialized);
     795             : 
     796           0 :   if (mCDM) {
     797           0 :     mCDM->Destroy();
     798           0 :     mCDM = nullptr;
     799             :   }
     800             : 
     801           0 :   Unused << Send__delete__(this);
     802             : 
     803           0 :   return IPC_OK();
     804             : }
     805             : 
     806             : mozilla::ipc::IPCResult
     807           0 : ChromiumCDMChild::RecvGiveBuffer(ipc::Shmem&& aBuffer)
     808             : {
     809           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     810             : 
     811           0 :   GiveBuffer(Move(aBuffer));
     812           0 :   return IPC_OK();
     813             : }
     814             : 
     815             : void
     816           0 : ChromiumCDMChild::GiveBuffer(ipc::Shmem&& aBuffer)
     817             : {
     818           0 :   MOZ_ASSERT(IsOnMessageLoopThread());
     819           0 :   size_t sz = aBuffer.Size<uint8_t>();
     820           0 :   mBuffers.AppendElement(Move(aBuffer));
     821           0 :   GMP_LOG("ChromiumCDMChild::RecvGiveBuffer(capacity=%" PRIuSIZE
     822             :           ") bufferSizes={%s} mDecoderInitialized=%d",
     823             :           sz,
     824             :           ToString(mBuffers).get(),
     825             :           mDecoderInitialized);
     826           0 : }
     827             : 
     828             : } // namespace gmp
     829             : } // namespace mozilla

Generated by: LCOV version 1.13