Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / media / MediaDecoder.cpp
blob159c6a61210a0e21fde2b8c6306151aea26a7947
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/. */
7 #include "MediaDecoder.h"
9 #include "AudioDeviceInfo.h"
10 #include "DOMMediaStream.h"
11 #include "DecoderBenchmark.h"
12 #include "ImageContainer.h"
13 #include "MediaDecoderStateMachineBase.h"
14 #include "MediaFormatReader.h"
15 #include "MediaResource.h"
16 #include "MediaShutdownManager.h"
17 #include "MediaTrackGraph.h"
18 #include "TelemetryProbesReporter.h"
19 #include "VideoFrameContainer.h"
20 #include "VideoUtils.h"
21 #include "mozilla/AbstractThread.h"
22 #include "mozilla/dom/DOMTypes.h"
23 #include "mozilla/FloatingPoint.h"
24 #include "mozilla/MathAlgorithms.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/StaticPrefs_media.h"
27 #include "mozilla/StaticPtr.h"
28 #include "mozilla/Telemetry.h"
29 #include "mozilla/Unused.h"
30 #include "mozilla/glean/GleanMetrics.h"
31 #include "nsComponentManagerUtils.h"
32 #include "nsContentUtils.h"
33 #include "nsError.h"
34 #include "nsIMemoryReporter.h"
35 #include "nsPrintfCString.h"
36 #include "nsServiceManagerUtils.h"
37 #include "nsTArray.h"
38 #include "WindowRenderer.h"
39 #include <algorithm>
40 #include <cmath>
41 #include <limits>
43 using namespace mozilla::dom;
44 using namespace mozilla::layers;
45 using namespace mozilla::media;
47 namespace mozilla {
49 // avoid redefined macro in unified build
50 #undef LOG
51 #undef DUMP
53 LazyLogModule gMediaDecoderLog("MediaDecoder");
55 #define LOG(x, ...) \
56 DDMOZ_LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
58 #define DUMP(x, ...) printf_stderr(x "\n", ##__VA_ARGS__)
60 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
62 static const char* ToPlayStateStr(MediaDecoder::PlayState aState) {
63 switch (aState) {
64 case MediaDecoder::PLAY_STATE_LOADING:
65 return "LOADING";
66 case MediaDecoder::PLAY_STATE_PAUSED:
67 return "PAUSED";
68 case MediaDecoder::PLAY_STATE_PLAYING:
69 return "PLAYING";
70 case MediaDecoder::PLAY_STATE_ENDED:
71 return "ENDED";
72 case MediaDecoder::PLAY_STATE_SHUTDOWN:
73 return "SHUTDOWN";
74 default:
75 MOZ_ASSERT_UNREACHABLE("Invalid playState.");
77 return "UNKNOWN";
80 class MediaMemoryTracker : public nsIMemoryReporter {
81 virtual ~MediaMemoryTracker();
83 NS_DECL_THREADSAFE_ISUPPORTS
84 NS_DECL_NSIMEMORYREPORTER
86 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf);
88 MediaMemoryTracker();
89 void InitMemoryReporter();
91 static StaticRefPtr<MediaMemoryTracker> sUniqueInstance;
93 static MediaMemoryTracker* UniqueInstance() {
94 if (!sUniqueInstance) {
95 sUniqueInstance = new MediaMemoryTracker();
96 sUniqueInstance->InitMemoryReporter();
98 return sUniqueInstance;
101 using DecodersArray = nsTArray<MediaDecoder*>;
102 static DecodersArray& Decoders() { return UniqueInstance()->mDecoders; }
104 DecodersArray mDecoders;
106 public:
107 static void AddMediaDecoder(MediaDecoder* aDecoder) {
108 Decoders().AppendElement(aDecoder);
111 static void RemoveMediaDecoder(MediaDecoder* aDecoder) {
112 DecodersArray& decoders = Decoders();
113 decoders.RemoveElement(aDecoder);
114 if (decoders.IsEmpty()) {
115 sUniqueInstance = nullptr;
120 StaticRefPtr<MediaMemoryTracker> MediaMemoryTracker::sUniqueInstance;
122 LazyLogModule gMediaTimerLog("MediaTimer");
124 constexpr TimeUnit MediaDecoder::DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED;
126 void MediaDecoder::InitStatics() {
127 MOZ_ASSERT(NS_IsMainThread());
128 // Eagerly init gMediaDecoderLog to work around bug 1415441.
129 MOZ_LOG(gMediaDecoderLog, LogLevel::Info, ("MediaDecoder::InitStatics"));
131 #if defined(NIGHTLY_BUILD)
132 // Allow people to force a bit but try to warn them about filing bugs if audio
133 // decoding does not work on utility
134 static const bool allowLockPrefs =
135 PR_GetEnv("MOZ_DONT_LOCK_UTILITY_PLZ_FILE_A_BUG") == nullptr;
136 if (XRE_IsParentProcess() && allowLockPrefs) {
137 // Lock Utility process preferences so that people cannot opt-out of
138 // Utility process
139 Preferences::Lock("media.utility-process.enabled");
140 # if defined(MOZ_FFMPEG)
141 Preferences::Lock("media.utility-ffmpeg.enabled");
142 # endif // defined(MOZ_FFMPEG)
143 Preferences::Lock("media.utility-ffvpx.enabled");
144 # if defined(MOZ_WMF)
145 Preferences::Lock("media.utility-wmf.enabled");
146 # endif // defined(MOZ_WMF)
147 # if defined(MOZ_APPLEMEDIA)
148 Preferences::Lock("media.utility-applemedia.enabled");
149 # endif // defined(MOZ_APPLEMEDIA)
150 Preferences::Lock("media.utility-vorbis.enabled");
151 Preferences::Lock("media.utility-wav.enabled");
152 Preferences::Lock("media.utility-opus.enabled");
154 #endif // defined(NIGHTLY_BUILD)
157 NS_IMPL_ISUPPORTS(MediaMemoryTracker, nsIMemoryReporter)
159 void MediaDecoder::NotifyOwnerActivityChanged(bool aIsOwnerInvisible,
160 bool aIsOwnerConnected) {
161 MOZ_ASSERT(NS_IsMainThread());
162 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
163 SetElementVisibility(aIsOwnerInvisible, aIsOwnerConnected);
165 NotifyCompositor();
168 void MediaDecoder::Pause() {
169 MOZ_ASSERT(NS_IsMainThread());
170 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
171 LOG("Pause");
172 if (mPlayState == PLAY_STATE_LOADING || IsEnded()) {
173 mNextState = PLAY_STATE_PAUSED;
174 return;
176 ChangeState(PLAY_STATE_PAUSED);
179 void MediaDecoder::SetVolume(double aVolume) {
180 MOZ_ASSERT(NS_IsMainThread());
181 mVolume = aVolume;
184 RefPtr<GenericPromise> MediaDecoder::SetSink(AudioDeviceInfo* aSinkDevice) {
185 MOZ_ASSERT(NS_IsMainThread());
186 mSinkDevice = aSinkDevice;
187 return GetStateMachine()->InvokeSetSink(aSinkDevice);
190 void MediaDecoder::SetOutputCaptureState(OutputCaptureState aState,
191 SharedDummyTrack* aDummyTrack) {
192 MOZ_ASSERT(NS_IsMainThread());
193 MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
194 MOZ_ASSERT_IF(aState == OutputCaptureState::Capture, aDummyTrack);
196 if (mOutputCaptureState.Ref() != aState) {
197 LOG("Capture state change from %s to %s",
198 OutputCaptureStateToStr(mOutputCaptureState.Ref()),
199 OutputCaptureStateToStr(aState));
201 mOutputCaptureState = aState;
202 if (mOutputDummyTrack.Ref().get() != aDummyTrack) {
203 mOutputDummyTrack = nsMainThreadPtrHandle<SharedDummyTrack>(
204 MakeAndAddRef<nsMainThreadPtrHolder<SharedDummyTrack>>(
205 "MediaDecoder::mOutputDummyTrack", aDummyTrack));
209 void MediaDecoder::AddOutputTrack(RefPtr<ProcessedMediaTrack> aTrack) {
210 MOZ_ASSERT(NS_IsMainThread());
211 MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
212 CopyableTArray<RefPtr<ProcessedMediaTrack>> tracks = mOutputTracks;
213 tracks.AppendElement(std::move(aTrack));
214 mOutputTracks = tracks;
217 void MediaDecoder::RemoveOutputTrack(
218 const RefPtr<ProcessedMediaTrack>& aTrack) {
219 MOZ_ASSERT(NS_IsMainThread());
220 MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
221 CopyableTArray<RefPtr<ProcessedMediaTrack>> tracks = mOutputTracks;
222 if (tracks.RemoveElement(aTrack)) {
223 mOutputTracks = tracks;
227 void MediaDecoder::SetOutputTracksPrincipal(
228 const RefPtr<nsIPrincipal>& aPrincipal) {
229 MOZ_ASSERT(NS_IsMainThread());
230 MOZ_ASSERT(mDecoderStateMachine, "Must be called after Load().");
231 mOutputPrincipal = MakePrincipalHandle(aPrincipal);
234 double MediaDecoder::GetDuration() {
235 MOZ_ASSERT(NS_IsMainThread());
236 return ToMicrosecondResolution(mDuration.match(DurationToDouble()));
239 bool MediaDecoder::IsInfinite() const {
240 MOZ_ASSERT(NS_IsMainThread());
241 return std::isinf(mDuration.match(DurationToDouble()));
244 #define INIT_MIRROR(name, val) \
245 name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
246 #define INIT_CANONICAL(name, val) \
247 name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
249 MediaDecoder::MediaDecoder(MediaDecoderInit& aInit)
250 : mWatchManager(this, aInit.mOwner->AbstractMainThread()),
251 mLogicalPosition(0.0),
252 mDuration(TimeUnit::Invalid()),
253 mOwner(aInit.mOwner),
254 mAbstractMainThread(aInit.mOwner->AbstractMainThread()),
255 mFrameStats(new FrameStatistics()),
256 mDecoderBenchmark(new DecoderBenchmark()),
257 mVideoFrameContainer(aInit.mOwner->GetVideoFrameContainer()),
258 mMinimizePreroll(aInit.mMinimizePreroll),
259 mFiredMetadataLoaded(false),
260 mIsOwnerInvisible(false),
261 mIsOwnerConnected(false),
262 mForcedHidden(false),
263 mHasSuspendTaint(aInit.mHasSuspendTaint),
264 mShouldResistFingerprinting(
265 aInit.mOwner->ShouldResistFingerprinting(RFPTarget::AudioSampleRate)),
266 mPlaybackRate(aInit.mPlaybackRate),
267 mLogicallySeeking(false, "MediaDecoder::mLogicallySeeking"),
268 INIT_MIRROR(mBuffered, TimeIntervals()),
269 INIT_MIRROR(mCurrentPosition, TimeUnit::Zero()),
270 INIT_MIRROR(mStateMachineDuration, NullableTimeUnit()),
271 INIT_MIRROR(mIsAudioDataAudible, false),
272 INIT_CANONICAL(mVolume, aInit.mVolume),
273 INIT_CANONICAL(mPreservesPitch, aInit.mPreservesPitch),
274 INIT_CANONICAL(mLooping, aInit.mLooping),
275 INIT_CANONICAL(mStreamName, aInit.mStreamName),
276 INIT_CANONICAL(mSinkDevice, nullptr),
277 INIT_CANONICAL(mSecondaryVideoContainer, nullptr),
278 INIT_CANONICAL(mOutputCaptureState, OutputCaptureState::None),
279 INIT_CANONICAL(mOutputDummyTrack, nullptr),
280 INIT_CANONICAL(mOutputTracks, nsTArray<RefPtr<ProcessedMediaTrack>>()),
281 INIT_CANONICAL(mOutputPrincipal, PRINCIPAL_HANDLE_NONE),
282 INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING),
283 mSameOriginMedia(false),
284 mVideoDecodingOberver(
285 new BackgroundVideoDecodingPermissionObserver(this)),
286 mIsBackgroundVideoDecodingAllowed(false),
287 mTelemetryReported(false),
288 mContainerType(aInit.mContainerType),
289 mTelemetryProbesReporter(
290 new TelemetryProbesReporter(aInit.mReporterOwner)) {
291 MOZ_ASSERT(NS_IsMainThread());
292 MOZ_ASSERT(mAbstractMainThread);
293 MediaMemoryTracker::AddMediaDecoder(this);
296 // Initialize watchers.
299 // mDuration
300 mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
302 // readyState
303 mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
304 // ReadyState computation depends on MediaDecoder::CanPlayThrough, which
305 // depends on the download rate.
306 mWatchManager.Watch(mBuffered, &MediaDecoder::UpdateReadyState);
308 // mLogicalPosition
309 mWatchManager.Watch(mCurrentPosition, &MediaDecoder::UpdateLogicalPosition);
310 mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateLogicalPosition);
311 mWatchManager.Watch(mLogicallySeeking, &MediaDecoder::UpdateLogicalPosition);
313 mWatchManager.Watch(mIsAudioDataAudible,
314 &MediaDecoder::NotifyAudibleStateChanged);
316 mWatchManager.Watch(mVolume, &MediaDecoder::NotifyVolumeChanged);
318 mVideoDecodingOberver->RegisterEvent();
321 #undef INIT_MIRROR
322 #undef INIT_CANONICAL
324 void MediaDecoder::Shutdown() {
325 MOZ_ASSERT(NS_IsMainThread());
326 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
328 // Unwatch all watch targets to prevent further notifications.
329 mWatchManager.Shutdown();
331 DiscardOngoingSeekIfExists();
333 // This changes the decoder state to SHUTDOWN and does other things
334 // necessary to unblock the state machine thread if it's blocked, so
335 // the asynchronous shutdown in nsDestroyStateMachine won't deadlock.
336 if (mDecoderStateMachine) {
337 ShutdownStateMachine()->Then(mAbstractMainThread, __func__, this,
338 &MediaDecoder::FinishShutdown,
339 &MediaDecoder::FinishShutdown);
340 } else {
341 // Ensure we always unregister asynchronously in order not to disrupt
342 // the hashtable iterating in MediaShutdownManager::Shutdown().
343 RefPtr<MediaDecoder> self = this;
344 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
345 "MediaDecoder::Shutdown", [self]() { self->ShutdownInternal(); });
346 mAbstractMainThread->Dispatch(r.forget());
349 ChangeState(PLAY_STATE_SHUTDOWN);
350 mVideoDecodingOberver->UnregisterEvent();
351 mVideoDecodingOberver = nullptr;
352 mOwner = nullptr;
355 void MediaDecoder::NotifyXPCOMShutdown() {
356 MOZ_ASSERT(NS_IsMainThread());
357 // NotifyXPCOMShutdown will clear its reference to mDecoder. So we must ensure
358 // that this MediaDecoder stays alive until completion.
359 RefPtr<MediaDecoder> kungFuDeathGrip = this;
360 if (auto* owner = GetOwner()) {
361 owner->NotifyXPCOMShutdown();
362 } else if (!IsShutdown()) {
363 Shutdown();
365 MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
368 MediaDecoder::~MediaDecoder() {
369 MOZ_ASSERT(NS_IsMainThread());
370 MOZ_DIAGNOSTIC_ASSERT(IsShutdown());
371 MediaMemoryTracker::RemoveMediaDecoder(this);
374 void MediaDecoder::OnPlaybackEvent(MediaPlaybackEvent&& aEvent) {
375 switch (aEvent.mType) {
376 case MediaPlaybackEvent::PlaybackEnded:
377 PlaybackEnded();
378 break;
379 case MediaPlaybackEvent::SeekStarted:
380 SeekingStarted();
381 break;
382 case MediaPlaybackEvent::Invalidate:
383 Invalidate();
384 break;
385 case MediaPlaybackEvent::EnterVideoSuspend:
386 GetOwner()->DispatchAsyncEvent(u"mozentervideosuspend"_ns);
387 mTelemetryProbesReporter->OnDecodeSuspended();
388 mIsVideoDecodingSuspended = true;
389 break;
390 case MediaPlaybackEvent::ExitVideoSuspend:
391 GetOwner()->DispatchAsyncEvent(u"mozexitvideosuspend"_ns);
392 mTelemetryProbesReporter->OnDecodeResumed();
393 mIsVideoDecodingSuspended = false;
394 break;
395 case MediaPlaybackEvent::StartVideoSuspendTimer:
396 GetOwner()->DispatchAsyncEvent(u"mozstartvideosuspendtimer"_ns);
397 break;
398 case MediaPlaybackEvent::CancelVideoSuspendTimer:
399 GetOwner()->DispatchAsyncEvent(u"mozcancelvideosuspendtimer"_ns);
400 break;
401 case MediaPlaybackEvent::VideoOnlySeekBegin:
402 GetOwner()->DispatchAsyncEvent(u"mozvideoonlyseekbegin"_ns);
403 break;
404 case MediaPlaybackEvent::VideoOnlySeekCompleted:
405 GetOwner()->DispatchAsyncEvent(u"mozvideoonlyseekcompleted"_ns);
406 break;
407 default:
408 break;
412 bool MediaDecoder::IsVideoDecodingSuspended() const {
413 return mIsVideoDecodingSuspended;
416 void MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError) {
417 MOZ_ASSERT(NS_IsMainThread());
418 #ifndef MOZ_WMF_MEDIA_ENGINE
419 DecodeError(aError);
420 #else
421 if (aError != NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR &&
422 aError != NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR) {
423 DecodeError(aError);
424 return;
427 // Already in shutting down decoder, no need to create another state machine.
428 if (mPlayState == PLAY_STATE_SHUTDOWN) {
429 return;
432 // External engine can't play the resource or we intentionally disable it, try
433 // to use our own state machine again. Here we will create a new state machine
434 // immediately and asynchrously shutdown the old one because we don't want to
435 // dispatch any task to the old state machine. Therefore, we will disconnect
436 // anything related with the old state machine, create a new state machine and
437 // setup events/mirror/etc, then shutdown the old one and release its
438 // reference once it finishes shutdown.
439 RefPtr<MediaDecoderStateMachineBase> discardStateMachine =
440 mDecoderStateMachine;
442 // Disconnect mirror and events first.
443 SetStateMachine(nullptr);
444 DisconnectEvents();
446 // Recreate a state machine and shutdown the old one.
447 bool needExternalEngine = false;
448 if (aError == NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR) {
449 # ifdef MOZ_WMF_CDM
450 if (aError.GetCDMProxy()->AsWMFCDMProxy()) {
451 needExternalEngine = true;
453 # endif
455 LOG("Need to create a new %s state machine",
456 needExternalEngine ? "external engine" : "normal");
457 mStateMachineRecreated = true;
459 nsresult rv = CreateAndInitStateMachine(
460 false /* live stream */,
461 !needExternalEngine /* disable external engine */);
462 if (NS_WARN_IF(NS_FAILED(rv))) {
463 LOG("Failed to create a new state machine!");
464 glean::mfcdm::ErrorExtra extraData;
465 extraData.errorName = Some("FAILED_TO_FALLBACK_TO_STATE_MACHINE"_ns);
466 nsAutoCString resolution;
467 if (mInfo) {
468 if (mInfo->HasAudio()) {
469 extraData.audioCodec = Some(mInfo->mAudio.mMimeType);
471 if (mInfo->HasVideo()) {
472 extraData.videoCodec = Some(mInfo->mVideo.mMimeType);
473 DetermineResolutionForTelemetry(*mInfo, resolution);
474 extraData.resolution = Some(resolution);
477 glean::mfcdm::error.Record(Some(extraData));
478 if (MOZ_LOG_TEST(gMediaDecoderLog, LogLevel::Debug)) {
479 nsPrintfCString logMessage{"MFCDM Error event, error=%s",
480 extraData.errorName->get()};
481 if (mInfo) {
482 if (mInfo->HasAudio()) {
483 logMessage.Append(
484 nsPrintfCString{", audio=%s", mInfo->mAudio.mMimeType.get()});
486 if (mInfo->HasVideo()) {
487 logMessage.Append(nsPrintfCString{", video=%s, resolution=%s",
488 mInfo->mVideo.mMimeType.get(),
489 resolution.get()});
492 LOG("%s", logMessage.get());
496 // Some attributes might have been set on the destroyed state machine, and
497 // won't be reflected on the new MDSM by the state mirroring. We need to
498 // update them manually later, after MDSM finished reading the
499 // metadata because the MDSM might not be ready to perform the operations yet.
500 mPendingStatusUpdateForNewlyCreatedStateMachine = true;
502 // If there is ongoing seek performed on the old MDSM, cancel it because we
503 // will perform seeking later again and don't want the old seeking affecting
504 // us.
505 DiscardOngoingSeekIfExists();
507 discardStateMachine->BeginShutdown()->Then(
508 AbstractThread::MainThread(), __func__, [discardStateMachine] {});
509 #endif
512 void MediaDecoder::OnDecoderDoctorEvent(DecoderDoctorEvent aEvent) {
513 MOZ_ASSERT(NS_IsMainThread());
514 // OnDecoderDoctorEvent is disconnected at shutdown time.
515 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
516 Document* doc = GetOwner()->GetDocument();
517 if (!doc) {
518 return;
520 DecoderDoctorDiagnostics diags;
521 diags.StoreEvent(doc, aEvent, __func__);
524 static const char* NextFrameStatusToStr(
525 MediaDecoderOwner::NextFrameStatus aStatus) {
526 switch (aStatus) {
527 case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
528 return "NEXT_FRAME_AVAILABLE";
529 case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
530 return "NEXT_FRAME_UNAVAILABLE";
531 case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
532 return "NEXT_FRAME_UNAVAILABLE_BUFFERING";
533 case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING:
534 return "NEXT_FRAME_UNAVAILABLE_SEEKING";
535 case MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED:
536 return "NEXT_FRAME_UNINITIALIZED";
538 return "UNKNOWN";
541 void MediaDecoder::OnNextFrameStatus(
542 MediaDecoderOwner::NextFrameStatus aStatus) {
543 MOZ_ASSERT(NS_IsMainThread());
544 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
545 if (mNextFrameStatus != aStatus) {
546 LOG("Changed mNextFrameStatus to %s", NextFrameStatusToStr(aStatus));
547 mNextFrameStatus = aStatus;
548 UpdateReadyState();
552 void MediaDecoder::OnTrackInfoUpdated(const VideoInfo& aVideoInfo,
553 const AudioInfo& aAudioInfo) {
554 MOZ_ASSERT(NS_IsMainThread());
555 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
557 // Note that we don't check HasVideo() or HasAudio() here, because
558 // those are checks for existing validity. If we always set the values
559 // to what we receive, then we can go from not-video to video, for
560 // example.
561 mInfo->mVideo = aVideoInfo;
562 mInfo->mAudio = aAudioInfo;
564 Invalidate();
566 EnsureTelemetryReported();
569 void MediaDecoder::OnSecondaryVideoContainerInstalled(
570 const RefPtr<VideoFrameContainer>& aSecondaryVideoContainer) {
571 MOZ_ASSERT(NS_IsMainThread());
572 GetOwner()->OnSecondaryVideoContainerInstalled(aSecondaryVideoContainer);
575 void MediaDecoder::OnStoreDecoderBenchmark(const VideoInfo& aInfo) {
576 MOZ_ASSERT(NS_IsMainThread());
578 int32_t videoFrameRate = aInfo.GetFrameRate().ref();
580 if (mFrameStats && videoFrameRate) {
581 DecoderBenchmarkInfo benchmarkInfo{
582 aInfo.mMimeType,
583 aInfo.mDisplay.width,
584 aInfo.mDisplay.height,
585 videoFrameRate,
586 BitDepthForColorDepth(aInfo.mColorDepth),
589 LOG("Store benchmark: Video width=%d, height=%d, frameRate=%d, content "
590 "type = %s\n",
591 benchmarkInfo.mWidth, benchmarkInfo.mHeight, benchmarkInfo.mFrameRate,
592 benchmarkInfo.mContentType.BeginReading());
594 mDecoderBenchmark->Store(benchmarkInfo, mFrameStats);
598 void MediaDecoder::ShutdownInternal() {
599 MOZ_ASSERT(NS_IsMainThread());
600 mVideoFrameContainer = nullptr;
601 mSecondaryVideoContainer = nullptr;
602 MediaShutdownManager::Instance().Unregister(this);
605 void MediaDecoder::FinishShutdown() {
606 MOZ_ASSERT(NS_IsMainThread());
607 SetStateMachine(nullptr);
608 ShutdownInternal();
611 nsresult MediaDecoder::CreateAndInitStateMachine(bool aIsLiveStream,
612 bool aDisableExternalEngine) {
613 MOZ_ASSERT(NS_IsMainThread());
614 SetStateMachine(CreateStateMachine(aDisableExternalEngine));
616 NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
617 GetStateMachine()->DispatchIsLiveStream(aIsLiveStream);
619 mMDSMCreationTime = Some(TimeStamp::Now());
620 nsresult rv = mDecoderStateMachine->Init(this);
621 NS_ENSURE_SUCCESS(rv, rv);
623 // If some parameters got set before the state machine got created,
624 // set them now
625 SetStateMachineParameters();
627 return NS_OK;
630 void MediaDecoder::SetStateMachineParameters() {
631 MOZ_ASSERT(NS_IsMainThread());
632 if (mPlaybackRate != 1 && mPlaybackRate != 0) {
633 mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
635 mTimedMetadataListener = mDecoderStateMachine->TimedMetadataEvent().Connect(
636 mAbstractMainThread, this, &MediaDecoder::OnMetadataUpdate);
637 mMetadataLoadedListener = mDecoderStateMachine->MetadataLoadedEvent().Connect(
638 mAbstractMainThread, this, &MediaDecoder::MetadataLoaded);
639 mFirstFrameLoadedListener =
640 mDecoderStateMachine->FirstFrameLoadedEvent().Connect(
641 mAbstractMainThread, this, &MediaDecoder::FirstFrameLoaded);
643 mOnPlaybackEvent = mDecoderStateMachine->OnPlaybackEvent().Connect(
644 mAbstractMainThread, this, &MediaDecoder::OnPlaybackEvent);
645 mOnPlaybackErrorEvent = mDecoderStateMachine->OnPlaybackErrorEvent().Connect(
646 mAbstractMainThread, this, &MediaDecoder::OnPlaybackErrorEvent);
647 mOnDecoderDoctorEvent = mDecoderStateMachine->OnDecoderDoctorEvent().Connect(
648 mAbstractMainThread, this, &MediaDecoder::OnDecoderDoctorEvent);
649 mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
650 mAbstractMainThread, this, &MediaDecoder::OnMediaNotSeekable);
651 mOnNextFrameStatus = mDecoderStateMachine->OnNextFrameStatus().Connect(
652 mAbstractMainThread, this, &MediaDecoder::OnNextFrameStatus);
653 mOnTrackInfoUpdated = mDecoderStateMachine->OnTrackInfoUpdatedEvent().Connect(
654 mAbstractMainThread, this, &MediaDecoder::OnTrackInfoUpdated);
655 mOnSecondaryVideoContainerInstalled =
656 mDecoderStateMachine->OnSecondaryVideoContainerInstalled().Connect(
657 mAbstractMainThread, this,
658 &MediaDecoder::OnSecondaryVideoContainerInstalled);
659 mOnStoreDecoderBenchmark = mReader->OnStoreDecoderBenchmark().Connect(
660 mAbstractMainThread, this, &MediaDecoder::OnStoreDecoderBenchmark);
662 mOnEncrypted = mReader->OnEncrypted().Connect(
663 mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DispatchEncrypted);
664 mOnWaitingForKey = mReader->OnWaitingForKey().Connect(
665 mAbstractMainThread, GetOwner(), &MediaDecoderOwner::NotifyWaitingForKey);
666 mOnDecodeWarning = mReader->OnDecodeWarning().Connect(
667 mAbstractMainThread, GetOwner(), &MediaDecoderOwner::DecodeWarning);
670 void MediaDecoder::DisconnectEvents() {
671 MOZ_ASSERT(NS_IsMainThread());
672 mTimedMetadataListener.Disconnect();
673 mMetadataLoadedListener.Disconnect();
674 mFirstFrameLoadedListener.Disconnect();
675 mOnPlaybackEvent.Disconnect();
676 mOnPlaybackErrorEvent.Disconnect();
677 mOnDecoderDoctorEvent.Disconnect();
678 mOnMediaNotSeekable.Disconnect();
679 mOnEncrypted.Disconnect();
680 mOnWaitingForKey.Disconnect();
681 mOnDecodeWarning.Disconnect();
682 mOnNextFrameStatus.Disconnect();
683 mOnTrackInfoUpdated.Disconnect();
684 mOnSecondaryVideoContainerInstalled.Disconnect();
685 mOnStoreDecoderBenchmark.Disconnect();
688 RefPtr<ShutdownPromise> MediaDecoder::ShutdownStateMachine() {
689 MOZ_ASSERT(NS_IsMainThread());
690 MOZ_ASSERT(GetStateMachine());
691 DisconnectEvents();
692 return mDecoderStateMachine->BeginShutdown();
695 void MediaDecoder::Play() {
696 MOZ_ASSERT(NS_IsMainThread());
698 NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
699 LOG("Play");
700 if (mPlaybackRate == 0) {
701 return;
704 if (IsEnded()) {
705 Seek(0, SeekTarget::PrevSyncPoint);
706 return;
709 if (mPlayState == PLAY_STATE_LOADING) {
710 mNextState = PLAY_STATE_PLAYING;
711 return;
714 ChangeState(PLAY_STATE_PLAYING);
717 void MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType) {
718 MOZ_ASSERT(NS_IsMainThread());
719 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
721 MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
723 LOG("Seek");
724 auto time = TimeUnit::FromSeconds(aTime);
726 mLogicalPosition = aTime;
727 mLogicallySeeking = true;
728 SeekTarget target = SeekTarget(time, aSeekType);
729 CallSeek(target);
731 if (mPlayState == PLAY_STATE_ENDED) {
732 ChangeState(GetOwner()->GetPaused() ? PLAY_STATE_PAUSED
733 : PLAY_STATE_PLAYING);
737 void MediaDecoder::SetDelaySeekMode(bool aShouldDelaySeek) {
738 MOZ_ASSERT(NS_IsMainThread());
739 LOG("SetDelaySeekMode, shouldDelaySeek=%d", aShouldDelaySeek);
740 if (mShouldDelaySeek == aShouldDelaySeek) {
741 return;
743 mShouldDelaySeek = aShouldDelaySeek;
744 if (!mShouldDelaySeek && mDelayedSeekTarget) {
745 Seek(mDelayedSeekTarget->GetTime().ToSeconds(),
746 mDelayedSeekTarget->GetType());
747 mDelayedSeekTarget.reset();
751 void MediaDecoder::DiscardOngoingSeekIfExists() {
752 MOZ_ASSERT(NS_IsMainThread());
753 mSeekRequest.DisconnectIfExists();
756 void MediaDecoder::CallSeek(const SeekTarget& aTarget) {
757 MOZ_ASSERT(NS_IsMainThread());
758 if (mShouldDelaySeek) {
759 LOG("Delay seek to %f and store it to delayed seek target",
760 mDelayedSeekTarget->GetTime().ToSeconds());
761 mDelayedSeekTarget = Some(aTarget);
762 return;
764 DiscardOngoingSeekIfExists();
765 mDecoderStateMachine->InvokeSeek(aTarget)
766 ->Then(mAbstractMainThread, __func__, this, &MediaDecoder::OnSeekResolved,
767 &MediaDecoder::OnSeekRejected)
768 ->Track(mSeekRequest);
771 double MediaDecoder::GetCurrentTime() {
772 MOZ_ASSERT(NS_IsMainThread());
773 return mLogicalPosition;
776 void MediaDecoder::OnMetadataUpdate(TimedMetadata&& aMetadata) {
777 MOZ_ASSERT(NS_IsMainThread());
778 MetadataLoaded(MakeUnique<MediaInfo>(*aMetadata.mInfo),
779 UniquePtr<MetadataTags>(std::move(aMetadata.mTags)),
780 MediaDecoderEventVisibility::Observable);
781 FirstFrameLoaded(std::move(aMetadata.mInfo),
782 MediaDecoderEventVisibility::Observable);
785 void MediaDecoder::MetadataLoaded(
786 UniquePtr<MediaInfo> aInfo, UniquePtr<MetadataTags> aTags,
787 MediaDecoderEventVisibility aEventVisibility) {
788 MOZ_ASSERT(NS_IsMainThread());
789 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
791 LOG("MetadataLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d",
792 aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
793 aInfo->HasVideo());
795 mMediaSeekable = aInfo->mMediaSeekable;
796 mMediaSeekableOnlyInBufferedRanges =
797 aInfo->mMediaSeekableOnlyInBufferedRanges;
798 mInfo = std::move(aInfo);
800 mTelemetryProbesReporter->OnMediaContentChanged(
801 TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo));
803 // Make sure the element and the frame (if any) are told about
804 // our new size.
805 if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
806 mFiredMetadataLoaded = true;
807 GetOwner()->MetadataLoaded(mInfo.get(), std::move(aTags));
809 // Invalidate() will end up calling GetOwner()->UpdateMediaSize with the last
810 // dimensions retrieved from the video frame container. The video frame
811 // container contains more up to date dimensions than aInfo.
812 // So we call Invalidate() after calling GetOwner()->MetadataLoaded to ensure
813 // the media element has the latest dimensions.
814 Invalidate();
816 #ifdef MOZ_WMF_MEDIA_ENGINE
817 if (mPendingStatusUpdateForNewlyCreatedStateMachine) {
818 mPendingStatusUpdateForNewlyCreatedStateMachine = false;
819 LOG("Set pending statuses if necessary (mLogicallySeeking=%d, "
820 "mLogicalPosition=%f, mPlaybackRate=%f)",
821 mLogicallySeeking.Ref(), mLogicalPosition, mPlaybackRate);
822 if (mLogicalPosition != 0) {
823 Seek(mLogicalPosition, SeekTarget::Accurate);
825 if (mPlaybackRate != 0 && mPlaybackRate != 1.0) {
826 mDecoderStateMachine->DispatchSetPlaybackRate(mPlaybackRate);
829 #endif
831 EnsureTelemetryReported();
834 void MediaDecoder::EnsureTelemetryReported() {
835 MOZ_ASSERT(NS_IsMainThread());
837 if (mTelemetryReported || !mInfo) {
838 // Note: sometimes we get multiple MetadataLoaded calls (for example
839 // for chained ogg). So we ensure we don't report duplicate results for
840 // these resources.
841 return;
844 nsTArray<nsCString> codecs;
845 if (mInfo->HasAudio() &&
846 !mInfo->mAudio.GetAsAudioInfo()->mMimeType.IsEmpty()) {
847 codecs.AppendElement(mInfo->mAudio.GetAsAudioInfo()->mMimeType);
849 if (mInfo->HasVideo() &&
850 !mInfo->mVideo.GetAsVideoInfo()->mMimeType.IsEmpty()) {
851 codecs.AppendElement(mInfo->mVideo.GetAsVideoInfo()->mMimeType);
853 if (codecs.IsEmpty()) {
854 codecs.AppendElement(nsPrintfCString(
855 "resource; %s", ContainerType().OriginalString().Data()));
857 for (const nsCString& codec : codecs) {
858 LOG("Telemetry MEDIA_CODEC_USED= '%s'", codec.get());
859 Telemetry::Accumulate(Telemetry::HistogramID::MEDIA_CODEC_USED, codec);
862 mTelemetryReported = true;
865 const char* MediaDecoder::PlayStateStr() {
866 MOZ_ASSERT(NS_IsMainThread());
867 return ToPlayStateStr(mPlayState);
870 void MediaDecoder::FirstFrameLoaded(
871 UniquePtr<MediaInfo> aInfo, MediaDecoderEventVisibility aEventVisibility) {
872 MOZ_ASSERT(NS_IsMainThread());
873 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
875 LOG("FirstFrameLoaded, channels=%u rate=%u hasAudio=%d hasVideo=%d "
876 "mPlayState=%s transportSeekable=%d",
877 aInfo->mAudio.mChannels, aInfo->mAudio.mRate, aInfo->HasAudio(),
878 aInfo->HasVideo(), PlayStateStr(), IsTransportSeekable());
880 mInfo = std::move(aInfo);
881 mTelemetryProbesReporter->OnMediaContentChanged(
882 TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo));
884 Invalidate();
886 // The element can run javascript via events
887 // before reaching here, so only change the
888 // state if we're still set to the original
889 // loading state.
890 if (mPlayState == PLAY_STATE_LOADING) {
891 ChangeState(mNextState);
894 // We only care about video first frame.
895 if (mInfo->HasVideo() && mMDSMCreationTime) {
896 auto info = MakeUnique<dom::MediaDecoderDebugInfo>();
897 RequestDebugInfo(*info)->Then(
898 GetMainThreadSerialEventTarget(), __func__,
899 [self = RefPtr<MediaDecoder>{this}, this, now = TimeStamp::Now(),
900 creationTime = *mMDSMCreationTime, result = std::move(info)](
901 GenericPromise::ResolveOrRejectValue&& aValue) mutable {
902 if (IsShutdown()) {
903 return;
905 if (aValue.IsReject()) {
906 NS_WARNING("Failed to get debug info for the first frame probe!");
907 return;
909 auto firstFrameLoadedTime = (now - creationTime).ToMilliseconds();
910 MOZ_ASSERT(result->mReader.mTotalReadMetadataTimeMs >= 0.0);
911 MOZ_ASSERT(result->mReader.mTotalWaitingForVideoDataTimeMs >= 0.0);
912 MOZ_ASSERT(result->mStateMachine.mTotalBufferingTimeMs >= 0.0);
914 using FirstFrameLoadedFlag =
915 TelemetryProbesReporter::FirstFrameLoadedFlag;
916 TelemetryProbesReporter::FirstFrameLoadedFlagSet flags;
917 if (IsMSE()) {
918 flags += FirstFrameLoadedFlag::IsMSE;
920 if (mDecoderStateMachine->IsExternalEngineStateMachine()) {
921 flags += FirstFrameLoadedFlag::IsExternalEngineStateMachine;
923 if (IsHLSDecoder()) {
924 flags += FirstFrameLoadedFlag::IsHLS;
926 if (result->mReader.mVideoHardwareAccelerated) {
927 flags += FirstFrameLoadedFlag::IsHardwareDecoding;
929 mTelemetryProbesReporter->OntFirstFrameLoaded(
930 firstFrameLoadedTime, result->mReader.mTotalReadMetadataTimeMs,
931 result->mReader.mTotalWaitingForVideoDataTimeMs,
932 result->mStateMachine.mTotalBufferingTimeMs, flags, *mInfo);
934 mMDSMCreationTime.reset();
937 // GetOwner()->FirstFrameLoaded() might call us back. Put it at the bottom of
938 // this function to avoid unexpected shutdown from reentrant calls.
939 if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
940 GetOwner()->FirstFrameLoaded();
944 void MediaDecoder::NetworkError(const MediaResult& aError) {
945 MOZ_ASSERT(NS_IsMainThread());
946 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
947 GetOwner()->NetworkError(aError);
950 void MediaDecoder::DecodeError(const MediaResult& aError) {
951 MOZ_ASSERT(NS_IsMainThread());
952 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
953 GetOwner()->DecodeError(aError);
956 void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin) {
957 MOZ_ASSERT(NS_IsMainThread());
958 mSameOriginMedia = aSameOrigin;
961 bool MediaDecoder::IsSeeking() const {
962 MOZ_ASSERT(NS_IsMainThread());
963 return mLogicallySeeking;
966 bool MediaDecoder::OwnerHasError() const {
967 MOZ_ASSERT(NS_IsMainThread());
968 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
969 return GetOwner()->HasError();
972 bool MediaDecoder::IsEnded() const {
973 MOZ_ASSERT(NS_IsMainThread());
974 return mPlayState == PLAY_STATE_ENDED;
977 bool MediaDecoder::IsShutdown() const {
978 MOZ_ASSERT(NS_IsMainThread());
979 return mPlayState == PLAY_STATE_SHUTDOWN;
982 void MediaDecoder::PlaybackEnded() {
983 MOZ_ASSERT(NS_IsMainThread());
984 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
986 if (mLogicallySeeking || mPlayState == PLAY_STATE_LOADING ||
987 mPlayState == PLAY_STATE_ENDED) {
988 LOG("MediaDecoder::PlaybackEnded bailed out, "
989 "mLogicallySeeking=%d mPlayState=%s",
990 mLogicallySeeking.Ref(), ToPlayStateStr(mPlayState));
991 return;
994 LOG("MediaDecoder::PlaybackEnded");
996 ChangeState(PLAY_STATE_ENDED);
997 InvalidateWithFlags(VideoFrameContainer::INVALIDATE_FORCE);
998 GetOwner()->PlaybackEnded();
1001 void MediaDecoder::NotifyPrincipalChanged() {
1002 MOZ_ASSERT(NS_IsMainThread());
1003 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1004 GetOwner()->NotifyDecoderPrincipalChanged();
1007 void MediaDecoder::OnSeekResolved() {
1008 MOZ_ASSERT(NS_IsMainThread());
1009 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1010 LOG("MediaDecoder::OnSeekResolved");
1011 mLogicallySeeking = false;
1013 // Ensure logical position is updated after seek.
1014 UpdateLogicalPositionInternal();
1015 mSeekRequest.Complete();
1017 GetOwner()->SeekCompleted();
1020 void MediaDecoder::OnSeekRejected() {
1021 MOZ_ASSERT(NS_IsMainThread());
1022 LOG("MediaDecoder::OnSeekRejected");
1023 mSeekRequest.Complete();
1024 mLogicallySeeking = false;
1026 GetOwner()->SeekAborted();
1029 void MediaDecoder::SeekingStarted() {
1030 MOZ_ASSERT(NS_IsMainThread());
1031 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1032 GetOwner()->SeekStarted();
1035 void MediaDecoder::ChangeState(PlayState aState) {
1036 MOZ_ASSERT(NS_IsMainThread());
1037 MOZ_ASSERT(!IsShutdown(), "SHUTDOWN is the final state.");
1039 if (mNextState == aState) {
1040 mNextState = PLAY_STATE_PAUSED;
1043 if (mPlayState != aState) {
1044 DDLOG(DDLogCategory::Property, "play_state", ToPlayStateStr(aState));
1045 LOG("Play state changes from %s to %s", ToPlayStateStr(mPlayState),
1046 ToPlayStateStr(aState));
1047 mPlayState = aState;
1048 UpdateTelemetryHelperBasedOnPlayState(aState);
1052 TelemetryProbesReporter::Visibility MediaDecoder::OwnerVisibility() const {
1053 return GetOwner()->IsActuallyInvisible() || mForcedHidden
1054 ? TelemetryProbesReporter::Visibility::eInvisible
1055 : TelemetryProbesReporter::Visibility::eVisible;
1058 void MediaDecoder::UpdateTelemetryHelperBasedOnPlayState(
1059 PlayState aState) const {
1060 if (aState == PlayState::PLAY_STATE_PLAYING) {
1061 mTelemetryProbesReporter->OnPlay(
1062 OwnerVisibility(),
1063 TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo),
1064 mVolume == 0.f);
1065 } else if (aState == PlayState::PLAY_STATE_PAUSED ||
1066 aState == PlayState::PLAY_STATE_ENDED) {
1067 mTelemetryProbesReporter->OnPause(OwnerVisibility());
1068 } else if (aState == PLAY_STATE_SHUTDOWN) {
1069 mTelemetryProbesReporter->OnShutdown();
1073 MediaDecoder::PositionUpdate MediaDecoder::GetPositionUpdateReason(
1074 double aPrevPos, const TimeUnit& aCurPos) const {
1075 MOZ_ASSERT(NS_IsMainThread());
1076 // If current position is earlier than previous position and we didn't do
1077 // seek, that means we looped back to the start position.
1078 const bool notSeeking = !mSeekRequest.Exists();
1079 if (mLooping && notSeeking && aCurPos.ToSeconds() < aPrevPos) {
1080 return PositionUpdate::eSeamlessLoopingSeeking;
1082 return aPrevPos != aCurPos.ToSeconds() && notSeeking
1083 ? PositionUpdate::ePeriodicUpdate
1084 : PositionUpdate::eOther;
1087 void MediaDecoder::UpdateLogicalPositionInternal() {
1088 MOZ_ASSERT(NS_IsMainThread());
1089 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1091 TimeUnit currentPosition = CurrentPosition();
1092 if (mPlayState == PLAY_STATE_ENDED) {
1093 currentPosition =
1094 std::max(currentPosition, mDuration.match(DurationToTimeUnit()));
1097 const PositionUpdate reason =
1098 GetPositionUpdateReason(mLogicalPosition, currentPosition);
1099 switch (reason) {
1100 case PositionUpdate::ePeriodicUpdate:
1101 SetLogicalPosition(currentPosition);
1102 // This is actually defined in `TimeMarchesOn`, but we do that in decoder.
1103 // https://html.spec.whatwg.org/multipage/media.html#playing-the-media-resource:event-media-timeupdate-7
1104 // TODO (bug 1688137): should we move it back to `TimeMarchesOn`?
1105 GetOwner()->MaybeQueueTimeupdateEvent();
1106 break;
1107 case PositionUpdate::eSeamlessLoopingSeeking:
1108 // When seamless seeking occurs, seeking was performed on the demuxer so
1109 // the decoder doesn't know. That means decoder still thinks it's in
1110 // playing. Therefore, we have to manually call those methods to notify
1111 // the owner about seeking.
1112 GetOwner()->SeekStarted();
1113 SetLogicalPosition(currentPosition);
1114 GetOwner()->SeekCompleted();
1115 break;
1116 default:
1117 MOZ_ASSERT(reason == PositionUpdate::eOther);
1118 SetLogicalPosition(currentPosition);
1119 break;
1122 // Invalidate the frame so any video data is displayed.
1123 // Do this before the timeupdate event so that if that
1124 // event runs JavaScript that queries the media size, the
1125 // frame has reflowed and the size updated beforehand.
1126 Invalidate();
1129 void MediaDecoder::SetLogicalPosition(const TimeUnit& aNewPosition) {
1130 MOZ_ASSERT(NS_IsMainThread());
1131 if (TimeUnit::FromSeconds(mLogicalPosition) == aNewPosition ||
1132 mLogicalPosition == aNewPosition.ToSeconds()) {
1133 return;
1135 mLogicalPosition = aNewPosition.ToSeconds();
1136 DDLOG(DDLogCategory::Property, "currentTime", mLogicalPosition);
1139 void MediaDecoder::DurationChanged() {
1140 MOZ_ASSERT(NS_IsMainThread());
1141 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1143 Variant<TimeUnit, double> oldDuration = mDuration;
1145 // Use the explicit duration if we have one.
1146 // Otherwise use the duration mirrored from MDSM.
1147 if (mExplicitDuration.isSome()) {
1148 mDuration.emplace<double>(mExplicitDuration.ref());
1149 } else if (mStateMachineDuration.Ref().isSome()) {
1150 MOZ_ASSERT(mStateMachineDuration.Ref().ref().IsValid());
1151 mDuration.emplace<TimeUnit>(mStateMachineDuration.Ref().ref());
1154 LOG("New duration: %s",
1155 mDuration.match(DurationToTimeUnit()).ToString().get());
1156 if (oldDuration.is<TimeUnit>() && oldDuration.as<TimeUnit>().IsValid()) {
1157 LOG("Old Duration %s",
1158 oldDuration.match(DurationToTimeUnit()).ToString().get());
1161 if ((oldDuration.is<double>() || oldDuration.as<TimeUnit>().IsValid())) {
1162 if (mDuration.match(DurationToDouble()) ==
1163 oldDuration.match(DurationToDouble())) {
1164 return;
1168 LOG("Duration changed to %s",
1169 mDuration.match(DurationToTimeUnit()).ToString().get());
1171 // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=28822 for a discussion
1172 // of whether we should fire durationchange on explicit infinity.
1173 if (mFiredMetadataLoaded &&
1174 (!std::isinf(mDuration.match(DurationToDouble())) ||
1175 mExplicitDuration.isSome())) {
1176 GetOwner()->DispatchAsyncEvent(u"durationchange"_ns);
1179 if (CurrentPosition().ToSeconds() > mDuration.match(DurationToDouble())) {
1180 Seek(mDuration.match(DurationToDouble()), SeekTarget::Accurate);
1184 already_AddRefed<KnowsCompositor> MediaDecoder::GetCompositor() {
1185 MediaDecoderOwner* owner = GetOwner();
1186 Document* ownerDoc = owner ? owner->GetDocument() : nullptr;
1187 WindowRenderer* renderer =
1188 ownerDoc ? nsContentUtils::WindowRendererForDocument(ownerDoc) : nullptr;
1189 RefPtr<KnowsCompositor> knows =
1190 renderer ? renderer->AsKnowsCompositor() : nullptr;
1191 return knows ? knows->GetForMedia().forget() : nullptr;
1194 void MediaDecoder::NotifyCompositor() {
1195 RefPtr<KnowsCompositor> knowsCompositor = GetCompositor();
1196 if (knowsCompositor) {
1197 nsCOMPtr<nsIRunnable> r =
1198 NewRunnableMethod<already_AddRefed<KnowsCompositor>&&>(
1199 "MediaFormatReader::UpdateCompositor", mReader,
1200 &MediaFormatReader::UpdateCompositor, knowsCompositor.forget());
1201 Unused << mReader->OwnerThread()->Dispatch(r.forget());
1205 void MediaDecoder::SetElementVisibility(bool aIsOwnerInvisible,
1206 bool aIsOwnerConnected) {
1207 MOZ_ASSERT(NS_IsMainThread());
1208 mIsOwnerInvisible = aIsOwnerInvisible;
1209 mIsOwnerConnected = aIsOwnerConnected;
1210 mTelemetryProbesReporter->OnVisibilityChanged(OwnerVisibility());
1211 UpdateVideoDecodeMode();
1214 void MediaDecoder::SetForcedHidden(bool aForcedHidden) {
1215 MOZ_ASSERT(NS_IsMainThread());
1216 mForcedHidden = aForcedHidden;
1217 mTelemetryProbesReporter->OnVisibilityChanged(OwnerVisibility());
1218 UpdateVideoDecodeMode();
1221 void MediaDecoder::SetSuspendTaint(bool aTainted) {
1222 MOZ_ASSERT(NS_IsMainThread());
1223 mHasSuspendTaint = aTainted;
1224 UpdateVideoDecodeMode();
1227 void MediaDecoder::UpdateVideoDecodeMode() {
1228 MOZ_ASSERT(NS_IsMainThread());
1230 // The MDSM may yet be set.
1231 if (!mDecoderStateMachine) {
1232 LOG("UpdateVideoDecodeMode(), early return because we don't have MDSM.");
1233 return;
1236 // Seeking is required when leaving suspend mode.
1237 if (!mMediaSeekable) {
1238 LOG("UpdateVideoDecodeMode(), set Normal because the media is not "
1239 "seekable");
1240 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1241 return;
1244 // If mHasSuspendTaint is set, never suspend the video decoder.
1245 if (mHasSuspendTaint) {
1246 LOG("UpdateVideoDecodeMode(), set Normal because the element has been "
1247 "tainted.");
1248 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1249 return;
1252 // If mSecondaryVideoContainer is set, never suspend the video decoder.
1253 if (mSecondaryVideoContainer.Ref()) {
1254 LOG("UpdateVideoDecodeMode(), set Normal because the element is cloning "
1255 "itself visually to another video container.");
1256 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1257 return;
1260 // Don't suspend elements that is not in a connected tree.
1261 if (!mIsOwnerConnected) {
1262 LOG("UpdateVideoDecodeMode(), set Normal because the element is not in "
1263 "tree.");
1264 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1265 return;
1268 // If mForcedHidden is set, suspend the video decoder anyway.
1269 if (mForcedHidden) {
1270 LOG("UpdateVideoDecodeMode(), set Suspend because the element is forced to "
1271 "be suspended.");
1272 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
1273 return;
1276 // Resume decoding in the advance, even the element is in the background.
1277 if (mIsBackgroundVideoDecodingAllowed) {
1278 LOG("UpdateVideoDecodeMode(), set Normal because the tab is in background "
1279 "and hovered.");
1280 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1281 return;
1284 if (mIsOwnerInvisible) {
1285 LOG("UpdateVideoDecodeMode(), set Suspend because of invisible element.");
1286 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Suspend);
1287 } else {
1288 LOG("UpdateVideoDecodeMode(), set Normal because of visible element.");
1289 mDecoderStateMachine->SetVideoDecodeMode(VideoDecodeMode::Normal);
1293 void MediaDecoder::SetIsBackgroundVideoDecodingAllowed(bool aAllowed) {
1294 mIsBackgroundVideoDecodingAllowed = aAllowed;
1295 UpdateVideoDecodeMode();
1298 bool MediaDecoder::HasSuspendTaint() const {
1299 MOZ_ASSERT(NS_IsMainThread());
1300 return mHasSuspendTaint;
1303 void MediaDecoder::SetSecondaryVideoContainer(
1304 const RefPtr<VideoFrameContainer>& aSecondaryVideoContainer) {
1305 MOZ_ASSERT(NS_IsMainThread());
1306 if (mSecondaryVideoContainer.Ref() == aSecondaryVideoContainer) {
1307 return;
1309 mSecondaryVideoContainer = aSecondaryVideoContainer;
1310 UpdateVideoDecodeMode();
1313 bool MediaDecoder::IsMediaSeekable() {
1314 MOZ_ASSERT(NS_IsMainThread());
1315 NS_ENSURE_TRUE(GetStateMachine(), false);
1316 return mMediaSeekable;
1319 namespace {
1321 // Returns zero, either as a TimeUnit or as a double.
1322 template <typename T>
1323 constexpr T Zero() {
1324 if constexpr (std::is_same<T, double>::value) {
1325 return 0.0;
1326 } else if constexpr (std::is_same<T, TimeUnit>::value) {
1327 return TimeUnit::Zero();
1329 MOZ_RELEASE_ASSERT(false);
1332 // Returns Infinity either as a TimeUnit or as a double.
1333 template <typename T>
1334 constexpr T Infinity() {
1335 if constexpr (std::is_same<T, double>::value) {
1336 return std::numeric_limits<double>::infinity();
1337 } else if constexpr (std::is_same<T, TimeUnit>::value) {
1338 return TimeUnit::FromInfinity();
1340 MOZ_RELEASE_ASSERT(false);
1343 }; // namespace
1345 // This method can be made to return either TimeIntervals, that is a set of
1346 // interval that are delimited with TimeUnit, or TimeRanges, that is a set of
1347 // intervals that are delimited by seconds, as doubles.
1348 // seekable often depends on the duration of a media, in the very common case
1349 // where the seekable range is [0, duration]. When playing a MediaSource, the
1350 // duration of a media element can be set as an arbitrary number, that are
1351 // 64-bits floating point values.
1352 // This allows returning an interval that is [0, duration], with duration being
1353 // a double that cannot be represented as a TimeUnit, either because it has too
1354 // many significant digits, or because it's outside of the int64_t range that
1355 // TimeUnit internally uses.
1356 template <typename IntervalType>
1357 IntervalType MediaDecoder::GetSeekableImpl() {
1358 MOZ_ASSERT(NS_IsMainThread());
1359 if (std::isnan(GetDuration())) {
1360 // We do not have a duration yet, we can't determine the seekable range.
1361 return IntervalType();
1364 // Compute [0, duration] -- When dealing with doubles, use ::GetDuration to
1365 // avoid rounding the value differently. When dealing with TimeUnit, it's
1366 // returned directly.
1367 typename IntervalType::InnerType duration;
1368 if constexpr (std::is_same<typename IntervalType::InnerType, double>::value) {
1369 duration = GetDuration();
1370 } else {
1371 duration = mDuration.as<TimeUnit>();
1373 typename IntervalType::ElemType zeroToDuration =
1374 typename IntervalType::ElemType(
1375 Zero<typename IntervalType::InnerType>(),
1376 IsInfinite() ? Infinity<typename IntervalType::InnerType>()
1377 : duration);
1378 auto buffered = IntervalType(GetBuffered());
1379 // Remove any negative range in the interval -- seeking to a non-positive
1380 // position isn't possible.
1381 auto positiveBuffered = buffered.Intersection(zeroToDuration);
1383 // We can seek in buffered range if the media is seekable. Also, we can seek
1384 // in unbuffered ranges if the transport level is seekable (local file or the
1385 // server supports range requests, etc.) or in cue-less WebMs
1386 if (mMediaSeekableOnlyInBufferedRanges) {
1387 return IntervalType(positiveBuffered);
1389 if (!IsMediaSeekable()) {
1390 return IntervalType();
1392 if (!IsTransportSeekable()) {
1393 return IntervalType(positiveBuffered);
1396 // Common case: seeking is possible at any point of the stream.
1397 return IntervalType(zeroToDuration);
1400 media::TimeIntervals MediaDecoder::GetSeekable() {
1401 return GetSeekableImpl<media::TimeIntervals>();
1404 media::TimeRanges MediaDecoder::GetSeekableTimeRanges() {
1405 return GetSeekableImpl<media::TimeRanges>();
1408 void MediaDecoder::SetFragmentEndTime(double aTime) {
1409 MOZ_ASSERT(NS_IsMainThread());
1410 if (mDecoderStateMachine) {
1411 mDecoderStateMachine->DispatchSetFragmentEndTime(
1412 TimeUnit::FromSeconds(aTime));
1416 void MediaDecoder::SetPlaybackRate(double aPlaybackRate) {
1417 MOZ_ASSERT(NS_IsMainThread());
1419 double oldRate = mPlaybackRate;
1420 mPlaybackRate = aPlaybackRate;
1421 if (aPlaybackRate == 0) {
1422 Pause();
1423 return;
1426 if (oldRate == 0 && !GetOwner()->GetPaused()) {
1427 // PlaybackRate is no longer null.
1428 // Restart the playback if the media was playing.
1429 Play();
1432 if (mDecoderStateMachine) {
1433 mDecoderStateMachine->DispatchSetPlaybackRate(aPlaybackRate);
1437 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch) {
1438 MOZ_ASSERT(NS_IsMainThread());
1439 mPreservesPitch = aPreservesPitch;
1442 void MediaDecoder::SetLooping(bool aLooping) {
1443 MOZ_ASSERT(NS_IsMainThread());
1444 mLooping = aLooping;
1447 void MediaDecoder::SetStreamName(const nsAutoString& aStreamName) {
1448 MOZ_ASSERT(NS_IsMainThread());
1449 mStreamName = aStreamName;
1452 void MediaDecoder::ConnectMirrors(MediaDecoderStateMachineBase* aObject) {
1453 MOZ_ASSERT(NS_IsMainThread());
1454 MOZ_ASSERT(aObject);
1455 mStateMachineDuration.Connect(aObject->CanonicalDuration());
1456 mBuffered.Connect(aObject->CanonicalBuffered());
1457 mCurrentPosition.Connect(aObject->CanonicalCurrentPosition());
1458 mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible());
1461 void MediaDecoder::DisconnectMirrors() {
1462 MOZ_ASSERT(NS_IsMainThread());
1463 mStateMachineDuration.DisconnectIfConnected();
1464 mBuffered.DisconnectIfConnected();
1465 mCurrentPosition.DisconnectIfConnected();
1466 mIsAudioDataAudible.DisconnectIfConnected();
1469 void MediaDecoder::SetStateMachine(
1470 MediaDecoderStateMachineBase* aStateMachine) {
1471 MOZ_ASSERT(NS_IsMainThread());
1472 MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
1473 if (aStateMachine) {
1474 mDecoderStateMachine = aStateMachine;
1475 LOG("set state machine %p", mDecoderStateMachine.get());
1476 ConnectMirrors(aStateMachine);
1477 UpdateVideoDecodeMode();
1478 } else if (mDecoderStateMachine) {
1479 LOG("null out state machine %p", mDecoderStateMachine.get());
1480 mDecoderStateMachine = nullptr;
1481 DisconnectMirrors();
1485 ImageContainer* MediaDecoder::GetImageContainer() {
1486 return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer()
1487 : nullptr;
1490 void MediaDecoder::InvalidateWithFlags(uint32_t aFlags) {
1491 if (mVideoFrameContainer) {
1492 mVideoFrameContainer->InvalidateWithFlags(aFlags);
1496 void MediaDecoder::Invalidate() {
1497 if (mVideoFrameContainer) {
1498 mVideoFrameContainer->Invalidate();
1502 void MediaDecoder::Suspend() {
1503 MOZ_ASSERT(NS_IsMainThread());
1504 GetStateMachine()->InvokeSuspendMediaSink();
1507 void MediaDecoder::Resume() {
1508 MOZ_ASSERT(NS_IsMainThread());
1509 GetStateMachine()->InvokeResumeMediaSink();
1512 // Constructs the time ranges representing what segments of the media
1513 // are buffered and playable.
1514 media::TimeIntervals MediaDecoder::GetBuffered() {
1515 MOZ_ASSERT(NS_IsMainThread());
1516 return mBuffered.Ref();
1519 size_t MediaDecoder::SizeOfVideoQueue() {
1520 MOZ_ASSERT(NS_IsMainThread());
1521 if (mDecoderStateMachine) {
1522 return mDecoderStateMachine->SizeOfVideoQueue();
1524 return 0;
1527 size_t MediaDecoder::SizeOfAudioQueue() {
1528 MOZ_ASSERT(NS_IsMainThread());
1529 if (mDecoderStateMachine) {
1530 return mDecoderStateMachine->SizeOfAudioQueue();
1532 return 0;
1535 void MediaDecoder::NotifyReaderDataArrived() {
1536 MOZ_ASSERT(NS_IsMainThread());
1537 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1539 nsresult rv = mReader->OwnerThread()->Dispatch(
1540 NewRunnableMethod("MediaFormatReader::NotifyDataArrived", mReader.get(),
1541 &MediaFormatReader::NotifyDataArrived));
1542 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1543 Unused << rv;
1546 // Provide access to the state machine object
1547 MediaDecoderStateMachineBase* MediaDecoder::GetStateMachine() const {
1548 MOZ_ASSERT(NS_IsMainThread());
1549 return mDecoderStateMachine;
1552 bool MediaDecoder::CanPlayThrough() {
1553 MOZ_ASSERT(NS_IsMainThread());
1554 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1555 return CanPlayThroughImpl();
1558 RefPtr<SetCDMPromise> MediaDecoder::SetCDMProxy(CDMProxy* aProxy) {
1559 MOZ_ASSERT(NS_IsMainThread());
1560 #ifdef MOZ_WMF_MEDIA_ENGINE
1561 // Switch to another state machine if the current one doesn't support the
1562 // given CDM proxy.
1563 if (aProxy && !GetStateMachine()->IsCDMProxySupported(aProxy)) {
1564 LOG("CDM proxy not supported! Switch to another state machine.");
1565 OnPlaybackErrorEvent(
1566 MediaResult{NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR, aProxy});
1568 #endif
1569 MOZ_DIAGNOSTIC_ASSERT_IF(aProxy,
1570 GetStateMachine()->IsCDMProxySupported(aProxy));
1571 return GetStateMachine()->SetCDMProxy(aProxy);
1574 bool MediaDecoder::IsOpusEnabled() { return StaticPrefs::media_opus_enabled(); }
1576 bool MediaDecoder::IsOggEnabled() { return StaticPrefs::media_ogg_enabled(); }
1578 bool MediaDecoder::IsWaveEnabled() { return StaticPrefs::media_wave_enabled(); }
1580 bool MediaDecoder::IsWebMEnabled() { return StaticPrefs::media_webm_enabled(); }
1582 NS_IMETHODIMP
1583 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
1584 nsISupports* aData, bool aAnonymize) {
1585 // NB: When resourceSizes' ref count goes to 0 the promise will report the
1586 // resources memory and finish the asynchronous memory report.
1587 RefPtr<MediaDecoder::ResourceSizes> resourceSizes =
1588 new MediaDecoder::ResourceSizes(MediaMemoryTracker::MallocSizeOf);
1590 nsCOMPtr<nsIHandleReportCallback> handleReport = aHandleReport;
1591 nsCOMPtr<nsISupports> data = aData;
1593 resourceSizes->Promise()->Then(
1594 AbstractThread::MainThread(), __func__,
1595 [handleReport, data](size_t size) {
1596 handleReport->Callback(
1597 ""_ns, "explicit/media/resources"_ns, KIND_HEAP, UNITS_BYTES,
1598 static_cast<int64_t>(size),
1599 nsLiteralCString("Memory used by media resources including "
1600 "streaming buffers, caches, etc."),
1601 data);
1603 nsCOMPtr<nsIMemoryReporterManager> imgr =
1604 do_GetService("@mozilla.org/memory-reporter-manager;1");
1606 if (imgr) {
1607 imgr->EndReport();
1610 [](size_t) { /* unused reject function */ });
1612 int64_t video = 0;
1613 int64_t audio = 0;
1614 DecodersArray& decoders = Decoders();
1615 for (size_t i = 0; i < decoders.Length(); ++i) {
1616 MediaDecoder* decoder = decoders[i];
1617 video += static_cast<int64_t>(decoder->SizeOfVideoQueue());
1618 audio += static_cast<int64_t>(decoder->SizeOfAudioQueue());
1619 decoder->AddSizeOfResources(resourceSizes);
1622 MOZ_COLLECT_REPORT("explicit/media/decoded/video", KIND_HEAP, UNITS_BYTES,
1623 video, "Memory used by decoded video frames.");
1625 MOZ_COLLECT_REPORT("explicit/media/decoded/audio", KIND_HEAP, UNITS_BYTES,
1626 audio, "Memory used by decoded audio chunks.");
1628 return NS_OK;
1631 MediaDecoderOwner* MediaDecoder::GetOwner() const {
1632 MOZ_ASSERT(NS_IsMainThread());
1633 // mOwner is valid until shutdown.
1634 return mOwner;
1637 MediaDecoderOwner::NextFrameStatus MediaDecoder::NextFrameBufferedStatus() {
1638 MOZ_ASSERT(NS_IsMainThread());
1639 // Next frame hasn't been decoded yet.
1640 // Use the buffered range to consider if we have the next frame available.
1641 auto currentPosition = CurrentPosition();
1642 media::TimeInterval interval(
1643 currentPosition, currentPosition + DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED);
1644 return GetBuffered().Contains(interval)
1645 ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
1646 : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
1649 void MediaDecoder::GetDebugInfo(dom::MediaDecoderDebugInfo& aInfo) {
1650 MOZ_ASSERT(NS_IsMainThread());
1651 CopyUTF8toUTF16(nsPrintfCString("%p", this), aInfo.mInstance);
1652 aInfo.mChannels = mInfo ? mInfo->mAudio.mChannels : 0;
1653 aInfo.mRate = mInfo ? mInfo->mAudio.mRate : 0;
1654 aInfo.mHasAudio = mInfo ? mInfo->HasAudio() : false;
1655 aInfo.mHasVideo = mInfo ? mInfo->HasVideo() : false;
1656 CopyUTF8toUTF16(MakeStringSpan(PlayStateStr()), aInfo.mPlayState);
1657 aInfo.mContainerType =
1658 NS_ConvertUTF8toUTF16(ContainerType().Type().AsString());
1661 RefPtr<GenericPromise> MediaDecoder::RequestDebugInfo(
1662 MediaDecoderDebugInfo& aInfo) {
1663 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1664 if (!NS_IsMainThread()) {
1665 // Run the request on the main thread if it's not already.
1666 return InvokeAsync(AbstractThread::MainThread(), __func__,
1667 [this, self = RefPtr{this}, &aInfo]() {
1668 return RequestDebugInfo(aInfo);
1671 GetDebugInfo(aInfo);
1673 return mReader->RequestDebugInfo(aInfo.mReader)
1674 ->Then(AbstractThread::MainThread(), __func__,
1675 [this, self = RefPtr{this}, &aInfo] {
1676 if (!GetStateMachine()) {
1677 return GenericPromise::CreateAndResolve(true, __func__);
1679 return GetStateMachine()->RequestDebugInfo(aInfo.mStateMachine);
1683 void MediaDecoder::NotifyAudibleStateChanged() {
1684 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1685 GetOwner()->SetAudibleState(mIsAudioDataAudible);
1686 mTelemetryProbesReporter->OnAudibleChanged(
1687 mIsAudioDataAudible ? TelemetryProbesReporter::AudibleState::eAudible
1688 : TelemetryProbesReporter::AudibleState::eNotAudible);
1691 void MediaDecoder::NotifyVolumeChanged() {
1692 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
1693 mTelemetryProbesReporter->OnMutedChanged(mVolume == 0.f);
1696 double MediaDecoder::GetTotalVideoPlayTimeInSeconds() const {
1697 return mTelemetryProbesReporter->GetTotalVideoPlayTimeInSeconds();
1700 double MediaDecoder::GetTotalVideoHDRPlayTimeInSeconds() const {
1701 return mTelemetryProbesReporter->GetTotalVideoHDRPlayTimeInSeconds();
1704 double MediaDecoder::GetVisibleVideoPlayTimeInSeconds() const {
1705 return mTelemetryProbesReporter->GetVisibleVideoPlayTimeInSeconds();
1708 double MediaDecoder::GetInvisibleVideoPlayTimeInSeconds() const {
1709 return mTelemetryProbesReporter->GetInvisibleVideoPlayTimeInSeconds();
1712 double MediaDecoder::GetVideoDecodeSuspendedTimeInSeconds() const {
1713 return mTelemetryProbesReporter->GetVideoDecodeSuspendedTimeInSeconds();
1716 double MediaDecoder::GetTotalAudioPlayTimeInSeconds() const {
1717 return mTelemetryProbesReporter->GetTotalAudioPlayTimeInSeconds();
1720 double MediaDecoder::GetAudiblePlayTimeInSeconds() const {
1721 return mTelemetryProbesReporter->GetAudiblePlayTimeInSeconds();
1724 double MediaDecoder::GetInaudiblePlayTimeInSeconds() const {
1725 return mTelemetryProbesReporter->GetInaudiblePlayTimeInSeconds();
1728 double MediaDecoder::GetMutedPlayTimeInSeconds() const {
1729 return mTelemetryProbesReporter->GetMutedPlayTimeInSeconds();
1732 MediaMemoryTracker::MediaMemoryTracker() = default;
1734 void MediaMemoryTracker::InitMemoryReporter() {
1735 RegisterWeakAsyncMemoryReporter(this);
1738 MediaMemoryTracker::~MediaMemoryTracker() {
1739 UnregisterWeakMemoryReporter(this);
1742 } // namespace mozilla
1744 // avoid redefined macro in unified build
1745 #undef DUMP
1746 #undef LOG
1747 #undef NS_DispatchToMainThread