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