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

Quelle  DecoderAgent.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 "DecoderAgent.h"

#include "ImageContainer.h"
#include "MP4Decoder.h"
#include "MediaDataDecoderProxy.h"
#include "PDMFactory.h"
#include "VideoUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "nsThreadUtils.h"

extern mozilla::LazyLogModule gWebCodecsLog;

namespace mozilla {

#ifdef LOG_INTERNAL
#  undef LOG_INTERNAL
#endif  // LOG_INTERNAL
#define LOG_INTERNAL(level, msg, ...) \
  MOZ_LOG(gWebCodecsLog, LogLevel::level, (msg, ##__VA_ARGS__))

#ifdef LOG
#  undef LOG
#endif  // LOG
#define LOG(msg, ...) LOG_INTERNAL(Debug, msg, ##__VA_ARGS__)

#ifdef LOGW
#  undef LOGW
#endif  // LOGE
#define LOGW(msg, ...) LOG_INTERNAL(Warning, msg, ##__VA_ARGS__)

#ifdef LOGE
#  undef LOGE
#endif  // LOGE
#define LOGE(msg, ...) LOG_INTERNAL(Error, msg, ##__VA_ARGS__)

#ifdef LOGV
#  undef LOGV
#endif  // LOGV
#define LOGV(msg, ...) LOG_INTERNAL(Verbose, msg, ##__VA_ARGS__)

DecoderAgent::DecoderAgent(Id aId, UniquePtr<TrackInfo>&& aInfo)
    : mId(aId),
      mInfo(std::move(aInfo)),
      mOwnerThread(GetCurrentSerialEventTarget()),
      mPDMFactory(MakeRefPtr<PDMFactory>()),
      mImageContainer(MakeAndAddRef<layers::ImageContainer>(
          layers::ImageUsageType::WebCodecs,
          layers::ImageContainer::ASYNCHRONOUS)),
      mDecoder(nullptr),
      mState(State::Unconfigured) {
  MOZ_ASSERT(mInfo);
  MOZ_ASSERT(mOwnerThread);
  MOZ_ASSERT(mPDMFactory);
  MOZ_ASSERT(mImageContainer);
  LOG("DecoderAgent #%d (%p) ctor", mId, this);
}

DecoderAgent::~DecoderAgent() {
  LOG("DecoderAgent #%d (%p) dtor", mId, this);
  MOZ_ASSERT(mState == State::Unconfigured, "decoder release in wrong state");
  MOZ_ASSERT(!mDecoder, "decoder must be shutdown");
}

RefPtr<DecoderAgent::ConfigurePromise> DecoderAgent::Configure(
    bool aPreferSoftwareDecoder, bool aLowLatency) {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
  MOZ_ASSERT(mState == State::Unconfigured || mState == State::Error);
  MOZ_ASSERT(mConfigurePromise.IsEmpty());
  MOZ_ASSERT(!mCreateRequest.Exists());
  MOZ_ASSERT(!mInitRequest.Exists());

  if (mState == State::Error) {
    LOGE("DecoderAgent #%d (%p) tried to configure in error state", mId, this);
    return ConfigurePromise::CreateAndReject(
        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                    "Cannot configure in error state"),
        __func__);
  }

  MOZ_ASSERT(mState == State::Unconfigured);
  MOZ_ASSERT(!mDecoder);
  SetState(State::Configuring);

  RefPtr<layers::KnowsCompositor> knowsCompositor =
      layers::ImageBridgeChild::GetSingleton();

  auto params = CreateDecoderParams{
      *mInfo,
      CreateDecoderParams::OptionSet(
          aPreferSoftwareDecoder
              ? CreateDecoderParams::Option::HardwareDecoderNotAllowed
              : CreateDecoderParams::Option::Default),
      mInfo->GetType(), mImageContainer, knowsCompositor};
  if (aLowLatency) {
    params.mOptions += CreateDecoderParams::Option::LowLatency;
  }
  // MediaChangeMonitor is requested to decode raw AAC in ADTS.
  if (MP4Decoder::IsAAC(mInfo->mMimeType)) {
    params.mWrappers += media::Wrapper::MediaChangeMonitor;
  }
  // This should only be used for testing.
  if (StaticPrefs::media_test_null_decoder_creation_failure()) {
    params.mUseNullDecoder = CreateDecoderParams::UseNullDecoder(true);
  }

  // Always even use the pts that were set on the input samples when returning
  // decoded video frames.
  params.mOptions += CreateDecoderParams::Option::KeepOriginalPts;

  LOG("DecoderAgent #%d (%p) is creating a decoder (mime: %s) - PreferSW: %s, "
      "low-latency: %s, create-decoder-params: %s",
      mId, this, mInfo->mMimeType.get(), aPreferSoftwareDecoder ? "yes" : "no",
      aLowLatency ? "yes" : "no", params.ToString().get());

  RefPtr<ConfigurePromise> p = mConfigurePromise.Ensure(__func__);

  mPDMFactory->CreateDecoder(params)
      ->Then(
          mOwnerThread, __func__,
          [self = RefPtr{this}](RefPtr<MediaDataDecoder>&& aDecoder) {
            self->mCreateRequest.Complete();

            // If DecoderAgent has been shut down, shut the created decoder down
            // and return.
            if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
              MOZ_ASSERT(self->mState == State::ShuttingDown);
              MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
                         "configuration should have been rejected");

              LOGW(
                  "DecoderAgent #%d (%p) has been shut down. We need to shut "
                  "the newly created decoder down",
                  self->mId, self.get());
              aDecoder->Shutdown()->Then(
                  self->mOwnerThread, __func__,
                  [self](const ShutdownPromise::ResolveOrRejectValue& aValue) {
                    MOZ_ASSERT(self->mState == State::ShuttingDown);

                    LOGW(
                        "DecoderAgent #%d (%p), newly created decoder shutdown "
                        "has been %s",
                        self->mId, self.get(),
                        aValue.IsResolve() ? "resolved" : "rejected");

                    self->SetState(State::Unconfigured);

                    self->mShutdownWhileCreationPromise.ResolveOrReject(
                        aValue, __func__);
                  });
              return;
            }

            self->mDecoder = new MediaDataDecoderProxy(
                aDecoder.forget(),
                CreateMediaDecodeTaskQueue("DecoderAgent TaskQueue"));
            LOG("DecoderAgent #%d (%p) has created a decoder, now initialize "
                "it",
                self->mId, self.get());
            self->mDecoder->Init()
                ->Then(
                    self->mOwnerThread, __func__,
                    [self](const TrackInfo::TrackType aTrackType) {
                      self->mInitRequest.Complete();
                      LOG("DecoderAgent #%d (%p) has initialized the decoder",
                          self->mId, self.get());
                      MOZ_ASSERT(aTrackType == self->mInfo->GetType());
                      self->SetState(State::Configured);
                      self->mConfigurePromise.Resolve(true, __func__);
                    },
                    [self](const MediaResult& aError) {
                      self->mInitRequest.Complete();
                      LOGE(
                          "DecoderAgent #%d (%p) failed to initialize the "
                          "decoder",
                          self->mId, self.get());
                      self->SetState(State::Error);
                      self->mConfigurePromise.Reject(aError, __func__);
                    })
                ->Track(self->mInitRequest);
          },
          [self = RefPtr{this}](const MediaResult& aError) {
            self->mCreateRequest.Complete();
            LOGE("DecoderAgent #%d (%p) failed to create a decoder", self->mId,
                 self.get());

            // If DecoderAgent has been shut down, we need to resolve the
            // shutdown promise.
            if (!self->mShutdownWhileCreationPromise.IsEmpty()) {
              MOZ_ASSERT(self->mState == State::ShuttingDown);
              MOZ_ASSERT(self->mConfigurePromise.IsEmpty(),
                         "configuration should have been rejected");

              LOGW(
                  "DecoderAgent #%d (%p) has been shut down. Resolve the "
                  "shutdown promise right away since decoder creation failed",
                  self->mId, self.get());

              self->SetState(State::Unconfigured);
              self->mShutdownWhileCreationPromise.Resolve(true, __func__);
              return;
            }

            self->SetState(State::Error);
            self->mConfigurePromise.Reject(aError, __func__);
          })
      ->Track(mCreateRequest);

  return p;
}

RefPtr<ShutdownPromise> DecoderAgent::Shutdown() {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());

  LOG("DecoderAgent #%d (%p), shutdown in %s state", mId, this,
      EnumValueToString(mState));

  MOZ_ASSERT(mShutdownWhileCreationPromise.IsEmpty(),
             "Shutdown while shutting down is prohibited");

  auto r =
      MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, "Canceled by decoder shutdown");

  // If the decoder creation has not been completed yet, wait until the decoder
  // being created has been shut down.
  if (mCreateRequest.Exists()) {
    MOZ_ASSERT(!mInitRequest.Exists());
    MOZ_ASSERT(!mConfigurePromise.IsEmpty());
    MOZ_ASSERT(!mDecoder);
    MOZ_ASSERT(mState == State::Configuring);

    LOGW(
        "DecoderAgent #%d (%p) shutdown while the decoder-creation for "
        "configuration is in flight. Reject the configuration now and defer "
        "the shutdown until the created decoder has been shut down",
        mId, this);

    // Reject the configuration in flight.
    mConfigurePromise.Reject(r, __func__);

    // Get the promise that will be resolved when the decoder being created has
    // been destroyed.
    SetState(State::ShuttingDown);
    return mShutdownWhileCreationPromise.Ensure(__func__);
  }

  // If decoder creation has been completed but failed, no decoder is set.
  if (!mDecoder) {
    LOG("DecoderAgent #%d (%p) shutdown without an active decoder", mId, this);
    MOZ_ASSERT(mState == State::Error);
    MOZ_ASSERT(!mInitRequest.Exists());
    MOZ_ASSERT(mConfigurePromise.IsEmpty());
    MOZ_ASSERT(!mDecodeRequest.Exists());
    MOZ_ASSERT(mDecodePromise.IsEmpty());
    MOZ_ASSERT(!mDrainRequest.Exists());
    MOZ_ASSERT(!mFlushRequest.Exists());
    MOZ_ASSERT(!mDryRequest.Exists());
    MOZ_ASSERT(mDryPromise.IsEmpty());
    MOZ_ASSERT(mDrainAndFlushPromise.IsEmpty());
    // ~DecoderAgent() will ensure that the decoder is shutdown.
    SetState(State::Unconfigured);
    return ShutdownPromise::CreateAndResolve(true, __func__);
  }

  // If decoder creation has succeeded, we must have the decoder now.

  // Cancel pending initialization for configuration in flight if any.
  mInitRequest.DisconnectIfExists();
  mConfigurePromise.RejectIfExists(r, __func__);

  // Cancel decode in flight if any.
  mDecodeRequest.DisconnectIfExists();
  mDecodePromise.RejectIfExists(r, __func__);

  // Cancel flush-out in flight if any.
  mDrainRequest.DisconnectIfExists();
  mFlushRequest.DisconnectIfExists();
  mDryRequest.DisconnectIfExists();
  mDryPromise.RejectIfExists(r, __func__);
  mDrainAndFlushPromise.RejectIfExists(r, __func__);
  mDryData.Clear();
  mDrainAndFlushData.Clear();

  SetState(State::Unconfigured);

  RefPtr<MediaDataDecoder> decoder = std::move(mDecoder);
  return decoder->Shutdown();
}

RefPtr<DecoderAgent::DecodePromise> DecoderAgent::Decode(
    MediaRawData* aSample) {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
  MOZ_ASSERT(aSample);
  MOZ_ASSERT(mState == State::Configured || mState == State::Error);
  MOZ_ASSERT(mDecodePromise.IsEmpty());
  MOZ_ASSERT(!mDecodeRequest.Exists());

  if (mState == State::Error) {
    LOGE("DecoderAgent #%d (%p) tried to decode in error state", mId, this);
    return DecodePromise::CreateAndReject(
        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                    "Cannot decode in error state"),
        __func__);
  }

  MOZ_ASSERT(mState == State::Configured);
  MOZ_ASSERT(mDecoder);
  SetState(State::Decoding);

  RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);

  mDecoder->Decode(aSample)
      ->Then(
          mOwnerThread, __func__,
          [self = RefPtr{this}](MediaDataDecoder::DecodedData&& aData) {
            self->mDecodeRequest.Complete();
            LOGV("DecoderAgent #%d (%p) decode successfully", self->mId,
                 self.get());
            self->SetState(State::Configured);
            self->mDecodePromise.Resolve(std::move(aData), __func__);
          },
          [self = RefPtr{this}](const MediaResult& aError) {
            self->mDecodeRequest.Complete();
            LOGV("DecoderAgent #%d (%p) failed to decode", self->mId,
                 self.get());
            self->SetState(State::Error);
            self->mDecodePromise.Reject(aError, __func__);
          })
      ->Track(mDecodeRequest);

  return p;
}

RefPtr<DecoderAgent::DecodePromise> DecoderAgent::DrainAndFlush() {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
  MOZ_ASSERT(mState == State::Configured || mState == State::Error);
  MOZ_ASSERT(mDrainAndFlushPromise.IsEmpty());
  MOZ_ASSERT(mDrainAndFlushData.IsEmpty());
  MOZ_ASSERT(!mDryRequest.Exists());
  MOZ_ASSERT(mDryPromise.IsEmpty());
  MOZ_ASSERT(mDryData.IsEmpty());
  MOZ_ASSERT(!mDrainRequest.Exists());
  MOZ_ASSERT(!mFlushRequest.Exists());

  if (mState == State::Error) {
    LOGE("DecoderAgent #%d (%p) tried to flush-out in error state", mId, this);
    return DecodePromise::CreateAndReject(
        MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                    "Cannot flush in error state"),
        __func__);
  }

  MOZ_ASSERT(mState == State::Configured);
  MOZ_ASSERT(mDecoder);
  SetState(State::Flushing);

  RefPtr<DecoderAgent::DecodePromise> p =
      mDrainAndFlushPromise.Ensure(__func__);

  Dry()
      ->Then(
          mOwnerThread, __func__,
          [self = RefPtr{this}](MediaDataDecoder::DecodedData&& aData) {
            self->mDryRequest.Complete();
            LOG("DecoderAgent #%d (%p) has dried the decoder. Now flushing the "
                "decoder",
                self->mId, self.get());
            MOZ_ASSERT(self->mDrainAndFlushData.IsEmpty());
            self->mDrainAndFlushData.AppendElements(std::move(aData));
            self->mDecoder->Flush()
                ->Then(
                    self->mOwnerThread, __func__,
                    [self](const bool /* aUnUsed */) {
                      self->mFlushRequest.Complete();
                      LOG("DecoderAgent #%d (%p) has flushed the decoder",
                          self->mId, self.get());
                      self->SetState(State::Configured);
                      self->mDrainAndFlushPromise.Resolve(
                          std::move(self->mDrainAndFlushData), __func__);
                    },
                    [self](const MediaResult& aError) {
                      self->mFlushRequest.Complete();
                      LOGE("DecoderAgent #%d (%p) failed to flush the decoder",
                           self->mId, self.get());
                      self->SetState(State::Error);
                      self->mDrainAndFlushData.Clear();
                      self->mDrainAndFlushPromise.Reject(aError, __func__);
                    })
                ->Track(self->mFlushRequest);
          },
          [self = RefPtr{this}](const MediaResult& aError) {
            self->mDryRequest.Complete();
            LOGE("DecoderAgent #%d (%p) failed to dry the decoder", self->mId,
                 self.get());
            self->SetState(State::Error);
            self->mDrainAndFlushPromise.Reject(aError, __func__);
          })
      ->Track(mDryRequest);

  return p;
}

RefPtr<DecoderAgent::DecodePromise> DecoderAgent::Dry() {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
  MOZ_ASSERT(mState == State::Flushing);
  MOZ_ASSERT(mDryPromise.IsEmpty());
  MOZ_ASSERT(!mDryRequest.Exists());
  MOZ_ASSERT(mDryData.IsEmpty());
  MOZ_ASSERT(mDecoder);

  RefPtr<DecodePromise> p = mDryPromise.Ensure(__func__);
  DrainUntilDry();
  return p;
}

void DecoderAgent::DrainUntilDry() {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());
  MOZ_ASSERT(mState == State::Flushing);
  MOZ_ASSERT(!mDryPromise.IsEmpty());
  MOZ_ASSERT(!mDrainRequest.Exists());
  MOZ_ASSERT(mDecoder);

  LOG("DecoderAgent #%d (%p) is drainng the decoder", mId, this);
  mDecoder->Drain()
      ->Then(
          mOwnerThread, __func__,
          [self = RefPtr{this}](MediaDataDecoder::DecodedData&& aData) {
            self->mDrainRequest.Complete();

            if (aData.IsEmpty()) {
              LOG("DecoderAgent #%d (%p) is dry now", self->mId, self.get());
              self->mDryPromise.Resolve(std::move(self->mDryData), __func__);
              return;
            }

            LOG("DecoderAgent #%d (%p) drained %zu decoded data. Keep draining "
                "until dry",
                self->mId, self.get(), aData.Length());
            self->mDryData.AppendElements(std::move(aData));
            self->DrainUntilDry();
          },
          [self = RefPtr{this}](const MediaResult& aError) {
            self->mDrainRequest.Complete();

            LOGE("DecoderAgent %p failed to drain decoder", self.get());
            self->mDryData.Clear();
            self->mDryPromise.Reject(aError, __func__);
          })
      ->Track(mDrainRequest);
}

void DecoderAgent::SetState(State aState) {
  MOZ_ASSERT(mOwnerThread->IsOnCurrentThread());

  auto validateStateTransition = [](State aOldState, State aNewState) {
    switch (aOldState) {
      case State::Unconfigured:
        return aNewState == State::Configuring;
      case State::Configuring:
        return aNewState == State::Configured || aNewState == State::Error ||
               aNewState == State::Unconfigured ||
               aNewState == State::ShuttingDown;
      case State::Configured:
        return aNewState == State::Unconfigured ||
               aNewState == State::Decoding || aNewState == State::Flushing;
      case State::Decoding:
      case State::Flushing:
        return aNewState == State::Configured || aNewState == State::Error ||
               aNewState == State::Unconfigured;
      case State::ShuttingDown:
        return aNewState == State::Unconfigured;
      case State::Error:
        return aNewState == State::Unconfigured;
      default:
        break;
    }
    MOZ_ASSERT_UNREACHABLE("Unhandled state transition");
    return false;
  };

  DebugOnly<bool> isValid = validateStateTransition(mState, aState);
  MOZ_ASSERT(isValid);
  LOG("DecoderAgent #%d (%p) state change: %s -> %s", mId, this,
      EnumValueToString(mState), EnumValueToString(aState));
  mState = aState;
}

#undef LOG
#undef LOGW
#undef LOGE
#undef LOGV
#undef LOG_INTERNAL

}  // namespace mozilla

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

¤ 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.0.14Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






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.