MediaFormatReader.cpp (132978B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "MediaFormatReader.h" 8 9 #include <algorithm> 10 #include <queue> 11 12 #include "AllocationPolicy.h" 13 #ifdef MOZ_AV1 14 # include "AOMDecoder.h" 15 #endif 16 #include "MP4Decoder.h" 17 #include "MediaData.h" 18 #include "MediaDataDecoderProxy.h" 19 #include "MediaInfo.h" 20 #include "PDMFactory.h" 21 #include "PerformanceRecorder.h" 22 #include "VPXDecoder.h" 23 #include "VideoFrameContainer.h" 24 #include "VideoUtils.h" 25 #include "mozilla/AbstractThread.h" 26 #include "mozilla/CDMProxy.h" 27 #include "mozilla/ClearOnShutdown.h" 28 #include "mozilla/NotNull.h" 29 #include "mozilla/Preferences.h" 30 #include "mozilla/ProfilerLabels.h" 31 #include "mozilla/ProfilerMarkers.h" 32 #include "mozilla/SharedThreadPool.h" 33 #include "mozilla/StaticPrefs_media.h" 34 #include "mozilla/TaskQueue.h" 35 #include "mozilla/glean/DomMediaMetrics.h" 36 #include "nsContentUtils.h" 37 #include "nsLiteralString.h" 38 #include "nsPrintfCString.h" 39 #include "nsTHashSet.h" 40 41 using namespace mozilla::media; 42 43 static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader"); 44 45 #define LOG(arg, ...) \ 46 DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, "::%s: " arg, \ 47 __func__, ##__VA_ARGS__) 48 #define LOGV(arg, ...) \ 49 DDMOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, "::%s: " arg, \ 50 __func__, ##__VA_ARGS__) 51 52 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead 53 54 namespace mozilla { 55 56 using MediaDataDecoderID = void*; 57 58 /** 59 * This class tracks shutdown promises to ensure all decoders are shut down 60 * completely before MFR continues the rest of the shutdown procedure. 61 */ 62 class MediaFormatReader::ShutdownPromisePool { 63 public: 64 ShutdownPromisePool() 65 : mOnShutdownComplete(new ShutdownPromise::Private(__func__)) {} 66 67 // Return a promise which will be resolved when all the tracking promises 68 // are resolved. Note no more promises should be added for tracking once 69 // this function is called. 70 RefPtr<ShutdownPromise> Shutdown(); 71 72 // Track a shutdown promise. 73 void Track(const RefPtr<ShutdownPromise>& aPromise); 74 75 // Shut down a decoder and track its shutdown promise. 76 void ShutdownDecoder(already_AddRefed<MediaDataDecoder> aDecoder) { 77 Track(RefPtr<MediaDataDecoder>(aDecoder)->Shutdown()); 78 } 79 80 private: 81 bool mShutdown = false; 82 const RefPtr<ShutdownPromise::Private> mOnShutdownComplete; 83 nsTHashSet<RefPtr<ShutdownPromise>> mPromises; 84 }; 85 86 RefPtr<ShutdownPromise> MediaFormatReader::ShutdownPromisePool::Shutdown() { 87 MOZ_DIAGNOSTIC_ASSERT(!mShutdown); 88 mShutdown = true; 89 if (mPromises.Count() == 0) { 90 mOnShutdownComplete->Resolve(true, __func__); 91 } 92 return mOnShutdownComplete; 93 } 94 95 void MediaFormatReader::ShutdownPromisePool::Track( 96 const RefPtr<ShutdownPromise>& aPromise) { 97 MOZ_DIAGNOSTIC_ASSERT(!mShutdown); 98 MOZ_DIAGNOSTIC_ASSERT(!mPromises.Contains(aPromise)); 99 mPromises.Insert(aPromise); 100 aPromise->Then(AbstractThread::GetCurrent(), __func__, [aPromise, this]() { 101 MOZ_DIAGNOSTIC_ASSERT(mPromises.Contains(aPromise)); 102 mPromises.Remove(aPromise); 103 if (mShutdown && mPromises.Count() == 0) { 104 mOnShutdownComplete->Resolve(true, __func__); 105 } 106 }); 107 } 108 109 void MediaFormatReader::DecoderData::ShutdownDecoder() { 110 MOZ_ASSERT(mOwner->OnTaskQueue()); 111 112 MutexAutoLock lock(mMutex); 113 114 if (!mDecoder) { 115 // No decoder to shut down. 116 return; 117 } 118 119 if (mFlushing) { 120 // Flush is is in action. Shutdown will be initiated after flush completes. 121 MOZ_DIAGNOSTIC_ASSERT(mShutdownPromise); 122 mOwner->mShutdownPromisePool->Track(mShutdownPromise->Ensure(__func__)); 123 // The order of decoder creation and shutdown is handled by LocalAllocPolicy 124 // and ShutdownPromisePool. MFR can now reset these members to a fresh state 125 // and be ready to create new decoders again without explicitly waiting for 126 // flush/shutdown to complete. 127 mShutdownPromise = nullptr; 128 mFlushing = false; 129 } else { 130 // No flush is in action. We can shut down the decoder now. 131 mOwner->mShutdownPromisePool->Track(mDecoder->Shutdown()); 132 } 133 134 // mShutdownPromisePool will handle the order of decoder shutdown so 135 // we can forget mDecoder and be ready to create a new one. 136 mDecoder = nullptr; 137 mDescription = "shutdown"_ns; 138 mHasReportedVideoHardwareSupportTelemtry = false; 139 mOwner->ScheduleUpdate(mType == MediaData::Type::AUDIO_DATA 140 ? TrackType::kAudioTrack 141 : TrackType::kVideoTrack); 142 } 143 144 void MediaFormatReader::DecoderData::Flush() { 145 AUTO_PROFILER_LABEL("MediaFormatReader::Flush", MEDIA_PLAYBACK); 146 MOZ_ASSERT(mOwner->OnTaskQueue()); 147 148 if (mFlushing || mFlushed) { 149 // Flush still pending or already flushed, nothing more to do. 150 return; 151 } 152 mDecodeRequest.DisconnectIfExists(); 153 mDrainRequest.DisconnectIfExists(); 154 mDrainState = DrainState::None; 155 CancelWaitingForKey(); 156 mOutput.Clear(); 157 mNumSamplesInput = 0; 158 mNumSamplesOutput = 0; 159 mSizeOfQueue = 0; 160 if (mDecoder) { 161 TrackType type = mType == MediaData::Type::AUDIO_DATA 162 ? TrackType::kAudioTrack 163 : TrackType::kVideoTrack; 164 mFlushing = true; 165 MOZ_DIAGNOSTIC_ASSERT(!mShutdownPromise); 166 mShutdownPromise = new SharedShutdownPromiseHolder(); 167 RefPtr<SharedShutdownPromiseHolder> p = mShutdownPromise; 168 RefPtr<MediaDataDecoder> d = mDecoder; 169 DDLOGEX2("MediaFormatReader::DecoderData", this, DDLogCategory::Log, 170 "flushing", DDNoValue{}); 171 mDecoder->Flush()->Then( 172 mOwner->OwnerThread(), __func__, 173 [type, this, p, d]() { 174 AUTO_PROFILER_LABEL("MediaFormatReader::Flush:Resolved", 175 MEDIA_PLAYBACK); 176 DDLOGEX2("MediaFormatReader::DecoderData", this, DDLogCategory::Log, 177 "flushed", DDNoValue{}); 178 if (!p->IsEmpty()) { 179 // Shutdown happened before flush completes. 180 // Let's continue to shut down the decoder. Note 181 // we don't access |this| because this decoder 182 // is no longer managed by MFR::DecoderData. 183 d->Shutdown()->ChainTo(p->Steal(), __func__); 184 return; 185 } 186 mFlushing = false; 187 mShutdownPromise = nullptr; 188 mOwner->ScheduleUpdate(type); 189 }, 190 [type, this, p, d](const MediaResult& aError) { 191 AUTO_PROFILER_LABEL("MediaFormatReader::Flush:Rejected", 192 MEDIA_PLAYBACK); 193 DDLOGEX2("MediaFormatReader::DecoderData", this, DDLogCategory::Log, 194 "flush_error", aError); 195 if (!p->IsEmpty()) { 196 d->Shutdown()->ChainTo(p->Steal(), __func__); 197 return; 198 } 199 mFlushing = false; 200 mShutdownPromise = nullptr; 201 mOwner->NotifyError(type, aError); 202 }); 203 } 204 mFlushed = true; 205 } 206 207 void MediaFormatReader::DecoderData::RequestDrain() { 208 LOG(""); 209 MOZ_RELEASE_ASSERT(mDrainState == DrainState::None); 210 mDrainState = DrainState::DrainRequested; 211 } 212 213 class MediaFormatReader::DecoderFactory { 214 using InitPromise = MediaDataDecoder::InitPromise; 215 using TokenPromise = AllocPolicy::Promise; 216 using Token = AllocPolicy::Token; 217 using CreateDecoderPromise = PlatformDecoderModule::CreateDecoderPromise; 218 219 public: 220 explicit DecoderFactory(MediaFormatReader* aOwner) 221 : mAudio(aOwner->mAudio, TrackInfo::kAudioTrack, aOwner->OwnerThread()), 222 mVideo(aOwner->mVideo, TrackInfo::kVideoTrack, aOwner->OwnerThread()), 223 mOwner(WrapNotNull(aOwner)) { 224 DecoderDoctorLogger::LogConstruction("MediaFormatReader::DecoderFactory", 225 this); 226 DecoderDoctorLogger::LinkParentAndChild( 227 aOwner, "decoder factory", "MediaFormatReader::DecoderFactory", this); 228 } 229 230 ~DecoderFactory() { 231 DecoderDoctorLogger::LogDestruction("MediaFormatReader::DecoderFactory", 232 this); 233 } 234 235 void CreateDecoder(TrackType aTrack); 236 237 // Shutdown any decoder pending initialization and reset mAudio/mVideo to its 238 // pristine state so CreateDecoder() is ready to be called again immediately. 239 void ShutdownDecoder(TrackType aTrack) { 240 MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || 241 aTrack == TrackInfo::kVideoTrack); 242 auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo; 243 data.mPolicy->Cancel(); 244 data.mTokenRequest.DisconnectIfExists(); 245 if (data.mLiveToken) { 246 // We haven't completed creation of the decoder, and it hasn't been 247 // initialised yet. 248 data.mLiveToken = nullptr; 249 // The decoder will be shutdown as soon as it's available and tracked by 250 // the ShutdownPromisePool. 251 mOwner->mShutdownPromisePool->Track(data.mCreateDecoderPromise->Then( 252 mOwner->mTaskQueue, __func__, 253 [](CreateDecoderPromise::ResolveOrRejectValue&& aResult) { 254 if (aResult.IsReject()) { 255 return ShutdownPromise::CreateAndResolve(true, __func__); 256 } 257 return aResult.ResolveValue()->Shutdown(); 258 })); 259 // Free the token to leave room for a new decoder. 260 data.mToken = nullptr; 261 } 262 data.mInitRequest.DisconnectIfExists(); 263 if (data.mDecoder) { 264 mOwner->mShutdownPromisePool->ShutdownDecoder(data.mDecoder.forget()); 265 } 266 data.mStage = Stage::None; 267 MOZ_ASSERT(!data.mToken); 268 } 269 270 private: 271 enum class Stage : int8_t { None, WaitForToken, CreateDecoder, WaitForInit }; 272 273 struct Data { 274 Data(DecoderData& aOwnerData, TrackType aTrack, TaskQueue* aThread) 275 : mOwnerData(aOwnerData), 276 mTrack(aTrack), 277 mPolicy(new SingleAllocPolicy(aTrack, aThread)) {} 278 DecoderData& mOwnerData; 279 const TrackType mTrack; 280 RefPtr<SingleAllocPolicy> mPolicy; 281 Stage mStage = Stage::None; 282 RefPtr<Token> mToken; 283 RefPtr<MediaDataDecoder> mDecoder; 284 MozPromiseRequestHolder<TokenPromise> mTokenRequest; 285 struct DecoderCancelled : public SupportsWeakPtr { 286 NS_INLINE_DECL_REFCOUNTING_ONEVENTTARGET(DecoderCancelled) 287 private: 288 ~DecoderCancelled() = default; 289 }; 290 // Set when decoder is about to be created. If cleared before the decoder 291 // creation promise is resolved; it indicates that Shutdown() was called and 292 // further processing such as initialization should stop. 293 RefPtr<DecoderCancelled> mLiveToken; 294 RefPtr<CreateDecoderPromise> mCreateDecoderPromise; 295 MozPromiseRequestHolder<InitPromise> mInitRequest; 296 } mAudio, mVideo; 297 298 void RunStage(Data& aData); 299 void DoCreateDecoder(Data& aData); 300 void DoInitDecoder(Data& aData); 301 302 // guaranteed to be valid by the owner. 303 const NotNull<MediaFormatReader*> mOwner; 304 }; 305 306 void MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack) { 307 MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || 308 aTrack == TrackInfo::kVideoTrack); 309 Data& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo; 310 MOZ_DIAGNOSTIC_ASSERT_IF(mOwner->GetDecoderData(data.mTrack).IsEncrypted(), 311 mOwner->mCDMProxy); 312 RunStage(data); 313 } 314 315 void MediaFormatReader::DecoderFactory::RunStage(Data& aData) { 316 switch (aData.mStage) { 317 case Stage::None: { 318 MOZ_DIAGNOSTIC_ASSERT(!aData.mToken); 319 aData.mPolicy->Alloc() 320 ->Then( 321 mOwner->OwnerThread(), __func__, 322 [this, &aData](RefPtr<Token> aToken) { 323 aData.mTokenRequest.Complete(); 324 aData.mToken = std::move(aToken); 325 aData.mStage = Stage::CreateDecoder; 326 RunStage(aData); 327 }, 328 [&aData]() { 329 aData.mTokenRequest.Complete(); 330 aData.mStage = Stage::None; 331 }) 332 ->Track(aData.mTokenRequest); 333 aData.mStage = Stage::WaitForToken; 334 break; 335 } 336 337 case Stage::WaitForToken: { 338 MOZ_DIAGNOSTIC_ASSERT(!aData.mToken); 339 MOZ_DIAGNOSTIC_ASSERT(aData.mTokenRequest.Exists()); 340 break; 341 } 342 343 case Stage::CreateDecoder: { 344 MOZ_DIAGNOSTIC_ASSERT(aData.mToken); 345 MOZ_DIAGNOSTIC_ASSERT(!aData.mDecoder); 346 MOZ_DIAGNOSTIC_ASSERT(!aData.mInitRequest.Exists()); 347 348 DoCreateDecoder(aData); 349 aData.mStage = Stage::WaitForInit; 350 break; 351 } 352 353 case Stage::WaitForInit: { 354 MOZ_DIAGNOSTIC_ASSERT((aData.mDecoder && aData.mInitRequest.Exists()) || 355 aData.mLiveToken); 356 break; 357 } 358 } 359 } 360 361 void MediaFormatReader::DecoderFactory::DoCreateDecoder(Data& aData) { 362 AUTO_PROFILER_LABEL("DecoderFactory::DoCreateDecoder", MEDIA_PLAYBACK); 363 auto& ownerData = aData.mOwnerData; 364 auto& decoder = mOwner->GetDecoderData(aData.mTrack); 365 366 RefPtr<PDMFactory> platform = new PDMFactory(); 367 if (decoder.IsEncrypted()) { 368 MOZ_DIAGNOSTIC_ASSERT(mOwner->mCDMProxy); 369 platform->SetCDMProxy(mOwner->mCDMProxy); 370 } 371 372 RefPtr<PlatformDecoderModule::CreateDecoderPromise> p; 373 MediaFormatReader* owner = mOwner; 374 auto onWaitingForKeyEvent = 375 [owner = ThreadSafeWeakPtr<MediaFormatReader>(owner)]() { 376 RefPtr<MediaFormatReader> mfr(owner); 377 MOZ_DIAGNOSTIC_ASSERT(mfr, "The MediaFormatReader didn't wait for us"); 378 return mfr ? &mfr->OnTrackWaitingForKeyProducer() : nullptr; 379 }; 380 381 switch (aData.mTrack) { 382 case TrackInfo::kAudioTrack: { 383 p = platform->CreateDecoder( 384 {*ownerData.GetCurrentInfo()->GetAsAudioInfo(), mOwner->mCrashHelper, 385 CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode), 386 TrackInfo::kAudioTrack, std::move(onWaitingForKeyEvent), 387 mOwner->mMediaEngineId, mOwner->mTrackingId, 388 mOwner->mEncryptedCustomIdent 389 ? CreateDecoderParams::EncryptedCustomIdent::True 390 : CreateDecoderParams::EncryptedCustomIdent::False}); 391 break; 392 } 393 394 case TrackType::kVideoTrack: { 395 // Decoders use the layers backend to decide if they can use hardware 396 // decoding, so specify LAYERS_NONE if we want to forcibly disable it. 397 using Option = CreateDecoderParams::Option; 398 using OptionSet = CreateDecoderParams::OptionSet; 399 400 p = platform->CreateDecoder( 401 {*ownerData.GetCurrentInfo()->GetAsVideoInfo(), 402 mOwner->mKnowsCompositor, mOwner->GetImageContainer(), 403 mOwner->mCrashHelper, 404 CreateDecoderParams::UseNullDecoder(ownerData.mIsNullDecode), 405 TrackType::kVideoTrack, std::move(onWaitingForKeyEvent), 406 CreateDecoderParams::VideoFrameRate(ownerData.mMeanRate.Mean()), 407 OptionSet(ownerData.mHardwareDecodingDisabled 408 ? Option::HardwareDecoderNotAllowed 409 : Option::Default, 410 mOwner->mVideoFrameContainer->SupportsOnly8BitImage() 411 ? Option::Output8BitPerChannel 412 : Option::Default), 413 mOwner->mMediaEngineId, mOwner->mTrackingId, 414 mOwner->mEncryptedCustomIdent 415 ? CreateDecoderParams::EncryptedCustomIdent::True 416 : CreateDecoderParams::EncryptedCustomIdent::False}); 417 break; 418 } 419 420 default: 421 p = PlatformDecoderModule::CreateDecoderPromise::CreateAndReject( 422 NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__); 423 } 424 425 aData.mLiveToken = MakeRefPtr<Data::DecoderCancelled>(); 426 427 aData.mCreateDecoderPromise = p->Then( 428 mOwner->OwnerThread(), __func__, 429 [this, &aData, &ownerData, live = WeakPtr{aData.mLiveToken}, 430 owner = ThreadSafeWeakPtr<MediaFormatReader>(owner)]( 431 RefPtr<MediaDataDecoder>&& aDecoder) { 432 if (!live) { 433 return CreateDecoderPromise::CreateAndResolve(std::move(aDecoder), 434 __func__); 435 } 436 aData.mLiveToken = nullptr; 437 aData.mDecoder = new MediaDataDecoderProxy( 438 aDecoder.forget(), do_AddRef(ownerData.mTaskQueue.get())); 439 aData.mDecoder = new AllocationWrapper(aData.mDecoder.forget(), 440 aData.mToken.forget()); 441 DecoderDoctorLogger::LinkParentAndChild( 442 aData.mDecoder.get(), "decoder", 443 "MediaFormatReader::DecoderFactory", this); 444 445 DoInitDecoder(aData); 446 447 return CreateDecoderPromise::CreateAndResolve(aData.mDecoder, __func__); 448 }, 449 [this, &aData, 450 live = WeakPtr{aData.mLiveToken}](const MediaResult& aError) { 451 NS_WARNING("Error constructing decoders"); 452 if (!live) { 453 return CreateDecoderPromise::CreateAndReject(aError, __func__); 454 } 455 aData.mLiveToken = nullptr; 456 aData.mToken = nullptr; 457 aData.mStage = Stage::None; 458 aData.mOwnerData.mDescription = aError.Description(); 459 DDLOGEX2("MediaFormatReader::DecoderFactory", this, DDLogCategory::Log, 460 "create_decoder_error", aError); 461 mOwner->NotifyError(aData.mTrack, aError); 462 463 return CreateDecoderPromise::CreateAndReject(aError, __func__); 464 }); 465 } 466 467 void MediaFormatReader::DecoderFactory::DoInitDecoder(Data& aData) { 468 AUTO_PROFILER_LABEL("DecoderFactory::DoInitDecoder", MEDIA_PLAYBACK); 469 auto& ownerData = aData.mOwnerData; 470 471 DDLOGEX2("MediaFormatReader::DecoderFactory", this, DDLogCategory::Log, 472 "initialize_decoder", DDNoValue{}); 473 aData.mDecoder->Init() 474 ->Then( 475 mOwner->OwnerThread(), __func__, 476 [this, &aData, &ownerData](TrackType aTrack) { 477 AUTO_PROFILER_LABEL("DecoderFactory::DoInitDecoder:Resolved", 478 MEDIA_PLAYBACK); 479 aData.mInitRequest.Complete(); 480 aData.mStage = Stage::None; 481 MutexAutoLock lock(ownerData.mMutex); 482 ownerData.mDecoder = std::move(aData.mDecoder); 483 ownerData.mDescription = ownerData.mDecoder->GetDescriptionName(); 484 DDLOGEX2("MediaFormatReader::DecoderFactory", this, 485 DDLogCategory::Log, "decoder_initialized", DDNoValue{}); 486 DecoderDoctorLogger::LinkParentAndChild( 487 "MediaFormatReader::DecoderData", &ownerData, "decoder", 488 ownerData.mDecoder.get()); 489 mOwner->SetVideoDecodeThreshold(); 490 mOwner->ScheduleUpdate(aTrack); 491 if (aTrack == TrackInfo::kAudioTrack) { 492 ownerData.mProcessName = ownerData.mDecoder->GetProcessName(); 493 ownerData.mCodecName = ownerData.mDecoder->GetCodecName(); 494 } 495 nsCString needsConversion; 496 switch (ownerData.mDecoder->NeedsConversion()) { 497 case MediaDataDecoder::ConversionRequired::kNeedNone: 498 needsConversion = "false"; 499 break; 500 case MediaDataDecoder::ConversionRequired::kNeedAVCC: 501 needsConversion = "AVCC"; 502 break; 503 case MediaDataDecoder::ConversionRequired::kNeedAnnexB: 504 needsConversion = "AnnexB"; 505 break; 506 default: 507 needsConversion = "Unknown"; 508 } 509 nsCString dummy; 510 MOZ_LOG_FMT(sFormatDecoderLog, mozilla::LogLevel::Debug, 511 "Decoder init finished for " 512 "{} codec: \"{}\", " 513 "description: \"{}\", " 514 "process: \"{}\", " 515 "hw: \"{}\", " 516 "needs conversion: \"{}\"", 517 (aTrack == TrackInfo::kVideoTrack) ? "video" : "audio", 518 ownerData.mDecoder->GetCodecName(), 519 ownerData.mDecoder->GetDescriptionName(), 520 ownerData.mDecoder->GetProcessName(), 521 ownerData.mDecoder->IsHardwareAccelerated(dummy), 522 needsConversion); 523 }, 524 [this, &aData, &ownerData](const MediaResult& aError) { 525 AUTO_PROFILER_LABEL("DecoderFactory::DoInitDecoder:Rejected", 526 MEDIA_PLAYBACK); 527 aData.mInitRequest.Complete(); 528 MOZ_RELEASE_ASSERT(!ownerData.mDecoder, 529 "Can't have a decoder already set"); 530 aData.mStage = Stage::None; 531 mOwner->mShutdownPromisePool->ShutdownDecoder( 532 aData.mDecoder.forget()); 533 DDLOGEX2("MediaFormatReader::DecoderFactory", this, 534 DDLogCategory::Log, "initialize_decoder_error", aError); 535 mOwner->NotifyError(aData.mTrack, aError); 536 }) 537 ->Track(aData.mInitRequest); 538 } 539 540 // DemuxerProxy ensures that the original main demuxer is only ever accessed 541 // via its own dedicated task queue. 542 // This ensure that the reader's taskqueue will never blocked while a demuxer 543 // is itself blocked attempting to access the MediaCache or the MediaResource. 544 class MediaFormatReader::DemuxerProxy { 545 using TrackType = TrackInfo::TrackType; 546 class Wrapper; 547 548 public: 549 explicit DemuxerProxy(MediaDataDemuxer* aDemuxer) 550 : mTaskQueue(TaskQueue::Create( 551 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), 552 "DemuxerProxy::mTaskQueue")), 553 mData(new Data(aDemuxer)) { 554 MOZ_COUNT_CTOR(DemuxerProxy); 555 } 556 557 MOZ_COUNTED_DTOR(DemuxerProxy) 558 559 RefPtr<ShutdownPromise> Shutdown() { 560 RefPtr<Data> data = std::move(mData); 561 return InvokeAsync(mTaskQueue, __func__, [data]() { 562 // We need to clear our reference to the demuxer now. So that in the event 563 // the init promise wasn't resolved, such as what can happen with the 564 // mediasource demuxer that is waiting on more data, it will force the 565 // init promise to be rejected. 566 data->mDemuxer = nullptr; 567 data->mAudioDemuxer = nullptr; 568 data->mVideoDemuxer = nullptr; 569 return ShutdownPromise::CreateAndResolve(true, __func__); 570 }); 571 } 572 573 RefPtr<MediaDataDemuxer::InitPromise> Init(); 574 575 Wrapper* GetTrackDemuxer(TrackType aTrack, uint32_t aTrackNumber) { 576 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 577 578 switch (aTrack) { 579 case TrackInfo::kAudioTrack: 580 return mData->mAudioDemuxer; 581 case TrackInfo::kVideoTrack: 582 return mData->mVideoDemuxer; 583 default: 584 return nullptr; 585 } 586 } 587 588 uint32_t GetNumberTracks(TrackType aTrack) const { 589 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 590 591 switch (aTrack) { 592 case TrackInfo::kAudioTrack: 593 return mData->mNumAudioTrack; 594 case TrackInfo::kVideoTrack: 595 return mData->mNumVideoTrack; 596 default: 597 return 0; 598 } 599 } 600 601 bool IsSeekable() const { 602 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 603 604 return mData->mSeekable; 605 } 606 607 bool IsSeekableOnlyInBufferedRanges() const { 608 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 609 610 return mData->mSeekableOnlyInBufferedRange; 611 } 612 613 UniquePtr<EncryptionInfo> GetCrypto() const { 614 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 615 616 if (!mData->mCrypto) { 617 return nullptr; 618 } 619 auto crypto = MakeUnique<EncryptionInfo>(); 620 *crypto = *mData->mCrypto; 621 return crypto; 622 } 623 624 RefPtr<NotifyDataArrivedPromise> NotifyDataArrived(); 625 626 bool ShouldComputeStartTime() const { 627 MOZ_RELEASE_ASSERT(mData && mData->mInitDone); 628 629 return mData->mShouldComputeStartTime; 630 } 631 632 private: 633 const RefPtr<TaskQueue> mTaskQueue; 634 struct Data { 635 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Data) 636 637 explicit Data(MediaDataDemuxer* aDemuxer) 638 : mInitDone(false), mDemuxer(aDemuxer) {} 639 640 Atomic<bool> mInitDone; 641 // Only ever accessed over mTaskQueue once. 642 RefPtr<MediaDataDemuxer> mDemuxer; 643 // Only accessed once InitPromise has been resolved and immutable after. 644 // So we can safely access them without the use of the mutex. 645 uint32_t mNumAudioTrack = 0; 646 RefPtr<Wrapper> mAudioDemuxer; 647 uint32_t mNumVideoTrack = 0; 648 RefPtr<Wrapper> mVideoDemuxer; 649 bool mSeekable = false; 650 bool mSeekableOnlyInBufferedRange = false; 651 bool mShouldComputeStartTime = true; 652 UniquePtr<EncryptionInfo> mCrypto; 653 654 private: 655 ~Data() = default; 656 }; 657 RefPtr<Data> mData; 658 }; 659 660 class MediaFormatReader::DemuxerProxy::Wrapper : public MediaTrackDemuxer { 661 public: 662 Wrapper(MediaTrackDemuxer* aTrackDemuxer, TaskQueue* aTaskQueue) 663 : mMutex("TrackDemuxer Mutex"), 664 mTaskQueue(aTaskQueue), 665 mGetSamplesMayBlock(aTrackDemuxer->GetSamplesMayBlock()), 666 mInfo(aTrackDemuxer->GetInfo()), 667 mTrackDemuxer(aTrackDemuxer) { 668 DecoderDoctorLogger::LogConstructionAndBase( 669 "MediaFormatReader::DemuxerProxy::Wrapper", this, 670 static_cast<const MediaTrackDemuxer*>(this)); 671 DecoderDoctorLogger::LinkParentAndChild( 672 "MediaFormatReader::DemuxerProxy::Wrapper", this, "track demuxer", 673 aTrackDemuxer); 674 } 675 676 UniquePtr<TrackInfo> GetInfo() const override { 677 if (!mInfo) { 678 return nullptr; 679 } 680 return mInfo->Clone(); 681 } 682 683 RefPtr<SeekPromise> Seek(const TimeUnit& aTime) override { 684 RefPtr<Wrapper> self = this; 685 return InvokeAsync( 686 mTaskQueue, __func__, 687 [self, aTime]() { return self->mTrackDemuxer->Seek(aTime); }) 688 ->Then( 689 mTaskQueue, __func__, 690 [self](const TimeUnit& aTime) { 691 self->UpdateRandomAccessPoint(); 692 return SeekPromise::CreateAndResolve(aTime, __func__); 693 }, 694 [self](const MediaResult& aError) { 695 self->UpdateRandomAccessPoint(); 696 return SeekPromise::CreateAndReject(aError, __func__); 697 }); 698 } 699 700 RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override { 701 RefPtr<Wrapper> self = this; 702 return InvokeAsync(mTaskQueue, __func__, 703 [self, aNumSamples]() { 704 return self->mTrackDemuxer->GetSamples(aNumSamples); 705 }) 706 ->Then( 707 mTaskQueue, __func__, 708 [self](RefPtr<SamplesHolder> aSamples) { 709 self->UpdateRandomAccessPoint(); 710 return SamplesPromise::CreateAndResolve(aSamples.forget(), 711 __func__); 712 }, 713 [self](const MediaResult& aError) { 714 self->UpdateRandomAccessPoint(); 715 return SamplesPromise::CreateAndReject(aError, __func__); 716 }); 717 } 718 719 bool GetSamplesMayBlock() const override { return mGetSamplesMayBlock; } 720 721 void Reset() override { 722 RefPtr<Wrapper> self = this; 723 nsresult rv = mTaskQueue->Dispatch(NS_NewRunnableFunction( 724 "MediaFormatReader::DemuxerProxy::Wrapper::Reset", 725 [self]() { self->mTrackDemuxer->Reset(); })); 726 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 727 (void)rv; 728 } 729 730 nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override { 731 MutexAutoLock lock(mMutex); 732 if (NS_SUCCEEDED(mNextRandomAccessPointResult)) { 733 *aTime = mNextRandomAccessPoint; 734 } 735 return mNextRandomAccessPointResult; 736 } 737 738 RefPtr<SkipAccessPointPromise> SkipToNextRandomAccessPoint( 739 const TimeUnit& aTimeThreshold) override { 740 RefPtr<Wrapper> self = this; 741 return InvokeAsync( 742 mTaskQueue, __func__, 743 [self, aTimeThreshold]() { 744 return self->mTrackDemuxer->SkipToNextRandomAccessPoint( 745 aTimeThreshold); 746 }) 747 ->Then( 748 mTaskQueue, __func__, 749 [self](uint32_t aVal) { 750 self->UpdateRandomAccessPoint(); 751 return SkipAccessPointPromise::CreateAndResolve(aVal, __func__); 752 }, 753 [self](const SkipFailureHolder& aError) { 754 self->UpdateRandomAccessPoint(); 755 return SkipAccessPointPromise::CreateAndReject(aError, __func__); 756 }); 757 } 758 759 TimeIntervals GetBuffered() override { 760 MutexAutoLock lock(mMutex); 761 return mBuffered; 762 } 763 764 void BreakCycles() override {} 765 766 private: 767 Mutex mMutex MOZ_UNANNOTATED; 768 const RefPtr<TaskQueue> mTaskQueue; 769 const bool mGetSamplesMayBlock; 770 const UniquePtr<TrackInfo> mInfo; 771 // mTrackDemuxer is only ever accessed on demuxer's task queue. 772 RefPtr<MediaTrackDemuxer> mTrackDemuxer; 773 // All following members are protected by mMutex 774 nsresult mNextRandomAccessPointResult = NS_OK; 775 TimeUnit mNextRandomAccessPoint; 776 TimeIntervals mBuffered; 777 friend class DemuxerProxy; 778 779 ~Wrapper() { 780 RefPtr<MediaTrackDemuxer> trackDemuxer = std::move(mTrackDemuxer); 781 nsresult rv = mTaskQueue->Dispatch(NS_NewRunnableFunction( 782 "MediaFormatReader::DemuxerProxy::Wrapper::~Wrapper", 783 [trackDemuxer]() { trackDemuxer->BreakCycles(); })); 784 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 785 (void)rv; 786 DecoderDoctorLogger::LogDestruction( 787 "MediaFormatReader::DemuxerProxy::Wrapper", this); 788 } 789 790 void UpdateRandomAccessPoint() { 791 MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); 792 if (!mTrackDemuxer) { 793 // Detached. 794 return; 795 } 796 MutexAutoLock lock(mMutex); 797 mNextRandomAccessPointResult = 798 mTrackDemuxer->GetNextRandomAccessPoint(&mNextRandomAccessPoint); 799 } 800 801 void UpdateBuffered() { 802 MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); 803 if (!mTrackDemuxer) { 804 // Detached. 805 return; 806 } 807 MutexAutoLock lock(mMutex); 808 mBuffered = mTrackDemuxer->GetBuffered(); 809 } 810 }; 811 812 RefPtr<MediaDataDemuxer::InitPromise> MediaFormatReader::DemuxerProxy::Init() { 813 AUTO_PROFILER_LABEL("DemuxerProxy::Init", MEDIA_PLAYBACK); 814 using InitPromise = MediaDataDemuxer::InitPromise; 815 816 RefPtr<Data> data = mData; 817 RefPtr<TaskQueue> taskQueue = mTaskQueue; 818 return InvokeAsync(mTaskQueue, __func__, 819 [data, taskQueue]() { 820 if (!data->mDemuxer) { 821 return InitPromise::CreateAndReject( 822 NS_ERROR_DOM_MEDIA_CANCELED, __func__); 823 } 824 return data->mDemuxer->Init(); 825 }) 826 ->Then( 827 taskQueue, __func__, 828 [data, taskQueue]() { 829 AUTO_PROFILER_LABEL("DemuxerProxy::Init:Resolved", MEDIA_PLAYBACK); 830 if (!data->mDemuxer) { // Was shutdown. 831 return InitPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, 832 __func__); 833 } 834 data->mNumAudioTrack = 835 data->mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); 836 if (data->mNumAudioTrack) { 837 RefPtr<MediaTrackDemuxer> d = 838 data->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); 839 if (d) { 840 RefPtr<Wrapper> wrapper = 841 new DemuxerProxy::Wrapper(d, taskQueue); 842 wrapper->UpdateBuffered(); 843 data->mAudioDemuxer = wrapper; 844 DecoderDoctorLogger::LinkParentAndChild( 845 data->mDemuxer.get(), "decoder factory wrapper", 846 "MediaFormatReader::DecoderFactory::Wrapper", 847 wrapper.get()); 848 } 849 } 850 data->mNumVideoTrack = 851 data->mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack); 852 if (data->mNumVideoTrack) { 853 RefPtr<MediaTrackDemuxer> d = 854 data->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); 855 if (d) { 856 RefPtr<Wrapper> wrapper = 857 new DemuxerProxy::Wrapper(d, taskQueue); 858 wrapper->UpdateBuffered(); 859 data->mVideoDemuxer = wrapper; 860 DecoderDoctorLogger::LinkParentAndChild( 861 data->mDemuxer.get(), "decoder factory wrapper", 862 "MediaFormatReader::DecoderFactory::Wrapper", 863 wrapper.get()); 864 } 865 } 866 data->mCrypto = data->mDemuxer->GetCrypto(); 867 data->mSeekable = data->mDemuxer->IsSeekable(); 868 data->mSeekableOnlyInBufferedRange = 869 data->mDemuxer->IsSeekableOnlyInBufferedRanges(); 870 data->mShouldComputeStartTime = 871 data->mDemuxer->ShouldComputeStartTime(); 872 data->mInitDone = true; 873 return InitPromise::CreateAndResolve(NS_OK, __func__); 874 }, 875 [](const MediaResult& aError) { 876 return InitPromise::CreateAndReject(aError, __func__); 877 }); 878 } 879 880 RefPtr<MediaFormatReader::NotifyDataArrivedPromise> 881 MediaFormatReader::DemuxerProxy::NotifyDataArrived() { 882 RefPtr<Data> data = mData; 883 return InvokeAsync(mTaskQueue, __func__, [data]() { 884 if (!data->mDemuxer) { 885 // Was shutdown. 886 return NotifyDataArrivedPromise::CreateAndReject( 887 NS_ERROR_DOM_MEDIA_CANCELED, __func__); 888 } 889 data->mDemuxer->NotifyDataArrived(); 890 if (data->mAudioDemuxer) { 891 data->mAudioDemuxer->UpdateBuffered(); 892 } 893 if (data->mVideoDemuxer) { 894 data->mVideoDemuxer->UpdateBuffered(); 895 } 896 return NotifyDataArrivedPromise::CreateAndResolve(true, __func__); 897 }); 898 } 899 900 MediaFormatReader::MediaFormatReader(MediaFormatReaderInit& aInit, 901 MediaDataDemuxer* aDemuxer) 902 : mTaskQueue( 903 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 904 "MediaFormatReader::mTaskQueue", 905 /* aSupportsTailDispatch = */ true)), 906 mAudio(this, MediaData::Type::AUDIO_DATA, 907 StaticPrefs::media_audio_max_decode_error()), 908 mVideo(this, MediaData::Type::VIDEO_DATA, 909 StaticPrefs::media_video_max_decode_error()), 910 mWorkingInfoChanged(false, "MediaFormatReader::mWorkingInfoChanged"), 911 mWatchManager(this, OwnerThread()), 912 mIsWatchingWorkingInfo(false), 913 mDemuxer(new DemuxerProxy(aDemuxer)), 914 mDemuxerInitDone(false), 915 mPendingNotifyDataArrived(false), 916 mLastReportedNumDecodedFrames(0), 917 mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe), 918 mKnowsCompositor(aInit.mKnowsCompositor), 919 mInitDone(false), 920 mTrackDemuxersMayBlock(false), 921 mSeekScheduled(false), 922 mVideoFrameContainer(aInit.mVideoFrameContainer), 923 mCrashHelper(aInit.mCrashHelper), 924 mDecoderFactory(new DecoderFactory(this)), 925 mShutdownPromisePool(new ShutdownPromisePool()), 926 mBuffered(mTaskQueue, TimeIntervals(), 927 "MediaFormatReader::mBuffered (Canonical)"), 928 mFrameStats(aInit.mFrameStats), 929 mMediaDecoderOwnerID(aInit.mMediaDecoderOwnerID), 930 mTrackingId(std::move(aInit.mTrackingId)), 931 mReadMetadataStartTime(Nothing()), 932 mReadMetaDataTime(TimeDuration::Zero()), 933 mTotalWaitingForVideoDataTime(TimeDuration::Zero()), 934 mEncryptedCustomIdent(false) { 935 MOZ_ASSERT(aDemuxer); 936 MOZ_COUNT_CTOR(MediaFormatReader); 937 DDLINKCHILD("audio decoder data", "MediaFormatReader::DecoderDataWithPromise", 938 &mAudio); 939 DDLINKCHILD("video decoder data", "MediaFormatReader::DecoderDataWithPromise", 940 &mVideo); 941 DDLINKCHILD("demuxer", aDemuxer); 942 mOnTrackWaitingForKeyListener = OnTrackWaitingForKey().Connect( 943 mTaskQueue, this, &MediaFormatReader::NotifyWaitingForKey); 944 } 945 946 MediaFormatReader::~MediaFormatReader() { 947 MOZ_COUNT_DTOR(MediaFormatReader); 948 MOZ_ASSERT(mShutdown); 949 } 950 951 RefPtr<ShutdownPromise> MediaFormatReader::Shutdown() { 952 MOZ_ASSERT(OnTaskQueue()); 953 LOG(""); 954 955 mDemuxerInitRequest.DisconnectIfExists(); 956 mNotifyDataArrivedPromise.DisconnectIfExists(); 957 mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 958 mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 959 mSkipRequest.DisconnectIfExists(); 960 mSetCDMPromise.RejectIfExists( 961 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 962 "MediaFormatReader is shutting down"), 963 __func__); 964 965 if (mIsWatchingWorkingInfo) { 966 mWatchManager.Unwatch(mWorkingInfoChanged, 967 &MediaFormatReader::NotifyTrackInfoUpdated); 968 } 969 mWatchManager.Shutdown(); 970 971 if (mAudio.HasPromise()) { 972 mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 973 } 974 if (mVideo.HasPromise()) { 975 mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 976 } 977 978 if (HasAudio()) { 979 mAudio.ResetDemuxer(); 980 mAudio.mTrackDemuxer->BreakCycles(); 981 { 982 MutexAutoLock lock(mAudio.mMutex); 983 mAudio.mTrackDemuxer = nullptr; 984 } 985 mAudio.ResetState(); 986 ShutdownDecoder(TrackInfo::kAudioTrack); 987 } 988 989 if (HasVideo()) { 990 mVideo.ResetDemuxer(); 991 mVideo.mTrackDemuxer->BreakCycles(); 992 { 993 MutexAutoLock lock(mVideo.mMutex); 994 mVideo.mTrackDemuxer = nullptr; 995 } 996 mVideo.ResetState(); 997 ShutdownDecoder(TrackInfo::kVideoTrack); 998 } 999 1000 mShutdownPromisePool->Track(mDemuxer->Shutdown()); 1001 mDemuxer = nullptr; 1002 1003 mOnTrackWaitingForKeyListener.Disconnect(); 1004 1005 mShutdown = true; 1006 return mShutdownPromisePool->Shutdown()->Then( 1007 OwnerThread(), __func__, this, &MediaFormatReader::TearDownDecoders, 1008 &MediaFormatReader::TearDownDecoders); 1009 } 1010 1011 void MediaFormatReader::ShutdownDecoder(TrackType aTrack) { 1012 LOGV("%s", TrackTypeToStr(aTrack)); 1013 1014 // Shut down the pending decoder if any. 1015 mDecoderFactory->ShutdownDecoder(aTrack); 1016 1017 auto& decoder = GetDecoderData(aTrack); 1018 // Flush the decoder if necessary. 1019 decoder.Flush(); 1020 1021 // Shut down the decoder if any. 1022 decoder.ShutdownDecoder(); 1023 } 1024 1025 void MediaFormatReader::NotifyTrackInfoUpdated() { 1026 MOZ_ASSERT(OnTaskQueue()); 1027 if (mWorkingInfoChanged) { 1028 mWorkingInfoChanged = false; 1029 1030 VideoInfo videoInfo; 1031 AudioInfo audioInfo; 1032 { 1033 MutexAutoLock lock(mVideo.mMutex); 1034 if (HasVideo()) { 1035 videoInfo = *mVideo.GetWorkingInfo()->GetAsVideoInfo(); 1036 } 1037 } 1038 { 1039 MutexAutoLock lock(mAudio.mMutex); 1040 if (HasAudio()) { 1041 audioInfo = *mAudio.GetWorkingInfo()->GetAsAudioInfo(); 1042 } 1043 } 1044 1045 mTrackInfoUpdatedEvent.Notify(videoInfo, audioInfo); 1046 } 1047 } 1048 1049 RefPtr<ShutdownPromise> MediaFormatReader::TearDownDecoders() { 1050 if (mAudio.mTaskQueue) { 1051 mAudio.mTaskQueue->BeginShutdown(); 1052 mAudio.mTaskQueue->AwaitShutdownAndIdle(); 1053 mAudio.mTaskQueue = nullptr; 1054 } 1055 if (mVideo.mTaskQueue) { 1056 mVideo.mTaskQueue->BeginShutdown(); 1057 mVideo.mTaskQueue->AwaitShutdownAndIdle(); 1058 mVideo.mTaskQueue = nullptr; 1059 } 1060 1061 mDecoderFactory = nullptr; 1062 mVideoFrameContainer = nullptr; 1063 1064 ReleaseResources(); 1065 mBuffered.DisconnectAll(); 1066 return mTaskQueue->BeginShutdown(); 1067 } 1068 1069 nsresult MediaFormatReader::Init() { 1070 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); 1071 1072 mAudio.mTaskQueue = 1073 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), 1074 "MFR::mAudio::mTaskQueue"); 1075 1076 mVideo.mTaskQueue = 1077 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER), 1078 "MFR::mVideo::mTaskQueue"); 1079 1080 return NS_OK; 1081 } 1082 1083 bool MediaFormatReader::ResolveSetCDMPromiseIfDone(TrackType aTrack) { 1084 // When a CDM proxy is set, MFR would shutdown the existing MediaDataDecoder 1085 // and would create new one for specific track in the next Update. 1086 MOZ_ASSERT(OnTaskQueue()); 1087 1088 if (mSetCDMPromise.IsEmpty()) { 1089 return true; 1090 } 1091 1092 MOZ_ASSERT(mCDMProxy); 1093 if (mSetCDMForTracks.contains(aTrack)) { 1094 mSetCDMForTracks -= aTrack; 1095 } 1096 1097 if (mSetCDMForTracks.isEmpty()) { 1098 LOGV("%s : Done ", __func__); 1099 mSetCDMPromise.Resolve(/* aResolveValue = */ true, __func__); 1100 if (HasAudio()) { 1101 ScheduleUpdate(TrackInfo::kAudioTrack); 1102 } 1103 if (HasVideo()) { 1104 ScheduleUpdate(TrackInfo::kVideoTrack); 1105 } 1106 return true; 1107 } 1108 LOGV("%s : %s track is ready.", __func__, TrackTypeToStr(aTrack)); 1109 return false; 1110 } 1111 1112 void MediaFormatReader::PrepareToSetCDMForTrack(TrackType aTrack) { 1113 MOZ_ASSERT(OnTaskQueue()); 1114 LOGV("%s : %s", __func__, TrackTypeToStr(aTrack)); 1115 1116 mSetCDMForTracks += aTrack; 1117 if (mCDMProxy) { 1118 // An old cdm proxy exists, so detaching old cdm proxy by shutting down 1119 // MediaDataDecoder. 1120 ShutdownDecoder(aTrack); 1121 } 1122 ScheduleUpdate(aTrack); 1123 } 1124 1125 bool MediaFormatReader::IsDecoderWaitingForCDM(TrackType aTrack) { 1126 MOZ_ASSERT(OnTaskQueue()); 1127 return GetDecoderData(aTrack).IsEncrypted() && 1128 mSetCDMForTracks.contains(aTrack) && !mCDMProxy; 1129 } 1130 1131 RefPtr<SetCDMPromise> MediaFormatReader::SetCDMProxy(CDMProxy* aProxy) { 1132 MOZ_ASSERT(OnTaskQueue()); 1133 LOGV("SetCDMProxy (%p)", aProxy); 1134 1135 if (mShutdown) { 1136 return SetCDMPromise::CreateAndReject( 1137 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 1138 "MediaFormatReader is shutting down"), 1139 __func__); 1140 } 1141 1142 mSetCDMPromise.RejectIfExists( 1143 MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR, 1144 "Another new CDM proxy is being set."), 1145 __func__); 1146 1147 // Shutdown all decoders as switching CDM proxy indicates that it's 1148 // inappropriate for the existing decoders to continue decoding via the old 1149 // CDM proxy. 1150 if (HasAudio()) { 1151 PrepareToSetCDMForTrack(TrackInfo::kAudioTrack); 1152 } 1153 if (HasVideo()) { 1154 PrepareToSetCDMForTrack(TrackInfo::kVideoTrack); 1155 } 1156 1157 mCDMProxy = aProxy; 1158 1159 if (!mInitDone || mSetCDMForTracks.isEmpty() || !mCDMProxy) { 1160 // 1) MFR is not initialized yet or 1161 // 2) Demuxer is initialized without active audio and video or 1162 // 3) A null cdm proxy is set 1163 // the promise can be resolved directly. 1164 mSetCDMForTracks.clear(); 1165 return SetCDMPromise::CreateAndResolve(/* aResolveValue = */ true, 1166 __func__); 1167 } 1168 1169 RefPtr<SetCDMPromise> p = mSetCDMPromise.Ensure(__func__); 1170 return p; 1171 } 1172 1173 bool MediaFormatReader::IsWaitingOnCDMResource() { 1174 MOZ_ASSERT(OnTaskQueue()); 1175 return IsEncrypted() && !mCDMProxy; 1176 } 1177 1178 RefPtr<MediaFormatReader::MetadataPromise> 1179 MediaFormatReader::AsyncReadMetadata() { 1180 AUTO_PROFILER_LABEL("MediaFormatReader::AsyncReadMetadata", MEDIA_PLAYBACK); 1181 MOZ_ASSERT(OnTaskQueue()); 1182 1183 MOZ_DIAGNOSTIC_ASSERT(mMetadataPromise.IsEmpty()); 1184 1185 if (mInitDone) { 1186 // We are returning from dormant. 1187 MetadataHolder metadata; 1188 metadata.mInfo = MakeUnique<MediaInfo>(mInfo); 1189 return MetadataPromise::CreateAndResolve(std::move(metadata), __func__); 1190 } 1191 1192 if (!mReadMetadataStartTime) { 1193 mReadMetadataStartTime = Some(TimeStamp::Now()); 1194 } 1195 1196 RefPtr<MetadataPromise> p = mMetadataPromise.Ensure(__func__); 1197 1198 mDemuxer->Init() 1199 ->Then(OwnerThread(), __func__, this, 1200 &MediaFormatReader::OnDemuxerInitDone, 1201 &MediaFormatReader::OnDemuxerInitFailed) 1202 ->Track(mDemuxerInitRequest); 1203 return p; 1204 } 1205 1206 void MediaFormatReader::OnDemuxerInitDone(const MediaResult& aResult) { 1207 AUTO_PROFILER_LABEL("MediaFormatReader::OnDemuxerInitDone", MEDIA_PLAYBACK); 1208 MOZ_ASSERT(OnTaskQueue()); 1209 mDemuxerInitRequest.Complete(); 1210 1211 if (NS_FAILED(aResult) && StaticPrefs::media_playback_warnings_as_errors()) { 1212 mMetadataPromise.Reject(aResult, __func__); 1213 return; 1214 } 1215 1216 mDemuxerInitDone = true; 1217 1218 UniquePtr<MetadataTags> tags(MakeUnique<MetadataTags>()); 1219 1220 RefPtr<PDMFactory> platform; 1221 if (!IsWaitingOnCDMResource()) { 1222 platform = new PDMFactory(); 1223 } 1224 1225 // To decode, we need valid video and a place to put it. 1226 bool videoActive = !!mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack) && 1227 GetImageContainer(); 1228 1229 if (videoActive) { 1230 // We currently only handle the first video track. 1231 MutexAutoLock lock(mVideo.mMutex); 1232 mVideo.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); 1233 if (!mVideo.mTrackDemuxer) { 1234 LOG("No video track demuxer"); 1235 mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); 1236 return; 1237 } 1238 1239 UniquePtr<TrackInfo> videoInfo = mVideo.mTrackDemuxer->GetInfo(); 1240 videoActive = videoInfo && videoInfo->IsValid(); 1241 if (videoActive) { 1242 if (platform && 1243 platform->SupportsMimeType(videoInfo->mMimeType).isEmpty()) { 1244 // We have no decoder for this track. Error. 1245 LOG("No supported decoder for video track (%s)", 1246 videoInfo->mMimeType.get()); 1247 if (!videoInfo->mMimeType.IsEmpty()) { 1248 mozilla::glean::media_playback::not_supported_video_per_mime_type 1249 .Get(videoInfo->mMimeType) 1250 .Add(1); 1251 } 1252 mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); 1253 return; 1254 } 1255 mInfo.mVideo = *videoInfo->GetAsVideoInfo(); 1256 mVideo.mWorkingInfo = MakeUnique<VideoInfo>(mInfo.mVideo); 1257 for (const MetadataTag& tag : videoInfo->mTags) { 1258 tags->InsertOrUpdate(tag.mKey, tag.mValue); 1259 } 1260 mWorkingInfoChanged = true; 1261 mVideo.mOriginalInfo = std::move(videoInfo); 1262 mTrackDemuxersMayBlock |= mVideo.mTrackDemuxer->GetSamplesMayBlock(); 1263 } else { 1264 mVideo.mTrackDemuxer->BreakCycles(); 1265 mVideo.mTrackDemuxer = nullptr; 1266 } 1267 } 1268 1269 bool audioActive = !!mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); 1270 if (audioActive) { 1271 MutexAutoLock lock(mAudio.mMutex); 1272 mAudio.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); 1273 if (!mAudio.mTrackDemuxer) { 1274 LOG("No audio track demuxer"); 1275 mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); 1276 return; 1277 } 1278 1279 UniquePtr<TrackInfo> audioInfo = mAudio.mTrackDemuxer->GetInfo(); 1280 // We actively ignore audio tracks that we know we can't play. 1281 audioActive = audioInfo && audioInfo->IsValid() && 1282 (!platform || 1283 !platform->SupportsMimeType(audioInfo->mMimeType).isEmpty()); 1284 1285 if (audioActive) { 1286 mInfo.mAudio = *audioInfo->GetAsAudioInfo(); 1287 mAudio.mWorkingInfo = MakeUnique<AudioInfo>(mInfo.mAudio); 1288 for (const MetadataTag& tag : audioInfo->mTags) { 1289 tags->InsertOrUpdate(tag.mKey, tag.mValue); 1290 } 1291 mWorkingInfoChanged = true; 1292 mAudio.mOriginalInfo = std::move(audioInfo); 1293 mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock(); 1294 } else { 1295 mAudio.mTrackDemuxer->BreakCycles(); 1296 mAudio.mTrackDemuxer = nullptr; 1297 } 1298 } 1299 1300 UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto(); 1301 if (crypto && crypto->IsEncrypted()) { 1302 // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING. 1303 for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) { 1304 mOnEncrypted.Notify(crypto->mInitDatas[i].mInitData, 1305 crypto->mInitDatas[i].mType); 1306 } 1307 mInfo.mCrypto = *crypto; 1308 } 1309 1310 auto videoDuration = HasVideo() ? mInfo.mVideo.mDuration : TimeUnit::Zero(); 1311 auto audioDuration = HasAudio() ? mInfo.mAudio.mDuration : TimeUnit::Zero(); 1312 1313 // If the duration is 0 on both audio and video, it mMetadataDuration is to be 1314 // Nothing(). Duration will use buffered ranges. 1315 LOG("videoDuration=%" PRId64 ", audioDuration=%" PRId64, 1316 videoDuration.ToMicroseconds(), audioDuration.ToMicroseconds()); 1317 if (videoDuration.IsPositive() || audioDuration.IsPositive()) { 1318 auto duration = std::max(videoDuration, audioDuration); 1319 LOG("Determine mMetadataDuration=%" PRId64, duration.ToMicroseconds()); 1320 mInfo.mMetadataDuration = Some(duration); 1321 } 1322 1323 mInfo.mMediaSeekable = mDemuxer->IsSeekable(); 1324 mInfo.mMediaSeekableOnlyInBufferedRanges = 1325 mDemuxer->IsSeekableOnlyInBufferedRanges(); 1326 1327 if (!videoActive && !audioActive) { 1328 LOG("No active audio or video track"); 1329 mMetadataPromise.Reject(NS_ERROR_DOM_MEDIA_METADATA_ERR, __func__); 1330 return; 1331 } 1332 1333 mTags = std::move(tags); 1334 mInitDone = true; 1335 1336 // Try to get the start time. 1337 // For MSE case, the start time of each track is assumed to be 0. 1338 // For others, we must demux the first sample to know the start time for each 1339 // track. 1340 if (!mDemuxer->ShouldComputeStartTime()) { 1341 mAudio.mFirstDemuxedSampleTime.emplace(TimeUnit::Zero()); 1342 mVideo.mFirstDemuxedSampleTime.emplace(TimeUnit::Zero()); 1343 } else { 1344 if (HasAudio()) { 1345 RequestDemuxSamples(TrackInfo::kAudioTrack); 1346 } 1347 1348 if (HasVideo()) { 1349 RequestDemuxSamples(TrackInfo::kVideoTrack); 1350 } 1351 } 1352 1353 if (aResult != NS_OK) { 1354 mOnDecodeWarning.Notify(aResult); 1355 } 1356 1357 MaybeResolveMetadataPromise(); 1358 } 1359 1360 void MediaFormatReader::MaybeResolveMetadataPromise() { 1361 MOZ_ASSERT(OnTaskQueue()); 1362 1363 if ((HasAudio() && mAudio.mFirstDemuxedSampleTime.isNothing()) || 1364 (HasVideo() && mVideo.mFirstDemuxedSampleTime.isNothing())) { 1365 return; 1366 } 1367 1368 TimeUnit startTime = 1369 std::min(mAudio.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity()), 1370 mVideo.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity())); 1371 1372 if (!startTime.IsInfinite()) { 1373 mInfo.mStartTime = startTime; // mInfo.mStartTime is initialized to 0. 1374 LOG("Set start time=%s", mInfo.mStartTime.ToString().get()); 1375 } 1376 1377 MetadataHolder metadata; 1378 metadata.mInfo = MakeUnique<MediaInfo>(mInfo); 1379 metadata.mTags = mTags->Count() ? std::move(mTags) : nullptr; 1380 1381 // We now have all the informations required to calculate the initial buffered 1382 // range. 1383 mHasStartTime = true; 1384 UpdateBuffered(); 1385 1386 mWatchManager.Watch(mWorkingInfoChanged, 1387 &MediaFormatReader::NotifyTrackInfoUpdated); 1388 mIsWatchingWorkingInfo = true; 1389 1390 if (mReadMetadataStartTime) { 1391 mReadMetaDataTime = TimeStamp::Now() - *mReadMetadataStartTime; 1392 mReadMetadataStartTime.reset(); 1393 } 1394 1395 mMetadataPromise.Resolve(std::move(metadata), __func__); 1396 } 1397 1398 bool MediaFormatReader::IsEncrypted() const { 1399 return (HasAudio() && mAudio.GetCurrentInfo()->mCrypto.IsEncrypted()) || 1400 (HasVideo() && mVideo.GetCurrentInfo()->mCrypto.IsEncrypted()); 1401 } 1402 1403 void MediaFormatReader::OnDemuxerInitFailed(const MediaResult& aError) { 1404 mDemuxerInitRequest.Complete(); 1405 mMetadataPromise.Reject(aError, __func__); 1406 } 1407 1408 void MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo) { 1409 // Called on the MDSM's TaskQueue. 1410 { 1411 MutexAutoLock lock(mVideo.mMutex); 1412 if (HasVideo()) { 1413 aInfo->mVideo = *mVideo.GetWorkingInfo()->GetAsVideoInfo(); 1414 } 1415 } 1416 { 1417 MutexAutoLock lock(mAudio.mMutex); 1418 if (HasAudio()) { 1419 aInfo->mAudio = *mAudio.GetWorkingInfo()->GetAsAudioInfo(); 1420 } 1421 } 1422 } 1423 1424 MediaFormatReader::DecoderData& MediaFormatReader::GetDecoderData( 1425 TrackType aTrack) { 1426 MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack || 1427 aTrack == TrackInfo::kVideoTrack); 1428 if (aTrack == TrackInfo::kAudioTrack) { 1429 return mAudio; 1430 } 1431 return mVideo; 1432 } 1433 1434 Maybe<TimeUnit> MediaFormatReader::ShouldSkip(TimeUnit aTimeThreshold, 1435 bool aRequestNextVideoKeyFrame) { 1436 MOZ_ASSERT(OnTaskQueue()); 1437 MOZ_ASSERT(HasVideo()); 1438 1439 if (!StaticPrefs::media_decoder_skip_to_next_key_frame_enabled()) { 1440 return Nothing(); 1441 } 1442 1443 // Ensure we have no pending seek going as skip-to-keyframe could return out 1444 // of date information. 1445 if (mVideo.HasInternalSeekPending()) { 1446 return Nothing(); 1447 } 1448 1449 TimeUnit nextKeyframe; 1450 nsresult rv = mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe); 1451 if (NS_FAILED(rv)) { 1452 // Only OggTrackDemuxer with video type gets into here. 1453 // We don't support skip-to-next-frame for this case. 1454 return Nothing(); 1455 } 1456 1457 const bool isNextKeyframeValid = 1458 nextKeyframe.ToMicroseconds() >= 0 && !nextKeyframe.IsInfinite(); 1459 // If we request the next keyframe, only return times greater than 1460 // aTimeThreshold. Otherwise, data will be already behind the threshold and 1461 // will be eventually discarded somewhere in the media pipeline. 1462 if (aRequestNextVideoKeyFrame && isNextKeyframeValid && 1463 nextKeyframe > aTimeThreshold) { 1464 return Some(nextKeyframe); 1465 } 1466 1467 const bool isNextVideoBehindTheThreshold = 1468 (isNextKeyframeValid && nextKeyframe <= aTimeThreshold) || 1469 GetInternalSeekTargetEndTime() < aTimeThreshold; 1470 return isNextVideoBehindTheThreshold ? Some(aTimeThreshold) : Nothing(); 1471 } 1472 1473 RefPtr<MediaFormatReader::VideoDataPromise> MediaFormatReader::RequestVideoData( 1474 const TimeUnit& aTimeThreshold, bool aRequestNextVideoKeyFrame) { 1475 MOZ_ASSERT(OnTaskQueue()); 1476 MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests"); 1477 // Requesting video can be done independently from audio, even during audio 1478 // seeking. But it shouldn't happen if we're doing video seek. 1479 if (!IsAudioOnlySeeking()) { 1480 MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), 1481 "No sample requests allowed while seeking"); 1482 MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() || 1483 mVideo.mTimeThreshold.isSome()); 1484 MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); 1485 } 1486 LOGV("RequestVideoData(%" PRId64 "), requestNextKeyFrame=%d", 1487 aTimeThreshold.ToMicroseconds(), aRequestNextVideoKeyFrame); 1488 1489 if (!HasVideo()) { 1490 LOG("called with no video track"); 1491 return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, 1492 __func__); 1493 } 1494 1495 if (IsSeeking()) { 1496 LOG("called mid-seek. Rejecting."); 1497 return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, 1498 __func__); 1499 } 1500 1501 if (mShutdown) { 1502 NS_WARNING("RequestVideoData on shutdown MediaFormatReader!"); 1503 return VideoDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, 1504 __func__); 1505 } 1506 1507 if (Maybe<TimeUnit> target = 1508 ShouldSkip(aTimeThreshold, aRequestNextVideoKeyFrame)) { 1509 PROFILER_MARKER_UNTYPED("RequestVideoData SkipVideoDemuxToNextKeyFrame", 1510 MEDIA_PLAYBACK); 1511 RefPtr<VideoDataPromise> p = mVideo.EnsurePromise(__func__); 1512 SkipVideoDemuxToNextKeyFrame(*target); 1513 return p; 1514 } 1515 1516 RefPtr<VideoDataPromise> p = mVideo.EnsurePromise(__func__); 1517 ScheduleUpdate(TrackInfo::kVideoTrack); 1518 1519 return p; 1520 } 1521 1522 void MediaFormatReader::OnDemuxFailed(TrackType aTrack, 1523 const MediaResult& aError) { 1524 AUTO_PROFILER_LABEL("MediaFormatReader::OnDemuxFailed", MEDIA_PLAYBACK); 1525 MOZ_ASSERT(OnTaskQueue()); 1526 LOG("Failed to demux %s, failure:%s", 1527 aTrack == TrackType::kVideoTrack ? "video" : "audio", 1528 aError.ErrorName().get()); 1529 auto& decoder = GetDecoderData(aTrack); 1530 decoder.mDemuxRequest.Complete(); 1531 switch (aError.Code()) { 1532 case NS_ERROR_DOM_MEDIA_END_OF_STREAM: 1533 DDLOG(DDLogCategory::Log, 1534 aTrack == TrackType::kVideoTrack ? "video_demux_interruption" 1535 : "audio_demux_interruption", 1536 aError); 1537 if (!decoder.mWaitingForDataStartTime) { 1538 decoder.RequestDrain(); 1539 } 1540 NotifyEndOfStream(aTrack); 1541 break; 1542 case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: 1543 DDLOG(DDLogCategory::Log, 1544 aTrack == TrackType::kVideoTrack ? "video_demux_interruption" 1545 : "audio_demux_interruption", 1546 aError); 1547 if (!decoder.mWaitingForDataStartTime) { 1548 decoder.RequestDrain(); 1549 } else { 1550 // mWaitingForDataStartTime was already set. The decoder is being 1551 // primed after an internal seek. A drain has already been performed. 1552 MOZ_ASSERT(decoder.mTimeThreshold.isSome() || 1553 decoder.mNumSamplesInput == decoder.mNumSamplesOutput); 1554 } 1555 NotifyWaitingForData(aTrack); 1556 break; 1557 case NS_ERROR_DOM_MEDIA_CANCELED: 1558 DDLOG(DDLogCategory::Log, 1559 aTrack == TrackType::kVideoTrack ? "video_demux_interruption" 1560 : "audio_demux_interruption", 1561 aError); 1562 if (decoder.HasPromise()) { 1563 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 1564 } 1565 break; 1566 default: 1567 DDLOG(DDLogCategory::Log, 1568 aTrack == TrackType::kVideoTrack ? "video_demux_error" 1569 : "audio_demux_error", 1570 aError); 1571 NotifyError(aTrack, aError); 1572 break; 1573 } 1574 } 1575 1576 void MediaFormatReader::DoDemuxVideo() { 1577 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxVideo", MEDIA_PLAYBACK); 1578 using SamplesPromise = MediaTrackDemuxer::SamplesPromise; 1579 1580 DDLOG(DDLogCategory::Log, "video_demuxing", DDNoValue{}); 1581 PerformanceRecorder<PlaybackStage> perfRecorder( 1582 MediaStage::RequestDemux, 1583 mVideo.GetCurrentInfo()->GetAsVideoInfo()->mImage.height); 1584 auto p = mVideo.mTrackDemuxer->GetSamples(1); 1585 1586 RefPtr<MediaFormatReader> self = this; 1587 if (mVideo.mFirstDemuxedSampleTime.isNothing()) { 1588 p = p->Then( 1589 OwnerThread(), __func__, 1590 [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) { 1591 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxVideo:Resolved", 1592 MEDIA_PLAYBACK); 1593 DDLOGEX(self.get(), DDLogCategory::Log, "video_first_demuxed", 1594 DDNoValue{}); 1595 self->OnFirstDemuxCompleted(TrackInfo::kVideoTrack, aSamples); 1596 return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__); 1597 }, 1598 [self](const MediaResult& aError) { 1599 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxVideo:Rejected", 1600 MEDIA_PLAYBACK); 1601 DDLOGEX(self.get(), DDLogCategory::Log, "video_first_demuxing_error", 1602 aError); 1603 self->OnFirstDemuxFailed(TrackInfo::kVideoTrack, aError); 1604 return SamplesPromise::CreateAndReject(aError, __func__); 1605 }); 1606 } 1607 1608 p->Then( 1609 OwnerThread(), __func__, 1610 [self, perfRecorder(std::move(perfRecorder))]( 1611 const RefPtr<MediaTrackDemuxer::SamplesHolder>& aSamples) mutable { 1612 perfRecorder.Record(); 1613 self->OnVideoDemuxCompleted(aSamples); 1614 }, 1615 [self](const MediaResult& aError) { self->OnVideoDemuxFailed(aError); }) 1616 ->Track(mVideo.mDemuxRequest); 1617 } 1618 1619 void MediaFormatReader::OnVideoDemuxCompleted( 1620 const RefPtr<MediaTrackDemuxer::SamplesHolder>& aSamples) { 1621 AUTO_PROFILER_LABEL("MediaFormatReader::OnVideoDemuxCompleted", 1622 MEDIA_PLAYBACK); 1623 LOGV("%zu video samples demuxed (sid:%d)", aSamples->GetSamples().Length(), 1624 aSamples->GetSamples()[0]->mTrackInfo 1625 ? aSamples->GetSamples()[0]->mTrackInfo->GetID() 1626 : 0); 1627 DDLOG(DDLogCategory::Log, "video_demuxed_samples", 1628 uint64_t(aSamples->GetSamples().Length())); 1629 mVideo.mDemuxRequest.Complete(); 1630 MOZ_ASSERT(mVideo.mQueuedSamples.IsEmpty()); 1631 mVideo.mQueuedSamples = aSamples->GetMovableSamples(); 1632 ScheduleUpdate(TrackInfo::kVideoTrack); 1633 } 1634 1635 RefPtr<MediaFormatReader::AudioDataPromise> 1636 MediaFormatReader::RequestAudioData() { 1637 MOZ_ASSERT(OnTaskQueue()); 1638 MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise(), "No duplicate sample requests"); 1639 // Requesting audio can be done independently from video, even during video 1640 // seeking. But it shouldn't happen if we're doing audio seek. 1641 if (!IsVideoOnlySeeking()) { 1642 MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), 1643 "No sample requests allowed while seeking"); 1644 MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists() || 1645 mAudio.mTimeThreshold.isSome()); 1646 MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); 1647 } 1648 LOGV(""); 1649 1650 if (!HasAudio()) { 1651 LOG("called with no audio track"); 1652 return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_FATAL_ERR, 1653 __func__); 1654 } 1655 1656 if (IsSeeking()) { 1657 LOG("called mid-seek. Rejecting."); 1658 return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, 1659 __func__); 1660 } 1661 1662 if (mShutdown) { 1663 NS_WARNING("RequestAudioData on shutdown MediaFormatReader!"); 1664 return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED, 1665 __func__); 1666 } 1667 1668 RefPtr<AudioDataPromise> p = mAudio.EnsurePromise(__func__); 1669 ScheduleUpdate(TrackInfo::kAudioTrack); 1670 1671 return p; 1672 } 1673 1674 void MediaFormatReader::DoDemuxAudio() { 1675 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxAudio", MEDIA_PLAYBACK); 1676 using SamplesPromise = MediaTrackDemuxer::SamplesPromise; 1677 1678 DDLOG(DDLogCategory::Log, "audio_demuxing", DDNoValue{}); 1679 PerformanceRecorder<PlaybackStage> perfRecorder(MediaStage::RequestDemux); 1680 auto p = mAudio.mTrackDemuxer->GetSamples(1); 1681 1682 RefPtr<MediaFormatReader> self = this; 1683 if (mAudio.mFirstDemuxedSampleTime.isNothing()) { 1684 p = p->Then( 1685 OwnerThread(), __func__, 1686 [self](RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples) { 1687 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxAudio:Resolved", 1688 MEDIA_PLAYBACK); 1689 DDLOGEX(self.get(), DDLogCategory::Log, "audio_first_demuxed", 1690 DDNoValue{}); 1691 self->OnFirstDemuxCompleted(TrackInfo::kAudioTrack, aSamples); 1692 return SamplesPromise::CreateAndResolve(aSamples.forget(), __func__); 1693 }, 1694 [self](const MediaResult& aError) { 1695 AUTO_PROFILER_LABEL("MediaFormatReader::DoDemuxAudio:Rejected", 1696 MEDIA_PLAYBACK); 1697 DDLOGEX(self.get(), DDLogCategory::Log, "audio_first_demuxing_error", 1698 aError); 1699 self->OnFirstDemuxFailed(TrackInfo::kAudioTrack, aError); 1700 return SamplesPromise::CreateAndReject(aError, __func__); 1701 }); 1702 } 1703 1704 p->Then( 1705 OwnerThread(), __func__, 1706 [self, perfRecorder(std::move(perfRecorder))]( 1707 const RefPtr<MediaTrackDemuxer::SamplesHolder>& aSamples) mutable { 1708 perfRecorder.Record(); 1709 self->OnAudioDemuxCompleted(aSamples); 1710 }, 1711 [self](const MediaResult& aError) { self->OnAudioDemuxFailed(aError); }) 1712 ->Track(mAudio.mDemuxRequest); 1713 } 1714 1715 void MediaFormatReader::OnAudioDemuxCompleted( 1716 const RefPtr<MediaTrackDemuxer::SamplesHolder>& aSamples) { 1717 LOGV("%zu audio samples demuxed (sid:%d)", aSamples->GetSamples().Length(), 1718 aSamples->GetSamples()[0]->mTrackInfo 1719 ? aSamples->GetSamples()[0]->mTrackInfo->GetID() 1720 : 0); 1721 DDLOG(DDLogCategory::Log, "audio_demuxed_samples", 1722 uint64_t(aSamples->GetSamples().Length())); 1723 mAudio.mDemuxRequest.Complete(); 1724 MOZ_ASSERT(mAudio.mQueuedSamples.IsEmpty()); 1725 mAudio.mQueuedSamples = aSamples->GetMovableSamples(); 1726 ScheduleUpdate(TrackInfo::kAudioTrack); 1727 } 1728 1729 void MediaFormatReader::NotifyNewOutput( 1730 TrackType aTrack, MediaDataDecoder::DecodedData&& aResults) { 1731 AUTO_PROFILER_LABEL("MediaFormatReader::NotifyNewOutput", MEDIA_PLAYBACK); 1732 MOZ_ASSERT(OnTaskQueue()); 1733 auto& decoder = GetDecoderData(aTrack); 1734 if (aResults.IsEmpty()) { 1735 DDLOG(DDLogCategory::Log, 1736 aTrack == TrackInfo::kAudioTrack ? "decoded_audio" : "decoded_video", 1737 "no output samples"); 1738 } else { 1739 for (auto&& sample : aResults) { 1740 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { 1741 switch (sample->mType) { 1742 case MediaData::Type::AUDIO_DATA: 1743 DDLOGPR(DDLogCategory::Log, 1744 aTrack == TrackInfo::kAudioTrack ? "decoded_audio" 1745 : "decoded_got_audio!?", 1746 "{\"type\":\"AudioData\", \"offset\":%" PRIi64 1747 ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 1748 ", \"duration_us\":%" PRIi64 ", \"frames\":%" PRIu32 1749 ", \"channels\":%" PRIu32 ", \"rate\":%" PRIu32 1750 ", \"bytes\":%zu}", 1751 sample->mOffset, sample->mTime.ToMicroseconds(), 1752 sample->mTimecode.ToMicroseconds(), 1753 sample->mDuration.ToMicroseconds(), 1754 sample->As<AudioData>()->Frames(), 1755 sample->As<AudioData>()->mChannels, 1756 sample->As<AudioData>()->mRate, 1757 sample->As<AudioData>()->Data().Length()); 1758 break; 1759 case MediaData::Type::VIDEO_DATA: 1760 DDLOGPR(DDLogCategory::Log, 1761 aTrack == TrackInfo::kVideoTrack ? "decoded_video" 1762 : "decoded_got_video!?", 1763 "{\"type\":\"VideoData\", \"offset\":%" PRIi64 1764 ", \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 1765 ", \"duration_us\":%" PRIi64 1766 ", \"kf\":%s, \"size\":[%" PRIi32 ",%" PRIi32 "]}", 1767 sample->mOffset, sample->mTime.ToMicroseconds(), 1768 sample->mTimecode.ToMicroseconds(), 1769 sample->mDuration.ToMicroseconds(), 1770 sample->mKeyframe ? "true" : "false", 1771 sample->As<VideoData>()->mDisplay.width, 1772 sample->As<VideoData>()->mDisplay.height); 1773 break; 1774 case MediaData::Type::RAW_DATA: 1775 DDLOGPR(DDLogCategory::Log, 1776 aTrack == TrackInfo::kAudioTrack ? "decoded_audio" 1777 : aTrack == TrackInfo::kVideoTrack ? "decoded_video" 1778 : "decoded_?", 1779 "{\"type\":\"RawData\", \"offset\":%" PRIi64 1780 " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 1781 ", \"duration_us\":%" PRIi64 ", \"kf\":%s}", 1782 sample->mOffset, sample->mTime.ToMicroseconds(), 1783 sample->mTimecode.ToMicroseconds(), 1784 sample->mDuration.ToMicroseconds(), 1785 sample->mKeyframe ? "true" : "false"); 1786 break; 1787 case MediaData::Type::NULL_DATA: 1788 DDLOGPR(DDLogCategory::Log, 1789 aTrack == TrackInfo::kAudioTrack ? "decoded_audio" 1790 : aTrack == TrackInfo::kVideoTrack ? "decoded_video" 1791 : "decoded_?", 1792 "{\"type\":\"NullData\", \"offset\":%" PRIi64 1793 " \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 1794 ", \"duration_us\":%" PRIi64 ", \"kf\":%s}", 1795 sample->mOffset, sample->mTime.ToMicroseconds(), 1796 sample->mTimecode.ToMicroseconds(), 1797 sample->mDuration.ToMicroseconds(), 1798 sample->mKeyframe ? "true" : "false"); 1799 break; 1800 } 1801 } 1802 LOGV("Received new %s sample time:%" PRId64 " duration:%" PRId64, 1803 TrackTypeToStr(aTrack), sample->mTime.ToMicroseconds(), 1804 sample->mDuration.ToMicroseconds()); 1805 decoder.mOutput.AppendElement(sample); 1806 decoder.mNumSamplesOutput++; 1807 decoder.mNumOfConsecutiveDecodingError = 0; 1808 decoder.mNumOfConsecutiveRDDOrGPUCrashes = 0; 1809 if (aTrack == TrackInfo::kAudioTrack) { 1810 decoder.mNumOfConsecutiveUtilityCrashes = 0; 1811 } 1812 if (sample->mType == MediaData::Type::VIDEO_DATA) { 1813 nsCString dummy; 1814 bool wasHardwareAccelerated = decoder.mIsHardwareAccelerated; 1815 decoder.mIsHardwareAccelerated = 1816 mVideo.mDecoder->IsHardwareAccelerated(dummy); 1817 if (!decoder.mHasReportedVideoHardwareSupportTelemtry || 1818 wasHardwareAccelerated != decoder.mIsHardwareAccelerated) { 1819 decoder.mHasReportedVideoHardwareSupportTelemtry = true; 1820 VideoData* videoData = sample->As<VideoData>(); 1821 glean::media::video_hardware_decoding_support 1822 .Get(decoder.GetCurrentInfo()->mMimeType) 1823 .Set(!!decoder.mIsHardwareAccelerated); 1824 static constexpr gfx::IntSize HD_VIDEO_SIZE{1280, 720}; 1825 if (videoData->mDisplay.width >= HD_VIDEO_SIZE.Width() && 1826 videoData->mDisplay.height >= HD_VIDEO_SIZE.Height()) { 1827 glean::media::video_hd_hardware_decoding_support 1828 .Get(decoder.GetCurrentInfo()->mMimeType) 1829 .Set(!!decoder.mIsHardwareAccelerated); 1830 } 1831 } 1832 // As the real video decoder creation might be delayed, we want to 1833 // update the decoder name again, instead of using the wrong name. 1834 if (decoder.mNumSamplesOutput == 1) { 1835 decoder.mDescription = mVideo.mDecoder->GetDescriptionName(); 1836 decoder.LoadDecodeProperties(); 1837 } 1838 } 1839 decoder.mDecodePerfRecorder->Record( 1840 sample->mTime.ToMicroseconds(), 1841 [startTime = sample->mTime.ToMicroseconds(), 1842 endTime = sample->GetEndTime().ToMicroseconds(), 1843 flag = 1844 sample->mType == MediaData::Type::VIDEO_DATA && 1845 decoder.mIsHardwareAccelerated 1846 ? MediaInfoFlag::HardwareDecoding 1847 : MediaInfoFlag::SoftwareDecoding](PlaybackStage& aStage) { 1848 aStage.SetStartTimeAndEndTime(startTime, endTime); 1849 aStage.AddFlag(flag); 1850 }); 1851 } 1852 } 1853 LOG("Done processing new %s samples", TrackTypeToStr(aTrack)); 1854 1855 if (!aResults.IsEmpty()) { 1856 // We have decoded our first frame, we can now starts to skip future errors. 1857 decoder.mFirstFrameTime.reset(); 1858 } 1859 ScheduleUpdate(aTrack); 1860 } 1861 1862 void MediaFormatReader::NotifyError(TrackType aTrack, 1863 const MediaResult& aError) { 1864 MOZ_ASSERT(OnTaskQueue()); 1865 NS_WARNING(aError.Description().get()); 1866 LOG("%s Decoding error: %s", TrackTypeToStr(aTrack), 1867 aError.Description().get()); 1868 auto& decoder = GetDecoderData(aTrack); 1869 decoder.mError = decoder.HasFatalError() ? decoder.mError : Some(aError); 1870 1871 ScheduleUpdate(aTrack); 1872 } 1873 1874 void MediaFormatReader::NotifyWaitingForData(TrackType aTrack) { 1875 MOZ_ASSERT(OnTaskQueue()); 1876 LOGV("%s", TrackTypeToStr(aTrack)); 1877 auto& decoder = GetDecoderData(aTrack); 1878 // mWaitingForDataStartTime may have already been set before draining the 1879 // decoder. 1880 if (!decoder.mWaitingForDataStartTime) { 1881 decoder.mWaitingForDataStartTime.emplace(TimeStamp::Now()); 1882 } 1883 if (decoder.mTimeThreshold) { 1884 // An InternalSeek() is in progress, which might be performed while 1885 // mWaitingForDataStartTime is set, such as when priming the decoder after 1886 // a drain. Set mWaiting to indicate that the internal seek cannot make 1887 // progress. 1888 decoder.mTimeThreshold.ref().mWaiting = true; 1889 } 1890 ScheduleUpdate(aTrack); 1891 } 1892 1893 void MediaFormatReader::NotifyWaitingForKey(TrackType aTrack) { 1894 MOZ_ASSERT(OnTaskQueue()); 1895 auto& decoder = GetDecoderData(aTrack); 1896 mOnWaitingForKey.Notify(); 1897 if (!decoder.mDecodeRequest.Exists()) { 1898 LOGV("WaitingForKey received while no pending decode. Ignoring"); 1899 return; 1900 } 1901 decoder.mWaitingForKey = true; 1902 ScheduleUpdate(aTrack); 1903 } 1904 1905 void MediaFormatReader::NotifyEndOfStream(TrackType aTrack) { 1906 MOZ_ASSERT(OnTaskQueue()); 1907 auto& decoder = GetDecoderData(aTrack); 1908 decoder.mDemuxEOS = true; 1909 ScheduleUpdate(aTrack); 1910 } 1911 1912 bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { 1913 // The decoder will not be fed a new raw sample until the current decoding 1914 // requests has completed. 1915 return (aDecoder.HasPromise() || aDecoder.mTimeThreshold.isSome()) && 1916 !aDecoder.HasPendingDrain() && !aDecoder.HasFatalError() && 1917 !aDecoder.mDemuxRequest.Exists() && !aDecoder.mOutput.Length() && 1918 !aDecoder.HasInternalSeekPending() && 1919 !aDecoder.mDecodeRequest.Exists(); 1920 } 1921 1922 void MediaFormatReader::ScheduleUpdate(TrackType aTrack) { 1923 MOZ_ASSERT(OnTaskQueue()); 1924 if (mShutdown) { 1925 return; 1926 } 1927 auto& decoder = GetDecoderData(aTrack); 1928 MOZ_RELEASE_ASSERT(decoder.GetCurrentInfo(), 1929 "Can only schedule update when track exists"); 1930 1931 if (decoder.mUpdateScheduled) { 1932 return; 1933 } 1934 LOGV("SchedulingUpdate(%s)", TrackTypeToStr(aTrack)); 1935 decoder.mUpdateScheduled = true; 1936 RefPtr<nsIRunnable> task(NewRunnableMethod<TrackType>( 1937 "MediaFormatReader::Update", this, &MediaFormatReader::Update, aTrack)); 1938 nsresult rv = OwnerThread()->Dispatch(task.forget()); 1939 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 1940 (void)rv; 1941 } 1942 1943 bool MediaFormatReader::UpdateReceivedNewData(TrackType aTrack) { 1944 MOZ_ASSERT(OnTaskQueue()); 1945 auto& decoder = GetDecoderData(aTrack); 1946 1947 if (!decoder.mReceivedNewData) { 1948 LOGV("!decoder.mReceivedNewData"); 1949 return false; 1950 } 1951 1952 // We do not want to clear mWaitingForDataStartTime while there are pending 1953 // demuxing or seeking operations that could affect the value of this flag. 1954 // This is in order to ensure that we will retry once they complete as we may 1955 // now have new data that could potentially allow those operations to 1956 // successfully complete if tried again. 1957 LOGV("%s", TrackTypeToStr(aTrack)); 1958 if (decoder.mSeekRequest.Exists()) { 1959 // Nothing more to do until this operation complete. 1960 return true; 1961 } 1962 1963 if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) { 1964 LOGV("Skipping in progress, nothing more to do"); 1965 return true; 1966 } 1967 1968 if (decoder.mDemuxRequest.Exists()) { 1969 LOGV("decoder.mDemuxRequest.Exists()"); 1970 // We may have pending operations to process, so we want to continue 1971 // after UpdateReceivedNewData returns. 1972 return false; 1973 } 1974 1975 if (decoder.HasPendingDrain()) { 1976 LOGV("decoder.HasPendingDrain()"); 1977 // We do not want to clear mWaitingForDataStartTime or mDemuxEOS while 1978 // a drain is in progress in order to properly complete the operation. 1979 // The drain may provide more frames that should be returned before 1980 // rejection of a MediaData promise. 1981 return false; 1982 } 1983 1984 decoder.mReceivedNewData = false; 1985 if (decoder.mTimeThreshold) { 1986 decoder.mTimeThreshold.ref().mWaiting = false; 1987 } 1988 if (aTrack == TrackType::kVideoTrack && decoder.mWaitingForDataStartTime) { 1989 mTotalWaitingForVideoDataTime += 1990 TimeStamp::Now() - *decoder.mWaitingForDataStartTime; 1991 } 1992 decoder.mWaitingForDataStartTime.reset(); 1993 1994 if (decoder.HasFatalError()) { 1995 LOGV("decoder.HasFatalError()"); 1996 return false; 1997 } 1998 1999 if (!mSeekPromise.IsEmpty() && 2000 (!IsVideoOnlySeeking() || aTrack == TrackInfo::kVideoTrack)) { 2001 MOZ_ASSERT(!decoder.HasPromise()); 2002 MOZ_DIAGNOSTIC_ASSERT( 2003 (IsVideoOnlySeeking() || !mAudio.mTimeThreshold) && 2004 !mVideo.mTimeThreshold, 2005 "InternalSeek must have been aborted when Seek was first called"); 2006 MOZ_DIAGNOSTIC_ASSERT( 2007 (IsVideoOnlySeeking() || !mAudio.HasWaitingPromise()) && 2008 !mVideo.HasWaitingPromise(), 2009 "Waiting promises must have been rejected when Seek was first called"); 2010 if (mVideo.mSeekRequest.Exists() || 2011 (!IsVideoOnlySeeking() && mAudio.mSeekRequest.Exists())) { 2012 // Already waiting for a seek to complete. Nothing more to do. 2013 return true; 2014 } 2015 LOG("Attempting Seek"); 2016 ScheduleSeek(); 2017 return true; 2018 } 2019 if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) { 2020 if (decoder.HasInternalSeekPending()) { 2021 LOG("Attempting Internal Seek"); 2022 InternalSeek(aTrack, decoder.mTimeThreshold.ref()); 2023 } 2024 if (decoder.HasWaitingPromise() && !decoder.IsWaitingForKey()) { 2025 MOZ_ASSERT(!decoder.HasPromise()); 2026 LOG("We have new data. Resolving WaitingPromise"); 2027 decoder.mWaitingPromise.Resolve(decoder.mType, __func__); 2028 MOZ_ASSERT(!decoder.IsWaitingForData()); 2029 } 2030 return true; 2031 } 2032 return false; 2033 } 2034 2035 void MediaFormatReader::RequestDemuxSamples(TrackType aTrack) { 2036 MOZ_ASSERT(OnTaskQueue()); 2037 auto& decoder = GetDecoderData(aTrack); 2038 MOZ_ASSERT(!decoder.mDemuxRequest.Exists()); 2039 2040 if (!decoder.mQueuedSamples.IsEmpty()) { 2041 // No need to demux new samples. 2042 return; 2043 } 2044 2045 if (decoder.mDemuxEOS) { 2046 // Nothing left to demux. 2047 // We do not want to attempt to demux while in waiting for data mode 2048 // as it would retrigger an unnecessary drain. 2049 return; 2050 } 2051 2052 LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack)); 2053 if (aTrack == TrackInfo::kVideoTrack) { 2054 DoDemuxVideo(); 2055 } else { 2056 DoDemuxAudio(); 2057 } 2058 } 2059 2060 void MediaFormatReader::DecoderData::StartRecordDecodingPerf( 2061 const TrackType aTrack, const MediaRawData* aSample) { 2062 if (!mDecodePerfRecorder) { 2063 mDecodePerfRecorder.reset(new PerformanceRecorderMulti<PlaybackStage>()); 2064 } 2065 const int32_t height = aTrack == TrackInfo::kVideoTrack 2066 ? GetCurrentInfo()->GetAsVideoInfo()->mImage.height 2067 : 0; 2068 MediaInfoFlag flag = MediaInfoFlag::None; 2069 flag |= 2070 aSample->mKeyframe ? MediaInfoFlag::KeyFrame : MediaInfoFlag::NonKeyFrame; 2071 if (aTrack == TrackInfo::kVideoTrack) { 2072 const nsCString& mimeType = GetCurrentInfo()->mMimeType; 2073 if (MP4Decoder::IsH264(mimeType)) { 2074 flag |= MediaInfoFlag::VIDEO_H264; 2075 } else if (VPXDecoder::IsVPX(mimeType, VPXDecoder::VP8)) { 2076 flag |= MediaInfoFlag::VIDEO_VP8; 2077 } else if (VPXDecoder::IsVPX(mimeType, VPXDecoder::VP9)) { 2078 flag |= MediaInfoFlag::VIDEO_VP9; 2079 } else if (MP4Decoder::IsHEVC(mimeType)) { 2080 flag |= MediaInfoFlag::VIDEO_HEVC; 2081 } 2082 #ifdef MOZ_AV1 2083 else if (AOMDecoder::IsAV1(mimeType)) { 2084 flag |= MediaInfoFlag::VIDEO_AV1; 2085 } 2086 #endif 2087 } 2088 mDecodePerfRecorder->Start(aSample->mTime.ToMicroseconds(), 2089 MediaStage::RequestDecode, height, flag); 2090 } 2091 2092 void MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack, 2093 MediaRawData* aSample) { 2094 MOZ_ASSERT(OnTaskQueue()); 2095 auto& decoder = GetDecoderData(aTrack); 2096 RefPtr<MediaFormatReader> self = this; 2097 decoder.mFlushed = false; 2098 DDLOGPR(DDLogCategory::Log, 2099 aTrack == TrackInfo::kAudioTrack ? "decode_audio" 2100 : aTrack == TrackInfo::kVideoTrack ? "decode_video" 2101 : "decode_?", 2102 "{\"type\":\"MediaRawData\", \"offset\":%" PRIi64 2103 ", \"bytes\":%zu, \"time_us\":%" PRIi64 ", \"timecode_us\":%" PRIi64 2104 ", \"duration_us\":%" PRIi64 ",%s%s}", 2105 aSample->mOffset, aSample->Size(), aSample->mTime.ToMicroseconds(), 2106 aSample->mTimecode.ToMicroseconds(), 2107 aSample->mDuration.ToMicroseconds(), aSample->mKeyframe ? " kf" : "", 2108 aSample->mEOS ? " eos" : ""); 2109 2110 decoder.StartRecordDecodingPerf(aTrack, aSample); 2111 if (aSample->mCrypto.IsEncrypted() && 2112 (mMediaEngineId || (mCDMProxy && !!mCDMProxy->AsRemoteCDMChild()))) { 2113 aSample->mShouldCopyCryptoToRemoteRawData = true; 2114 } 2115 decoder.mDecoder->Decode(aSample) 2116 ->Then( 2117 mTaskQueue, __func__, 2118 [self, aTrack, 2119 &decoder](MediaDataDecoder::DecodedData&& aResults) mutable { 2120 decoder.mDecodeRequest.Complete(); 2121 self->NotifyNewOutput(aTrack, std::move(aResults)); 2122 }, 2123 [self, aTrack, &decoder](const MediaResult& aError) { 2124 decoder.mDecodeRequest.Complete(); 2125 self->NotifyError(aTrack, aError); 2126 }) 2127 ->Track(decoder.mDecodeRequest); 2128 } 2129 2130 void MediaFormatReader::HandleDemuxedSamples( 2131 TrackType aTrack, FrameStatistics::AutoNotifyDecoded& aA) { 2132 MOZ_ASSERT(OnTaskQueue()); 2133 2134 auto& decoder = GetDecoderData(aTrack); 2135 2136 if (decoder.mFlushing) { 2137 LOGV("Decoder operation in progress, let it complete."); 2138 return; 2139 } 2140 2141 if (decoder.mQueuedSamples.IsEmpty()) { 2142 return; 2143 } 2144 2145 RefPtr<MediaRawData> sample = decoder.mQueuedSamples[0]; 2146 const RefPtr<TrackInfoSharedPtr> info = sample->mTrackInfo; 2147 2148 if (info && decoder.mLastStreamSourceID != info->GetID()) { 2149 nsTArray<RefPtr<MediaRawData>> samples; 2150 if (decoder.mDecoder) { 2151 bool recyclable = 2152 StaticPrefs::media_decoder_recycle_enabled() && 2153 decoder.mDecoder->SupportDecoderRecycling() && 2154 (*info)->mCrypto.mCryptoScheme == 2155 decoder.GetCurrentInfo()->mCrypto.mCryptoScheme && 2156 (*info)->mMimeType == decoder.GetCurrentInfo()->mMimeType; 2157 LOG("%s stream id has changed from:%d to:%d, recyclable=%d, " 2158 "alwaysRecyle=%d", 2159 TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID(), 2160 recyclable, decoder.mDecoder->ShouldDecoderAlwaysBeRecycled()); 2161 recyclable |= decoder.mDecoder->ShouldDecoderAlwaysBeRecycled(); 2162 if (!recyclable && decoder.mTimeThreshold.isNothing() && 2163 (decoder.mNextStreamSourceID.isNothing() || 2164 decoder.mNextStreamSourceID.ref() != info->GetID())) { 2165 LOG("draining decoder for stream id change."); 2166 decoder.RequestDrain(); 2167 decoder.mNextStreamSourceID = Some(info->GetID()); 2168 ScheduleUpdate(aTrack); 2169 return; 2170 } 2171 2172 // If flushing is required, it will clear our array of queued samples. 2173 // So we may need to make a copy. 2174 samples = decoder.mQueuedSamples.Clone(); 2175 if (!recyclable) { 2176 LOG("Decoder does not support recycling, recreate decoder."); 2177 ShutdownDecoder(aTrack); 2178 // We're going to be using a new decoder following the change of content 2179 // We can attempt to use hardware decoding again. 2180 decoder.mHardwareDecodingDisabled = false; 2181 decoder.mFirstFrameTime = Some(sample->mTime); 2182 } else if (decoder.HasWaitingPromise()) { 2183 decoder.Flush(); 2184 } 2185 } 2186 2187 nsPrintfCString markerString( 2188 "%s stream id changed from:%" PRIu32 " to:%" PRIu32, 2189 TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID()); 2190 PROFILER_MARKER_TEXT("StreamID Change", MEDIA_PLAYBACK, {}, markerString); 2191 LOG("%s", markerString.get()); 2192 2193 decoder.mNextStreamSourceID.reset(); 2194 decoder.mLastStreamSourceID = info->GetID(); 2195 decoder.mInfo = info; 2196 { 2197 MutexAutoLock lock(decoder.mMutex); 2198 if (aTrack == TrackInfo::kAudioTrack) { 2199 decoder.mWorkingInfo = MakeUnique<AudioInfo>(*info->GetAsAudioInfo()); 2200 } else if (aTrack == TrackInfo::kVideoTrack) { 2201 decoder.mWorkingInfo = MakeUnique<VideoInfo>(*info->GetAsVideoInfo()); 2202 } 2203 mWorkingInfoChanged = true; 2204 } 2205 2206 decoder.mMeanRate.Reset(); 2207 2208 if (sample->mKeyframe) { 2209 if (samples.Length()) { 2210 decoder.mQueuedSamples = std::move(samples); 2211 } 2212 } else { 2213 auto time = TimeInterval(sample->mTime, sample->GetEndTime()); 2214 InternalSeekTarget seekTarget = 2215 decoder.mTimeThreshold.refOr(InternalSeekTarget(time, false)); 2216 LOG("Stream change occurred on a non-keyframe. Seeking to:%" PRId64, 2217 sample->mTime.ToMicroseconds()); 2218 InternalSeek(aTrack, seekTarget); 2219 return; 2220 } 2221 } 2222 2223 // Calculate the average frame rate. The first frame will be accounted 2224 // for twice. 2225 decoder.mMeanRate.Update(sample->mDuration); 2226 2227 if (!decoder.mDecoder) { 2228 // In Clear Lead situation, the `mInfo` could change from unencrypted to 2229 // encrypted so we need to ensure the CDM proxy is ready before creating a 2230 // decoder. 2231 if (decoder.IsEncrypted() && 2232 (IsWaitingOnCDMResource() || !ResolveSetCDMPromiseIfDone(aTrack))) { 2233 return; 2234 } 2235 mDecoderFactory->CreateDecoder(aTrack); 2236 return; 2237 } 2238 2239 LOGV("%s Input:%" PRId64 " (dts:%" PRId64 " kf:%d)", TrackTypeToStr(aTrack), 2240 sample->mTime.ToMicroseconds(), sample->mTimecode.ToMicroseconds(), 2241 sample->mKeyframe); 2242 decoder.mNumSamplesInput++; 2243 decoder.mSizeOfQueue++; 2244 if (aTrack == TrackInfo::kVideoTrack) { 2245 aA.mStats.mParsedFrames++; 2246 } 2247 2248 DecodeDemuxedSamples(aTrack, sample); 2249 2250 decoder.mQueuedSamples.RemoveElementAt(0); 2251 } 2252 2253 media::TimeUnit MediaFormatReader::GetInternalSeekTargetEndTime() const { 2254 MOZ_ASSERT(OnTaskQueue()); 2255 return mVideo.mTimeThreshold ? mVideo.mTimeThreshold->EndTime() 2256 : TimeUnit::FromInfinity(); 2257 } 2258 2259 void MediaFormatReader::InternalSeek(TrackType aTrack, 2260 const InternalSeekTarget& aTarget) { 2261 MOZ_ASSERT(OnTaskQueue()); 2262 LOG("%s internal seek to %f", TrackTypeToStr(aTrack), 2263 aTarget.Time().ToSeconds()); 2264 2265 auto& decoder = GetDecoderData(aTrack); 2266 decoder.Flush(); 2267 decoder.ResetDemuxer(); 2268 decoder.mTimeThreshold = Some(aTarget); 2269 DDLOG(DDLogCategory::Log, "seeking", DDNoValue{}); 2270 RefPtr<MediaFormatReader> self = this; 2271 decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().Time()) 2272 ->Then( 2273 OwnerThread(), __func__, 2274 [self, aTrack](TimeUnit aTime) { 2275 DDLOGEX(self.get(), DDLogCategory::Log, "seeked", DDNoValue{}); 2276 auto& decoder = self->GetDecoderData(aTrack); 2277 decoder.mSeekRequest.Complete(); 2278 MOZ_ASSERT(decoder.mTimeThreshold, 2279 "Seek promise must be disconnected when " 2280 "timethreshold is reset"); 2281 decoder.mTimeThreshold.ref().mHasSeeked = true; 2282 self->SetVideoDecodeThreshold(); 2283 self->ScheduleUpdate(aTrack); 2284 }, 2285 [self, aTrack](const MediaResult& aError) { 2286 auto& decoder = self->GetDecoderData(aTrack); 2287 decoder.mSeekRequest.Complete(); 2288 switch (aError.Code()) { 2289 case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: 2290 DDLOGEX(self.get(), DDLogCategory::Log, "seeking_interrupted", 2291 aError); 2292 self->NotifyWaitingForData(aTrack); 2293 break; 2294 case NS_ERROR_DOM_MEDIA_END_OF_STREAM: 2295 DDLOGEX(self.get(), DDLogCategory::Log, "seeking_interrupted", 2296 aError); 2297 decoder.mTimeThreshold.reset(); 2298 self->NotifyEndOfStream(aTrack); 2299 break; 2300 case NS_ERROR_DOM_MEDIA_CANCELED: 2301 DDLOGEX(self.get(), DDLogCategory::Log, "seeking_interrupted", 2302 aError); 2303 decoder.mTimeThreshold.reset(); 2304 break; 2305 default: 2306 DDLOGEX(self.get(), DDLogCategory::Log, "seeking_error", 2307 aError); 2308 decoder.mTimeThreshold.reset(); 2309 self->NotifyError(aTrack, aError); 2310 break; 2311 } 2312 }) 2313 ->Track(decoder.mSeekRequest); 2314 } 2315 2316 void MediaFormatReader::DrainDecoder(TrackType aTrack) { 2317 AUTO_PROFILER_LABEL("MediaFormatReader::DrainDecoder", MEDIA_PLAYBACK); 2318 MOZ_ASSERT(OnTaskQueue()); 2319 2320 auto& decoder = GetDecoderData(aTrack); 2321 if (decoder.mDrainState == DrainState::Draining) { 2322 return; 2323 } 2324 if (!decoder.mDecoder || 2325 (decoder.mDrainState != DrainState::PartialDrainPending && 2326 decoder.mNumSamplesInput == decoder.mNumSamplesOutput)) { 2327 // No frames to drain. 2328 LOGV("Draining %s with nothing to drain", TrackTypeToStr(aTrack)); 2329 decoder.mDrainState = DrainState::DrainAborted; 2330 ScheduleUpdate(aTrack); 2331 return; 2332 } 2333 2334 decoder.mDrainState = DrainState::Draining; 2335 2336 DDLOG(DDLogCategory::Log, "draining", DDNoValue{}); 2337 RefPtr<MediaFormatReader> self = this; 2338 decoder.mDecoder->Drain() 2339 ->Then( 2340 mTaskQueue, __func__, 2341 [this, self, aTrack, 2342 &decoder](MediaDataDecoder::DecodedData&& aResults) { 2343 decoder.mDrainRequest.Complete(); 2344 DDLOGEX(self.get(), DDLogCategory::Log, "drained", DDNoValue{}); 2345 if (aResults.IsEmpty()) { 2346 LOG("DrainDecoder drained"); 2347 decoder.mDrainState = DrainState::DrainCompleted; 2348 } else { 2349 NotifyNewOutput(aTrack, std::move(aResults)); 2350 // Let's see if we have any more data available to drain. 2351 decoder.mDrainState = DrainState::PartialDrainPending; 2352 } 2353 ScheduleUpdate(aTrack); 2354 }, 2355 [self, aTrack, &decoder](const MediaResult& aError) { 2356 decoder.mDrainRequest.Complete(); 2357 DDLOGEX(self.get(), DDLogCategory::Log, "draining_error", aError); 2358 self->NotifyError(aTrack, aError); 2359 }) 2360 ->Track(decoder.mDrainRequest); 2361 LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack)); 2362 } 2363 2364 // See https://firefox-source-docs.mozilla.org/media/MediaFormatReader.html 2365 // for a state diagram overview. 2366 void MediaFormatReader::Update(TrackType aTrack) { 2367 AUTO_PROFILER_LABEL("MediaFormatReader::Update", MEDIA_PLAYBACK); 2368 MOZ_ASSERT(OnTaskQueue()); 2369 2370 if (mShutdown) { 2371 return; 2372 } 2373 2374 LOGV("Processing update for %s", TrackTypeToStr(aTrack)); 2375 2376 bool needOutput = false; 2377 auto& decoder = GetDecoderData(aTrack); 2378 decoder.mUpdateScheduled = false; 2379 2380 if (!mInitDone) { 2381 return; 2382 } 2383 2384 if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) { 2385 LOGV("Skipping in progress, nothing more to do"); 2386 return; 2387 } 2388 2389 if (UpdateReceivedNewData(aTrack)) { 2390 LOGV("Nothing more to do"); 2391 return; 2392 } 2393 2394 if (decoder.mSeekRequest.Exists()) { 2395 LOGV("Seeking hasn't completed, nothing more to do"); 2396 return; 2397 } 2398 2399 MOZ_DIAGNOSTIC_ASSERT( 2400 !decoder.HasInternalSeekPending() || 2401 (!decoder.mOutput.Length() && !decoder.mQueuedSamples.Length()), 2402 "No frames can be demuxed or decoded while an internal seek is pending"); 2403 2404 // Record number of frames decoded and parsed. Automatically update the 2405 // stats counters using the AutoNotifyDecoded stack-based class. 2406 FrameStatistics::AutoNotifyDecoded a(mFrameStats); 2407 2408 // Drop any frames found prior our internal seek target. 2409 while (decoder.mTimeThreshold && decoder.mOutput.Length()) { 2410 RefPtr<MediaData>& output = decoder.mOutput[0]; 2411 InternalSeekTarget target = decoder.mTimeThreshold.ref(); 2412 auto time = output->mTime; 2413 if (time >= target.Time()) { 2414 // We have reached our internal seek target. 2415 decoder.mTimeThreshold.reset(); 2416 // We might have dropped some keyframes. 2417 if (aTrack == TrackType::kVideoTrack) { 2418 mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; 2419 } 2420 } 2421 if (time < target.Time() || (target.mDropTarget && target.Contains(time))) { 2422 LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)", 2423 TrackTypeToStr(aTrack), output->mTime.ToSeconds(), 2424 target.Time().ToSeconds(), output->mKeyframe); 2425 decoder.mOutput.RemoveElementAt(0); 2426 decoder.mSizeOfQueue -= 1; 2427 } 2428 } 2429 2430 while (decoder.mOutput.Length() && 2431 decoder.mOutput[0]->mType == MediaData::Type::NULL_DATA) { 2432 LOGV("Dropping null data. Time: %" PRId64, 2433 decoder.mOutput[0]->mTime.ToMicroseconds()); 2434 decoder.mOutput.RemoveElementAt(0); 2435 decoder.mSizeOfQueue -= 1; 2436 } 2437 2438 if (decoder.HasPromise()) { 2439 needOutput = true; 2440 if (decoder.mOutput.Length()) { 2441 RefPtr<MediaData> output = decoder.mOutput[0]; 2442 decoder.mOutput.RemoveElementAt(0); 2443 decoder.mSizeOfQueue -= 1; 2444 decoder.mLastDecodedSampleTime = 2445 Some(TimeInterval(output->mTime, output->GetEndTime())); 2446 decoder.mNumSamplesOutputTotal++; 2447 ReturnOutput(output, aTrack); 2448 // We have a decoded sample ready to be returned. 2449 if (aTrack == TrackType::kVideoTrack) { 2450 uint64_t delta = 2451 decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames; 2452 a.mStats.mDecodedFrames = static_cast<uint32_t>(delta); 2453 mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal; 2454 if (output->mKeyframe) { 2455 if (mPreviousDecodedKeyframeTime_us < 2456 output->mTime.ToMicroseconds()) { 2457 // There is a previous keyframe -> Record inter-keyframe stats. 2458 uint64_t segment_us = output->mTime.ToMicroseconds() - 2459 mPreviousDecodedKeyframeTime_us; 2460 a.mStats.mInterKeyframeSum_us += segment_us; 2461 a.mStats.mInterKeyframeCount += 1; 2462 if (a.mStats.mInterKeyFrameMax_us < segment_us) { 2463 a.mStats.mInterKeyFrameMax_us = segment_us; 2464 } 2465 } 2466 mPreviousDecodedKeyframeTime_us = output->mTime.ToMicroseconds(); 2467 } 2468 } 2469 } else if (decoder.HasFatalError()) { 2470 nsCString mimeType = decoder.GetCurrentInfo()->mMimeType; 2471 if (!mimeType.IsEmpty()) { 2472 glean::media_playback::DecodeErrorExtra extraData; 2473 extraData.mimeType = Some(mimeType); 2474 extraData.errorName = Some(decoder.mError->ErrorName()); 2475 if (mCDMProxy) { 2476 extraData.keySystem = 2477 Some(NS_ConvertUTF16toUTF8(mCDMProxy->KeySystem())); 2478 } 2479 extraData.decoderName = Some(decoder.mDescription); 2480 extraData.isHardwareAccelerated = Some(decoder.mIsHardwareAccelerated); 2481 glean::media_playback::decode_error.Record(Some(extraData)); 2482 } 2483 LOG("Rejecting %s promise for %s : DECODE_ERROR", TrackTypeToStr(aTrack), 2484 mimeType.get()); 2485 decoder.RejectPromise(decoder.mError.ref(), __func__); 2486 return; 2487 } else if (decoder.HasCompletedDrain()) { 2488 if (decoder.mDemuxEOS) { 2489 LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack)); 2490 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); 2491 } else if (decoder.mWaitingForDataStartTime) { 2492 if (decoder.mDrainState == DrainState::DrainCompleted && 2493 decoder.mLastDecodedSampleTime && !decoder.mNextStreamSourceID) { 2494 // We have completed draining the decoder following WaitingForData. 2495 // Prime the decoder so that it is ready to resume from the 2496 // last sample decoded. 2497 LOG("Seeking to last sample time: %" PRId64, 2498 decoder.mLastDecodedSampleTime.ref().mStart.ToMicroseconds()); 2499 InternalSeek(aTrack, InternalSeekTarget( 2500 decoder.mLastDecodedSampleTime.ref(), true)); 2501 } 2502 if (!decoder.mReceivedNewData) { 2503 LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack)); 2504 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); 2505 } 2506 } 2507 2508 decoder.mDrainState = DrainState::None; 2509 2510 // Now that draining has completed, we check if we have received 2511 // new data again as the result may now be different from the earlier 2512 // run. 2513 if (UpdateReceivedNewData(aTrack) || decoder.mSeekRequest.Exists()) { 2514 LOGV("Completed drain: Nothing more to do"); 2515 return; 2516 } 2517 } else if (decoder.IsWaitingForData() && !decoder.HasPendingDrain()) { 2518 // This path is triggered when an internal seek request on mTrackDemuxer 2519 // returns NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, in which case there is 2520 // no drain. No new output samples will be available until more data 2521 // arrives. 2522 MOZ_ASSERT(!decoder.mDemuxRequest.Exists()); 2523 MOZ_ASSERT(decoder.mQueuedSamples.IsEmpty()); 2524 // mReceivedNewData would have caused UpdateReceivedNewData() to clear 2525 // mWaitingForDataStartTime. 2526 MOZ_ASSERT(!decoder.mReceivedNewData); 2527 LOG("Rejecting %s promise: WAITING_FOR_DATA during internal seek", 2528 TrackTypeToStr(aTrack)); 2529 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); 2530 // Wait until more data arrives. 2531 return; 2532 } else if (decoder.mDemuxEOS && !decoder.HasPendingDrain() && 2533 decoder.mQueuedSamples.IsEmpty()) { 2534 // It is possible to transition from WAITING_FOR_DATA directly to EOS 2535 // state during the internal seek; in which case no draining would occur. 2536 // There is no more samples left to be decoded and we are already in 2537 // EOS state. We can immediately reject the data promise. 2538 LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack)); 2539 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__); 2540 } else if (decoder.mWaitingForKey) { 2541 LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for key", 2542 TrackTypeToStr(aTrack)); 2543 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); 2544 } else if (IsDecoderWaitingForCDM(aTrack)) { 2545 // Rejecting the promise could lead to entering buffering state for MDSM, 2546 // once a qualified(with the same key system and sessions created by the 2547 // same InitData) new cdm proxy is set, decoding can be resumed. 2548 LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for CDM", 2549 TrackTypeToStr(aTrack)); 2550 decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); 2551 } 2552 } 2553 2554 if (decoder.mDrainState == DrainState::DrainRequested || 2555 decoder.mDrainState == DrainState::PartialDrainPending) { 2556 if (decoder.mOutput.IsEmpty()) { 2557 DrainDecoder(aTrack); 2558 } 2559 return; 2560 } 2561 2562 if (decoder.mError && !decoder.HasFatalError()) { 2563 MOZ_RELEASE_ASSERT(!decoder.HasInternalSeekPending(), 2564 "No error can occur while an internal seek is pending"); 2565 2566 nsCString error; 2567 bool firstFrameDecodingFailedWithHardware = 2568 decoder.mFirstFrameTime && 2569 decoder.mError.ref() == NS_ERROR_DOM_MEDIA_DECODE_ERR && 2570 decoder.mDecoder && decoder.mDecoder->IsHardwareAccelerated(error) && 2571 !decoder.mHardwareDecodingDisabled; 2572 bool needsNewDecoder = 2573 decoder.mError.ref() == NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER || 2574 firstFrameDecodingFailedWithHardware; 2575 // Limit number of process restarts after crash 2576 if ((decoder.mError.ref() == 2577 NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_RDD_OR_GPU_ERR && 2578 decoder.mNumOfConsecutiveRDDOrGPUCrashes++ < 2579 decoder.mMaxConsecutiveRDDOrGPUCrashes) || 2580 (decoder.mError.ref() == 2581 NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_UTILITY_ERR && 2582 decoder.mNumOfConsecutiveUtilityCrashes++ < 2583 decoder.mMaxConsecutiveUtilityCrashes)) { 2584 needsNewDecoder = true; 2585 } 2586 // For MF CDM crash, it needs to be handled differently. We need to shutdown 2587 // current decoder and report that error to the state machine in order to 2588 // let it to determine if playback can keep going or not. 2589 if (decoder.mError.ref() == NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_MF_CDM_ERR) { 2590 LOG("Error: notify MF CDM crash and shutdown %s decoder", 2591 TrackTypeToStr(aTrack)); 2592 ShutdownDecoder(aTrack); 2593 decoder.RejectPromise(decoder.mError.ref(), __func__); 2594 decoder.mError.reset(); 2595 return; 2596 } 2597 #ifdef XP_LINUX 2598 // We failed to decode on Linux with HW decoder, 2599 // give it another try without HW decoder. 2600 if (decoder.mError.ref() == NS_ERROR_DOM_MEDIA_DECODE_ERR && 2601 decoder.mDecoder->IsHardwareAccelerated(error)) { 2602 LOG("Error: %s decode error, disable HW acceleration", 2603 TrackTypeToStr(aTrack)); 2604 needsNewDecoder = true; 2605 decoder.mHardwareDecodingDisabled = true; 2606 } 2607 // RDD process crashed on Linux, give it another try without HW decoder. 2608 if (decoder.mError.ref() == 2609 NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_RDD_OR_GPU_ERR) { 2610 LOG("Error: %s remote decoder crashed, disable HW acceleration", 2611 TrackTypeToStr(aTrack)); 2612 decoder.mHardwareDecodingDisabled = true; 2613 } 2614 #endif 2615 // We don't want to expose crash error so switch to 2616 // NS_ERROR_DOM_MEDIA_DECODE_ERR. 2617 if (decoder.mError.ref() == 2618 NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_RDD_OR_GPU_ERR || 2619 decoder.mError.ref() == NS_ERROR_DOM_MEDIA_REMOTE_CRASHED_UTILITY_ERR) { 2620 decoder.mError = Some(MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, 2621 RESULT_DETAIL("Unable to decode"))); 2622 } 2623 if (!needsNewDecoder && ++decoder.mNumOfConsecutiveDecodingError > 2624 decoder.mMaxConsecutiveDecodingError) { 2625 DDLOG(DDLogCategory::Log, "too_many_decode_errors", decoder.mError.ref()); 2626 NotifyError(aTrack, decoder.mError.ref()); 2627 return; 2628 } 2629 2630 if (firstFrameDecodingFailedWithHardware) { 2631 decoder.mHardwareDecodingDisabled = true; 2632 } 2633 decoder.mError.reset(); 2634 2635 LOG("%s decoded error count %d RDD crashes count %d", 2636 TrackTypeToStr(aTrack), decoder.mNumOfConsecutiveDecodingError, 2637 decoder.mNumOfConsecutiveRDDOrGPUCrashes); 2638 2639 if (needsNewDecoder) { 2640 LOG("Error: %s needs a new decoder", TrackTypeToStr(aTrack)); 2641 ShutdownDecoder(aTrack); 2642 } 2643 if (decoder.mFirstFrameTime) { 2644 TimeInterval seekInterval = TimeInterval(decoder.mFirstFrameTime.ref(), 2645 decoder.mFirstFrameTime.ref()); 2646 InternalSeek(aTrack, InternalSeekTarget(seekInterval, false)); 2647 return; 2648 } 2649 2650 TimeUnit nextKeyframe; 2651 if (aTrack == TrackType::kVideoTrack && 2652 NS_SUCCEEDED( 2653 decoder.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe)) && 2654 !nextKeyframe.IsInfinite()) { 2655 SkipVideoDemuxToNextKeyFrame( 2656 decoder.mLastDecodedSampleTime.refOr(TimeInterval()).Length()); 2657 } else if (aTrack == TrackType::kAudioTrack) { 2658 decoder.Flush(); 2659 } else { 2660 DDLOG(DDLogCategory::Log, "no_keyframe", NS_ERROR_DOM_MEDIA_FATAL_ERR); 2661 // We can't recover from this error. 2662 NotifyError(aTrack, NS_ERROR_DOM_MEDIA_FATAL_ERR); 2663 } 2664 return; 2665 } 2666 2667 bool needInput = NeedInput(decoder); 2668 2669 LOGV("Update(%s) ni=%d no=%d in:%" PRIu64 " out:%" PRIu64 2670 " qs=%u decoding:%d flushing:%d desc:%s pending:%u waiting:%d eos:%d " 2671 "ds:%d sid:%u waitcdm:%d", 2672 TrackTypeToStr(aTrack), needInput, needOutput, decoder.mNumSamplesInput, 2673 decoder.mNumSamplesOutput, uint32_t(size_t(decoder.mSizeOfQueue)), 2674 decoder.mDecodeRequest.Exists(), decoder.mFlushing, 2675 decoder.mDescription.get(), uint32_t(decoder.mOutput.Length()), 2676 !!decoder.mWaitingForDataStartTime, decoder.mDemuxEOS, 2677 int32_t(decoder.mDrainState), decoder.mLastStreamSourceID, 2678 IsDecoderWaitingForCDM(aTrack)); 2679 2680 if (IsWaitingOnCDMResource() || !ResolveSetCDMPromiseIfDone(aTrack)) { 2681 // If the content is encrypted, MFR won't start to create decoder until 2682 // CDMProxy is set. 2683 return; 2684 } 2685 2686 if ((decoder.IsWaitingForData() && 2687 // If mTimeThreshold is set then more samples might need to be demuxed 2688 // to advance from the random access point to the target, but if its 2689 // mWaiting is set then demuxing would continue to fail. 2690 (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) || 2691 (decoder.IsWaitingForKey())) { 2692 // Nothing more we can do at present. 2693 LOGV("Still waiting for data or key. data(%d)/key(%d)", 2694 !!decoder.mWaitingForDataStartTime, decoder.mWaitingForKey); 2695 return; 2696 } 2697 2698 if (decoder.CancelWaitingForKey()) { 2699 LOGV("No longer waiting for key. Resolving waiting promise"); 2700 return; 2701 } 2702 2703 if (!needInput) { 2704 LOGV("No need for additional input (pending:%u)", 2705 uint32_t(decoder.mOutput.Length())); 2706 return; 2707 } 2708 2709 // Demux samples if we don't have some. 2710 RequestDemuxSamples(aTrack); 2711 2712 HandleDemuxedSamples(aTrack, a); 2713 // At least one of RequestDemuxSamples() or HandleDemuxedSamples() was a 2714 // no-op, so only one operation is in progress at one time. 2715 MOZ_ASSERT(!decoder.mDemuxRequest.Exists() || 2716 !decoder.mDecodeRequest.Exists()); 2717 } 2718 2719 void MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack) { 2720 AUTO_PROFILER_LABEL("MediaFormatReader::ReturnOutput", MEDIA_PLAYBACK); 2721 MOZ_ASSERT(GetDecoderData(aTrack).HasPromise()); 2722 MOZ_DIAGNOSTIC_ASSERT(aData->mType != MediaData::Type::NULL_DATA); 2723 LOG("Resolved data promise for %s [%" PRId64 ", %" PRId64 "]", 2724 TrackTypeToStr(aTrack), aData->mTime.ToMicroseconds(), 2725 aData->GetEndTime().ToMicroseconds()); 2726 2727 if (aTrack == TrackInfo::kAudioTrack) { 2728 AudioData* audioData = aData->As<AudioData>(); 2729 2730 if (audioData->mChannels != mInfo.mAudio.mChannels || 2731 audioData->mRate != mInfo.mAudio.mRate) { 2732 LOG("change of audio format (rate:%d->%d). " 2733 "This is an unsupported configuration", 2734 mInfo.mAudio.mRate, audioData->mRate); 2735 mInfo.mAudio.mRate = audioData->mRate; 2736 mInfo.mAudio.mChannels = audioData->mChannels; 2737 MutexAutoLock lock(mAudio.mMutex); 2738 mAudio.mWorkingInfo->GetAsAudioInfo()->mRate = audioData->mRate; 2739 mAudio.mWorkingInfo->GetAsAudioInfo()->mChannels = audioData->mChannels; 2740 mWorkingInfoChanged = true; 2741 } 2742 mAudio.ResolvePromise(audioData, __func__); 2743 } else if (aTrack == TrackInfo::kVideoTrack) { 2744 VideoData* videoData = aData->As<VideoData>(); 2745 2746 if (videoData->mDisplay != mInfo.mVideo.mDisplay) { 2747 LOG("change of video display size (%dx%d->%dx%d)", 2748 mInfo.mVideo.mDisplay.width, mInfo.mVideo.mDisplay.height, 2749 videoData->mDisplay.width, videoData->mDisplay.height); 2750 mInfo.mVideo.mDisplay = videoData->mDisplay; 2751 MutexAutoLock lock(mVideo.mMutex); 2752 mVideo.mWorkingInfo->GetAsVideoInfo()->mDisplay = videoData->mDisplay; 2753 mWorkingInfoChanged = true; 2754 } 2755 2756 mozilla::gfx::ColorDepth colorDepth = videoData->GetColorDepth(); 2757 if (colorDepth != mInfo.mVideo.mColorDepth) { 2758 LOG("change of video color depth (enum %u -> enum %u)", 2759 (unsigned)mInfo.mVideo.mColorDepth, (unsigned)colorDepth); 2760 mInfo.mVideo.mColorDepth = colorDepth; 2761 MutexAutoLock lock(mVideo.mMutex); 2762 mVideo.mWorkingInfo->GetAsVideoInfo()->mColorDepth = colorDepth; 2763 mWorkingInfoChanged = true; 2764 } 2765 2766 TimeUnit nextKeyframe; 2767 if (!mVideo.HasInternalSeekPending() && 2768 NS_SUCCEEDED( 2769 mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&nextKeyframe))) { 2770 videoData->SetNextKeyFrameTime(nextKeyframe); 2771 } 2772 2773 mVideo.ResolvePromise(videoData, __func__); 2774 } 2775 } 2776 2777 size_t MediaFormatReader::SizeOfVideoQueueInFrames() { 2778 return SizeOfQueue(TrackInfo::kVideoTrack); 2779 } 2780 2781 size_t MediaFormatReader::SizeOfAudioQueueInFrames() { 2782 return SizeOfQueue(TrackInfo::kAudioTrack); 2783 } 2784 2785 size_t MediaFormatReader::SizeOfQueue(TrackType aTrack) { 2786 auto& decoder = GetDecoderData(aTrack); 2787 return decoder.mSizeOfQueue; 2788 } 2789 2790 RefPtr<MediaFormatReader::WaitForDataPromise> MediaFormatReader::WaitForData( 2791 MediaData::Type aType) { 2792 MOZ_ASSERT(OnTaskQueue()); 2793 TrackType trackType = aType == MediaData::Type::VIDEO_DATA 2794 ? TrackType::kVideoTrack 2795 : TrackType::kAudioTrack; 2796 auto& decoder = GetDecoderData(trackType); 2797 if (!decoder.IsWaitingForData() && !decoder.IsWaitingForKey()) { 2798 // We aren't waiting for anything. 2799 LOGV("Not waiting. Returning resolved promise"); 2800 return WaitForDataPromise::CreateAndResolve(decoder.mType, __func__); 2801 } 2802 RefPtr<WaitForDataPromise> p = decoder.mWaitingPromise.Ensure(__func__); 2803 ScheduleUpdate(trackType); 2804 return p; 2805 } 2806 2807 nsresult MediaFormatReader::ResetDecode(const TrackSet& aTracks) { 2808 AUTO_PROFILER_LABEL("MediaFormatReader::ResetDecode", MEDIA_PLAYBACK); 2809 MOZ_ASSERT(OnTaskQueue()); 2810 LOGV(""); 2811 2812 mSeekPromise.RejectIfExists(NS_OK, __func__); 2813 mSkipRequest.DisconnectIfExists(); 2814 2815 // Do the same for any data wait promises. 2816 if (aTracks.contains(TrackInfo::kAudioTrack)) { 2817 mAudio.mWaitingPromise.RejectIfExists( 2818 WaitForDataRejectValue(MediaData::Type::AUDIO_DATA, 2819 WaitForDataRejectValue::CANCELED), 2820 __func__); 2821 } 2822 2823 if (aTracks.contains(TrackInfo::kVideoTrack)) { 2824 mVideo.mWaitingPromise.RejectIfExists( 2825 WaitForDataRejectValue(MediaData::Type::VIDEO_DATA, 2826 WaitForDataRejectValue::CANCELED), 2827 __func__); 2828 } 2829 2830 // Reset miscellaneous seeking state. 2831 mPendingSeekTime.reset(); 2832 2833 if (HasVideo() && aTracks.contains(TrackInfo::kVideoTrack)) { 2834 mVideo.ResetDemuxer(); 2835 mVideo.mFirstFrameTime = Some(media::TimeUnit::Zero()); 2836 Reset(TrackInfo::kVideoTrack); 2837 if (mVideo.HasPromise()) { 2838 mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 2839 } 2840 } 2841 2842 if (HasAudio() && aTracks.contains(TrackInfo::kAudioTrack)) { 2843 mAudio.ResetDemuxer(); 2844 mVideo.mFirstFrameTime = Some(media::TimeUnit::Zero()); 2845 Reset(TrackInfo::kAudioTrack); 2846 if (mAudio.HasPromise()) { 2847 mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 2848 } 2849 } 2850 2851 return NS_OK; 2852 } 2853 2854 void MediaFormatReader::Reset(TrackType aTrack) { 2855 MOZ_ASSERT(OnTaskQueue()); 2856 LOG("Reset(%s) BEGIN", TrackTypeToStr(aTrack)); 2857 2858 auto& decoder = GetDecoderData(aTrack); 2859 2860 decoder.ResetState(); 2861 decoder.Flush(); 2862 2863 LOG("Reset(%s) END", TrackTypeToStr(aTrack)); 2864 } 2865 2866 void MediaFormatReader::DropDecodedSamples(TrackType aTrack) { 2867 MOZ_ASSERT(OnTaskQueue()); 2868 auto& decoder = GetDecoderData(aTrack); 2869 size_t lengthDecodedQueue = decoder.mOutput.Length(); 2870 if (lengthDecodedQueue && decoder.mTimeThreshold.isSome()) { 2871 auto time = decoder.mOutput.LastElement()->mTime; 2872 if (time >= decoder.mTimeThreshold.ref().Time()) { 2873 // We would have reached our internal seek target. 2874 decoder.mTimeThreshold.reset(); 2875 } 2876 } 2877 decoder.mOutput.Clear(); 2878 decoder.mSizeOfQueue -= lengthDecodedQueue; 2879 if (aTrack == TrackInfo::kVideoTrack && mFrameStats) { 2880 mFrameStats->Accumulate({0, 0, 0, lengthDecodedQueue, 0, 0}); 2881 } 2882 } 2883 2884 void MediaFormatReader::SkipVideoDemuxToNextKeyFrame(TimeUnit aTimeThreshold) { 2885 AUTO_PROFILER_LABEL("MediaFormatReader::SkipVideoDemuxToNextKeyFrame", 2886 MEDIA_PLAYBACK); 2887 MOZ_ASSERT(OnTaskQueue()); 2888 LOG("Skipping up to %" PRId64, aTimeThreshold.ToMicroseconds()); 2889 2890 // We've reached SkipVideoDemuxToNextKeyFrame when our decoding is late. 2891 // As such we can drop all already decoded samples and discard all pending 2892 // samples. 2893 DropDecodedSamples(TrackInfo::kVideoTrack); 2894 2895 mVideo.mTrackDemuxer->SkipToNextRandomAccessPoint(aTimeThreshold) 2896 ->Then(OwnerThread(), __func__, this, 2897 &MediaFormatReader::OnVideoSkipCompleted, 2898 &MediaFormatReader::OnVideoSkipFailed) 2899 ->Track(mSkipRequest); 2900 } 2901 2902 void MediaFormatReader::VideoSkipReset(uint32_t aSkipped) { 2903 PROFILER_MARKER_UNTYPED("SkippedVideoDecode", MEDIA_PLAYBACK); 2904 MOZ_ASSERT(OnTaskQueue()); 2905 2906 // Some frames may have been output by the decoder since we initiated the 2907 // videoskip process and we know they would be late. 2908 DropDecodedSamples(TrackInfo::kVideoTrack); 2909 // Report the pending frames as dropped. 2910 if (mFrameStats) { 2911 uint32_t droppedDecoderCount = SizeOfVideoQueueInFrames(); 2912 mFrameStats->Accumulate({0, 0, 0, droppedDecoderCount, 0, 0}); 2913 } 2914 2915 // Cancel any pending demux request and pending demuxed samples. 2916 mVideo.mDemuxRequest.DisconnectIfExists(); 2917 Reset(TrackType::kVideoTrack); 2918 2919 if (mFrameStats) { 2920 mFrameStats->Accumulate({aSkipped, 0, 0, aSkipped, 0, 0}); 2921 } 2922 2923 mVideo.mNumSamplesSkippedTotal += aSkipped; 2924 } 2925 2926 void MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped) { 2927 AUTO_PROFILER_LABEL("MediaFormatReader::OnVideoSkipCompleted", 2928 MEDIA_PLAYBACK); 2929 MOZ_ASSERT(OnTaskQueue()); 2930 LOG("Skipping succeeded, skipped %u frames", aSkipped); 2931 mSkipRequest.Complete(); 2932 2933 DDLOG(DDLogCategory::Log, "video_skipped", DDNoValue()); 2934 2935 VideoSkipReset(aSkipped); 2936 2937 ScheduleUpdate(TrackInfo::kVideoTrack); 2938 } 2939 2940 void MediaFormatReader::OnVideoSkipFailed( 2941 MediaTrackDemuxer::SkipFailureHolder aFailure) { 2942 AUTO_PROFILER_LABEL("MediaFormatReader::OnVideoSkipFailed", MEDIA_PLAYBACK); 2943 MOZ_ASSERT(OnTaskQueue()); 2944 LOG("Skipping failed, skipped %u frames", aFailure.mSkipped); 2945 mSkipRequest.Complete(); 2946 2947 switch (aFailure.mFailure.Code()) { 2948 case NS_ERROR_DOM_MEDIA_END_OF_STREAM: 2949 case NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA: 2950 DDLOG(DDLogCategory::Log, "video_skipping_interruption", 2951 aFailure.mFailure); 2952 // Some frames may have been output by the decoder since we initiated the 2953 // videoskip process and we know they would be late. 2954 DropDecodedSamples(TrackInfo::kVideoTrack); 2955 // We can't complete the skip operation, will just service a video frame 2956 // normally. 2957 ScheduleUpdate(TrackInfo::kVideoTrack); 2958 break; 2959 case NS_ERROR_DOM_MEDIA_CANCELED: 2960 DDLOG(DDLogCategory::Log, "video_skipping_interruption", 2961 aFailure.mFailure); 2962 if (mVideo.HasPromise()) { 2963 mVideo.RejectPromise(aFailure.mFailure, __func__); 2964 } 2965 break; 2966 default: 2967 DDLOG(DDLogCategory::Log, "video_skipping_error", aFailure.mFailure); 2968 NotifyError(TrackType::kVideoTrack, aFailure.mFailure); 2969 break; 2970 } 2971 } 2972 2973 RefPtr<MediaFormatReader::SeekPromise> MediaFormatReader::Seek( 2974 const SeekTarget& aTarget) { 2975 AUTO_PROFILER_LABEL("MediaFormatReader::Seek", MEDIA_PLAYBACK); 2976 MOZ_ASSERT(OnTaskQueue()); 2977 2978 LOG("aTarget=(%" PRId64 "), track=%s", aTarget.GetTime().ToMicroseconds(), 2979 SeekTarget::EnumValueToString(aTarget.GetTrack())); 2980 2981 MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty()); 2982 MOZ_DIAGNOSTIC_ASSERT(mPendingSeekTime.isNothing()); 2983 // Should reset data request, and no pending internal seek. 2984 if (aTarget.IsAllTracks()) { 2985 MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise()); 2986 MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise()); 2987 MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing()); 2988 MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing()); 2989 } else if (aTarget.IsVideoOnly()) { 2990 MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise()); 2991 MOZ_DIAGNOSTIC_ASSERT(mVideo.mTimeThreshold.isNothing()); 2992 } else if (aTarget.IsAudioOnly()) { 2993 MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise()); 2994 MOZ_DIAGNOSTIC_ASSERT(mAudio.mTimeThreshold.isNothing()); 2995 } 2996 2997 if (!mInfo.mMediaSeekable && !mInfo.mMediaSeekableOnlyInBufferedRanges) { 2998 LOG("Seek() END (Unseekable)"); 2999 return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 3000 } 3001 3002 if (mShutdown) { 3003 return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 3004 } 3005 3006 SetSeekTarget(aTarget); 3007 3008 RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__); 3009 3010 ScheduleSeek(); 3011 3012 return p; 3013 } 3014 3015 void MediaFormatReader::SetSeekTarget(const SeekTarget& aTarget) { 3016 MOZ_ASSERT(OnTaskQueue()); 3017 3018 mOriginalSeekTarget = aTarget; 3019 mFallbackSeekTime = mPendingSeekTime = Some(aTarget.GetTime()); 3020 } 3021 3022 void MediaFormatReader::ScheduleSeek() { 3023 if (mSeekScheduled) { 3024 return; 3025 } 3026 mSeekScheduled = true; 3027 nsresult rv = OwnerThread()->Dispatch(NewRunnableMethod( 3028 "MediaFormatReader::AttemptSeek", this, &MediaFormatReader::AttemptSeek)); 3029 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 3030 (void)rv; 3031 } 3032 3033 void MediaFormatReader::AttemptSeek() { 3034 AUTO_PROFILER_LABEL("MediaFormatReader::AttemptSeek", MEDIA_PLAYBACK); 3035 MOZ_ASSERT(OnTaskQueue()); 3036 3037 mSeekScheduled = false; 3038 3039 if (mPendingSeekTime.isNothing()) { 3040 LOGV("AttemptSeek, no pending seek time?"); 3041 return; 3042 } 3043 3044 // Only reset the demuxers targeted by this SeekTarget, to avoid A/V sync 3045 // issues. 3046 const bool isSeekingAudio = HasAudio() && !mOriginalSeekTarget.IsVideoOnly(); 3047 const bool isSeekingVideo = HasVideo() && !mOriginalSeekTarget.IsAudioOnly(); 3048 LOG("AttemptSeek, seekingAudio=%d, seekingVideo=%d", isSeekingAudio, 3049 isSeekingVideo); 3050 if (isSeekingVideo) { 3051 mVideo.ResetDemuxer(); 3052 mVideo.ResetState(); 3053 } 3054 if (isSeekingAudio) { 3055 mAudio.ResetDemuxer(); 3056 mAudio.ResetState(); 3057 } 3058 3059 // If seeking both tracks, seek the video track, and then the audio track when 3060 // the video track seek has completed. Otherwise, only seek a specific track. 3061 if (isSeekingVideo) { 3062 DoVideoSeek(); 3063 } else if (isSeekingAudio) { 3064 DoAudioSeek(); 3065 } else { 3066 MOZ_CRASH(); 3067 } 3068 } 3069 3070 void MediaFormatReader::OnSeekFailed(TrackType aTrack, 3071 const MediaResult& aError) { 3072 AUTO_PROFILER_LABEL("MediaFormatReader::OnSeekFailed", MEDIA_PLAYBACK); 3073 MOZ_ASSERT(OnTaskQueue()); 3074 LOGV("%s failure:%s", TrackTypeToStr(aTrack), aError.ErrorName().get()); 3075 if (aTrack == TrackType::kVideoTrack) { 3076 mVideo.mSeekRequest.Complete(); 3077 } else { 3078 mAudio.mSeekRequest.Complete(); 3079 } 3080 3081 if (aError == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) { 3082 if (HasVideo() && aTrack == TrackType::kAudioTrack && 3083 mFallbackSeekTime.isSome() && 3084 mPendingSeekTime.ref() != mFallbackSeekTime.ref()) { 3085 // We have failed to seek audio where video seeked to earlier. 3086 // Attempt to seek instead to the closest point that we know we have in 3087 // order to limit A/V sync discrepency. 3088 3089 // Ensure we have the most up to date buffered ranges. 3090 UpdateReceivedNewData(TrackType::kAudioTrack); 3091 Maybe<TimeUnit> nextSeekTime; 3092 // Find closest buffered time found after video seeked time. 3093 for (const auto& timeRange : mAudio.mTimeRanges) { 3094 if (timeRange.mStart >= mPendingSeekTime.ref()) { 3095 nextSeekTime.emplace(timeRange.mStart); 3096 break; 3097 } 3098 } 3099 if (nextSeekTime.isNothing() || 3100 nextSeekTime.ref() > mFallbackSeekTime.ref()) { 3101 nextSeekTime = Some(mFallbackSeekTime.ref()); 3102 LOG("Unable to seek audio to video seek time. A/V sync may be broken"); 3103 } else { 3104 mFallbackSeekTime.reset(); 3105 } 3106 mPendingSeekTime = nextSeekTime; 3107 DoAudioSeek(); 3108 return; 3109 } 3110 NotifyWaitingForData(aTrack); 3111 } 3112 MOZ_ASSERT(!mVideo.mSeekRequest.Exists() && !mAudio.mSeekRequest.Exists()); 3113 mPendingSeekTime.reset(); 3114 3115 auto type = aTrack == TrackType::kAudioTrack ? MediaData::Type::AUDIO_DATA 3116 : MediaData::Type::VIDEO_DATA; 3117 mSeekPromise.RejectIfExists(SeekRejectValue(type, aError), __func__); 3118 } 3119 3120 void MediaFormatReader::DoVideoSeek() { 3121 AUTO_PROFILER_LABEL("MediaFormatReader::DoVideoSeek", MEDIA_PLAYBACK); 3122 MOZ_ASSERT(mPendingSeekTime.isSome()); 3123 LOGV("Seeking video to %" PRId64, mPendingSeekTime.ref().ToMicroseconds()); 3124 MOZ_DIAGNOSTIC_ASSERT(!IsAudioOnlySeeking()); 3125 MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists()); 3126 auto seekTime = mPendingSeekTime.ref(); 3127 mVideo.mTrackDemuxer->Seek(seekTime) 3128 ->Then(OwnerThread(), __func__, this, 3129 &MediaFormatReader::OnVideoSeekCompleted, 3130 &MediaFormatReader::OnVideoSeekFailed) 3131 ->Track(mVideo.mSeekRequest); 3132 } 3133 3134 void MediaFormatReader::OnVideoSeekCompleted(TimeUnit aTime) { 3135 AUTO_PROFILER_LABEL("MediaFormatReader::OnVideoSeekCompleted", 3136 MEDIA_PLAYBACK); 3137 MOZ_ASSERT(OnTaskQueue()); 3138 LOGV("Video seeked to %" PRId64, aTime.ToMicroseconds()); 3139 mVideo.mSeekRequest.Complete(); 3140 3141 mVideo.mFirstFrameTime = Some(aTime); 3142 mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; 3143 3144 SetVideoDecodeThreshold(); 3145 3146 if (HasAudio() && !mOriginalSeekTarget.IsVideoOnly()) { 3147 MOZ_ASSERT(mPendingSeekTime.isSome()); 3148 if (mOriginalSeekTarget.IsFast()) { 3149 // We are performing a fast seek. We need to seek audio to where the 3150 // video seeked to, to ensure proper A/V sync once playback resume. 3151 mPendingSeekTime = Some(aTime); 3152 } 3153 DoAudioSeek(); 3154 } else { 3155 mPendingSeekTime.reset(); 3156 mSeekPromise.ResolveIfExists(aTime, __func__); 3157 } 3158 } 3159 3160 void MediaFormatReader::OnVideoSeekFailed(const MediaResult& aError) { 3161 AUTO_PROFILER_LABEL("MediaFormatReader::OnVideoSeekFailed", MEDIA_PLAYBACK); 3162 mPreviousDecodedKeyframeTime_us = sNoPreviousDecodedKeyframe; 3163 OnSeekFailed(TrackType::kVideoTrack, aError); 3164 } 3165 3166 void MediaFormatReader::SetVideoDecodeThreshold() { 3167 MOZ_ASSERT(OnTaskQueue()); 3168 3169 if (!HasVideo() || !mVideo.mDecoder) { 3170 return; 3171 } 3172 3173 if (!mVideo.mTimeThreshold && !IsSeeking()) { 3174 return; 3175 } 3176 3177 TimeUnit threshold; 3178 if (mVideo.mTimeThreshold) { 3179 // For internalSeek. 3180 threshold = mVideo.mTimeThreshold.ref().Time(); 3181 } else if (IsSeeking()) { 3182 // If IsSeeking() is true, then video seek must have completed already. 3183 TimeUnit keyframe; 3184 if (NS_FAILED(mVideo.mTrackDemuxer->GetNextRandomAccessPoint(&keyframe))) { 3185 return; 3186 } 3187 3188 // If the key frame is invalid/infinite, it means the target position is 3189 // closing to end of stream. We don't want to skip any frame at this point. 3190 threshold = keyframe.IsValid() && !keyframe.IsInfinite() 3191 ? mOriginalSeekTarget.GetTime() 3192 : TimeUnit::Invalid(); 3193 } else { 3194 return; 3195 } 3196 3197 if (threshold.IsValid()) { 3198 LOG("Set seek threshold to %" PRId64, threshold.ToMicroseconds()); 3199 } else { 3200 LOG("Resetting seek threshold"); 3201 } 3202 mVideo.mDecoder->SetSeekThreshold(threshold); 3203 } 3204 3205 void MediaFormatReader::DoAudioSeek() { 3206 AUTO_PROFILER_LABEL("MediaFormatReader::DoAudioSeek", MEDIA_PLAYBACK); 3207 MOZ_ASSERT(mPendingSeekTime.isSome()); 3208 LOGV("Seeking audio to %" PRId64, mPendingSeekTime.ref().ToMicroseconds()); 3209 MOZ_DIAGNOSTIC_ASSERT(!IsVideoOnlySeeking()); 3210 MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists()); 3211 auto seekTime = mPendingSeekTime.ref(); 3212 mAudio.mTrackDemuxer->Seek(seekTime) 3213 ->Then(OwnerThread(), __func__, this, 3214 &MediaFormatReader::OnAudioSeekCompleted, 3215 &MediaFormatReader::OnAudioSeekFailed) 3216 ->Track(mAudio.mSeekRequest); 3217 } 3218 3219 void MediaFormatReader::OnAudioSeekCompleted(TimeUnit aTime) { 3220 MOZ_ASSERT(OnTaskQueue()); 3221 AUTO_PROFILER_LABEL("MediaFormatReader::OnAudioSeekCompleted", 3222 MEDIA_PLAYBACK); 3223 LOGV("Audio seeked to %" PRId64, aTime.ToMicroseconds()); 3224 mAudio.mSeekRequest.Complete(); 3225 mAudio.mFirstFrameTime = Some(aTime); 3226 mPendingSeekTime.reset(); 3227 mSeekPromise.ResolveIfExists(aTime, __func__); 3228 } 3229 3230 void MediaFormatReader::OnAudioSeekFailed(const MediaResult& aError) { 3231 AUTO_PROFILER_LABEL("MediaFormatReader::OnAudioSeekFailed", MEDIA_PLAYBACK); 3232 OnSeekFailed(TrackType::kAudioTrack, aError); 3233 } 3234 3235 void MediaFormatReader::ReleaseResources() { 3236 LOGV(""); 3237 if (mShutdown) { 3238 return; 3239 } 3240 ShutdownDecoder(TrackInfo::kAudioTrack); 3241 ShutdownDecoder(TrackInfo::kVideoTrack); 3242 } 3243 3244 bool MediaFormatReader::VideoIsHardwareAccelerated() const { 3245 return mVideo.mIsHardwareAccelerated; 3246 } 3247 3248 void MediaFormatReader::NotifyTrackDemuxers() { 3249 MOZ_ASSERT(OnTaskQueue()); 3250 3251 LOGV(""); 3252 3253 if (!mInitDone) { 3254 return; 3255 } 3256 3257 if (HasVideo()) { 3258 mVideo.mReceivedNewData = true; 3259 ScheduleUpdate(TrackType::kVideoTrack); 3260 } 3261 if (HasAudio()) { 3262 mAudio.mReceivedNewData = true; 3263 ScheduleUpdate(TrackType::kAudioTrack); 3264 } 3265 } 3266 3267 void MediaFormatReader::NotifyDataArrived() { 3268 AUTO_PROFILER_LABEL("MediaFormatReader::NotifyDataArrived", MEDIA_PLAYBACK); 3269 MOZ_ASSERT(OnTaskQueue()); 3270 3271 if (mShutdown || !mDemuxer || !mDemuxerInitDone) { 3272 return; 3273 } 3274 3275 if (mNotifyDataArrivedPromise.Exists()) { 3276 // Already one in progress. Set the dirty flag so we can process it later. 3277 mPendingNotifyDataArrived = true; 3278 return; 3279 } 3280 3281 RefPtr<MediaFormatReader> self = this; 3282 mDemuxer->NotifyDataArrived() 3283 ->Then( 3284 OwnerThread(), __func__, 3285 [self]() { 3286 AUTO_PROFILER_LABEL("MediaFormatReader::NotifyDataArrived:Resolved", 3287 MEDIA_PLAYBACK); 3288 self->mNotifyDataArrivedPromise.Complete(); 3289 self->UpdateBuffered(); 3290 self->NotifyTrackDemuxers(); 3291 if (self->mPendingNotifyDataArrived) { 3292 self->mPendingNotifyDataArrived = false; 3293 self->NotifyDataArrived(); 3294 } 3295 }, 3296 [self]() { self->mNotifyDataArrivedPromise.Complete(); }) 3297 ->Track(mNotifyDataArrivedPromise); 3298 } 3299 3300 void MediaFormatReader::UpdateMediaEngineId(uint64_t aMediaEngineId) { 3301 LOG("Update external media engine Id %" PRIu64, aMediaEngineId); 3302 mMediaEngineId = Some(aMediaEngineId); 3303 } 3304 3305 void MediaFormatReader::UpdateBuffered() { 3306 AUTO_PROFILER_LABEL("MediaFormatReader::UpdateBuffered", MEDIA_PLAYBACK); 3307 MOZ_ASSERT(OnTaskQueue()); 3308 3309 if (mShutdown) { 3310 return; 3311 } 3312 3313 if (!mInitDone || !mHasStartTime) { 3314 mBuffered = TimeIntervals(); 3315 return; 3316 } 3317 3318 if (HasVideo()) { 3319 mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered(); 3320 bool hasLastEnd; 3321 auto lastEnd = mVideo.mTimeRanges.GetEnd(&hasLastEnd); 3322 if (hasLastEnd) { 3323 if (mVideo.mLastTimeRangesEnd && 3324 mVideo.mLastTimeRangesEnd.ref() < lastEnd) { 3325 // New data was added after our previous end, we can clear the EOS flag. 3326 mVideo.mDemuxEOS = false; 3327 ScheduleUpdate(TrackInfo::kVideoTrack); 3328 } 3329 mVideo.mLastTimeRangesEnd = Some(lastEnd); 3330 } 3331 } 3332 if (HasAudio()) { 3333 mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered(); 3334 bool hasLastEnd; 3335 auto lastEnd = mAudio.mTimeRanges.GetEnd(&hasLastEnd); 3336 if (hasLastEnd) { 3337 if (mAudio.mLastTimeRangesEnd && 3338 mAudio.mLastTimeRangesEnd.ref() < lastEnd) { 3339 // New data was added after our previous end, we can clear the EOS flag. 3340 mAudio.mDemuxEOS = false; 3341 ScheduleUpdate(TrackInfo::kAudioTrack); 3342 } 3343 mAudio.mLastTimeRangesEnd = Some(lastEnd); 3344 } 3345 } 3346 3347 media::TimeIntervals intervals; 3348 if (HasAudio() && HasVideo()) { 3349 intervals = media::Intersection(mVideo.mTimeRanges, mAudio.mTimeRanges); 3350 } else if (HasAudio()) { 3351 intervals = mAudio.mTimeRanges; 3352 } else if (HasVideo()) { 3353 intervals = mVideo.mTimeRanges; 3354 } 3355 3356 if (intervals.IsEmpty() || intervals.GetStart() == TimeUnit::Zero()) { 3357 // IntervalSet already starts at 0 or is empty, nothing to shift. 3358 mBuffered = intervals; 3359 } else { 3360 LOG("Subtract start time for buffered range, startTime=%" PRId64, 3361 mInfo.mStartTime.ToMicroseconds()); 3362 mBuffered = intervals.Shift(TimeUnit::Zero() - mInfo.mStartTime); 3363 } 3364 } 3365 3366 layers::ImageContainer* MediaFormatReader::GetImageContainer() { 3367 return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() 3368 : nullptr; 3369 } 3370 3371 RefPtr<GenericPromise> MediaFormatReader::RequestDebugInfo( 3372 dom::MediaFormatReaderDebugInfo& aInfo) { 3373 if (!OnTaskQueue()) { 3374 // Run the request on the task queue if it's not already. 3375 return InvokeAsync(mTaskQueue, __func__, 3376 [this, self = RefPtr{this}, &aInfo] { 3377 return RequestDebugInfo(aInfo); 3378 }); 3379 } 3380 GetDebugInfo(aInfo); 3381 return GenericPromise::CreateAndResolve(true, __func__); 3382 } 3383 3384 void MediaFormatReader::GetDebugInfo(dom::MediaFormatReaderDebugInfo& aInfo) { 3385 MOZ_ASSERT(OnTaskQueue(), 3386 "Don't call this off the task queue, it's going to touch a lot of " 3387 "data members"); 3388 nsCString result; 3389 nsAutoCString audioDecoderName("unavailable"); 3390 nsAutoCString videoDecoderName = audioDecoderName; 3391 nsAutoCString audioType("none"); 3392 nsAutoCString videoType("none"); 3393 3394 AudioInfo audioInfo; 3395 if (HasAudio()) { 3396 audioInfo = *mAudio.GetWorkingInfo()->GetAsAudioInfo(); 3397 audioDecoderName = mAudio.mDecoder ? mAudio.mDecoder->GetDescriptionName() 3398 : mAudio.mDescription; 3399 audioType = audioInfo.mMimeType; 3400 aInfo.mAudioState.mNeedInput = NeedInput(mAudio); 3401 aInfo.mAudioState.mHasPromise = mAudio.HasPromise(); 3402 aInfo.mAudioState.mWaitingPromise = !mAudio.mWaitingPromise.IsEmpty(); 3403 aInfo.mAudioState.mHasDemuxRequest = mAudio.mDemuxRequest.Exists(); 3404 aInfo.mAudioState.mDemuxQueueSize = mAudio.mQueuedSamples.Length(); 3405 aInfo.mAudioState.mHasDecoder = mAudio.mDecodeRequest.Exists(); 3406 aInfo.mAudioState.mTimeTreshold = 3407 mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().Time().ToSeconds() 3408 : -1.0; 3409 aInfo.mAudioState.mTimeTresholdHasSeeked = 3410 mAudio.mTimeThreshold ? mAudio.mTimeThreshold.ref().mHasSeeked : false; 3411 aInfo.mAudioState.mNumSamplesInput = mAudio.mNumSamplesInput; 3412 aInfo.mAudioState.mNumSamplesOutput = mAudio.mNumSamplesOutput; 3413 aInfo.mAudioState.mQueueSize = mAudio.mSizeOfQueue; 3414 aInfo.mAudioState.mPending = mAudio.mOutput.Length(); 3415 aInfo.mAudioState.mWaitingForData = !!mAudio.mWaitingForDataStartTime; 3416 aInfo.mAudioState.mDemuxEOS = mAudio.mDemuxEOS; 3417 aInfo.mAudioState.mDrainState = int32_t(mAudio.mDrainState); 3418 aInfo.mAudioState.mWaitingForKey = mAudio.mWaitingForKey; 3419 aInfo.mAudioState.mLastStreamSourceID = mAudio.mLastStreamSourceID; 3420 } 3421 3422 CopyUTF8toUTF16(audioDecoderName, aInfo.mAudioDecoderName); 3423 CopyUTF8toUTF16(audioType, aInfo.mAudioType); 3424 aInfo.mAudioChannels = audioInfo.mChannels; 3425 aInfo.mAudioRate = audioInfo.mRate; 3426 aInfo.mAudioFramesDecoded = mAudio.mNumSamplesOutputTotal; 3427 3428 VideoInfo videoInfo; 3429 if (HasVideo()) { 3430 videoInfo = *mVideo.GetWorkingInfo()->GetAsVideoInfo(); 3431 videoDecoderName = mVideo.mDecoder ? mVideo.mDecoder->GetDescriptionName() 3432 : mVideo.mDescription; 3433 videoType = videoInfo.mMimeType; 3434 aInfo.mVideoState.mNeedInput = NeedInput(mVideo); 3435 aInfo.mVideoState.mHasPromise = mVideo.HasPromise(); 3436 aInfo.mVideoState.mWaitingPromise = !mVideo.mWaitingPromise.IsEmpty(); 3437 aInfo.mVideoState.mHasDemuxRequest = mVideo.mDemuxRequest.Exists(); 3438 aInfo.mVideoState.mDemuxQueueSize = mVideo.mQueuedSamples.Length(); 3439 aInfo.mVideoState.mHasDecoder = mVideo.mDecodeRequest.Exists(); 3440 aInfo.mVideoState.mTimeTreshold = 3441 mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().Time().ToSeconds() 3442 : -1.0; 3443 aInfo.mVideoState.mTimeTresholdHasSeeked = 3444 mVideo.mTimeThreshold ? mVideo.mTimeThreshold.ref().mHasSeeked : false; 3445 aInfo.mVideoState.mNumSamplesInput = mVideo.mNumSamplesInput; 3446 aInfo.mVideoState.mNumSamplesOutput = mVideo.mNumSamplesOutput; 3447 aInfo.mVideoState.mQueueSize = mVideo.mSizeOfQueue; 3448 aInfo.mVideoState.mPending = mVideo.mOutput.Length(); 3449 aInfo.mVideoState.mWaitingForData = !!mVideo.mWaitingForDataStartTime; 3450 aInfo.mVideoState.mDemuxEOS = mVideo.mDemuxEOS; 3451 aInfo.mVideoState.mDrainState = int32_t(mVideo.mDrainState); 3452 aInfo.mVideoState.mWaitingForKey = mVideo.mWaitingForKey; 3453 aInfo.mVideoState.mLastStreamSourceID = mVideo.mLastStreamSourceID; 3454 aInfo.mTotalReadMetadataTimeMs = mReadMetaDataTime.ToMilliseconds(); 3455 aInfo.mTotalWaitingForVideoDataTimeMs = 3456 mTotalWaitingForVideoDataTime.ToMilliseconds(); 3457 } 3458 3459 CopyUTF8toUTF16(videoDecoderName, aInfo.mVideoDecoderName); 3460 CopyUTF8toUTF16(videoType, aInfo.mVideoType); 3461 aInfo.mVideoWidth = 3462 videoInfo.mDisplay.width < 0 ? 0 : videoInfo.mDisplay.width; 3463 aInfo.mVideoHeight = 3464 videoInfo.mDisplay.height < 0 ? 0 : videoInfo.mDisplay.height; 3465 aInfo.mVideoRate = mVideo.mMeanRate.Mean(); 3466 aInfo.mVideoHardwareAccelerated = VideoIsHardwareAccelerated(); 3467 aInfo.mVideoNumSamplesOutputTotal = mVideo.mNumSamplesOutputTotal; 3468 aInfo.mVideoNumSamplesSkippedTotal = mVideo.mNumSamplesSkippedTotal; 3469 3470 // Looking at dropped frames 3471 FrameStatisticsData stats = mFrameStats->GetFrameStatisticsData(); 3472 aInfo.mFrameStats.mDroppedDecodedFrames = stats.mDroppedDecodedFrames; 3473 aInfo.mFrameStats.mDroppedSinkFrames = stats.mDroppedSinkFrames; 3474 aInfo.mFrameStats.mDroppedCompositorFrames = stats.mDroppedCompositorFrames; 3475 } 3476 3477 void MediaFormatReader::SetVideoNullDecode(bool aIsNullDecode) { 3478 MOZ_ASSERT(OnTaskQueue()); 3479 return SetNullDecode(TrackType::kVideoTrack, aIsNullDecode); 3480 } 3481 3482 void MediaFormatReader::UpdateCompositor( 3483 already_AddRefed<layers::KnowsCompositor> aCompositor) { 3484 MOZ_ASSERT(OnTaskQueue()); 3485 mKnowsCompositor = aCompositor; 3486 } 3487 3488 void MediaFormatReader::SetNullDecode(TrackType aTrack, bool aIsNullDecode) { 3489 MOZ_ASSERT(OnTaskQueue()); 3490 3491 auto& decoder = GetDecoderData(aTrack); 3492 if (decoder.mIsNullDecode == aIsNullDecode) { 3493 return; 3494 } 3495 3496 LOG("%s, decoder.mIsNullDecode = %d => aIsNullDecode = %d", 3497 TrackTypeToStr(aTrack), decoder.mIsNullDecode, aIsNullDecode); 3498 3499 decoder.mIsNullDecode = aIsNullDecode; 3500 ShutdownDecoder(aTrack); 3501 } 3502 3503 void MediaFormatReader::OnFirstDemuxCompleted( 3504 TrackInfo::TrackType aType, 3505 const RefPtr<MediaTrackDemuxer::SamplesHolder>& aSamples) { 3506 AUTO_PROFILER_LABEL("MediaFormatReader::OnFirstDemuxCompleted", 3507 MEDIA_PLAYBACK); 3508 MOZ_ASSERT(OnTaskQueue()); 3509 3510 if (mShutdown) { 3511 return; 3512 } 3513 3514 auto& decoder = GetDecoderData(aType); 3515 MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing()); 3516 decoder.mFirstDemuxedSampleTime.emplace(aSamples->GetSamples()[0]->mTime); 3517 MaybeResolveMetadataPromise(); 3518 } 3519 3520 void MediaFormatReader::OnFirstDemuxFailed(TrackInfo::TrackType aType, 3521 const MediaResult& aError) { 3522 MOZ_ASSERT(OnTaskQueue()); 3523 3524 if (mShutdown) { 3525 return; 3526 } 3527 3528 auto& decoder = GetDecoderData(aType); 3529 MOZ_ASSERT(decoder.mFirstDemuxedSampleTime.isNothing()); 3530 decoder.mFirstDemuxedSampleTime.emplace(TimeUnit::FromInfinity()); 3531 MaybeResolveMetadataPromise(); 3532 } 3533 3534 void MediaFormatReader::SetEncryptedCustomIdent() { 3535 LOG("Set mEncryptedCustomIdent"); 3536 mEncryptedCustomIdent = true; 3537 } 3538 3539 void MediaFormatReader::VideoDecodeProperties::Load( 3540 RefPtr<MediaDataDecoder>& aDecoder) { 3541 using V = MediaDataDecoder::PropertyValue; 3542 aDecoder 3543 ->GetDecodeProperty(MediaDataDecoder::PropertyName::MaxNumVideoBuffers) 3544 .apply([this](const V& v) { mMaxQueueSize = Some(v.as<uint32_t>()); }); 3545 aDecoder 3546 ->GetDecodeProperty(MediaDataDecoder::PropertyName::MinNumVideoBuffers) 3547 .apply([this](const V& v) { mMinQueueSize = Some(v.as<uint32_t>()); }); 3548 aDecoder 3549 ->GetDecodeProperty(MediaDataDecoder::PropertyName::MaxNumCurrentImages) 3550 .apply([this](const V& v) { 3551 mSendToCompositorSize = Some(v.as<uint32_t>()); 3552 }); 3553 } 3554 3555 } // namespace mozilla 3556 3557 #undef NS_DispatchToMainThread 3558 #undef LOGV 3559 #undef LOG