mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	This patch removes the `cdm::SessionType::kPersistentUsageRecord`. It was introduced in June 2020 to comply with the EME spec #462 [1], although Mozilla's position [2] was ambiguous at the time. Subsequently, in January 2021, the `persistent-usage-record` was removed from `MediaKeySessionType` in #482 [3] for issue #480 [4]. However, the addition of `kPersistentUsageRecord` was not removed accordingly, until this patch. [1] https://github.com/w3c/encrypted-media/pull/462 [2] https://github.com/mozilla/standards-positions/issues/407 [3] https://github.com/w3c/encrypted-media/pull/482 [4] https://github.com/w3c/encrypted-media/issues/480 Differential Revision: https://phabricator.services.mozilla.com/D204965
		
			
				
	
	
		
			866 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			866 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "ChromiumCDMChild.h"
 | 
						|
#include "GMPContentChild.h"
 | 
						|
#include "WidevineUtils.h"
 | 
						|
#include "WidevineFileIO.h"
 | 
						|
#include "WidevineVideoFrame.h"
 | 
						|
#include "GMPLog.h"
 | 
						|
#include "GMPPlatform.h"
 | 
						|
#include "mozilla/Unused.h"
 | 
						|
#include "nsPrintfCString.h"
 | 
						|
#include "base/time.h"
 | 
						|
#include "GMPUtils.h"
 | 
						|
#include "mozilla/ScopeExit.h"
 | 
						|
#include "CDMStorageIdProvider.h"
 | 
						|
#include "nsReadableUtils.h"
 | 
						|
 | 
						|
#include <type_traits>
 | 
						|
 | 
						|
namespace mozilla::gmp {
 | 
						|
 | 
						|
ChromiumCDMChild::ChromiumCDMChild(GMPContentChild* aPlugin)
 | 
						|
    : mPlugin(aPlugin) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild:: ctor this=%p", this);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::Init(cdm::ContentDecryptionModule_10* aCDM,
 | 
						|
                            const nsACString& aStorageId) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  mCDM = aCDM;
 | 
						|
  MOZ_ASSERT(mCDM);
 | 
						|
  mStorageId = aStorageId;
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::TimerExpired(void* aContext) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->TimerExpired(aContext);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class CDMShmemBuffer : public CDMBuffer {
 | 
						|
 public:
 | 
						|
  CDMShmemBuffer(ChromiumCDMChild* aProtocol, ipc::Shmem aShmem)
 | 
						|
      : mProtocol(aProtocol), mSize(aShmem.Size<uint8_t>()), mShmem(aShmem) {
 | 
						|
    GMP_LOG_DEBUG("CDMShmemBuffer(size=%" PRIu32 ") created", Size());
 | 
						|
    // Note: Chrome initializes the size of a buffer to it capacity. We do the
 | 
						|
    // same.
 | 
						|
  }
 | 
						|
 | 
						|
  CDMShmemBuffer(ChromiumCDMChild* aProtocol, ipc::Shmem aShmem,
 | 
						|
                 WidevineBuffer* aLocalBuffer)
 | 
						|
      : CDMShmemBuffer(aProtocol, aShmem) {
 | 
						|
    MOZ_ASSERT(aLocalBuffer->Size() == Size());
 | 
						|
    memcpy(Data(), aLocalBuffer->Data(),
 | 
						|
           std::min<uint32_t>(aLocalBuffer->Size(), Size()));
 | 
						|
  }
 | 
						|
 | 
						|
  ~CDMShmemBuffer() override {
 | 
						|
    GMP_LOG_DEBUG("CDMShmemBuffer(size=%" PRIu32 ") destructed writable=%d",
 | 
						|
                  Size(), mShmem.IsWritable());
 | 
						|
    if (mShmem.IsWritable()) {
 | 
						|
      // The shmem wasn't extracted to send its data back up to the parent
 | 
						|
      // process, so we can reuse the shmem.
 | 
						|
      mProtocol->GiveBuffer(std::move(mShmem));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void Destroy() override {
 | 
						|
    GMP_LOG_DEBUG("CDMShmemBuffer::Destroy(size=%" PRIu32 ")", Size());
 | 
						|
    delete this;
 | 
						|
  }
 | 
						|
  uint32_t Capacity() const override { return mShmem.Size<uint8_t>(); }
 | 
						|
 | 
						|
  uint8_t* Data() override { return mShmem.get<uint8_t>(); }
 | 
						|
 | 
						|
  void SetSize(uint32_t aSize) override {
 | 
						|
    MOZ_ASSERT(aSize <= Capacity());
 | 
						|
    // Note: We can't use the shmem's size member after ExtractShmem(),
 | 
						|
    // has been called, so we track the size exlicitly so that we can use
 | 
						|
    // Size() in logging after we've called ExtractShmem().
 | 
						|
    GMP_LOG_DEBUG("CDMShmemBuffer::SetSize(size=%" PRIu32 ")", Size());
 | 
						|
    mSize = aSize;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t Size() const override { return mSize; }
 | 
						|
 | 
						|
  ipc::Shmem ExtractShmem() {
 | 
						|
    ipc::Shmem shmem = mShmem;
 | 
						|
    mShmem = ipc::Shmem();
 | 
						|
    return shmem;
 | 
						|
  }
 | 
						|
 | 
						|
  CDMShmemBuffer* AsShmemBuffer() override { return this; }
 | 
						|
 | 
						|
 private:
 | 
						|
  RefPtr<ChromiumCDMChild> mProtocol;
 | 
						|
  uint32_t mSize;
 | 
						|
  mozilla::ipc::Shmem mShmem;
 | 
						|
  CDMShmemBuffer(const CDMShmemBuffer&);
 | 
						|
  void operator=(const CDMShmemBuffer&);
 | 
						|
};
 | 
						|
 | 
						|
static auto ToString(const nsTArray<ipc::Shmem>& aBuffers) {
 | 
						|
  return StringJoin(","_ns, aBuffers, [](auto& s, const ipc::Shmem& shmem) {
 | 
						|
    s.AppendInt(static_cast<uint32_t>(shmem.Size<uint8_t>()));
 | 
						|
  });
 | 
						|
}
 | 
						|
 | 
						|
cdm::Buffer* ChromiumCDMChild::Allocate(uint32_t aCapacity) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::Allocate(capacity=%" PRIu32
 | 
						|
                ") bufferSizes={%s}",
 | 
						|
                aCapacity, ToString(mBuffers).get());
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
 | 
						|
  if (mBuffers.IsEmpty()) {
 | 
						|
    Unused << SendIncreaseShmemPoolSize();
 | 
						|
  }
 | 
						|
 | 
						|
  // Find the shmem with the least amount of wasted space if we were to
 | 
						|
  // select it for this sized allocation. We need to do this because shmems
 | 
						|
  // for decrypted audio as well as video frames are both stored in this
 | 
						|
  // list, and we don't want to use the video frame shmems for audio samples.
 | 
						|
  const size_t invalid = std::numeric_limits<size_t>::max();
 | 
						|
  size_t best = invalid;
 | 
						|
  auto wastedSpace = [this, aCapacity](size_t index) {
 | 
						|
    return mBuffers[index].Size<uint8_t>() - aCapacity;
 | 
						|
  };
 | 
						|
  for (size_t i = 0; i < mBuffers.Length(); i++) {
 | 
						|
    if (mBuffers[i].Size<uint8_t>() >= aCapacity &&
 | 
						|
        (best == invalid || wastedSpace(i) < wastedSpace(best))) {
 | 
						|
      best = i;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (best == invalid) {
 | 
						|
    // The parent process should have bestowed upon us a shmem of appropriate
 | 
						|
    // size, but did not! Do a "dive and catch", and create an non-shared
 | 
						|
    // memory buffer. The parent will detect this and send us an extra shmem
 | 
						|
    // so future frames can be in shmems, i.e. returned on the fast path.
 | 
						|
    return new WidevineBuffer(aCapacity);
 | 
						|
  }
 | 
						|
  ipc::Shmem shmem = mBuffers[best];
 | 
						|
  mBuffers.RemoveElementAt(best);
 | 
						|
  return new CDMShmemBuffer(this, shmem);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::SetTimer(delay=%" PRId64 ", context=0x%p)",
 | 
						|
                aDelayMs, aContext);
 | 
						|
  RefPtr<ChromiumCDMChild> self(this);
 | 
						|
  SetTimerOnMainThread(
 | 
						|
      NewGMPTask([self, aContext]() { self->TimerExpired(aContext); }),
 | 
						|
      aDelayMs);
 | 
						|
}
 | 
						|
 | 
						|
cdm::Time ChromiumCDMChild::GetCurrentWallTime() {
 | 
						|
  return base::Time::Now().ToDoubleT();
 | 
						|
}
 | 
						|
 | 
						|
template <typename MethodType, typename... ParamType>
 | 
						|
void ChromiumCDMChild::CallMethod(MethodType aMethod, ParamType&&... aParams) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  // Avoid calling member function after destroy.
 | 
						|
  if (!mDestroyed) {
 | 
						|
    Unused << (this->*aMethod)(std::forward<ParamType>(aParams)...);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
template <typename MethodType, typename... ParamType>
 | 
						|
void ChromiumCDMChild::CallOnMessageLoopThread(const char* const aName,
 | 
						|
                                               MethodType aMethod,
 | 
						|
                                               ParamType&&... aParams) {
 | 
						|
  if (NS_WARN_IF(!mPlugin)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (IsOnMessageLoopThread()) {
 | 
						|
    CallMethod(aMethod, std::forward<ParamType>(aParams)...);
 | 
						|
  } else {
 | 
						|
    auto m = &ChromiumCDMChild::CallMethod<
 | 
						|
        decltype(aMethod), const std::remove_reference_t<ParamType>&...>;
 | 
						|
    RefPtr<mozilla::Runnable> t =
 | 
						|
        NewRunnableMethod<decltype(aMethod),
 | 
						|
                          const std::remove_reference_t<ParamType>...>(
 | 
						|
            aName, this, m, aMethod, std::forward<ParamType>(aParams)...);
 | 
						|
    mPlugin->GMPMessageLoop()->PostTask(t.forget());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnResolveKeyStatusPromise(uint32_t aPromiseId,
 | 
						|
                                                 cdm::KeyStatus aKeyStatus) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnResolveKeyStatusPromise(pid=%" PRIu32
 | 
						|
                "keystatus=%d)",
 | 
						|
                aPromiseId, aKeyStatus);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveKeyStatusPromise",
 | 
						|
                          &ChromiumCDMChild::SendOnResolvePromiseWithKeyStatus,
 | 
						|
                          aPromiseId, static_cast<uint32_t>(aKeyStatus));
 | 
						|
}
 | 
						|
 | 
						|
bool ChromiumCDMChild::OnResolveNewSessionPromiseInternal(
 | 
						|
    uint32_t aPromiseId, const nsACString& aSessionId) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
 | 
						|
    // As laid out in the Chromium CDM API, if the CDM fails to load
 | 
						|
    // a session it calls OnResolveNewSessionPromise with nullptr as the
 | 
						|
    // sessionId. We can safely assume this means that we have failed to load a
 | 
						|
    // session as the other methods specify calling 'OnRejectPromise' when they
 | 
						|
    // fail.
 | 
						|
    bool loadSuccessful = !aSessionId.IsEmpty();
 | 
						|
    GMP_LOG_DEBUG(
 | 
						|
        "ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
 | 
						|
        "resolving %s load session ",
 | 
						|
        aPromiseId, PromiseFlatCString(aSessionId).get(),
 | 
						|
        (loadSuccessful ? "successful" : "failed"));
 | 
						|
    mLoadSessionPromiseIds.RemoveElement(aPromiseId);
 | 
						|
    return SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
 | 
						|
  }
 | 
						|
 | 
						|
  return SendOnResolveNewSessionPromise(aPromiseId, aSessionId);
 | 
						|
}
 | 
						|
void ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
 | 
						|
                                                  const char* aSessionId,
 | 
						|
                                                  uint32_t aSessionIdSize) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
 | 
						|
                ", sid=%s)",
 | 
						|
                aPromiseId, aSessionId);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveNewSessionPromise",
 | 
						|
                          &ChromiumCDMChild::OnResolveNewSessionPromiseInternal,
 | 
						|
                          aPromiseId, nsCString(aSessionId, aSessionIdSize));
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")",
 | 
						|
                aPromiseId);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolvePromise",
 | 
						|
                          &ChromiumCDMChild::SendOnResolvePromise, aPromiseId);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
 | 
						|
                                       cdm::Exception aException,
 | 
						|
                                       uint32_t aSystemCode,
 | 
						|
                                       const char* aErrorMessage,
 | 
						|
                                       uint32_t aErrorMessageSize) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32
 | 
						|
                ", err=%" PRIu32 " code=%" PRIu32 ", msg='%s')",
 | 
						|
                aPromiseId, aException, aSystemCode, aErrorMessage);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnRejectPromise",
 | 
						|
                          &ChromiumCDMChild::SendOnRejectPromise, aPromiseId,
 | 
						|
                          static_cast<uint32_t>(aException), aSystemCode,
 | 
						|
                          nsCString(aErrorMessage, aErrorMessageSize));
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
 | 
						|
                                        uint32_t aSessionIdSize,
 | 
						|
                                        cdm::MessageType aMessageType,
 | 
						|
                                        const char* aMessage,
 | 
						|
                                        uint32_t aMessageSize) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
 | 
						|
                " size=%" PRIu32 ")",
 | 
						|
                aSessionId, aMessageType, aMessageSize);
 | 
						|
  CopyableTArray<uint8_t> message;
 | 
						|
  message.AppendElements(aMessage, aMessageSize);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
 | 
						|
                          &ChromiumCDMChild::SendOnSessionMessage,
 | 
						|
                          nsCString(aSessionId, aSessionIdSize),
 | 
						|
                          static_cast<uint32_t>(aMessageType), message);
 | 
						|
}
 | 
						|
 | 
						|
static auto ToString(const cdm::KeyInformation* aKeysInfo,
 | 
						|
                     uint32_t aKeysInfoCount) {
 | 
						|
  return StringJoin(","_ns, Span{aKeysInfo, aKeysInfoCount},
 | 
						|
                    [](auto& str, const cdm::KeyInformation& key) {
 | 
						|
                      str.Append(ToHexString(key.key_id, key.key_id_size));
 | 
						|
                      str.AppendLiteral("=");
 | 
						|
                      str.AppendInt(key.status);
 | 
						|
                    });
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnSessionKeysChange(const char* aSessionId,
 | 
						|
                                           uint32_t aSessionIdSize,
 | 
						|
                                           bool aHasAdditionalUsableKey,
 | 
						|
                                           const cdm::KeyInformation* aKeysInfo,
 | 
						|
                                           uint32_t aKeysInfoCount) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
 | 
						|
                aSessionId, ToString(aKeysInfo, aKeysInfoCount).get());
 | 
						|
 | 
						|
  CopyableTArray<CDMKeyInformation> keys;
 | 
						|
  keys.SetCapacity(aKeysInfoCount);
 | 
						|
  for (uint32_t i = 0; i < aKeysInfoCount; i++) {
 | 
						|
    const cdm::KeyInformation& key = aKeysInfo[i];
 | 
						|
    nsTArray<uint8_t> kid;
 | 
						|
    kid.AppendElements(key.key_id, key.key_id_size);
 | 
						|
    keys.AppendElement(CDMKeyInformation(kid, key.status, key.system_code));
 | 
						|
  }
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionMessage",
 | 
						|
                          &ChromiumCDMChild::SendOnSessionKeysChange,
 | 
						|
                          nsCString(aSessionId, aSessionIdSize), keys);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
 | 
						|
                                          uint32_t aSessionIdSize,
 | 
						|
                                          cdm::Time aNewExpiryTime) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
 | 
						|
                aSessionId, aNewExpiryTime);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnExpirationChange",
 | 
						|
                          &ChromiumCDMChild::SendOnExpirationChange,
 | 
						|
                          nsCString(aSessionId, aSessionIdSize),
 | 
						|
                          aNewExpiryTime);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
 | 
						|
                                       uint32_t aSessionIdSize) {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnSessionClosed",
 | 
						|
                          &ChromiumCDMChild::SendOnSessionClosed,
 | 
						|
                          nsCString(aSessionId, aSessionIdSize));
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::QueryOutputProtectionStatus() {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::QueryOutputProtectionStatus()");
 | 
						|
  // We'll handle the response in `CompleteQueryOutputProtectionStatus`.
 | 
						|
  CallOnMessageLoopThread("gmp::ChromiumCDMChild::QueryOutputProtectionStatus",
 | 
						|
                          &ChromiumCDMChild::SendOnQueryOutputProtectionStatus);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::OnInitialized(bool aSuccess) {
 | 
						|
  MOZ_ASSERT(!mInitPromise.IsEmpty(),
 | 
						|
             "mInitPromise should exist during init callback!");
 | 
						|
  if (!aSuccess) {
 | 
						|
    mInitPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
 | 
						|
  }
 | 
						|
  mInitPromise.ResolveIfExists(true, __func__);
 | 
						|
}
 | 
						|
 | 
						|
cdm::FileIO* ChromiumCDMChild::CreateFileIO(cdm::FileIOClient* aClient) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::CreateFileIO()");
 | 
						|
  if (!mPersistentStateAllowed) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  return new WidevineFileIO(aClient);
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::RequestStorageId(uint32_t aVersion) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RequestStorageId() aVersion = %u", aVersion);
 | 
						|
  // aVersion >= 0x80000000 are reserved.
 | 
						|
  if (aVersion >= 0x80000000) {
 | 
						|
    mCDM->OnStorageId(aVersion, nullptr, 0);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (aVersion > CDMStorageIdProvider::kCurrentVersion) {
 | 
						|
    mCDM->OnStorageId(aVersion, nullptr, 0);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  mCDM->OnStorageId(CDMStorageIdProvider::kCurrentVersion,
 | 
						|
                    !mStorageId.IsEmpty()
 | 
						|
                        ? reinterpret_cast<const uint8_t*>(mStorageId.get())
 | 
						|
                        : nullptr,
 | 
						|
                    mStorageId.Length());
 | 
						|
}
 | 
						|
 | 
						|
ChromiumCDMChild::~ChromiumCDMChild() {
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild:: dtor this=%p", this);
 | 
						|
}
 | 
						|
 | 
						|
bool ChromiumCDMChild::IsOnMessageLoopThread() {
 | 
						|
  return mPlugin && mPlugin->GMPMessageLoop() == MessageLoop::current();
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::ActorDestroy(ActorDestroyReason aReason) {
 | 
						|
  mPlugin = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::PurgeShmems() {
 | 
						|
  for (ipc::Shmem& shmem : mBuffers) {
 | 
						|
    DeallocShmem(shmem);
 | 
						|
  }
 | 
						|
  mBuffers.Clear();
 | 
						|
}
 | 
						|
 | 
						|
ipc::IPCResult ChromiumCDMChild::RecvPurgeShmems() {
 | 
						|
  PurgeShmems();
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvInit(
 | 
						|
    const bool& aAllowDistinctiveIdentifier, const bool& aAllowPersistentState,
 | 
						|
    InitResolver&& aResolver) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG(
 | 
						|
      "ChromiumCDMChild::RecvInit(distinctiveId=%s, persistentState=%s)",
 | 
						|
      aAllowDistinctiveIdentifier ? "true" : "false",
 | 
						|
      aAllowPersistentState ? "true" : "false");
 | 
						|
  mPersistentStateAllowed = aAllowPersistentState;
 | 
						|
 | 
						|
  RefPtr<ChromiumCDMChild::InitPromise> promise = mInitPromise.Ensure(__func__);
 | 
						|
  promise->Then(
 | 
						|
      mPlugin->GMPMessageLoop()->SerialEventTarget(), __func__,
 | 
						|
      [aResolver](bool /* unused */) { aResolver(true); },
 | 
						|
      [aResolver](nsresult rv) {
 | 
						|
        GMP_LOG_DEBUG(
 | 
						|
            "ChromiumCDMChild::RecvInit() init promise rejected with "
 | 
						|
            "rv=%" PRIu32,
 | 
						|
            static_cast<uint32_t>(rv));
 | 
						|
        aResolver(false);
 | 
						|
      });
 | 
						|
 | 
						|
  if (mCDM) {
 | 
						|
    // Once the CDM is initialized we expect it to resolve mInitPromise via
 | 
						|
    // ChromiumCDMChild::OnInitialized
 | 
						|
    mCDM->Initialize(aAllowDistinctiveIdentifier, aAllowPersistentState,
 | 
						|
                     // We do not yet support hardware secure codecs
 | 
						|
                     false);
 | 
						|
  } else {
 | 
						|
    GMP_LOG_DEBUG(
 | 
						|
        "ChromiumCDMChild::RecvInit() mCDM not set! Is GMP shutting down?");
 | 
						|
    mInitPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvSetServerCertificate(
 | 
						|
    const uint32_t& aPromiseId, nsTArray<uint8_t>&& aServerCert)
 | 
						|
 | 
						|
{
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvSetServerCertificate() certlen=%zu",
 | 
						|
                aServerCert.Length());
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->SetServerCertificate(aPromiseId, aServerCert.Elements(),
 | 
						|
                               aServerCert.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvCreateSessionAndGenerateRequest(
 | 
						|
    const uint32_t& aPromiseId, const uint32_t& aSessionType,
 | 
						|
    const uint32_t& aInitDataType, nsTArray<uint8_t>&& aInitData) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG(
 | 
						|
      "ChromiumCDMChild::RecvCreateSessionAndGenerateRequest("
 | 
						|
      "pid=%" PRIu32 ", sessionType=%" PRIu32 ", initDataType=%" PRIu32
 | 
						|
      ") initDataLen=%zu",
 | 
						|
      aPromiseId, aSessionType, aInitDataType, aInitData.Length());
 | 
						|
  MOZ_ASSERT(aSessionType <= cdm::SessionType::kPersistentLicense);
 | 
						|
  MOZ_ASSERT(aInitDataType <= cdm::InitDataType::kWebM);
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->CreateSessionAndGenerateRequest(
 | 
						|
        aPromiseId, static_cast<cdm::SessionType>(aSessionType),
 | 
						|
        static_cast<cdm::InitDataType>(aInitDataType), aInitData.Elements(),
 | 
						|
        aInitData.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvLoadSession(
 | 
						|
    const uint32_t& aPromiseId, const uint32_t& aSessionType,
 | 
						|
    const nsACString& aSessionId) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG(
 | 
						|
      "ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
 | 
						|
      aPromiseId, aSessionType, PromiseFlatCString(aSessionId).get());
 | 
						|
  if (mCDM) {
 | 
						|
    mLoadSessionPromiseIds.AppendElement(aPromiseId);
 | 
						|
    mCDM->LoadSession(aPromiseId, static_cast<cdm::SessionType>(aSessionType),
 | 
						|
                      aSessionId.BeginReading(), aSessionId.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvUpdateSession(
 | 
						|
    const uint32_t& aPromiseId, const nsACString& aSessionId,
 | 
						|
    nsTArray<uint8_t>&& aResponse) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
 | 
						|
                ", sid=%s) responseLen=%zu",
 | 
						|
                aPromiseId, PromiseFlatCString(aSessionId).get(),
 | 
						|
                aResponse.Length());
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->UpdateSession(aPromiseId, aSessionId.BeginReading(),
 | 
						|
                        aSessionId.Length(), aResponse.Elements(),
 | 
						|
                        aResponse.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvCloseSession(
 | 
						|
    const uint32_t& aPromiseId, const nsACString& aSessionId) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvCloseSession(pid=%" PRIu32 ", sid=%s)",
 | 
						|
                aPromiseId, PromiseFlatCString(aSessionId).get());
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->CloseSession(aPromiseId, aSessionId.BeginReading(),
 | 
						|
                       aSessionId.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvRemoveSession(
 | 
						|
    const uint32_t& aPromiseId, const nsACString& aSessionId) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvRemoveSession(pid=%" PRIu32 ", sid=%s)",
 | 
						|
                aPromiseId, PromiseFlatCString(aSessionId).get());
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->RemoveSession(aPromiseId, aSessionId.BeginReading(),
 | 
						|
                        aSessionId.Length());
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult
 | 
						|
ChromiumCDMChild::RecvCompleteQueryOutputProtectionStatus(
 | 
						|
    const bool& aSuccess, const uint32_t& aLinkMask,
 | 
						|
    const uint32_t& aProtectionMask) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG(
 | 
						|
      "ChromiumCDMChild::RecvCompleteQueryOutputProtectionStatus(aSuccess=%s, "
 | 
						|
      "aLinkMask=%" PRIu32 ", aProtectionMask=%" PRIu32 ")",
 | 
						|
      aSuccess ? "true" : "false", aLinkMask, aProtectionMask);
 | 
						|
 | 
						|
  if (mCDM) {
 | 
						|
    cdm::QueryResult queryResult = aSuccess ? cdm::QueryResult::kQuerySucceeded
 | 
						|
                                            : cdm::QueryResult::kQueryFailed;
 | 
						|
    mCDM->OnQueryOutputProtectionStatus(queryResult, aLinkMask,
 | 
						|
                                        aProtectionMask);
 | 
						|
  }
 | 
						|
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvGetStatusForPolicy(
 | 
						|
    const uint32_t& aPromiseId, const cdm::HdcpVersion& aMinHdcpVersion) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvGetStatusForPolicy(pid=%" PRIu32
 | 
						|
                ", MinHdcpVersion=%" PRIu32 ")",
 | 
						|
                aPromiseId, static_cast<uint32_t>(aMinHdcpVersion));
 | 
						|
  if (mCDM) {
 | 
						|
    cdm::Policy policy;
 | 
						|
    policy.min_hdcp_version = aMinHdcpVersion;
 | 
						|
    mCDM->GetStatusForPolicy(aPromiseId, policy);
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
static void InitInputBuffer(const CDMInputBuffer& aBuffer,
 | 
						|
                            nsTArray<cdm::SubsampleEntry>& aSubSamples,
 | 
						|
                            cdm::InputBuffer_2& aInputBuffer) {
 | 
						|
  aInputBuffer.data = aBuffer.mData().get<uint8_t>();
 | 
						|
  aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
 | 
						|
 | 
						|
  if (aBuffer.mEncryptionScheme() != cdm::EncryptionScheme::kUnencrypted) {
 | 
						|
    MOZ_ASSERT(aBuffer.mEncryptionScheme() == cdm::EncryptionScheme::kCenc ||
 | 
						|
               aBuffer.mEncryptionScheme() == cdm::EncryptionScheme::kCbcs);
 | 
						|
    aInputBuffer.key_id = aBuffer.mKeyId().Elements();
 | 
						|
    aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
 | 
						|
 | 
						|
    aInputBuffer.iv = aBuffer.mIV().Elements();
 | 
						|
    aInputBuffer.iv_size = aBuffer.mIV().Length();
 | 
						|
 | 
						|
    aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
 | 
						|
    for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
 | 
						|
      aSubSamples.AppendElement(cdm::SubsampleEntry{aBuffer.mClearBytes()[i],
 | 
						|
                                                    aBuffer.mCipherBytes()[i]});
 | 
						|
    }
 | 
						|
    aInputBuffer.subsamples = aSubSamples.Elements();
 | 
						|
    aInputBuffer.num_subsamples = aSubSamples.Length();
 | 
						|
    aInputBuffer.encryption_scheme = aBuffer.mEncryptionScheme();
 | 
						|
  }
 | 
						|
  aInputBuffer.pattern.crypt_byte_block = aBuffer.mCryptByteBlock();
 | 
						|
  aInputBuffer.pattern.skip_byte_block = aBuffer.mSkipByteBlock();
 | 
						|
  aInputBuffer.timestamp = aBuffer.mTimestamp();
 | 
						|
}
 | 
						|
 | 
						|
bool ChromiumCDMChild::HasShmemOfSize(size_t aSize) const {
 | 
						|
  for (const ipc::Shmem& shmem : mBuffers) {
 | 
						|
    if (shmem.Size<uint8_t>() == aSize) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return false;
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvDecrypt(
 | 
						|
    const uint32_t& aId, const CDMInputBuffer& aBuffer) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDecrypt()");
 | 
						|
 | 
						|
  // Parent should have already gifted us a shmem to use as output.
 | 
						|
  size_t outputShmemSize = aBuffer.mData().Size<uint8_t>();
 | 
						|
  MOZ_ASSERT(HasShmemOfSize(outputShmemSize));
 | 
						|
 | 
						|
  // Ensure we deallocate the shmem used to send input.
 | 
						|
  RefPtr<ChromiumCDMChild> self = this;
 | 
						|
  auto autoDeallocateInputShmem =
 | 
						|
      MakeScopeExit([&, self] { self->DeallocShmem(aBuffer.mData()); });
 | 
						|
 | 
						|
  // On failure, we need to ensure that the shmem that the parent sent
 | 
						|
  // for the CDM to use to return output back to the parent is deallocated.
 | 
						|
  // Otherwise, it will leak.
 | 
						|
  auto autoDeallocateOutputShmem = MakeScopeExit([self, outputShmemSize] {
 | 
						|
    self->mBuffers.RemoveElementsBy(
 | 
						|
        [outputShmemSize, self](ipc::Shmem& aShmem) {
 | 
						|
          if (aShmem.Size<uint8_t>() != outputShmemSize) {
 | 
						|
            return false;
 | 
						|
          }
 | 
						|
          self->DeallocShmem(aShmem);
 | 
						|
          return true;
 | 
						|
        });
 | 
						|
  });
 | 
						|
 | 
						|
  if (!mCDM) {
 | 
						|
    GMP_LOG_DEBUG("ChromiumCDMChild::RecvDecrypt() no CDM");
 | 
						|
    Unused << SendDecryptFailed(aId, cdm::kDecryptError);
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
  if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
 | 
						|
    GMP_LOG_DEBUG(
 | 
						|
        "ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
 | 
						|
        "match");
 | 
						|
    Unused << SendDecryptFailed(aId, cdm::kDecryptError);
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
 | 
						|
  cdm::InputBuffer_2 input = {};
 | 
						|
  nsTArray<cdm::SubsampleEntry> subsamples;
 | 
						|
  InitInputBuffer(aBuffer, subsamples, input);
 | 
						|
 | 
						|
  WidevineDecryptedBlock output;
 | 
						|
  cdm::Status status = mCDM->Decrypt(input, &output);
 | 
						|
 | 
						|
  // CDM should have allocated a cdm::Buffer for output.
 | 
						|
  CDMShmemBuffer* buffer =
 | 
						|
      output.DecryptedBuffer()
 | 
						|
          ? static_cast<CDMShmemBuffer*>(output.DecryptedBuffer())
 | 
						|
          : nullptr;
 | 
						|
  MOZ_ASSERT_IF(buffer, buffer->AsShmemBuffer());
 | 
						|
  if (status != cdm::kSuccess || !buffer) {
 | 
						|
    Unused << SendDecryptFailed(aId, status);
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
 | 
						|
  // Success! Return the decrypted sample to parent.
 | 
						|
  MOZ_ASSERT(!HasShmemOfSize(outputShmemSize));
 | 
						|
  ipc::Shmem shmem = buffer->ExtractShmem();
 | 
						|
  if (SendDecrypted(aId, cdm::kSuccess, std::move(shmem))) {
 | 
						|
    // No need to deallocate the output shmem; it should have been returned
 | 
						|
    // to the content process.
 | 
						|
    autoDeallocateOutputShmem.release();
 | 
						|
  }
 | 
						|
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvInitializeVideoDecoder(
 | 
						|
    const CDMVideoDecoderConfig& aConfig) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  MOZ_ASSERT(!mDecoderInitialized);
 | 
						|
  if (!mCDM) {
 | 
						|
    GMP_LOG_DEBUG("ChromiumCDMChild::RecvInitializeVideoDecoder() no CDM");
 | 
						|
    Unused << SendOnDecoderInitDone(cdm::kInitializationError);
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
  cdm::VideoDecoderConfig_2 config = {};
 | 
						|
  config.codec = static_cast<cdm::VideoCodec>(aConfig.mCodec());
 | 
						|
  config.profile = static_cast<cdm::VideoCodecProfile>(aConfig.mProfile());
 | 
						|
  config.format = static_cast<cdm::VideoFormat>(aConfig.mFormat());
 | 
						|
  config.coded_size =
 | 
						|
      mCodedSize = {aConfig.mImageWidth(), aConfig.mImageHeight()};
 | 
						|
  nsTArray<uint8_t> extraData(aConfig.mExtraData().Clone());
 | 
						|
  config.extra_data = extraData.Elements();
 | 
						|
  config.extra_data_size = extraData.Length();
 | 
						|
  config.encryption_scheme = aConfig.mEncryptionScheme();
 | 
						|
  cdm::Status status = mCDM->InitializeVideoDecoder(config);
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvInitializeVideoDecoder() status=%u",
 | 
						|
                status);
 | 
						|
  Unused << SendOnDecoderInitDone(status);
 | 
						|
  mDecoderInitialized = status == cdm::kSuccess;
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvDeinitializeVideoDecoder() {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
 | 
						|
  MOZ_ASSERT(mDecoderInitialized);
 | 
						|
  if (mDecoderInitialized && mCDM) {
 | 
						|
    mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
 | 
						|
  }
 | 
						|
  mDecoderInitialized = false;
 | 
						|
  PurgeShmems();
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvResetVideoDecoder() {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvResetVideoDecoder()");
 | 
						|
  if (mDecoderInitialized && mCDM) {
 | 
						|
    mCDM->ResetDecoder(cdm::kStreamTypeVideo);
 | 
						|
  }
 | 
						|
  Unused << SendResetVideoDecoderComplete();
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvDecryptAndDecodeFrame(
 | 
						|
    const CDMInputBuffer& aBuffer) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64 ")",
 | 
						|
                aBuffer.mTimestamp());
 | 
						|
  MOZ_ASSERT(mDecoderInitialized);
 | 
						|
 | 
						|
  if (!mCDM) {
 | 
						|
    GMP_LOG_DEBUG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() no CDM");
 | 
						|
    Unused << SendDecodeFailed(cdm::kDecodeError);
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ChromiumCDMChild> self = this;
 | 
						|
  auto autoDeallocateShmem =
 | 
						|
      MakeScopeExit([&, self] { self->DeallocShmem(aBuffer.mData()); });
 | 
						|
 | 
						|
  // The output frame may not have the same timestamp as the frame we put in.
 | 
						|
  // We may need to input a number of frames before we receive output. The
 | 
						|
  // CDM's decoder reorders to ensure frames output are in presentation order.
 | 
						|
  // So we need to store the durations of the frames input, and retrieve them
 | 
						|
  // on output.
 | 
						|
  mFrameDurations.Insert(aBuffer.mTimestamp(), aBuffer.mDuration());
 | 
						|
 | 
						|
  cdm::InputBuffer_2 input = {};
 | 
						|
  nsTArray<cdm::SubsampleEntry> subsamples;
 | 
						|
  InitInputBuffer(aBuffer, subsamples, input);
 | 
						|
 | 
						|
  WidevineVideoFrame frame;
 | 
						|
  cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDecryptAndDecodeFrame() t=%" PRId64
 | 
						|
                " CDM decoder rv=%d",
 | 
						|
                aBuffer.mTimestamp(), rv);
 | 
						|
 | 
						|
  switch (rv) {
 | 
						|
    case cdm::kNeedMoreData:
 | 
						|
      Unused << SendDecodeFailed(rv);
 | 
						|
      break;
 | 
						|
    case cdm::kNoKey:
 | 
						|
      GMP_LOG_DEBUG("NoKey for sample at time=%" PRId64 "!", input.timestamp);
 | 
						|
      // Somehow our key became unusable. Typically this would happen when
 | 
						|
      // a stream requires output protection, and the configuration changed
 | 
						|
      // such that output protection is no longer available. For example, a
 | 
						|
      // non-compliant monitor was attached. The JS player should notice the
 | 
						|
      // key status changing to "output-restricted", and is supposed to switch
 | 
						|
      // to a stream that doesn't require OP. In order to keep the playback
 | 
						|
      // pipeline rolling, just output a black frame. See bug 1343140.
 | 
						|
      if (!frame.InitToBlack(mCodedSize.width, mCodedSize.height,
 | 
						|
                             input.timestamp)) {
 | 
						|
        Unused << SendDecodeFailed(cdm::kDecodeError);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      [[fallthrough]];
 | 
						|
    case cdm::kSuccess:
 | 
						|
      if (frame.FrameBuffer()) {
 | 
						|
        ReturnOutput(frame);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      // CDM didn't set a frame buffer on the sample, report it as an error.
 | 
						|
      [[fallthrough]];
 | 
						|
    default:
 | 
						|
      Unused << SendDecodeFailed(rv);
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  MOZ_ASSERT(aFrame.FrameBuffer());
 | 
						|
  gmp::CDMVideoFrame output;
 | 
						|
  output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
 | 
						|
  output.mImageWidth() = aFrame.Size().width;
 | 
						|
  output.mImageHeight() = aFrame.Size().height;
 | 
						|
  output.mYPlane() = {aFrame.PlaneOffset(cdm::VideoPlane::kYPlane),
 | 
						|
                      aFrame.Stride(cdm::VideoPlane::kYPlane)};
 | 
						|
  output.mUPlane() = {aFrame.PlaneOffset(cdm::VideoPlane::kUPlane),
 | 
						|
                      aFrame.Stride(cdm::VideoPlane::kUPlane)};
 | 
						|
  output.mVPlane() = {aFrame.PlaneOffset(cdm::VideoPlane::kVPlane),
 | 
						|
                      aFrame.Stride(cdm::VideoPlane::kVPlane)};
 | 
						|
  output.mTimestamp() = aFrame.Timestamp();
 | 
						|
 | 
						|
  uint64_t duration = 0;
 | 
						|
  if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
 | 
						|
    output.mDuration() = duration;
 | 
						|
  }
 | 
						|
 | 
						|
  CDMBuffer* base = reinterpret_cast<CDMBuffer*>(aFrame.FrameBuffer());
 | 
						|
  if (base->AsShmemBuffer()) {
 | 
						|
    ipc::Shmem shmem = base->AsShmemBuffer()->ExtractShmem();
 | 
						|
    Unused << SendDecodedShmem(output, std::move(shmem));
 | 
						|
  } else {
 | 
						|
    MOZ_ASSERT(base->AsArrayBuffer());
 | 
						|
    Unused << SendDecodedData(output, base->AsArrayBuffer()->ExtractBuffer());
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvDrain() {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  if (!mCDM) {
 | 
						|
    GMP_LOG_DEBUG("ChromiumCDMChild::RecvDrain() no CDM");
 | 
						|
    Unused << SendDrainComplete();
 | 
						|
    return IPC_OK();
 | 
						|
  }
 | 
						|
  WidevineVideoFrame frame;
 | 
						|
  cdm::InputBuffer_2 sample = {};
 | 
						|
  cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDrain();  DecryptAndDecodeFrame() rv=%d",
 | 
						|
                rv);
 | 
						|
  if (rv == cdm::kSuccess) {
 | 
						|
    MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
 | 
						|
    ReturnOutput(frame);
 | 
						|
  } else {
 | 
						|
    Unused << SendDrainComplete();
 | 
						|
  }
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvDestroy() {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  GMP_LOG_DEBUG("ChromiumCDMChild::RecvDestroy()");
 | 
						|
 | 
						|
  MOZ_ASSERT(!mDecoderInitialized);
 | 
						|
 | 
						|
  mInitPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
 | 
						|
 | 
						|
  if (mCDM) {
 | 
						|
    mCDM->Destroy();
 | 
						|
    mCDM = nullptr;
 | 
						|
  }
 | 
						|
  mDestroyed = true;
 | 
						|
 | 
						|
  Unused << Send__delete__(this);
 | 
						|
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
mozilla::ipc::IPCResult ChromiumCDMChild::RecvGiveBuffer(ipc::Shmem&& aBuffer) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
 | 
						|
  GiveBuffer(std::move(aBuffer));
 | 
						|
  return IPC_OK();
 | 
						|
}
 | 
						|
 | 
						|
void ChromiumCDMChild::GiveBuffer(ipc::Shmem&& aBuffer) {
 | 
						|
  MOZ_ASSERT(IsOnMessageLoopThread());
 | 
						|
  size_t sz = aBuffer.Size<uint8_t>();
 | 
						|
  mBuffers.AppendElement(std::move(aBuffer));
 | 
						|
  GMP_LOG_DEBUG(
 | 
						|
      "ChromiumCDMChild::RecvGiveBuffer(capacity=%zu"
 | 
						|
      ") bufferSizes={%s} mDecoderInitialized=%d",
 | 
						|
      sz, ToString(mBuffers).get(), mDecoderInitialized);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla::gmp
 |