Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/gmp/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 32 kB image not shown  

Quelle  ChromiumCDMChild.cpp   Sprache: 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_11* 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 charconst 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());
}

void ChromiumCDMChild::ReportMetrics(cdm::MetricName aMetricName,
                                     uint64_t aValue) {
  GMP_LOG_DEBUG("ChromiumCDMChild::ReportMetrics() aMetricName=%" PRIu32
                ", aValue=%" PRIu64,
                aMetricName, aValue);
}

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::kYPlane),
                      aFrame.Stride(cdm::kYPlane)};
  output.mUPlane() = {aFrame.PlaneOffset(cdm::kUPlane),
                      aFrame.Stride(cdm::kUPlane)};
  output.mVPlane() = {aFrame.PlaneOffset(cdm::kVPlane),
                      aFrame.Stride(cdm::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

Messung V0.5
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.