tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaDecoder.cpp (59087B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MediaDecoder.h"
      8 
      9 #include <algorithm>
     10 #include <cmath>
     11 #include <limits>
     12 
     13 #include "AudioDeviceInfo.h"
     14 #include "DOMMediaStream.h"
     15 #include "ImageContainer.h"
     16 #include "MediaDecoderStateMachineBase.h"
     17 #include "MediaFormatReader.h"
     18 #include "MediaResource.h"
     19 #include "MediaShutdownManager.h"
     20 #include "MediaTrackGraph.h"
     21 #include "TelemetryProbesReporter.h"
     22 #include "VideoFrameContainer.h"
     23 #include "VideoUtils.h"
     24 #include "WindowRenderer.h"
     25 #include "mozilla/AbstractThread.h"
     26 #include "mozilla/Preferences.h"
     27 #include "mozilla/StaticPrefs_media.h"
     28 #include "mozilla/StaticPtr.h"
     29 #include "mozilla/dom/DOMTypes.h"
     30 #include "mozilla/glean/DomMediaMetrics.h"
     31 #include "mozilla/glean/DomMediaPlatformsWmfMetrics.h"
     32 #include "nsComponentManagerUtils.h"
     33 #include "nsContentUtils.h"
     34 #include "nsError.h"
     35 #include "nsIMemoryReporter.h"
     36 #include "nsPrintfCString.h"
     37 #include "nsServiceManagerUtils.h"
     38 #include "nsTArray.h"
     39 
     40 using namespace mozilla::dom;
     41 using namespace mozilla::layers;
     42 using namespace mozilla::media;
     43 
     44 namespace mozilla {
     45 
     46 // avoid redefined macro in unified build
     47 #undef LOG
     48 #undef DUMP
     49 
     50 LazyLogModule gMediaDecoderLog("MediaDecoder");
     51 
     52 #define LOG(x, ...) \
     53  DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
     54 
     55 #define DUMP(x, ...) printf_stderr(x "\n", ##__VA_ARGS__)
     56 
     57 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
     58 
     59 class MediaMemoryTracker : public nsIMemoryReporter {
     60  virtual ~MediaMemoryTracker();
     61 
     62  NS_DECL_THREADSAFE_ISUPPORTS
     63  NS_DECL_NSIMEMORYREPORTER
     64 
     65  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
     66 
     67  MediaMemoryTracker();
     68  void InitMemoryReporter();
     69 
     70  static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
     71 
     72  static MediaMemoryTracker* UniqueInstance() {
     73    if (!sUniqueInstance) {
     74      sUniqueInstance = new MediaMemoryTracker();
     75      sUniqueInstance->InitMemoryReporter();
     76    }
     77    return sUniqueInstance;
     78  }
     79 
     80  using DecodersArray = nsTArray<MediaDecoder*>;
     81  static DecodersArray& Decoders() { return UniqueInstance()->mDecoders; }
     82 
     83  DecodersArray mDecoders;
     84 
     85 public:
     86  static void AddMediaDecoder(MediaDecoder* aDecoder) {
     87    Decoders().AppendElement(aDecoder);
     88  }
     89 
     90  static void RemoveMediaDecoder(MediaDecoder* aDecoder) {
     91    DecodersArray& decoders = Decoders();
     92    decoders.RemoveElement(aDecoder);
     93    if (decoders.IsEmpty()) {
     94      sUniqueInstance = nullptr;
     95    }
     96  }
     97 };
     98 
     99 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
    100 
    101 LazyLogModule gMediaTimerLog("MediaTimer");
    102 
    103 constexpr TimeUnit MediaDecoder::DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED;
    104 
    105 void MediaDecoder::InitStatics() {
    106  MOZ_ASSERT(NS_IsMainThread());
    107  // Eagerly init gMediaDecoderLog to work around bug 1415441.
    108  MOZ_LOG(gMediaDecoderLog, LogLevel::Info, ("MediaDecoder::InitStatics"));
    109 
    110  if (XRE_IsParentProcess()) {
    111    // Lock Utility process preferences so that people cannot opt-out of
    112    // Utility process
    113    Preferences::Lock("media.utility-process.enabled");
    114  }
    115 }
    116 
    117 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
    118 
    119 void MediaDecoder::NotifyOwnerActivityChanged(bool aIsOwnerInvisible,
    120                                              bool aIsOwnerConnected,
    121                                              bool aIsOwnerInBackground,
    122                                              bool aHasOwnerPendingCallbacks) {
    123  MOZ_ASSERT(NS_IsMainThread());
    124  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    125  SetElementVisibility(aIsOwnerInvisible, aIsOwnerConnected,
    126                       aIsOwnerInBackground, aHasOwnerPendingCallbacks);
    127 
    128  NotifyCompositor();
    129 }
    130 
    131 void MediaDecoder::Pause() {
    132  MOZ_ASSERT(NS_IsMainThread());
    133  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    134  LOG("Pause");
    135  if (mPlayState == PLAY_STATE_LOADING || IsEnded()) {
    136    mNextState = PLAY_STATE_PAUSED;
    137    return;
    138  }
    139  ChangeState(PLAY_STATE_PAUSED);
    140 }
    141 
    142 void MediaDecoder::SetVolume(double aVolume) {
    143  MOZ_ASSERT(NS_IsMainThread());
    144  mVolume = aVolume;
    145 }
    146 
    147 RefPtr<GenericPromise> MediaDecoder::SetSink(AudioDeviceInfo* aSinkDevice) {
    148  MOZ_ASSERT(NS_IsMainThread());
    149  mSinkDevice = aSinkDevice;
    150  return GetStateMachine()->InvokeSetSink(aSinkDevice);
    151 }
    152 
    153 void MediaDecoder::SetOutputCaptureState(OutputCaptureState aState,
    154                                         SharedDummyTrack* aDummyTrack) {
    155  MOZ_ASSERT(NS_IsMainThread());
    156  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
    157  MOZ_ASSERT_IF(aState == OutputCaptureState::Capture, aDummyTrack);
    158 
    159  if (mOutputCaptureState.Ref() != aState) {
    160    LOG("Capture state change from %s to %s",
    161        EnumValueToString(mOutputCaptureState.Ref()),
    162        EnumValueToString(aState));
    163  }
    164  mOutputCaptureState = aState;
    165  if (mOutputDummyTrack.Ref().get() != aDummyTrack) {
    166    mOutputDummyTrack = nsMainThreadPtrHandle<SharedDummyTrack>(
    167        MakeAndAddRef<nsMainThreadPtrHolder<SharedDummyTrack>>(
    168            "MediaDecoder::mOutputDummyTrack", aDummyTrack));
    169  }
    170 }
    171 
    172 void MediaDecoder::AddOutputTrack(RefPtr<ProcessedMediaTrack> aTrack) {
    173  MOZ_ASSERT(NS_IsMainThread());
    174  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
    175  CopyableTArray<RefPtr<ProcessedMediaTrack>> tracks = mOutputTracks;
    176  tracks.AppendElement(std::move(aTrack));
    177  mOutputTracks = tracks;
    178 }
    179 
    180 void MediaDecoder::RemoveOutputTrack(
    181    const RefPtr<ProcessedMediaTrack>& aTrack) {
    182  MOZ_ASSERT(NS_IsMainThread());
    183  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
    184  CopyableTArray<RefPtr<ProcessedMediaTrack>> tracks = mOutputTracks;
    185  if (tracks.RemoveElement(aTrack)) {
    186    mOutputTracks = tracks;
    187  }
    188 }
    189 
    190 void MediaDecoder::SetOutputTracksPrincipal(
    191    const RefPtr<nsIPrincipal>& aPrincipal) {
    192  MOZ_ASSERT(NS_IsMainThread());
    193  MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
    194  mOutputPrincipal = MakePrincipalHandle(aPrincipal);
    195 }
    196 
    197 double MediaDecoder::GetDuration() {
    198  MOZ_ASSERT(NS_IsMainThread());
    199  return ToMicrosecondResolution(mDuration.match(DurationToDouble()));
    200 }
    201 
    202 bool MediaDecoder::IsInfinite() const {
    203  MOZ_ASSERT(NS_IsMainThread());
    204  return std::isinf(mDuration.match(DurationToDouble()));
    205 }
    206 
    207 #define INIT_MIRROR(name, val) \
    208  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
    209 #define INIT_CANONICAL(name, val) \
    210  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
    211 
    212 MediaDecoder::MediaDecoder(MediaDecoderInit& aInit)
    213    : mWatchManager(this, aInit.mOwner->AbstractMainThread()),
    214      mLogicalPosition(0.0),
    215      mDuration(TimeUnit::Invalid()),
    216      mOwner(aInit.mOwner),
    217      mAbstractMainThread(aInit.mOwner->AbstractMainThread()),
    218      mFrameStats(new FrameStatistics()),
    219      mVideoFrameContainer(aInit.mOwner->GetVideoFrameContainer()),
    220      mMinimizePreroll(aInit.mMinimizePreroll),
    221      mFiredMetadataLoaded(false),
    222      mIsOwnerInvisible(false),
    223      mIsOwnerConnected(false),
    224      mIsOwnerInBackground(false),
    225      mHasOwnerPendingCallbacks(false),
    226      mForcedHidden(false),
    227      mHasSuspendTaint(aInit.mHasSuspendTaint),
    228      mShouldResistFingerprinting(
    229          aInit.mOwner->ShouldResistFingerprinting(RFPTarget::AudioSampleRate)),
    230      mPlaybackRate(aInit.mPlaybackRate),
    231      mLogicallySeeking(false, "MediaDecoder::mLogicallySeeking"),
    232      INIT_MIRROR(mBuffered, TimeIntervals()),
    233      INIT_MIRROR(mCurrentPosition, TimeUnit::Zero()),
    234      INIT_MIRROR(mStateMachineDuration, NullableTimeUnit()),
    235      INIT_MIRROR(mIsAudioDataAudible, false),
    236      INIT_CANONICAL(mVolume, aInit.mVolume),
    237      INIT_CANONICAL(mPreservesPitch, aInit.mPreservesPitch),
    238      INIT_CANONICAL(mLooping, aInit.mLooping),
    239      INIT_CANONICAL(mStreamName, aInit.mStreamName),
    240      INIT_CANONICAL(mSinkDevice, nullptr),
    241      INIT_CANONICAL(mSecondaryVideoContainer, nullptr),
    242      INIT_CANONICAL(mOutputCaptureState, OutputCaptureState::None),
    243      INIT_CANONICAL(mOutputDummyTrack, nullptr),
    244      INIT_CANONICAL(mOutputTracks, nsTArray<RefPtr<ProcessedMediaTrack>>()),
    245      INIT_CANONICAL(mOutputPrincipal, PRINCIPAL_HANDLE_NONE),
    246      INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING),
    247      mSameOriginMedia(false),
    248      mVideoDecodingOberver(
    249          new BackgroundVideoDecodingPermissionObserver(this)),
    250      mIsBackgroundVideoDecodingAllowed(false),
    251      mTelemetryReported(false),
    252      mContainerType(aInit.mContainerType),
    253      mTelemetryProbesReporter(
    254          new TelemetryProbesReporter(aInit.mReporterOwner)) {
    255  MOZ_ASSERT(NS_IsMainThread());
    256  MOZ_ASSERT(mAbstractMainThread);
    257  MediaMemoryTracker::AddMediaDecoder(this);
    258 
    259  //
    260  // Initialize watchers.
    261  //
    262 
    263  // mDuration
    264  mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
    265 
    266  // readyState
    267  mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
    268  // ReadyState computation depends on MediaDecoder::CanPlayThrough, which
    269  // depends on the download rate.
    270  mWatchManager.Watch(mBuffered, &MediaDecoder::UpdateReadyState);
    271 
    272  // mLogicalPosition
    273  mWatchManager.Watch(mCurrentPosition, &MediaDecoder::UpdateLogicalPosition);
    274  mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateLogicalPosition);
    275  mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::UpdateLogicalPosition);
    276 
    277  mWatchManager.Watch(mIsAudioDataAudible,
    278                      &MediaDecoder::NotifyAudibleStateChanged);
    279 
    280  mWatchManager.Watch(mVolume, &MediaDecoder::NotifyVolumeChanged);
    281 
    282  mVideoDecodingOberver->RegisterEvent();
    283 }
    284 
    285 #undef INIT_MIRROR
    286 #undef INIT_CANONICAL
    287 
    288 void MediaDecoder::Shutdown() {
    289  MOZ_ASSERT(NS_IsMainThread());
    290  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    291 
    292  // Unwatch all watch targets to prevent further notifications.
    293  mWatchManager.Shutdown();
    294 
    295  DiscardOngoingSeekIfExists();
    296 
    297  // This changes the decoder state to SHUTDOWN and does other things
    298  // necessary to unblock the state machine thread if it's blocked, so
    299  // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
    300  if (mDecoderStateMachine) {
    301    ShutdownStateMachine()->Then(mAbstractMainThread, __func__, this,
    302                                 &MediaDecoder::FinishShutdown,
    303                                 &MediaDecoder::FinishShutdown);
    304  } else {
    305    // Ensure we always unregister asynchronously in order not to disrupt
    306    // the hashtable iterating in MediaShutdownManager::Shutdown().
    307    RefPtr<MediaDecoder> self = this;
    308    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    309        "MediaDecoder::Shutdown", [self]() { self->ShutdownInternal(); });
    310    mAbstractMainThread->Dispatch(r.forget());
    311  }
    312 
    313  ChangeState(PLAY_STATE_SHUTDOWN);
    314  mVideoDecodingOberver->UnregisterEvent();
    315  mVideoDecodingOberver = nullptr;
    316  mOwner = nullptr;
    317 }
    318 
    319 void MediaDecoder::NotifyXPCOMShutdown() {
    320  MOZ_ASSERT(NS_IsMainThread());
    321  // NotifyXPCOMShutdown will clear its reference to mDecoder. So we must ensure
    322  // that this MediaDecoder stays alive until completion.
    323  RefPtr<MediaDecoder> kungFuDeathGrip = this;
    324  if (auto* owner = GetOwner()) {
    325    owner->NotifyXPCOMShutdown();
    326  } else if (!IsShutdown()) {
    327    Shutdown();
    328  }
    329  MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
    330 }
    331 
    332 MediaDecoder::~MediaDecoder() {
    333  MOZ_ASSERT(NS_IsMainThread());
    334  MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
    335  MediaMemoryTracker::RemoveMediaDecoder(this);
    336 }
    337 
    338 void MediaDecoder::OnPlaybackEvent(const MediaPlaybackEvent& aEvent) {
    339  switch (aEvent.mType) {
    340    case MediaPlaybackEvent::PlaybackEnded:
    341      PlaybackEnded();
    342      break;
    343    case MediaPlaybackEvent::SeekStarted:
    344      SeekingStarted();
    345      break;
    346    case MediaPlaybackEvent::Invalidate:
    347      Invalidate();
    348      break;
    349    case MediaPlaybackEvent::EnterVideoSuspend:
    350      GetOwner()->QueueEvent(u"mozentervideosuspend"_ns);
    351      mIsVideoDecodingSuspended = true;
    352      break;
    353    case MediaPlaybackEvent::ExitVideoSuspend:
    354      GetOwner()->QueueEvent(u"mozexitvideosuspend"_ns);
    355      mIsVideoDecodingSuspended = false;
    356      break;
    357    case MediaPlaybackEvent::StartVideoSuspendTimer:
    358      GetOwner()->QueueEvent(u"mozstartvideosuspendtimer"_ns);
    359      break;
    360    case MediaPlaybackEvent::CancelVideoSuspendTimer:
    361      GetOwner()->QueueEvent(u"mozcancelvideosuspendtimer"_ns);
    362      break;
    363    case MediaPlaybackEvent::VideoOnlySeekBegin:
    364      GetOwner()->QueueEvent(u"mozvideoonlyseekbegin"_ns);
    365      break;
    366    case MediaPlaybackEvent::VideoOnlySeekCompleted:
    367      GetOwner()->QueueEvent(u"mozvideoonlyseekcompleted"_ns);
    368      break;
    369    default:
    370      break;
    371  }
    372 }
    373 
    374 bool MediaDecoder::IsVideoDecodingSuspended() const {
    375  return mIsVideoDecodingSuspended;
    376 }
    377 
    378 void MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError) {
    379  MOZ_ASSERT(NS_IsMainThread());
    380 #ifdef MOZ_WMF_MEDIA_ENGINE
    381  if (aError == NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR ||
    382      aError == NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR) {
    383    (void)SwitchStateMachine(aError);
    384    return;
    385  }
    386 #endif
    387  DecodeError(aError);
    388 }
    389 
    390 #ifdef MOZ_WMF_MEDIA_ENGINE
    391 bool MediaDecoder::SwitchStateMachine(const MediaResult& aError) {
    392  MOZ_ASSERT(aError == NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR ||
    393             aError == NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR);
    394  // Already in shutting down decoder, no need to create another state machine.
    395  if (mPlayState == PLAY_STATE_SHUTDOWN) {
    396    return false;
    397  }
    398 
    399  // External engine can't play the resource or we intentionally disable it, try
    400  // to use our own state machine again. Here we will create a new state machine
    401  // immediately and asynchrously shutdown the old one because we don't want to
    402  // dispatch any task to the old state machine. Therefore, we will disconnect
    403  // anything related with the old state machine, create a new state machine and
    404  // setup events/mirror/etc, then shutdown the old one and release its
    405  // reference once it finishes shutdown.
    406  RefPtr<MediaDecoderStateMachineBase> discardStateMachine =
    407      mDecoderStateMachine;
    408 
    409  // Disconnect mirror and events first.
    410  SetStateMachine(nullptr);
    411  DisconnectEvents();
    412 
    413  // Recreate a state machine and shutdown the old one.
    414  bool needExternalEngine = false;
    415  if (aError == NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR) {
    416 #  ifdef MOZ_WMF_CDM
    417    if (aError.GetCDMProxy()->AsWMFCDMProxy()) {
    418      needExternalEngine = true;
    419    }
    420 #  endif
    421  }
    422  LOG("Need to create a new %s state machine",
    423      needExternalEngine ? "external engine" : "normal");
    424 
    425  nsresult rv = CreateAndInitStateMachine(
    426      discardStateMachine->IsLiveStream(),
    427      !needExternalEngine /* disable external engine */);
    428  if (NS_WARN_IF(NS_FAILED(rv))) {
    429    LOG("Failed to create a new state machine!");
    430    glean::mfcdm::ErrorExtra extraData;
    431    extraData.errorName = Some("FAILED_TO_FALLBACK_TO_STATE_MACHINE"_ns);
    432    nsAutoCString resolution;
    433    if (mInfo) {
    434      if (mInfo->HasAudio()) {
    435        extraData.audioCodec = Some(mInfo->mAudio.mMimeType);
    436      }
    437      if (mInfo->HasVideo()) {
    438        extraData.videoCodec = Some(mInfo->mVideo.mMimeType);
    439        DetermineResolutionForTelemetry(*mInfo, resolution);
    440        extraData.resolution = Some(resolution);
    441      }
    442    }
    443    glean::mfcdm::error.Record(Some(extraData));
    444    if (MOZ_LOG_TEST(gMediaDecoderLog, LogLevel::Debug)) {
    445      nsPrintfCString logMessage{"MFCDM Error event, error=%s",
    446                                 extraData.errorName->get()};
    447      if (mInfo) {
    448        if (mInfo->HasAudio()) {
    449          logMessage.Append(
    450              nsPrintfCString{", audio=%s", mInfo->mAudio.mMimeType.get()});
    451        }
    452        if (mInfo->HasVideo()) {
    453          logMessage.Append(nsPrintfCString{", video=%s, resolution=%s",
    454                                            mInfo->mVideo.mMimeType.get(),
    455                                            resolution.get()});
    456        }
    457      }
    458      LOG("%s", logMessage.get());
    459    }
    460  }
    461 
    462  // Some attributes might have been set on the destroyed state machine, and
    463  // won't be reflected on the new MDSM by the state mirroring. We need to
    464  // update them manually later, after MDSM finished reading the
    465  // metadata because the MDSM might not be ready to perform the operations yet.
    466  mPendingStatusUpdateForNewlyCreatedStateMachine = true;
    467 
    468  // If there is ongoing seek performed on the old MDSM, cancel it because we
    469  // will perform seeking later again and don't want the old seeking affecting
    470  // us.
    471  DiscardOngoingSeekIfExists();
    472 
    473  discardStateMachine->BeginShutdown()->Then(
    474      AbstractThread::MainThread(), __func__, [discardStateMachine] {});
    475  return true;
    476 }
    477 #endif
    478 
    479 void MediaDecoder::OnDecoderDoctorEvent(DecoderDoctorEvent aEvent) {
    480  MOZ_ASSERT(NS_IsMainThread());
    481  // OnDecoderDoctorEvent is disconnected at shutdown time.
    482  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    483  Document* doc = GetOwner()->GetDocument();
    484  if (!doc) {
    485    return;
    486  }
    487  DecoderDoctorDiagnostics diags;
    488  diags.StoreEvent(doc, aEvent, __func__);
    489 }
    490 
    491 void MediaDecoder::OnNextFrameStatus(
    492    MediaDecoderOwner::NextFrameStatus aStatus) {
    493  MOZ_ASSERT(NS_IsMainThread());
    494  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    495  if (mNextFrameStatus != aStatus) {
    496    LOG("Changed mNextFrameStatus to %s",
    497        MediaDecoderOwner::EnumValueToString(aStatus));
    498    mNextFrameStatus = aStatus;
    499    UpdateReadyState();
    500  }
    501 }
    502 
    503 void MediaDecoder::OnTrackInfoUpdated(const VideoInfo& aVideoInfo,
    504                                      const AudioInfo& aAudioInfo) {
    505  MOZ_ASSERT(NS_IsMainThread());
    506  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    507 
    508  // Note that we don't check HasVideo() or HasAudio() here, because
    509  // those are checks for existing validity. If we always set the values
    510  // to what we receive, then we can go from not-video to video, for
    511  // example.
    512  mInfo->mVideo = aVideoInfo;
    513  mInfo->mAudio = aAudioInfo;
    514 
    515  Invalidate();
    516 
    517  EnsureTelemetryReported();
    518 }
    519 
    520 void MediaDecoder::OnSecondaryVideoContainerInstalled(
    521    const RefPtr<VideoFrameContainer>& aSecondaryVideoContainer) {
    522  MOZ_ASSERT(NS_IsMainThread());
    523  GetOwner()->OnSecondaryVideoContainerInstalled(aSecondaryVideoContainer);
    524 }
    525 
    526 void MediaDecoder::ShutdownInternal() {
    527  MOZ_ASSERT(NS_IsMainThread());
    528  mVideoFrameContainer = nullptr;
    529  mSecondaryVideoContainer = nullptr;
    530  MediaShutdownManager::Instance().Unregister(this);
    531 }
    532 
    533 void MediaDecoder::FinishShutdown() {
    534  MOZ_ASSERT(NS_IsMainThread());
    535  SetStateMachine(nullptr);
    536  ShutdownInternal();
    537 }
    538 
    539 nsresult MediaDecoder::CreateAndInitStateMachine(bool aIsLiveStream,
    540                                                 bool aDisableExternalEngine) {
    541  MOZ_ASSERT(NS_IsMainThread());
    542  SetStateMachine(CreateStateMachine(aDisableExternalEngine));
    543 
    544  NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
    545  GetStateMachine()->DispatchIsLiveStream(aIsLiveStream);
    546 
    547  mMDSMCreationTime = Some(TimeStamp::Now());
    548  nsresult rv = mDecoderStateMachine->Init(this);
    549  NS_ENSURE_SUCCESS(rv, rv);
    550 
    551  // If some parameters got set before the state machine got created,
    552  // set them now
    553  SetStateMachineParameters();
    554 
    555  return NS_OK;
    556 }
    557 
    558 void MediaDecoder::SetStateMachineParameters() {
    559  MOZ_ASSERT(NS_IsMainThread());
    560  if (mPlaybackRate != 1 && mPlaybackRate != 0) {
    561    mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
    562  }
    563  mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
    564      mAbstractMainThread, this, &MediaDecoder::OnMetadataUpdate);
    565  mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
    566      mAbstractMainThread, this, &MediaDecoder::MetadataLoaded);
    567  mFirstFrameLoadedListener =
    568      mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
    569          mAbstractMainThread, this, &MediaDecoder::FirstFrameLoaded);
    570 
    571  mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect(
    572      mAbstractMainThread, this, &MediaDecoder::OnPlaybackEvent);
    573  mOnPlaybackErrorEvent = mDecoderStateMachine->OnPlaybackErrorEvent().Connect(
    574      mAbstractMainThread, this, &MediaDecoder::OnPlaybackErrorEvent);
    575  mOnDecoderDoctorEvent = mDecoderStateMachine->OnDecoderDoctorEvent().Connect(
    576      mAbstractMainThread, this, &MediaDecoder::OnDecoderDoctorEvent);
    577  mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
    578      mAbstractMainThread, this, &MediaDecoder::OnMediaNotSeekable);
    579  mOnNextFrameStatus = mDecoderStateMachine->OnNextFrameStatus().Connect(
    580      mAbstractMainThread, this, &MediaDecoder::OnNextFrameStatus);
    581  mOnTrackInfoUpdated = mDecoderStateMachine->OnTrackInfoUpdatedEvent().Connect(
    582      mAbstractMainThread, this, &MediaDecoder::OnTrackInfoUpdated);
    583  mOnSecondaryVideoContainerInstalled =
    584      mDecoderStateMachine->OnSecondaryVideoContainerInstalled().Connect(
    585          mAbstractMainThread, this,
    586          &MediaDecoder::OnSecondaryVideoContainerInstalled);
    587  mOnEncrypted = mReader->OnEncrypted().Connect(
    588      mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DispatchEncrypted);
    589  mOnWaitingForKey = mReader->OnWaitingForKey().Connect(
    590      mAbstractMainThread, GetOwner(), &MediaDecoderOwner::NotifyWaitingForKey);
    591  mOnDecodeWarning = mReader->OnDecodeWarning().Connect(
    592      mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DecodeWarning);
    593 }
    594 
    595 void MediaDecoder::DisconnectEvents() {
    596  MOZ_ASSERT(NS_IsMainThread());
    597  mTimedMetadataListener.Disconnect();
    598  mMetadataLoadedListener.Disconnect();
    599  mFirstFrameLoadedListener.Disconnect();
    600  mOnPlaybackEvent.Disconnect();
    601  mOnPlaybackErrorEvent.Disconnect();
    602  mOnDecoderDoctorEvent.Disconnect();
    603  mOnMediaNotSeekable.Disconnect();
    604  mOnEncrypted.Disconnect();
    605  mOnWaitingForKey.Disconnect();
    606  mOnDecodeWarning.Disconnect();
    607  mOnNextFrameStatus.Disconnect();
    608  mOnTrackInfoUpdated.Disconnect();
    609  mOnSecondaryVideoContainerInstalled.Disconnect();
    610 }
    611 
    612 RefPtr<ShutdownPromise> MediaDecoder::ShutdownStateMachine() {
    613  MOZ_ASSERT(NS_IsMainThread());
    614  MOZ_ASSERT(GetStateMachine());
    615  DisconnectEvents();
    616  return mDecoderStateMachine->BeginShutdown();
    617 }
    618 
    619 void MediaDecoder::Play() {
    620  MOZ_ASSERT(NS_IsMainThread());
    621 
    622  NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
    623  LOG("Play");
    624  if (mPlaybackRate == 0) {
    625    return;
    626  }
    627 
    628  if (IsEnded()) {
    629    Seek(0, SeekTarget::PrevSyncPoint);
    630    return;
    631  }
    632 
    633  if (mPlayState == PLAY_STATE_LOADING) {
    634    mNextState = PLAY_STATE_PLAYING;
    635    return;
    636  }
    637 
    638  ChangeState(PLAY_STATE_PLAYING);
    639 }
    640 
    641 void MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType) {
    642  MOZ_ASSERT(NS_IsMainThread());
    643  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    644 
    645  LOG("Seek, target=%f", aTime);
    646  MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
    647 
    648  auto time = TimeUnit::FromSeconds(aTime);
    649 
    650  mLogicalPosition = aTime;
    651  mLogicallySeeking = true;
    652  SeekTarget target = SeekTarget(time, aSeekType);
    653  CallSeek(target);
    654 
    655  if (mPlayState == PLAY_STATE_ENDED) {
    656    ChangeState(GetOwner()->GetPaused() ? PLAY_STATE_PAUSED
    657                                        : PLAY_STATE_PLAYING);
    658  }
    659 }
    660 
    661 void MediaDecoder::SetDelaySeekMode(bool aShouldDelaySeek) {
    662  MOZ_ASSERT(NS_IsMainThread());
    663  LOG("SetDelaySeekMode, shouldDelaySeek=%d", aShouldDelaySeek);
    664  if (mShouldDelaySeek == aShouldDelaySeek) {
    665    return;
    666  }
    667  mShouldDelaySeek = aShouldDelaySeek;
    668  if (!mShouldDelaySeek && mDelayedSeekTarget) {
    669    Seek(mDelayedSeekTarget->GetTime().ToSeconds(),
    670         mDelayedSeekTarget->GetType());
    671    mDelayedSeekTarget.reset();
    672  }
    673 }
    674 
    675 void MediaDecoder::DiscardOngoingSeekIfExists() {
    676  MOZ_ASSERT(NS_IsMainThread());
    677  mSeekRequest.DisconnectIfExists();
    678 }
    679 
    680 void MediaDecoder::CallSeek(const SeekTarget& aTarget) {
    681  MOZ_ASSERT(NS_IsMainThread());
    682  if (mShouldDelaySeek) {
    683    LOG("Delay seek to %f and store it to delayed seek target",
    684        mDelayedSeekTarget->GetTime().ToSeconds());
    685    mDelayedSeekTarget = Some(aTarget);
    686    return;
    687  }
    688  DiscardOngoingSeekIfExists();
    689  mDecoderStateMachine->InvokeSeek(aTarget)
    690      ->Then(mAbstractMainThread, __func__, this, &MediaDecoder::OnSeekResolved,
    691             &MediaDecoder::OnSeekRejected)
    692      ->Track(mSeekRequest);
    693 }
    694 
    695 double MediaDecoder::GetCurrentTime() {
    696  MOZ_ASSERT(NS_IsMainThread());
    697  return mLogicalPosition;
    698 }
    699 
    700 void MediaDecoder::OnMetadataUpdate(TimedMetadata&& aMetadata) {
    701  MOZ_ASSERT(NS_IsMainThread());
    702  MetadataLoaded(MakeUnique<MediaInfo>(*aMetadata.mInfo),
    703                 UniquePtr<MetadataTags>(std::move(aMetadata.mTags)),
    704                 MediaDecoderEventVisibility::Observable);
    705  FirstFrameLoaded(std::move(aMetadata.mInfo),
    706                   MediaDecoderEventVisibility::Observable);
    707 }
    708 
    709 void MediaDecoder::MetadataLoaded(
    710    UniquePtr<MediaInfo> aInfo, UniquePtr<MetadataTags> aTags,
    711    MediaDecoderEventVisibility aEventVisibility) {
    712  MOZ_ASSERT(NS_IsMainThread());
    713  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    714 
    715  LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
    716      aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
    717      aInfo->HasVideo());
    718 
    719  mMediaSeekable = aInfo->mMediaSeekable;
    720  mMediaSeekableOnlyInBufferedRanges =
    721      aInfo->mMediaSeekableOnlyInBufferedRanges;
    722  mInfo = std::move(aInfo);
    723 
    724  mTelemetryProbesReporter->OnMediaContentChanged(
    725      TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo));
    726 
    727  // Make sure the element and the frame (if any) are told about
    728  // our new size.
    729  if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
    730    mFiredMetadataLoaded = true;
    731    GetOwner()->MetadataLoaded(mInfo.get(), std::move(aTags));
    732  }
    733  // Invalidate() will end up calling GetOwner()->UpdateMediaSize with the last
    734  // dimensions retrieved from the video frame container. The video frame
    735  // container contains more up to date dimensions than aInfo.
    736  // So we call Invalidate() after calling GetOwner()->MetadataLoaded to ensure
    737  // the media element has the latest dimensions.
    738  Invalidate();
    739 
    740 #ifdef MOZ_WMF_MEDIA_ENGINE
    741  SetStatusUpdateForNewlyCreatedStateMachineIfNeeded();
    742 #endif
    743 
    744  EnsureTelemetryReported();
    745 }
    746 
    747 #ifdef MOZ_WMF_MEDIA_ENGINE
    748 void MediaDecoder::SetStatusUpdateForNewlyCreatedStateMachineIfNeeded() {
    749  if (!mPendingStatusUpdateForNewlyCreatedStateMachine) {
    750    return;
    751  }
    752  mPendingStatusUpdateForNewlyCreatedStateMachine = false;
    753  LOG("Set pending statuses if necessary (mLogicallySeeking=%d, "
    754      "mLogicalPosition=%f, mPlaybackRate=%f)",
    755      mLogicallySeeking.Ref(), mLogicalPosition, mPlaybackRate);
    756  if (mLogicallySeeking) {
    757    Seek(mLogicalPosition, SeekTarget::Accurate);
    758  }
    759  if (mPlaybackRate != 0 && mPlaybackRate != 1.0) {
    760    mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
    761  }
    762 }
    763 #endif
    764 
    765 void MediaDecoder::EnsureTelemetryReported() {
    766  MOZ_ASSERT(NS_IsMainThread());
    767 
    768  if (mTelemetryReported || !mInfo) {
    769    // Note: sometimes we get multiple MetadataLoaded calls (for example
    770    // for chained ogg). So we ensure we don't report duplicate results for
    771    // these resources.
    772    return;
    773  }
    774 
    775  nsTArray<nsCString> codecs;
    776  if (mInfo->HasAudio() &&
    777      !mInfo->mAudio.GetAsAudioInfo()->mMimeType.IsEmpty()) {
    778    codecs.AppendElement(mInfo->mAudio.GetAsAudioInfo()->mMimeType);
    779  }
    780  if (mInfo->HasVideo() &&
    781      !mInfo->mVideo.GetAsVideoInfo()->mMimeType.IsEmpty()) {
    782    codecs.AppendElement(mInfo->mVideo.GetAsVideoInfo()->mMimeType);
    783  }
    784  if (codecs.IsEmpty()) {
    785    codecs.AppendElement(nsPrintfCString(
    786        "resource; %s", ContainerType().OriginalString().Data()));
    787  }
    788  for (const nsCString& codec : codecs) {
    789    LOG("Telemetry MEDIA_CODEC_USED= '%s'", codec.get());
    790    glean::media::codec_used.Get(codec).Add(1);
    791  }
    792 
    793  mTelemetryReported = true;
    794 }
    795 
    796 void MediaDecoder::FirstFrameLoaded(
    797    UniquePtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) {
    798  MOZ_ASSERT(NS_IsMainThread());
    799  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    800 
    801  LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d "
    802      "mPlayState=%s transportSeekable=%d",
    803      aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
    804      aInfo->HasVideo(), EnumValueToString(mPlayState), IsTransportSeekable());
    805 
    806  mInfo = std::move(aInfo);
    807  mTelemetryProbesReporter->OnMediaContentChanged(
    808      TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo));
    809 
    810  Invalidate();
    811 
    812  // The element can run javascript via events
    813  // before reaching here, so only change the
    814  // state if we're still set to the original
    815  // loading state.
    816  if (mPlayState == PLAY_STATE_LOADING) {
    817    ChangeState(mNextState);
    818  }
    819 
    820  // We only care about video first frame.
    821  if (mInfo->HasVideo() && mMDSMCreationTime) {
    822    auto info = MakeUnique<dom::MediaDecoderDebugInfo>();
    823    RequestDebugInfo(*info)->Then(
    824        GetMainThreadSerialEventTarget(), __func__,
    825        [self = RefPtr<MediaDecoder>{this}, this, now = TimeStamp::Now(),
    826         creationTime = *mMDSMCreationTime, result = std::move(info)](
    827            GenericPromise::ResolveOrRejectValue&& aValue) mutable {
    828          if (IsShutdown()) {
    829            return;
    830          }
    831          if (aValue.IsReject()) {
    832            NS_WARNING("Failed to get debug info for the first frame probe!");
    833            return;
    834          }
    835          auto firstFrameLoadedTime = (now - creationTime).ToMilliseconds();
    836          MOZ_ASSERT(result->mReader.mTotalReadMetadataTimeMs >= 0.0);
    837          MOZ_ASSERT(result->mReader.mTotalWaitingForVideoDataTimeMs >= 0.0);
    838          MOZ_ASSERT(result->mStateMachine.mTotalBufferingTimeMs >= 0.0);
    839 
    840          using FirstFrameLoadedFlag =
    841              TelemetryProbesReporter::FirstFrameLoadedFlag;
    842          TelemetryProbesReporter::FirstFrameLoadedFlagSet flags;
    843          if (IsMSE()) {
    844            flags += FirstFrameLoadedFlag::IsMSE;
    845          }
    846          if (mDecoderStateMachine->IsExternalEngineStateMachine()) {
    847            flags += FirstFrameLoadedFlag::IsExternalEngineStateMachine;
    848          }
    849          if (IsHLSDecoder()) {
    850            flags += FirstFrameLoadedFlag::IsHLS;
    851          }
    852          if (result->mReader.mVideoHardwareAccelerated) {
    853            flags += FirstFrameLoadedFlag::IsHardwareDecoding;
    854          }
    855          mTelemetryProbesReporter->OnFirstFrameLoaded(
    856              firstFrameLoadedTime, result->mReader.mTotalReadMetadataTimeMs,
    857              result->mReader.mTotalWaitingForVideoDataTimeMs,
    858              result->mStateMachine.mTotalBufferingTimeMs, flags, *mInfo,
    859              NS_ConvertUTF16toUTF8(result->mReader.mVideoDecoderName));
    860        });
    861    mMDSMCreationTime.reset();
    862  }
    863 
    864  // GetOwner()->FirstFrameLoaded() might call us back. Put it at the bottom of
    865  // this function to avoid unexpected shutdown from reentrant calls.
    866  if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
    867    GetOwner()->FirstFrameLoaded();
    868  }
    869 }
    870 
    871 void MediaDecoder::NetworkError(const MediaResult& aError) {
    872  MOZ_ASSERT(NS_IsMainThread());
    873  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    874  GetOwner()->NetworkError(aError);
    875 }
    876 
    877 void MediaDecoder::DecodeError(const MediaResult& aError) {
    878  MOZ_ASSERT(NS_IsMainThread());
    879  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    880  LOG("DecodeError, type=%s, error=%s", ContainerType().OriginalString().get(),
    881      aError.ErrorName().get());
    882  GetOwner()->DecodeError(aError);
    883 }
    884 
    885 void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin) {
    886  MOZ_ASSERT(NS_IsMainThread());
    887  mSameOriginMedia = aSameOrigin;
    888 }
    889 
    890 bool MediaDecoder::IsSeeking() const {
    891  MOZ_ASSERT(NS_IsMainThread());
    892  return mLogicallySeeking;
    893 }
    894 
    895 bool MediaDecoder::OwnerHasError() const {
    896  MOZ_ASSERT(NS_IsMainThread());
    897  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    898  return GetOwner()->HasError();
    899 }
    900 
    901 bool MediaDecoder::IsEnded() const {
    902  MOZ_ASSERT(NS_IsMainThread());
    903  return mPlayState == PLAY_STATE_ENDED;
    904 }
    905 
    906 bool MediaDecoder::IsShutdown() const {
    907  MOZ_ASSERT(NS_IsMainThread());
    908  return mPlayState == PLAY_STATE_SHUTDOWN;
    909 }
    910 
    911 void MediaDecoder::PlaybackEnded() {
    912  MOZ_ASSERT(NS_IsMainThread());
    913  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    914 
    915  if (mLogicallySeeking || mPlayState == PLAY_STATE_LOADING ||
    916      mPlayState == PLAY_STATE_ENDED) {
    917    LOG("MediaDecoder::PlaybackEnded bailed out, "
    918        "mLogicallySeeking=%d mPlayState=%s",
    919        mLogicallySeeking.Ref(), EnumValueToString(mPlayState));
    920    return;
    921  }
    922 
    923  LOG("MediaDecoder::PlaybackEnded");
    924 
    925  ChangeState(PLAY_STATE_ENDED);
    926  InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
    927  GetOwner()->PlaybackEnded();
    928 }
    929 
    930 void MediaDecoder::NotifyPrincipalChanged() {
    931  MOZ_ASSERT(NS_IsMainThread());
    932  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    933  GetOwner()->NotifyDecoderPrincipalChanged();
    934 }
    935 
    936 void MediaDecoder::OnSeekResolved() {
    937  MOZ_ASSERT(NS_IsMainThread());
    938  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    939  LOG("MediaDecoder::OnSeekResolved");
    940  mLogicallySeeking = false;
    941 
    942  // Ensure logical position is updated after seek.
    943  UpdateLogicalPositionInternal();
    944  mSeekRequest.Complete();
    945 
    946  GetOwner()->SeekCompleted();
    947 }
    948 
    949 void MediaDecoder::OnSeekRejected() {
    950  MOZ_ASSERT(NS_IsMainThread());
    951  LOG("MediaDecoder::OnSeekRejected");
    952  mSeekRequest.Complete();
    953  mLogicallySeeking = false;
    954 
    955  GetOwner()->SeekAborted();
    956 }
    957 
    958 void MediaDecoder::SeekingStarted() {
    959  MOZ_ASSERT(NS_IsMainThread());
    960  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    961  GetOwner()->SeekStarted();
    962 }
    963 
    964 void MediaDecoder::ChangeState(PlayState aState) {
    965  MOZ_ASSERT(NS_IsMainThread());
    966  MOZ_ASSERT(!IsShutdown(), "SHUTDOWN is the final state.");
    967 
    968  if (mNextState == aState) {
    969    mNextState = PLAY_STATE_PAUSED;
    970  }
    971 
    972  if (mPlayState != aState) {
    973    DDLOG(DDLogCategory::Property, "play_state", EnumValueToString(aState));
    974    LOG("Play state changes from %s to %s", EnumValueToString(mPlayState),
    975        EnumValueToString(aState));
    976    mPlayState = aState;
    977    UpdateTelemetryHelperBasedOnPlayState(aState);
    978  }
    979 }
    980 
    981 TelemetryProbesReporter::Visibility MediaDecoder::OwnerVisibility() const {
    982  return GetOwner()->IsActuallyInvisible() || mForcedHidden
    983             ? TelemetryProbesReporter::Visibility::eInvisible
    984             : TelemetryProbesReporter::Visibility::eVisible;
    985 }
    986 
    987 void MediaDecoder::UpdateTelemetryHelperBasedOnPlayState(
    988    PlayState aState) const {
    989  if (aState == PlayState::PLAY_STATE_PLAYING) {
    990    mTelemetryProbesReporter->OnPlay(
    991        OwnerVisibility(),
    992        TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo),
    993        mVolume == 0.f);
    994  } else if (aState == PlayState::PLAY_STATE_PAUSED ||
    995             aState == PlayState::PLAY_STATE_ENDED) {
    996    mTelemetryProbesReporter->OnPause(OwnerVisibility());
    997  } else if (aState == PLAY_STATE_SHUTDOWN) {
    998    mTelemetryProbesReporter->OnShutdown();
    999  }
   1000 }
   1001 
   1002 MediaDecoder::PositionUpdate MediaDecoder::GetPositionUpdateReason(
   1003    double aPrevPos, const TimeUnit& aCurPos) const {
   1004  MOZ_ASSERT(NS_IsMainThread());
   1005  // If current position is earlier than previous position and we didn't do
   1006  // seek, that means we looped back to the start position.
   1007  const bool notSeeking = !mSeekRequest.Exists();
   1008  if (mLooping && notSeeking && aCurPos.ToSeconds() < aPrevPos) {
   1009    return PositionUpdate::eSeamlessLoopingSeeking;
   1010  }
   1011  return aPrevPos != aCurPos.ToSeconds() && notSeeking
   1012             ? PositionUpdate::ePeriodicUpdate
   1013             : PositionUpdate::eOther;
   1014 }
   1015 
   1016 void MediaDecoder::UpdateLogicalPositionInternal() {
   1017  MOZ_ASSERT(NS_IsMainThread());
   1018  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1019 
   1020  TimeUnit currentPosition = CurrentPosition();
   1021  if (mPlayState == PLAY_STATE_ENDED) {
   1022    currentPosition =
   1023        std::max(currentPosition, mDuration.match(DurationToTimeUnit()));
   1024  }
   1025 
   1026  const PositionUpdate reason =
   1027      GetPositionUpdateReason(mLogicalPosition, currentPosition);
   1028  switch (reason) {
   1029    case PositionUpdate::ePeriodicUpdate:
   1030      SetLogicalPosition(currentPosition);
   1031      // This is actually defined in `TimeMarchesOn`, but we do that in decoder.
   1032      // https://html.spec.whatwg.org/multipage/media.html#playing-the-media-resource:event-media-timeupdate-7
   1033      // TODO (bug 1688137): should we move it back to `TimeMarchesOn`?
   1034      GetOwner()->MaybeQueueTimeupdateEvent();
   1035      break;
   1036    case PositionUpdate::eSeamlessLoopingSeeking:
   1037      // When seamless seeking occurs, seeking was performed on the demuxer so
   1038      // the decoder doesn't know. That means decoder still thinks it's in
   1039      // playing. Therefore, we have to manually call those methods to notify
   1040      // the owner about seeking.
   1041      GetOwner()->SeekStarted();
   1042      SetLogicalPosition(currentPosition);
   1043      GetOwner()->SeekCompleted();
   1044      break;
   1045    default:
   1046      MOZ_ASSERT(reason == PositionUpdate::eOther);
   1047      SetLogicalPosition(currentPosition);
   1048      break;
   1049  }
   1050 
   1051  // Invalidate the frame so any video data is displayed.
   1052  // Do this before the timeupdate event so that if that
   1053  // event runs JavaScript that queries the media size, the
   1054  // frame has reflowed and the size updated beforehand.
   1055  Invalidate();
   1056 }
   1057 
   1058 void MediaDecoder::SetLogicalPosition(const TimeUnit& aNewPosition) {
   1059  MOZ_ASSERT(NS_IsMainThread());
   1060  if (TimeUnit::FromSeconds(mLogicalPosition) == aNewPosition ||
   1061      mLogicalPosition == aNewPosition.ToSeconds()) {
   1062    return;
   1063  }
   1064  mLogicalPosition = aNewPosition.ToSeconds();
   1065  DDLOG(DDLogCategory::Property, "currentTime", mLogicalPosition);
   1066 }
   1067 
   1068 void MediaDecoder::DurationChanged() {
   1069  MOZ_ASSERT(NS_IsMainThread());
   1070  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1071 
   1072  Variant<TimeUnit, double> oldDuration = mDuration;
   1073 
   1074  // Use the explicit duration if we have one.
   1075  // Otherwise use the duration mirrored from MDSM.
   1076  if (mExplicitDuration.isSome()) {
   1077    mDuration.emplace<double>(mExplicitDuration.ref());
   1078  } else if (mStateMachineDuration.Ref().isSome()) {
   1079    MOZ_ASSERT(mStateMachineDuration.Ref().ref().IsValid());
   1080    mDuration.emplace<TimeUnit>(mStateMachineDuration.Ref().ref());
   1081  }
   1082 
   1083  LOG("New duration: %s",
   1084      mDuration.match(DurationToTimeUnit()).ToString().get());
   1085  if (oldDuration.is<TimeUnit>() && oldDuration.as<TimeUnit>().IsValid()) {
   1086    LOG("Old Duration %s",
   1087        oldDuration.match(DurationToTimeUnit()).ToString().get());
   1088  }
   1089 
   1090  if ((oldDuration.is<double>() || oldDuration.as<TimeUnit>().IsValid())) {
   1091    if (mDuration.match(DurationToDouble()) ==
   1092        oldDuration.match(DurationToDouble())) {
   1093      return;
   1094    }
   1095  }
   1096 
   1097  LOG("Duration changed to %s",
   1098      mDuration.match(DurationToTimeUnit()).ToString().get());
   1099 
   1100  // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
   1101  // of whether we should fire durationchange on explicit infinity.
   1102  if (mFiredMetadataLoaded &&
   1103      (!std::isinf(mDuration.match(DurationToDouble())) ||
   1104       mExplicitDuration.isSome())) {
   1105    GetOwner()->QueueEvent(u"durationchange"_ns);
   1106  }
   1107 
   1108  if (CurrentPosition().ToSeconds() > mDuration.match(DurationToDouble())) {
   1109    Seek(mDuration.match(DurationToDouble()), SeekTarget::Accurate);
   1110  }
   1111 }
   1112 
   1113 already_AddRefed<KnowsCompositor> MediaDecoder::GetCompositor() {
   1114  MediaDecoderOwner* owner = GetOwner();
   1115  Document* ownerDoc = owner ? owner->GetDocument() : nullptr;
   1116  WindowRenderer* renderer =
   1117      ownerDoc ? nsContentUtils::WindowRendererForDocument(ownerDoc) : nullptr;
   1118  RefPtr<KnowsCompositor> knows =
   1119      renderer ? renderer->AsKnowsCompositor() : nullptr;
   1120  return knows ? knows->GetForMedia().forget() : nullptr;
   1121 }
   1122 
   1123 void MediaDecoder::NotifyCompositor() {
   1124  RefPtr<KnowsCompositor> knowsCompositor = GetCompositor();
   1125  if (knowsCompositor) {
   1126    nsCOMPtr<nsIRunnable> r =
   1127        NewRunnableMethod<already_AddRefed<KnowsCompositor>&&>(
   1128            "MediaFormatReader::UpdateCompositor", mReader,
   1129            &MediaFormatReader::UpdateCompositor, knowsCompositor.forget());
   1130    (void)mReader->OwnerThread()->Dispatch(r.forget());
   1131  }
   1132 }
   1133 
   1134 void MediaDecoder::SetElementVisibility(bool aIsOwnerInvisible,
   1135                                        bool aIsOwnerConnected,
   1136                                        bool aIsOwnerInBackground,
   1137                                        bool aHasOwnerPendingCallbacks) {
   1138  MOZ_ASSERT(NS_IsMainThread());
   1139  mIsOwnerInvisible = aIsOwnerInvisible;
   1140  mIsOwnerConnected = aIsOwnerConnected;
   1141  mIsOwnerInBackground = aIsOwnerInBackground;
   1142  mHasOwnerPendingCallbacks = aHasOwnerPendingCallbacks;
   1143  mTelemetryProbesReporter->OnVisibilityChanged(OwnerVisibility());
   1144  UpdateVideoDecodeMode();
   1145 }
   1146 
   1147 void MediaDecoder::SetForcedHidden(bool aForcedHidden) {
   1148  MOZ_ASSERT(NS_IsMainThread());
   1149  mForcedHidden = aForcedHidden;
   1150  mTelemetryProbesReporter->OnVisibilityChanged(OwnerVisibility());
   1151  UpdateVideoDecodeMode();
   1152 }
   1153 
   1154 void MediaDecoder::SetSuspendTaint(bool aTainted) {
   1155  MOZ_ASSERT(NS_IsMainThread());
   1156  mHasSuspendTaint = aTainted;
   1157  UpdateVideoDecodeMode();
   1158 }
   1159 
   1160 void MediaDecoder::UpdateVideoDecodeMode() {
   1161  MOZ_ASSERT(NS_IsMainThread());
   1162 
   1163  // The MDSM may yet be set.
   1164  if (!mDecoderStateMachine) {
   1165    LOG("UpdateVideoDecodeMode(), early return because we don't have MDSM.");
   1166    return;
   1167  }
   1168 
   1169  // Seeking is required when leaving suspend mode.
   1170  if (!mMediaSeekable) {
   1171    LOG("UpdateVideoDecodeMode(), set Normal because the media is not "
   1172        "seekable");
   1173    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1174    return;
   1175  }
   1176 
   1177  // If mHasSuspendTaint is set, never suspend the video decoder.
   1178  if (mHasSuspendTaint) {
   1179    LOG("UpdateVideoDecodeMode(), set Normal because the element has been "
   1180        "tainted.");
   1181    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1182    return;
   1183  }
   1184 
   1185  // If mSecondaryVideoContainer is set, never suspend the video decoder.
   1186  if (mSecondaryVideoContainer.Ref()) {
   1187    LOG("UpdateVideoDecodeMode(), set Normal because the element is cloning "
   1188        "itself visually to another video container.");
   1189    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1190    return;
   1191  }
   1192 
   1193  // Don't suspend elements that is not in a connected tree.
   1194  if (!mIsOwnerConnected) {
   1195    LOG("UpdateVideoDecodeMode(), set Normal because the element is not in "
   1196        "tree.");
   1197    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1198    return;
   1199  }
   1200 
   1201  // Don't suspend elements that have pending rVFC callbacks.
   1202  if (mHasOwnerPendingCallbacks && !mIsOwnerInBackground) {
   1203    LOG("UpdateVideoDecodeMode(), set Normal because the element has pending "
   1204        "callbacks while in foreground.");
   1205    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1206    return;
   1207  }
   1208 
   1209  // If mForcedHidden is set, suspend the video decoder anyway.
   1210  if (mForcedHidden) {
   1211    LOG("UpdateVideoDecodeMode(), set Suspend because the element is forced to "
   1212        "be suspended.");
   1213    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
   1214    return;
   1215  }
   1216 
   1217  // Resume decoding in the advance, even the element is in the background.
   1218  if (mIsBackgroundVideoDecodingAllowed) {
   1219    LOG("UpdateVideoDecodeMode(), set Normal because the tab is in background "
   1220        "and hovered.");
   1221    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1222    return;
   1223  }
   1224 
   1225  if (mIsOwnerInvisible) {
   1226    LOG("UpdateVideoDecodeMode(), set Suspend because of invisible element.");
   1227    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
   1228  } else {
   1229    LOG("UpdateVideoDecodeMode(), set Normal because of visible element.");
   1230    mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
   1231  }
   1232 }
   1233 
   1234 void MediaDecoder::SetIsBackgroundVideoDecodingAllowed(bool aAllowed) {
   1235  mIsBackgroundVideoDecodingAllowed = aAllowed;
   1236  UpdateVideoDecodeMode();
   1237 }
   1238 
   1239 bool MediaDecoder::HasSuspendTaint() const {
   1240  MOZ_ASSERT(NS_IsMainThread());
   1241  return mHasSuspendTaint;
   1242 }
   1243 
   1244 void MediaDecoder::SetSecondaryVideoContainer(
   1245    const RefPtr<VideoFrameContainer>& aSecondaryVideoContainer) {
   1246  MOZ_ASSERT(NS_IsMainThread());
   1247  if (mSecondaryVideoContainer.Ref() == aSecondaryVideoContainer) {
   1248    return;
   1249  }
   1250  mSecondaryVideoContainer = aSecondaryVideoContainer;
   1251  UpdateVideoDecodeMode();
   1252 }
   1253 
   1254 bool MediaDecoder::IsMediaSeekable() {
   1255  MOZ_ASSERT(NS_IsMainThread());
   1256  NS_ENSURE_TRUE(GetStateMachine(), false);
   1257  return mMediaSeekable;
   1258 }
   1259 
   1260 namespace {
   1261 
   1262 // Returns zero, either as a TimeUnit or as a double.
   1263 template <typename T>
   1264 constexpr T Zero() {
   1265  if constexpr (std::is_same<T, double>::value) {
   1266    return 0.0;
   1267  } else if constexpr (std::is_same<T, TimeUnit>::value) {
   1268    return TimeUnit::Zero();
   1269  }
   1270  MOZ_RELEASE_ASSERT(false);
   1271 };
   1272 
   1273 // Returns Infinity either as a TimeUnit or as a double.
   1274 template <typename T>
   1275 constexpr T Infinity() {
   1276  if constexpr (std::is_same<T, double>::value) {
   1277    return std::numeric_limits<double>::infinity();
   1278  } else if constexpr (std::is_same<T, TimeUnit>::value) {
   1279    return TimeUnit::FromInfinity();
   1280  }
   1281  MOZ_RELEASE_ASSERT(false);
   1282 };
   1283 
   1284 };  // namespace
   1285 
   1286 // This method can be made to return either TimeIntervals, that is a set of
   1287 // interval that are delimited with TimeUnit, or TimeRanges, that is a set of
   1288 // intervals that are delimited by seconds, as doubles.
   1289 // seekable often depends on the duration of a media, in the very common case
   1290 // where the seekable range is [0, duration]. When playing a MediaSource, the
   1291 // duration of a media element can be set as an arbitrary number, that are
   1292 // 64-bits floating point values.
   1293 // This allows returning an interval that is [0, duration], with duration being
   1294 // a double that cannot be represented as a TimeUnit, either because it has too
   1295 // many significant digits, or because it's outside of the int64_t range that
   1296 // TimeUnit internally uses.
   1297 template <typename IntervalType>
   1298 IntervalType MediaDecoder::GetSeekableImpl() {
   1299  MOZ_ASSERT(NS_IsMainThread());
   1300  if (std::isnan(GetDuration())) {
   1301    // We do not have a duration yet, we can't determine the seekable range.
   1302    return IntervalType();
   1303  }
   1304 
   1305  // Compute [0, duration] -- When dealing with doubles, use ::GetDuration to
   1306  // avoid rounding the value differently. When dealing with TimeUnit, it's
   1307  // returned directly.
   1308  typename IntervalType::InnerType duration;
   1309  if constexpr (std::is_same<typename IntervalType::InnerType, double>::value) {
   1310    duration = GetDuration();
   1311  } else {
   1312    duration = mDuration.as<TimeUnit>();
   1313  }
   1314  typename IntervalType::ElemType zeroToDuration =
   1315      typename IntervalType::ElemType(
   1316          Zero<typename IntervalType::InnerType>(),
   1317          IsInfinite() ? Infinity<typename IntervalType::InnerType>()
   1318                       : duration);
   1319  auto buffered = IntervalType(GetBuffered());
   1320  // Remove any negative range in the interval -- seeking to a non-positive
   1321  // position isn't possible.
   1322  auto positiveBuffered = buffered.Intersection(zeroToDuration);
   1323 
   1324  // We can seek in buffered range if the media is seekable. Also, we can seek
   1325  // in unbuffered ranges if the transport level is seekable (local file or the
   1326  // server supports range requests, etc.) or in cue-less WebMs
   1327  if (mMediaSeekableOnlyInBufferedRanges) {
   1328    return IntervalType(positiveBuffered);
   1329  }
   1330  if (!IsMediaSeekable()) {
   1331    return IntervalType();
   1332  }
   1333  if (!IsTransportSeekable()) {
   1334    return IntervalType(positiveBuffered);
   1335  }
   1336 
   1337  // Common case: seeking is possible at any point of the stream.
   1338  return IntervalType(zeroToDuration);
   1339 }
   1340 
   1341 media::TimeIntervals MediaDecoder::GetSeekable() {
   1342  return GetSeekableImpl<media::TimeIntervals>();
   1343 }
   1344 
   1345 media::TimeRanges MediaDecoder::GetSeekableTimeRanges() {
   1346  return GetSeekableImpl<media::TimeRanges>();
   1347 }
   1348 
   1349 void MediaDecoder::SetFragmentEndTime(double aTime) {
   1350  MOZ_ASSERT(NS_IsMainThread());
   1351  if (mDecoderStateMachine) {
   1352    mDecoderStateMachine->DispatchSetFragmentEndTime(
   1353        TimeUnit::FromSeconds(aTime));
   1354  }
   1355 }
   1356 
   1357 void MediaDecoder::SetPlaybackRate(double aPlaybackRate) {
   1358  MOZ_ASSERT(NS_IsMainThread());
   1359 
   1360  double oldRate = mPlaybackRate;
   1361  mPlaybackRate = aPlaybackRate;
   1362  if (aPlaybackRate == 0) {
   1363    Pause();
   1364    return;
   1365  }
   1366 
   1367  if (oldRate == 0 && !GetOwner()->GetPaused()) {
   1368    // PlaybackRate is no longer null.
   1369    // Restart the playback if the media was playing.
   1370    Play();
   1371  }
   1372 
   1373  if (mDecoderStateMachine) {
   1374    mDecoderStateMachine->DispatchSetPlaybackRate(aPlaybackRate);
   1375  }
   1376 }
   1377 
   1378 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch) {
   1379  MOZ_ASSERT(NS_IsMainThread());
   1380  mPreservesPitch = aPreservesPitch;
   1381 }
   1382 
   1383 void MediaDecoder::SetLooping(bool aLooping) {
   1384  MOZ_ASSERT(NS_IsMainThread());
   1385  mLooping = aLooping;
   1386 }
   1387 
   1388 void MediaDecoder::SetStreamName(const nsAutoString& aStreamName) {
   1389  MOZ_ASSERT(NS_IsMainThread());
   1390  mStreamName = aStreamName;
   1391 }
   1392 
   1393 void MediaDecoder::ConnectMirrors(MediaDecoderStateMachineBase* aObject) {
   1394  MOZ_ASSERT(NS_IsMainThread());
   1395  MOZ_ASSERT(aObject);
   1396  mStateMachineDuration.Connect(aObject->CanonicalDuration());
   1397  mBuffered.Connect(aObject->CanonicalBuffered());
   1398  mCurrentPosition.Connect(aObject->CanonicalCurrentPosition());
   1399  mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible());
   1400 }
   1401 
   1402 void MediaDecoder::DisconnectMirrors() {
   1403  MOZ_ASSERT(NS_IsMainThread());
   1404  mStateMachineDuration.DisconnectIfConnected();
   1405  mBuffered.DisconnectIfConnected();
   1406  mCurrentPosition.DisconnectIfConnected();
   1407  mIsAudioDataAudible.DisconnectIfConnected();
   1408 }
   1409 
   1410 void MediaDecoder::SetStateMachine(
   1411    MediaDecoderStateMachineBase* aStateMachine) {
   1412  MOZ_ASSERT(NS_IsMainThread());
   1413  MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
   1414  if (aStateMachine) {
   1415    mDecoderStateMachine = aStateMachine;
   1416    LOG("set state machine %p", mDecoderStateMachine.get());
   1417    ConnectMirrors(aStateMachine);
   1418    UpdateVideoDecodeMode();
   1419  } else if (mDecoderStateMachine) {
   1420    LOG("null out state machine %p", mDecoderStateMachine.get());
   1421    mDecoderStateMachine = nullptr;
   1422    DisconnectMirrors();
   1423  }
   1424 }
   1425 
   1426 ImageContainer* MediaDecoder::GetImageContainer() {
   1427  return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer()
   1428                              : nullptr;
   1429 }
   1430 
   1431 void MediaDecoder::InvalidateWithFlags(uint32_t aFlags) {
   1432  if (mVideoFrameContainer) {
   1433    mVideoFrameContainer->InvalidateWithFlags(aFlags);
   1434  }
   1435 }
   1436 
   1437 void MediaDecoder::Invalidate() {
   1438  if (mVideoFrameContainer) {
   1439    mVideoFrameContainer->Invalidate();
   1440  }
   1441 }
   1442 
   1443 void MediaDecoder::Suspend() {
   1444  MOZ_ASSERT(NS_IsMainThread());
   1445  GetStateMachine()->InvokeSuspendMediaSink();
   1446 }
   1447 
   1448 void MediaDecoder::Resume() {
   1449  MOZ_ASSERT(NS_IsMainThread());
   1450  GetStateMachine()->InvokeResumeMediaSink();
   1451 }
   1452 
   1453 // Constructs the time ranges representing what segments of the media
   1454 // are buffered and playable.
   1455 media::TimeIntervals MediaDecoder::GetBuffered() {
   1456  MOZ_ASSERT(NS_IsMainThread());
   1457  return mBuffered.Ref();
   1458 }
   1459 
   1460 size_t MediaDecoder::SizeOfVideoQueue() {
   1461  MOZ_ASSERT(NS_IsMainThread());
   1462  if (mDecoderStateMachine) {
   1463    return mDecoderStateMachine->SizeOfVideoQueue();
   1464  }
   1465  return 0;
   1466 }
   1467 
   1468 size_t MediaDecoder::SizeOfAudioQueue() {
   1469  MOZ_ASSERT(NS_IsMainThread());
   1470  if (mDecoderStateMachine) {
   1471    return mDecoderStateMachine->SizeOfAudioQueue();
   1472  }
   1473  return 0;
   1474 }
   1475 
   1476 void MediaDecoder::NotifyReaderDataArrived() {
   1477  MOZ_ASSERT(NS_IsMainThread());
   1478  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1479 
   1480  nsresult rv = mReader->OwnerThread()->Dispatch(
   1481      NewRunnableMethod("MediaFormatReader::NotifyDataArrived", mReader.get(),
   1482                        &MediaFormatReader::NotifyDataArrived));
   1483  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
   1484  (void)rv;
   1485 }
   1486 
   1487 // Provide access to the state machine object
   1488 MediaDecoderStateMachineBase* MediaDecoder::GetStateMachine() const {
   1489  MOZ_ASSERT(NS_IsMainThread());
   1490  return mDecoderStateMachine;
   1491 }
   1492 
   1493 bool MediaDecoder::CanPlayThrough() {
   1494  MOZ_ASSERT(NS_IsMainThread());
   1495  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1496  return CanPlayThroughImpl();
   1497 }
   1498 
   1499 RefPtr<SetCDMPromise> MediaDecoder::SetCDMProxy(CDMProxy* aProxy) {
   1500  MOZ_ASSERT(NS_IsMainThread());
   1501 #ifdef MOZ_WMF_CDM
   1502  if (aProxy) {
   1503    nsresult rv = GetStateMachine()->IsCDMProxySupported(aProxy);
   1504    if (rv == NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR) {
   1505      // We can't switch to another state machine because this CDM proxy type is
   1506      // disabled by pref.
   1507      LOG("CDM proxy %s not allowed!",
   1508          NS_ConvertUTF16toUTF8(aProxy->KeySystem()).get());
   1509      return SetCDMPromise::CreateAndReject(rv, __func__);
   1510    }
   1511    if (rv == NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR) {
   1512      // Switch to another state machine if the current one doesn't support the
   1513      // given CDM proxy.
   1514      LOG("CDM proxy %s not supported! Switch to another state machine.",
   1515          NS_ConvertUTF16toUTF8(aProxy->KeySystem()).get());
   1516      [[maybe_unused]] bool switched = SwitchStateMachine(
   1517          MediaResult{NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR, aProxy});
   1518      rv = GetStateMachine()->IsCDMProxySupported(aProxy);
   1519      if (NS_FAILED(rv)) {
   1520        MOZ_DIAGNOSTIC_ASSERT(
   1521            !switched, "We should only reach here if we failed to switch");
   1522        LOG("CDM proxy is still not supported!");
   1523        return SetCDMPromise::CreateAndReject(rv, __func__);
   1524      }
   1525    }
   1526    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv), "CDM proxy not supported!");
   1527  }
   1528 #endif
   1529  return GetStateMachine()->SetCDMProxy(aProxy);
   1530 }
   1531 
   1532 bool MediaDecoder::IsOpusEnabled() { return StaticPrefs::media_opus_enabled(); }
   1533 
   1534 bool MediaDecoder::IsOggEnabled() { return StaticPrefs::media_ogg_enabled(); }
   1535 
   1536 bool MediaDecoder::IsWaveEnabled() { return StaticPrefs::media_wave_enabled(); }
   1537 
   1538 bool MediaDecoder::IsWebMEnabled() { return StaticPrefs::media_webm_enabled(); }
   1539 
   1540 NS_IMETHODIMP
   1541 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
   1542                                   nsISupports* aData, bool aAnonymize) {
   1543  // NB: When resourceSizes' ref count goes to 0 the promise will report the
   1544  //     resources memory and finish the asynchronous memory report.
   1545  RefPtr<MediaDecoder::ResourceSizes> resourceSizes =
   1546      new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
   1547 
   1548  nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
   1549  nsCOMPtr<nsISupports> data = aData;
   1550 
   1551  resourceSizes->Promise()->Then(
   1552      AbstractThread::MainThread(), __func__,
   1553      [handleReport, data](size_t size) {
   1554        handleReport->Callback(
   1555            ""_ns, "explicit/media/resources"_ns, KIND_HEAP, UNITS_BYTES,
   1556            static_cast<int64_t>(size),
   1557            nsLiteralCString("Memory used by media resources including "
   1558                             "streaming buffers, caches, etc."),
   1559            data);
   1560 
   1561        nsCOMPtr<nsIMemoryReporterManager> imgr =
   1562            do_GetService("@mozilla.org/memory-reporter-manager;1");
   1563 
   1564        if (imgr) {
   1565          imgr->EndReport();
   1566        }
   1567      },
   1568      [](size_t) { /* unused reject function */ });
   1569 
   1570  int64_t video = 0;
   1571  int64_t audio = 0;
   1572  DecodersArray& decoders = Decoders();
   1573  for (size_t i = 0; i < decoders.Length(); ++i) {
   1574    MediaDecoder* decoder = decoders[i];
   1575    video += static_cast<int64_t>(decoder->SizeOfVideoQueue());
   1576    audio += static_cast<int64_t>(decoder->SizeOfAudioQueue());
   1577    decoder->AddSizeOfResources(resourceSizes);
   1578  }
   1579 
   1580  MOZ_COLLECT_REPORT("explicit/media/decoded/video", KIND_HEAP, UNITS_BYTES,
   1581                     video, "Memory used by decoded video frames.");
   1582 
   1583  MOZ_COLLECT_REPORT("explicit/media/decoded/audio", KIND_HEAP, UNITS_BYTES,
   1584                     audio, "Memory used by decoded audio chunks.");
   1585 
   1586  return NS_OK;
   1587 }
   1588 
   1589 MediaDecoderOwner* MediaDecoder::GetOwner() const {
   1590  MOZ_ASSERT(NS_IsMainThread());
   1591  // mOwner is valid until shutdown.
   1592  return mOwner;
   1593 }
   1594 
   1595 MediaDecoderOwner::NextFrameStatus MediaDecoder::NextFrameBufferedStatus() {
   1596  MOZ_ASSERT(NS_IsMainThread());
   1597  // Next frame hasn't been decoded yet.
   1598  // Use the buffered range to consider if we have the next frame available.
   1599  auto currentPosition = CurrentPosition();
   1600  media::TimeInterval interval(
   1601      currentPosition, currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
   1602  return GetBuffered().Contains(interval)
   1603             ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
   1604             : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
   1605 }
   1606 
   1607 void MediaDecoder::GetDebugInfo(dom::MediaDecoderDebugInfo& aInfo) {
   1608  MOZ_ASSERT(NS_IsMainThread());
   1609  CopyUTF8toUTF16(nsPrintfCString("%p", this), aInfo.mInstance);
   1610  aInfo.mChannels = mInfo ? mInfo->mAudio.mChannels : 0;
   1611  aInfo.mRate = mInfo ? mInfo->mAudio.mRate : 0;
   1612  aInfo.mHasAudio = mInfo ? mInfo->HasAudio() : false;
   1613  aInfo.mHasVideo = mInfo ? mInfo->HasVideo() : false;
   1614  CopyUTF8toUTF16(MakeStringSpan(EnumValueToString(mPlayState)),
   1615                  aInfo.mPlayState);
   1616  aInfo.mContainerType =
   1617      NS_ConvertUTF8toUTF16(ContainerType().Type().AsString());
   1618 }
   1619 
   1620 RefPtr<GenericPromise> MediaDecoder::RequestDebugInfo(
   1621    MediaDecoderDebugInfo& aInfo) {
   1622  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1623  if (!NS_IsMainThread()) {
   1624    // Run the request on the main thread if it's not already.
   1625    return InvokeAsync(AbstractThread::MainThread(), __func__,
   1626                       [this, self = RefPtr{this}, &aInfo]() {
   1627                         return RequestDebugInfo(aInfo);
   1628                       });
   1629  }
   1630  GetDebugInfo(aInfo);
   1631 
   1632  return mReader->RequestDebugInfo(aInfo.mReader)
   1633      ->Then(AbstractThread::MainThread(), __func__,
   1634             [this, self = RefPtr{this}, &aInfo] {
   1635               if (!GetStateMachine()) {
   1636                 return GenericPromise::CreateAndResolve(true, __func__);
   1637               }
   1638               return GetStateMachine()->RequestDebugInfo(aInfo.mStateMachine);
   1639             });
   1640 }
   1641 
   1642 void MediaDecoder::NotifyAudibleStateChanged() {
   1643  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1644  GetOwner()->SetAudibleState(mIsAudioDataAudible);
   1645  mTelemetryProbesReporter->OnAudibleChanged(
   1646      mIsAudioDataAudible ? TelemetryProbesReporter::AudibleState::eAudible
   1647                          : TelemetryProbesReporter::AudibleState::eNotAudible);
   1648 }
   1649 
   1650 void MediaDecoder::NotifyVolumeChanged() {
   1651  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
   1652  mTelemetryProbesReporter->OnMutedChanged(mVolume == 0.f);
   1653 }
   1654 
   1655 double MediaDecoder::GetTotalVideoPlayTimeInSeconds() const {
   1656  return mTelemetryProbesReporter->GetTotalVideoPlayTimeInSeconds();
   1657 }
   1658 
   1659 double MediaDecoder::GetTotalVideoHDRPlayTimeInSeconds() const {
   1660  return mTelemetryProbesReporter->GetTotalVideoHDRPlayTimeInSeconds();
   1661 }
   1662 
   1663 double MediaDecoder::GetVisibleVideoPlayTimeInSeconds() const {
   1664  return mTelemetryProbesReporter->GetVisibleVideoPlayTimeInSeconds();
   1665 }
   1666 
   1667 double MediaDecoder::GetInvisibleVideoPlayTimeInSeconds() const {
   1668  return mTelemetryProbesReporter->GetInvisibleVideoPlayTimeInSeconds();
   1669 }
   1670 
   1671 double MediaDecoder::GetTotalAudioPlayTimeInSeconds() const {
   1672  return mTelemetryProbesReporter->GetTotalAudioPlayTimeInSeconds();
   1673 }
   1674 
   1675 double MediaDecoder::GetAudiblePlayTimeInSeconds() const {
   1676  return mTelemetryProbesReporter->GetAudiblePlayTimeInSeconds();
   1677 }
   1678 
   1679 double MediaDecoder::GetInaudiblePlayTimeInSeconds() const {
   1680  return mTelemetryProbesReporter->GetInaudiblePlayTimeInSeconds();
   1681 }
   1682 
   1683 double MediaDecoder::GetMutedPlayTimeInSeconds() const {
   1684  return mTelemetryProbesReporter->GetMutedPlayTimeInSeconds();
   1685 }
   1686 
   1687 MediaMemoryTracker::MediaMemoryTracker() = default;
   1688 
   1689 void MediaMemoryTracker::InitMemoryReporter() {
   1690  RegisterWeakAsyncMemoryReporter(this);
   1691 }
   1692 
   1693 MediaMemoryTracker::~MediaMemoryTracker() {
   1694  UnregisterWeakMemoryReporter(this);
   1695 }
   1696 
   1697 }  // namespace mozilla
   1698 
   1699 // avoid redefined macro in unified build
   1700 #undef DUMP
   1701 #undef LOG
   1702 #undef NS_DispatchToMainThread