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"
34 #include "nsIMemoryReporter.h"
35 #include "nsPrintfCString.h"
36 #include "nsServiceManagerUtils.h"
38 #include "WindowRenderer.h"
43 using namespace mozilla::dom
;
44 using namespace mozilla::layers
;
45 using namespace mozilla::media
;
49 // avoid redefined macro in unified build
53 LazyLogModule
gMediaDecoderLog("MediaDecoder");
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
) {
64 case MediaDecoder::PLAY_STATE_LOADING
:
66 case MediaDecoder::PLAY_STATE_PAUSED
:
68 case MediaDecoder::PLAY_STATE_PLAYING
:
70 case MediaDecoder::PLAY_STATE_ENDED
:
72 case MediaDecoder::PLAY_STATE_SHUTDOWN
:
75 MOZ_ASSERT_UNREACHABLE("Invalid playState.");
80 class MediaMemoryTracker
: public nsIMemoryReporter
{
81 virtual ~MediaMemoryTracker();
83 NS_DECL_THREADSAFE_ISUPPORTS
84 NS_DECL_NSIMEMORYREPORTER
86 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf
);
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
;
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
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
);
168 void MediaDecoder::Pause() {
169 MOZ_ASSERT(NS_IsMainThread());
170 MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
172 if (mPlayState
== PLAY_STATE_LOADING
|| IsEnded()) {
173 mNextState
= PLAY_STATE_PAUSED
;
176 ChangeState(PLAY_STATE_PAUSED
);
179 void MediaDecoder::SetVolume(double aVolume
) {
180 MOZ_ASSERT(NS_IsMainThread());
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.
300 mWatchManager
.Watch(mStateMachineDuration
, &MediaDecoder::DurationChanged
);
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
);
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();
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
);
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;
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()) {
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
:
379 case MediaPlaybackEvent::SeekStarted
:
382 case MediaPlaybackEvent::Invalidate
:
385 case MediaPlaybackEvent::EnterVideoSuspend
:
386 GetOwner()->DispatchAsyncEvent(u
"mozentervideosuspend"_ns
);
387 mTelemetryProbesReporter
->OnDecodeSuspended();
388 mIsVideoDecodingSuspended
= true;
390 case MediaPlaybackEvent::ExitVideoSuspend
:
391 GetOwner()->DispatchAsyncEvent(u
"mozexitvideosuspend"_ns
);
392 mTelemetryProbesReporter
->OnDecodeResumed();
393 mIsVideoDecodingSuspended
= false;
395 case MediaPlaybackEvent::StartVideoSuspendTimer
:
396 GetOwner()->DispatchAsyncEvent(u
"mozstartvideosuspendtimer"_ns
);
398 case MediaPlaybackEvent::CancelVideoSuspendTimer
:
399 GetOwner()->DispatchAsyncEvent(u
"mozcancelvideosuspendtimer"_ns
);
401 case MediaPlaybackEvent::VideoOnlySeekBegin
:
402 GetOwner()->DispatchAsyncEvent(u
"mozvideoonlyseekbegin"_ns
);
404 case MediaPlaybackEvent::VideoOnlySeekCompleted
:
405 GetOwner()->DispatchAsyncEvent(u
"mozvideoonlyseekcompleted"_ns
);
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
421 if (aError
!= NS_ERROR_DOM_MEDIA_EXTERNAL_ENGINE_NOT_SUPPORTED_ERR
&&
422 aError
!= NS_ERROR_DOM_MEDIA_CDM_PROXY_NOT_SUPPORTED_ERR
) {
427 // Already in shutting down decoder, no need to create another state machine.
428 if (mPlayState
== PLAY_STATE_SHUTDOWN
) {
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);
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
) {
450 if (aError
.GetCDMProxy()->AsWMFCDMProxy()) {
451 needExternalEngine
= true;
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
;
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()};
482 if (mInfo
->HasAudio()) {
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(),
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
505 DiscardOngoingSeekIfExists();
507 discardStateMachine
->BeginShutdown()->Then(
508 AbstractThread::MainThread(), __func__
, [discardStateMachine
] {});
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();
520 DecoderDoctorDiagnostics diags
;
521 diags
.StoreEvent(doc
, aEvent
, __func__
);
524 static const char* NextFrameStatusToStr(
525 MediaDecoderOwner::NextFrameStatus 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";
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
;
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
561 mInfo
->mVideo
= aVideoInfo
;
562 mInfo
->mAudio
= aAudioInfo
;
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
{
583 aInfo
.mDisplay
.width
,
584 aInfo
.mDisplay
.height
,
586 BitDepthForColorDepth(aInfo
.mColorDepth
),
589 LOG("Store benchmark: Video width=%d, height=%d, frameRate=%d, content "
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);
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,
625 SetStateMachineParameters();
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());
692 return mDecoderStateMachine
->BeginShutdown();
695 void MediaDecoder::Play() {
696 MOZ_ASSERT(NS_IsMainThread());
698 NS_ASSERTION(mDecoderStateMachine
!= nullptr, "Should have state machine.");
700 if (mPlaybackRate
== 0) {
705 Seek(0, SeekTarget::PrevSyncPoint
);
709 if (mPlayState
== PLAY_STATE_LOADING
) {
710 mNextState
= PLAY_STATE_PLAYING
;
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.");
724 auto time
= TimeUnit::FromSeconds(aTime
);
726 mLogicalPosition
= aTime
;
727 mLogicallySeeking
= true;
728 SeekTarget target
= SeekTarget(time
, aSeekType
);
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
) {
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
);
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(),
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
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.
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
);
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
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
));
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
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 {
905 if (aValue
.IsReject()) {
906 NS_WARNING("Failed to get debug info for the first frame probe!");
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
;
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
));
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(
1063 TelemetryProbesReporter::MediaInfoToMediaContent(*mInfo
),
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
) {
1094 std::max(currentPosition
, mDuration
.match(DurationToTimeUnit()));
1097 const PositionUpdate reason
=
1098 GetPositionUpdateReason(mLogicalPosition
, currentPosition
);
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();
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();
1117 MOZ_ASSERT(reason
== PositionUpdate::eOther
);
1118 SetLogicalPosition(currentPosition
);
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.
1129 void MediaDecoder::SetLogicalPosition(const TimeUnit
& aNewPosition
) {
1130 MOZ_ASSERT(NS_IsMainThread());
1131 if (TimeUnit::FromSeconds(mLogicalPosition
) == aNewPosition
||
1132 mLogicalPosition
== aNewPosition
.ToSeconds()) {
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())) {
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.");
1236 // Seeking is required when leaving suspend mode.
1237 if (!mMediaSeekable
) {
1238 LOG("UpdateVideoDecodeMode(), set Normal because the media is not "
1240 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Normal
);
1244 // If mHasSuspendTaint is set, never suspend the video decoder.
1245 if (mHasSuspendTaint
) {
1246 LOG("UpdateVideoDecodeMode(), set Normal because the element has been "
1248 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Normal
);
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
);
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 "
1264 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Normal
);
1268 // If mForcedHidden is set, suspend the video decoder anyway.
1269 if (mForcedHidden
) {
1270 LOG("UpdateVideoDecodeMode(), set Suspend because the element is forced to "
1272 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Suspend
);
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 "
1280 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Normal
);
1284 if (mIsOwnerInvisible
) {
1285 LOG("UpdateVideoDecodeMode(), set Suspend because of invisible element.");
1286 mDecoderStateMachine
->SetVideoDecodeMode(VideoDecodeMode::Suspend
);
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
) {
1309 mSecondaryVideoContainer
= aSecondaryVideoContainer
;
1310 UpdateVideoDecodeMode();
1313 bool MediaDecoder::IsMediaSeekable() {
1314 MOZ_ASSERT(NS_IsMainThread());
1315 NS_ENSURE_TRUE(GetStateMachine(), false);
1316 return mMediaSeekable
;
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
) {
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);
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();
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
>()
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) {
1426 if (oldRate
== 0 && !GetOwner()->GetPaused()) {
1427 // PlaybackRate is no longer null.
1428 // Restart the playback if the media was playing.
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()
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();
1527 size_t MediaDecoder::SizeOfAudioQueue() {
1528 MOZ_ASSERT(NS_IsMainThread());
1529 if (mDecoderStateMachine
) {
1530 return mDecoderStateMachine
->SizeOfAudioQueue();
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
));
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
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
});
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(); }
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."),
1603 nsCOMPtr
<nsIMemoryReporterManager
> imgr
=
1604 do_GetService("@mozilla.org/memory-reporter-manager;1");
1610 [](size_t) { /* unused reject function */ });
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.");
1631 MediaDecoderOwner
* MediaDecoder::GetOwner() const {
1632 MOZ_ASSERT(NS_IsMainThread());
1633 // mOwner is valid until shutdown.
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
1747 #undef NS_DispatchToMainThread