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

Quelle  EMEDecoderModule.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "EMEDecoderModule.h"

#include <inttypes.h>

#include "Adts.h"
#include "BlankDecoderModule.h"
#include "ChromiumCDMVideoDecoder.h"
#include "DecryptThroughputLimit.h"
#include "GMPDecoderModule.h"
#include "GMPService.h"
#include "GMPVideoDecoder.h"
#include "MP4Decoder.h"
#include "MediaInfo.h"
#include "PDMFactory.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/EMEUtils.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "nsClassHashtable.h"
#include "nsServiceManagerUtils.h"

namespace mozilla {

using DecryptPromiseRequestHolder = MozPromiseRequestHolder<DecryptPromise>;

DDLoggedTypeDeclNameAndBase(EMEDecryptor, MediaDataDecoder);

class ADTSSampleConverter {
 public:
  explicit ADTSSampleConverter(const AudioInfo& aInfo)
      : mNumChannels(aInfo.mChannels)
        // Note: we set profile to 2 if we encounter an extended profile (which
        // set mProfile to 0 and then set mExtendedProfile) such as HE-AACv2
        // (profile 5). These can then pass through conversion to ADTS and back.
        // This is done as ADTS only has 2 bits for profile, and the transform
        // subtracts one from the value. We check if the profile supplied is > 4
        // for safety. 2 is used as a fallback value, though it seems the CDM
        // doesn't care what is set.
        ,
        mProfile(aInfo.mProfile < 1 || aInfo.mProfile > 4 ? 2 : aInfo.mProfile),
        mFrequencyIndex(ADTS::GetFrequencyIndex(aInfo.mRate).unwrapOr(255)) {
    EME_LOG("ADTSSampleConvertor(): aInfo.mProfile=%" PRIi8
            " aInfo.mExtendedProfile=%" PRIi8,
            aInfo.mProfile, aInfo.mExtendedProfile);
    if (aInfo.mProfile < 1 || aInfo.mProfile > 4) {
      EME_LOG(
          "ADTSSampleConvertor(): Profile not in [1, 4]! Samples will "
          "their profile set to 2!");
    }
  }
  bool Convert(MediaRawData* aSample) const {
    return ADTS::ConvertSample(mNumChannels, mFrequencyIndex, mProfile,
                               aSample);
  }
  bool Revert(MediaRawData* aSample) const {
    return ADTS::RevertSample(aSample);
  }

 private:
  const uint32_t mNumChannels;
  const uint8_t mProfile;
  const uint8_t mFrequencyIndex{};
};

class EMEDecryptor final : public MediaDataDecoder,
                           public DecoderDoctorLifeLogger<EMEDecryptor> {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(EMEDecryptor, final);

  EMEDecryptor(MediaDataDecoder* aDecoder, CDMProxy* aProxy,
               TrackInfo::TrackType aType,
               const std::function<MediaEventProducer<TrackInfo::TrackType>*()>&
                   aOnWaitingForKey,
               UniquePtr<ADTSSampleConverter> aConverter = nullptr)
      : mDecoder(aDecoder),
        mProxy(aProxy),
        mSamplesWaitingForKey(
            new SamplesWaitingForKey(mProxy, aType, aOnWaitingForKey)),
        mADTSSampleConverter(std::move(aConverter)),
        mIsShutdown(false) {
    DDLINKCHILD("decoder", mDecoder.get());
  }

  RefPtr<InitPromise> Init() override {
    MOZ_ASSERT(!mIsShutdown);
    mThread = GetCurrentSerialEventTarget();
    uint32_t maxThroughputMs = StaticPrefs::media_eme_max_throughput_ms();
    EME_LOG("EME max-throughput-ms=%" PRIu32, maxThroughputMs);
    mThroughputLimiter.emplace(mThread, maxThroughputMs);

    return mDecoder->Init();
  }

  RefPtr<DecodePromise> Decode(MediaRawData* aSample) override {
    MOZ_ASSERT(mThread->IsOnCurrentThread());
    MOZ_RELEASE_ASSERT(mDecrypts.Count() == 0,
                       "Can only process one sample at a time");
    RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);

    RefPtr<EMEDecryptor> self = this;
    mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)
        ->Then(
            mThread, __func__,
            [self](const RefPtr<MediaRawData>& aSample) {
              self->mKeyRequest.Complete();
              self->ThrottleDecode(aSample);
            },
            [self]() { self->mKeyRequest.Complete(); })
        ->Track(mKeyRequest);
    return p;
  }

  void ThrottleDecode(MediaRawData* aSample) {
    MOZ_ASSERT(mThread->IsOnCurrentThread());

    RefPtr<EMEDecryptor> self = this;
    mThroughputLimiter->Throttle(aSample)
        ->Then(
            mThread, __func__,
            [self](const RefPtr<MediaRawData>& aSample) {
              self->mThrottleRequest.Complete();
              self->AttemptDecode(aSample);
            },
            [self]() { self->mThrottleRequest.Complete(); })
        ->Track(mThrottleRequest);
  }

  void AttemptDecode(MediaRawData* aSample) {
    MOZ_ASSERT(mThread->IsOnCurrentThread());
    if (mIsShutdown) {
      NS_WARNING("EME encrypted sample arrived after shutdown");
      mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
      return;
    }

    if (mADTSSampleConverter && !mADTSSampleConverter->Convert(aSample)) {
      mDecodePromise.RejectIfExists(
          MediaResult(
              NS_ERROR_DOM_MEDIA_FATAL_ERR,
              RESULT_DETAIL("Failed to convert encrypted AAC sample to ADTS")),
          __func__);
      return;
    }

    const auto& decrypt = mDecrypts.InsertOrUpdate(
        aSample, MakeUnique<DecryptPromiseRequestHolder>());
    mProxy->Decrypt(aSample)
        ->Then(mThread, __func__, this, &EMEDecryptor::Decrypted,
               &EMEDecryptor::Decrypted)
        ->Track(*decrypt);
  }

  void Decrypted(const DecryptResult& aDecrypted) {
    MOZ_ASSERT(mThread->IsOnCurrentThread());
    MOZ_ASSERT(aDecrypted.mSample);

    UniquePtr<DecryptPromiseRequestHolder> holder;
    mDecrypts.Remove(aDecrypted.mSample, &holder);
    if (holder) {
      holder->Complete();
    } else {
      // Decryption is not in the list of decrypt operations waiting
      // for a result. It must have been flushed or drained. Ignore result.
      return;
    }

    if (mADTSSampleConverter &&
        !mADTSSampleConverter->Revert(aDecrypted.mSample)) {
      mDecodePromise.RejectIfExists(
          MediaResult(
              NS_ERROR_DOM_MEDIA_FATAL_ERR,
              RESULT_DETAIL("Failed to revert decrypted ADTS sample to AAC")),
          __func__);
      return;
    }

    if (mIsShutdown) {
      NS_WARNING("EME decrypted sample arrived after shutdown");
      return;
    }

    if (aDecrypted.mStatus == eme::NoKeyErr) {
      // Key became unusable after we sent the sample to CDM to decrypt.
      // Call Decode() again, so that the sample is enqueued for decryption
      // if the key becomes usable again.
      AttemptDecode(aDecrypted.mSample);
    } else if (aDecrypted.mStatus != eme::Ok) {
      mDecodePromise.RejectIfExists(
          MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                      RESULT_DETAIL("decrypted.mStatus=%u",
                                    uint32_t(aDecrypted.mStatus))),
          __func__);
    } else {
      MOZ_ASSERT(!mIsShutdown);
      // The sample is no longer encrypted, so clear its crypto metadata.
      UniquePtr<MediaRawDataWriter> writer(aDecrypted.mSample->CreateWriter());
      writer->mCrypto = CryptoSample();
      RefPtr<EMEDecryptor> self = this;
      mDecoder->Decode(aDecrypted.mSample)
          ->Then(mThread, __func__,
                 [self](DecodePromise::ResolveOrRejectValue&& aValue) {
                   self->mDecodeRequest.Complete();
                   self->mDecodePromise.ResolveOrReject(std::move(aValue),
                                                        __func__);
                 })
          ->Track(mDecodeRequest);
    }
  }

  RefPtr<FlushPromise> Flush() override {
    MOZ_ASSERT(mThread->IsOnCurrentThread());
    MOZ_ASSERT(!mIsShutdown);
    mKeyRequest.DisconnectIfExists();
    mThrottleRequest.DisconnectIfExists();
    mDecodeRequest.DisconnectIfExists();
    mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    mThroughputLimiter->Flush();
    for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
      auto* holder = iter.UserData();
      holder->DisconnectIfExists();
      iter.Remove();
    }
    RefPtr<SamplesWaitingForKey> k = mSamplesWaitingForKey;
    return mDecoder->Flush()->Then(mThread, __func__, [k]() {
      k->Flush();
      return FlushPromise::CreateAndResolve(true, __func__);
    });
  }

  RefPtr<DecodePromise> Drain() override {
    MOZ_ASSERT(mThread->IsOnCurrentThread());
    MOZ_ASSERT(!mIsShutdown);
    MOZ_ASSERT(mDecodePromise.IsEmpty() && !mDecodeRequest.Exists(),
               "Must wait for decoding to complete");
    for (auto iter = mDecrypts.Iter(); !iter.Done(); iter.Next()) {
      auto* holder = iter.UserData();
      holder->DisconnectIfExists();
      iter.Remove();
    }
    return mDecoder->Drain();
  }

  RefPtr<ShutdownPromise> Shutdown() override {
    // mThread may not be set if Init hasn't been called first.
    MOZ_ASSERT(!mThread || mThread->IsOnCurrentThread());
    MOZ_ASSERT(!mIsShutdown);
    mIsShutdown = true;
    mSamplesWaitingForKey->BreakCycles();
    mSamplesWaitingForKey = nullptr;
    RefPtr<MediaDataDecoder> decoder = std::move(mDecoder);
    mProxy = nullptr;
    return decoder->Shutdown();
  }

  nsCString GetProcessName() const override {
    return mDecoder->GetProcessName();
  }

  nsCString GetDescriptionName() const override {
    return mDecoder->GetDescriptionName();
  }

  nsCString GetCodecName() const override { return mDecoder->GetCodecName(); }

  ConversionRequired NeedsConversion() const override {
    return mDecoder->NeedsConversion();
  }

 private:
  ~EMEDecryptor() = default;

  RefPtr<MediaDataDecoder> mDecoder;
  nsCOMPtr<nsISerialEventTarget> mThread;
  RefPtr<CDMProxy> mProxy;
  nsClassHashtable<nsRefPtrHashKey<MediaRawData>, DecryptPromiseRequestHolder>
      mDecrypts;
  RefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
  MozPromiseRequestHolder<SamplesWaitingForKey::WaitForKeyPromise> mKeyRequest;
  Maybe<DecryptThroughputLimit> mThroughputLimiter;
  MozPromiseRequestHolder<DecryptThroughputLimit::ThrottlePromise>
      mThrottleRequest;
  MozPromiseHolder<DecodePromise> mDecodePromise;
  MozPromiseHolder<DecodePromise> mDrainPromise;
  MozPromiseHolder<FlushPromise> mFlushPromise;
  MozPromiseRequestHolder<DecodePromise> mDecodeRequest;
  UniquePtr<ADTSSampleConverter> mADTSSampleConverter;
  bool mIsShutdown;
};

EMEMediaDataDecoderProxy::EMEMediaDataDecoderProxy(
    const CreateDecoderParams& aParams,
    already_AddRefed<MediaDataDecoder> aProxyDecoder,
    already_AddRefed<nsISerialEventTarget> aProxyThread, CDMProxy* aProxy)
    : MediaDataDecoderProxy(std::move(aProxyDecoder), std::move(aProxyThread)),
      mThread(GetCurrentSerialEventTarget()),
      mSamplesWaitingForKey(new SamplesWaitingForKey(
          aProxy, aParams.mType, aParams.mOnWaitingForKeyEvent)),
      mProxy(aProxy) {}

EMEMediaDataDecoderProxy::EMEMediaDataDecoderProxy(
    const CreateDecoderParams& aParams,
    already_AddRefed<MediaDataDecoder> aProxyDecoder, CDMProxy* aProxy)
    : MediaDataDecoderProxy(std::move(aProxyDecoder),
                            do_AddRef(GetCurrentSerialEventTarget())),
      mThread(GetCurrentSerialEventTarget()),
      mSamplesWaitingForKey(new SamplesWaitingForKey(
          aProxy, aParams.mType, aParams.mOnWaitingForKeyEvent)),
      mProxy(aProxy) {}

RefPtr<MediaDataDecoder::DecodePromise> EMEMediaDataDecoderProxy::Decode(
    MediaRawData* aSample) {
  RefPtr<EMEMediaDataDecoderProxy> self = this;
  RefPtr<MediaRawData> sample = aSample;
  return InvokeAsync(mThread, __func__, [self, this, sample]() {
    RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
    mSamplesWaitingForKey->WaitIfKeyNotUsable(sample)
        ->Then(
            mThread, __func__,
            [self, this](const RefPtr<MediaRawData>& aSample) {
              mKeyRequest.Complete();

              MediaDataDecoderProxy::Decode(aSample)
                  ->Then(mThread, __func__,
                         [self,
                          this](DecodePromise::ResolveOrRejectValue&& aValue) {
                           mDecodeRequest.Complete();
                           mDecodePromise.ResolveOrReject(std::move(aValue),
                                                          __func__);
                         })
                  ->Track(mDecodeRequest);
            },
            [self]() {
              self->mKeyRequest.Complete();
              MOZ_CRASH("Should never get here");
            })
        ->Track(mKeyRequest);

    return p;
  });
}

RefPtr<MediaDataDecoder::FlushPromise> EMEMediaDataDecoderProxy::Flush() {
  RefPtr<EMEMediaDataDecoderProxy> self = this;
  return InvokeAsync(mThread, __func__, [self, this]() {
    mKeyRequest.DisconnectIfExists();
    mDecodeRequest.DisconnectIfExists();
    mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
    return MediaDataDecoderProxy::Flush();
  });
}

RefPtr<ShutdownPromise> EMEMediaDataDecoderProxy::Shutdown() {
  RefPtr<EMEMediaDataDecoderProxy> self = this;
  return InvokeAsync(mThread, __func__, [self, this]() {
    mSamplesWaitingForKey->BreakCycles();
    mSamplesWaitingForKey = nullptr;
    mProxy = nullptr;
    return MediaDataDecoderProxy::Shutdown();
  });
}

EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy, PDMFactory* aPDM)
    : mProxy(aProxy), mPDM(aPDM) {}

EMEDecoderModule::~EMEDecoderModule() = default;

static already_AddRefed<MediaDataDecoderProxy> CreateDecoderWrapper(
    CDMProxy* aProxy, const CreateDecoderParams& aParams) {
  RefPtr<gmp::GeckoMediaPluginService> s(
      gmp::GeckoMediaPluginService::GetGeckoMediaPluginService());
  if (!s) {
    return nullptr;
  }
  nsCOMPtr<nsISerialEventTarget> thread(s->GetGMPThread());
  if (!thread) {
    return nullptr;
  }
  RefPtr<MediaDataDecoderProxy> decoder(
      new EMEMediaDataDecoderProxy(aParams,
                                   do_AddRef(new ChromiumCDMVideoDecoder(
                                       GMPVideoDecoderParams(aParams), aProxy)),
                                   thread.forget(), aProxy));
  return decoder.forget();
}

RefPtr<EMEDecoderModule::CreateDecoderPromise>
EMEDecoderModule::AsyncCreateDecoder(const CreateDecoderParams& aParams) {
  MOZ_ASSERT(aParams.mConfig.mCrypto.IsEncrypted() ||
             aParams.mEncryptedCustomIdent ==
                 CreateDecoderParams::EncryptedCustomIdent::True);
  MOZ_ASSERT(mPDM);

  if (aParams.mConfig.IsVideo()) {
    if (StaticPrefs::media_eme_video_blank()) {
      EME_LOG(
          "EMEDecoderModule::CreateVideoDecoder() creating a blank decoder.");
      RefPtr<PlatformDecoderModule> m(BlankDecoderModule::Create());
      RefPtr<MediaDataDecoder> decoder = m->CreateVideoDecoder(aParams);
      return EMEDecoderModule::CreateDecoderPromise::CreateAndResolve(decoder,
                                                                      __func__);
    }

    if (!SupportsMimeType(aParams.mConfig.mMimeType, nullptr).isEmpty()) {
      // GMP decodes. Assume that means it can decrypt too.
      return EMEDecoderModule::CreateDecoderPromise::CreateAndResolve(
          CreateDecoderWrapper(mProxy, aParams), __func__);
    }

    RefPtr<EMEDecoderModule::CreateDecoderPromise> p =
        mPDM->CreateDecoder(aParams)->Then(
            GetCurrentSerialEventTarget(), __func__,
            [self = RefPtr{this},
             params = CreateDecoderParamsForAsync(aParams)](
                RefPtr<MediaDataDecoder>&& aDecoder) {
              RefPtr<MediaDataDecoder> emeDecoder(
                  new EMEDecryptor(aDecoder, self->mProxy, params.mType,
                                   params.mOnWaitingForKeyEvent));
              return EMEDecoderModule::CreateDecoderPromise::CreateAndResolve(
                  emeDecoder, __func__);
            },
            [](const MediaResult& aError) {
              return EMEDecoderModule::CreateDecoderPromise::CreateAndReject(
                  aError, __func__);
            });
    return p;
  }

  MOZ_ASSERT(aParams.mConfig.IsAudio());

  // We don't support using the GMP to decode audio.
  MOZ_ASSERT(SupportsMimeType(aParams.mConfig.mMimeType, nullptr).isEmpty());
  MOZ_ASSERT(mPDM);

  if (StaticPrefs::media_eme_audio_blank()) {
    EME_LOG("EMEDecoderModule::CreateAudioDecoder() creating a blank decoder.");
    RefPtr<PlatformDecoderModule> m(BlankDecoderModule::Create());
    RefPtr<MediaDataDecoder> decoder = m->CreateAudioDecoder(aParams);
    return EMEDecoderModule::CreateDecoderPromise::CreateAndResolve(decoder,
                                                                    __func__);
  }

  UniquePtr<ADTSSampleConverter> converter = nullptr;
  if (MP4Decoder::IsAAC(aParams.mConfig.mMimeType)) {
    // The CDM expects encrypted AAC to be in ADTS format.
    // See bug 1433344.
    converter = MakeUnique<ADTSSampleConverter>(aParams.AudioConfig());
  }

  RefPtr<EMEDecoderModule::CreateDecoderPromise> p =
      mPDM->CreateDecoder(aParams)->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self = RefPtr{this}, params = CreateDecoderParamsForAsync(aParams),
           converter = std::move(converter)](
              RefPtr<MediaDataDecoder>&& aDecoder) mutable {
            RefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(
                aDecoder, self->mProxy, params.mType,
                params.mOnWaitingForKeyEvent, std::move(converter)));
            return EMEDecoderModule::CreateDecoderPromise::CreateAndResolve(
                emeDecoder, __func__);
          },
          [](const MediaResult& aError) {
            return EMEDecoderModule::CreateDecoderPromise::CreateAndReject(
                aError, __func__);
          });
  return p;
}

media::DecodeSupportSet EMEDecoderModule::SupportsMimeType(
    const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
  Maybe<nsCString> keySystem;
  keySystem.emplace(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
  return GMPDecoderModule::SupportsMimeType(
      aMimeType, nsLiteralCString(CHROMIUM_CDM_API), keySystem);
}

}  // namespace mozilla

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

¤ Dauer der Verarbeitung: 0.14 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.