MediaSourceDemuxer.cpp (19778B)
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 "MediaSourceDemuxer.h" 8 9 #include <stdint.h> 10 11 #include <algorithm> 12 13 #include "MediaSourceUtils.h" 14 #include "SourceBufferList.h" 15 #include "VideoUtils.h" 16 #include "nsPrintfCString.h" 17 18 extern mozilla::LogModule* GetMediaSourceLog(); 19 20 #define MSE_DEBUG(arg, ...) \ 21 DDMOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, "::%s: " arg, \ 22 __func__, ##__VA_ARGS__) 23 24 namespace mozilla { 25 26 typedef TrackInfo::TrackType TrackType; 27 using media::TimeIntervals; 28 using media::TimeUnit; 29 30 MediaSourceDemuxer::MediaSourceDemuxer(AbstractThread* aAbstractMainThread) 31 : mTaskQueue( 32 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR), 33 "MediaSourceDemuxer::mTaskQueue")), 34 mMutex("MediaSourceDemuxer") { 35 MOZ_ASSERT(NS_IsMainThread()); 36 } 37 38 constexpr TimeUnit MediaSourceDemuxer::EOS_FUZZ; 39 constexpr TimeUnit MediaSourceDemuxer::EOS_FUZZ_START; 40 41 RefPtr<MediaSourceDemuxer::InitPromise> MediaSourceDemuxer::Init() { 42 RefPtr<MediaSourceDemuxer> self = this; 43 return InvokeAsync(GetTaskQueue(), __func__, [self]() { 44 if (self->ScanSourceBuffersForContent()) { 45 return InitPromise::CreateAndResolve(NS_OK, __func__); 46 } 47 48 RefPtr<InitPromise> p = self->mInitPromise.Ensure(__func__); 49 50 return p; 51 }); 52 } 53 54 void MediaSourceDemuxer::AddSizeOfResources( 55 MediaSourceDecoder::ResourceSizes* aSizes) { 56 MOZ_ASSERT(NS_IsMainThread()); 57 58 // NB: The track buffers must only be accessed on the TaskQueue. 59 RefPtr<MediaSourceDemuxer> self = this; 60 RefPtr<MediaSourceDecoder::ResourceSizes> sizes = aSizes; 61 nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction( 62 "MediaSourceDemuxer::AddSizeOfResources", [self, sizes]() { 63 for (const RefPtr<TrackBuffersManager>& manager : 64 self->mSourceBuffers) { 65 manager->AddSizeOfResources(sizes); 66 } 67 }); 68 69 nsresult rv = GetTaskQueue()->Dispatch(task.forget()); 70 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 71 (void)rv; 72 } 73 74 void MediaSourceDemuxer::NotifyInitDataArrived() { 75 RefPtr<MediaSourceDemuxer> self = this; 76 nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction( 77 "MediaSourceDemuxer::NotifyInitDataArrived", [self]() { 78 if (self->mInitPromise.IsEmpty()) { 79 return; 80 } 81 if (self->ScanSourceBuffersForContent()) { 82 self->mInitPromise.ResolveIfExists(NS_OK, __func__); 83 } 84 }); 85 nsresult rv = GetTaskQueue()->Dispatch(task.forget()); 86 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 87 (void)rv; 88 } 89 90 bool MediaSourceDemuxer::ScanSourceBuffersForContent() { 91 MOZ_ASSERT(OnTaskQueue()); 92 93 if (mSourceBuffers.IsEmpty()) { 94 return false; 95 } 96 97 MutexAutoLock mon(mMutex); 98 99 bool haveEmptySourceBuffer = false; 100 for (const auto& sourceBuffer : mSourceBuffers) { 101 MediaInfo info = sourceBuffer->GetMetadata(); 102 if (!info.HasAudio() && !info.HasVideo()) { 103 haveEmptySourceBuffer = true; 104 } 105 if (info.HasAudio() && !mAudioTrack) { 106 mInfo.mAudio = info.mAudio; 107 mAudioTrack = sourceBuffer; 108 } 109 if (info.HasVideo() && !mVideoTrack) { 110 mInfo.mVideo = info.mVideo; 111 mVideoTrack = sourceBuffer; 112 } 113 if (info.IsEncrypted() && !mInfo.IsEncrypted()) { 114 mInfo.mCrypto = info.mCrypto; 115 } 116 } 117 if (mInfo.HasAudio() && mInfo.HasVideo()) { 118 // We have both audio and video. We can ignore non-ready source buffer. 119 return true; 120 } 121 return !haveEmptySourceBuffer; 122 } 123 124 uint32_t MediaSourceDemuxer::GetNumberTracks(TrackType aType) const { 125 MutexAutoLock mon(mMutex); 126 127 switch (aType) { 128 case TrackType::kAudioTrack: 129 return mInfo.HasAudio() ? 1u : 0; 130 case TrackType::kVideoTrack: 131 return mInfo.HasVideo() ? 1u : 0; 132 default: 133 return 0; 134 } 135 } 136 137 already_AddRefed<MediaTrackDemuxer> MediaSourceDemuxer::GetTrackDemuxer( 138 TrackType aType, uint32_t aTrackNumber) { 139 MutexAutoLock mon(mMutex); 140 RefPtr<TrackBuffersManager> manager = GetManager(aType); 141 if (!manager) { 142 return nullptr; 143 } 144 RefPtr<MediaSourceTrackDemuxer> e = 145 new MediaSourceTrackDemuxer(this, aType, manager); 146 DDLINKCHILD("track demuxer", e.get()); 147 mDemuxers.AppendElement(e); 148 return e.forget(); 149 } 150 151 bool MediaSourceDemuxer::IsSeekable() const { return true; } 152 153 UniquePtr<EncryptionInfo> MediaSourceDemuxer::GetCrypto() { 154 MutexAutoLock mon(mMutex); 155 auto crypto = MakeUnique<EncryptionInfo>(); 156 *crypto = mInfo.mCrypto; 157 return crypto; 158 } 159 160 void MediaSourceDemuxer::AttachSourceBuffer( 161 const RefPtr<TrackBuffersManager>& aSourceBuffer) { 162 nsCOMPtr<nsIRunnable> task = NewRunnableMethod<RefPtr<TrackBuffersManager>&&>( 163 "MediaSourceDemuxer::DoAttachSourceBuffer", this, 164 &MediaSourceDemuxer::DoAttachSourceBuffer, aSourceBuffer); 165 nsresult rv = GetTaskQueue()->Dispatch(task.forget()); 166 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 167 (void)rv; 168 } 169 170 void MediaSourceDemuxer::DoAttachSourceBuffer( 171 RefPtr<mozilla::TrackBuffersManager>&& aSourceBuffer) { 172 MOZ_ASSERT(OnTaskQueue()); 173 mSourceBuffers.AppendElement(std::move(aSourceBuffer)); 174 ScanSourceBuffersForContent(); 175 } 176 177 void MediaSourceDemuxer::DetachSourceBuffer( 178 const RefPtr<TrackBuffersManager>& aSourceBuffer) { 179 nsCOMPtr<nsIRunnable> task = 180 NS_NewRunnableFunction("MediaSourceDemuxer::DoDetachSourceBuffer", 181 [self = RefPtr{this}, aSourceBuffer]() { 182 self->DoDetachSourceBuffer(aSourceBuffer); 183 }); 184 nsresult rv = GetTaskQueue()->Dispatch(task.forget()); 185 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 186 (void)rv; 187 } 188 189 void MediaSourceDemuxer::DoDetachSourceBuffer( 190 const RefPtr<TrackBuffersManager>& aSourceBuffer) { 191 MOZ_ASSERT(OnTaskQueue()); 192 mSourceBuffers.RemoveElementsBy( 193 [&aSourceBuffer](const RefPtr<TrackBuffersManager> aLinkedSourceBuffer) { 194 return aLinkedSourceBuffer == aSourceBuffer; 195 }); 196 197 AutoTArray<RefPtr<MediaSourceTrackDemuxer>, 2> matchingDemuxers; 198 { 199 MutexAutoLock mon(mMutex); 200 if (aSourceBuffer == mAudioTrack) { 201 mAudioTrack = nullptr; 202 } 203 if (aSourceBuffer == mVideoTrack) { 204 mVideoTrack = nullptr; 205 } 206 207 mDemuxers.RemoveElementsBy( 208 [&](RefPtr<MediaSourceTrackDemuxer>& elementRef) { 209 if (!elementRef->HasManager(aSourceBuffer)) { 210 return false; 211 } 212 matchingDemuxers.AppendElement(std::move(elementRef)); 213 return true; 214 }); 215 } 216 217 for (MediaSourceTrackDemuxer* demuxer : matchingDemuxers) { 218 demuxer->DetachManager(); 219 } 220 ScanSourceBuffersForContent(); 221 } 222 223 TrackInfo* MediaSourceDemuxer::GetTrackInfo(TrackType aTrack) { 224 switch (aTrack) { 225 case TrackType::kAudioTrack: 226 return &mInfo.mAudio; 227 case TrackType::kVideoTrack: 228 return &mInfo.mVideo; 229 default: 230 return nullptr; 231 } 232 } 233 234 RefPtr<TrackBuffersManager> MediaSourceDemuxer::GetManager(TrackType aTrack) { 235 switch (aTrack) { 236 case TrackType::kAudioTrack: 237 return mAudioTrack; 238 case TrackType::kVideoTrack: 239 return mVideoTrack; 240 default: 241 return nullptr; 242 } 243 } 244 245 MediaSourceDemuxer::~MediaSourceDemuxer() { 246 mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); 247 } 248 249 RefPtr<GenericPromise> MediaSourceDemuxer::GetDebugInfo( 250 dom::MediaSourceDemuxerDebugInfo& aInfo) const { 251 MutexAutoLock mon(mMutex); 252 nsTArray<RefPtr<GenericPromise>> promises; 253 if (mAudioTrack) { 254 promises.AppendElement(mAudioTrack->RequestDebugInfo(aInfo.mAudioTrack)); 255 } 256 if (mVideoTrack) { 257 promises.AppendElement(mVideoTrack->RequestDebugInfo(aInfo.mVideoTrack)); 258 } 259 return GenericPromise::All(GetCurrentSerialEventTarget(), promises) 260 ->Then( 261 GetCurrentSerialEventTarget(), __func__, 262 []() { return GenericPromise::CreateAndResolve(true, __func__); }, 263 [] { 264 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 265 }); 266 } 267 268 MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent, 269 TrackInfo::TrackType aType, 270 TrackBuffersManager* aManager) 271 : mParent(aParent), 272 mType(aType), 273 mLock("MediaSourceTrackDemuxer", mParent->GetTaskQueue()), 274 mManager(aManager), 275 mReset(true), 276 mPreRoll(TimeUnit::FromMicroseconds( 277 mParent->GetTrackInfo(mType)->mMimeType.EqualsLiteral("audio/opus") || 278 mParent->GetTrackInfo(mType)->mMimeType.EqualsLiteral( 279 "audio/vorbis") 280 ? 80000 281 : mParent->GetTrackInfo(mType)->mMimeType.EqualsLiteral( 282 "audio/mp4a-latm") 283 // AAC encoder delay is by default 2112 audio frames. 284 // See 285 // https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFAppenG/QTFFAppenG.html 286 // So we always seek 2112 frames 287 ? (2112 * 1000000ULL / 288 mParent->GetTrackInfo(mType)->GetAsAudioInfo()->mRate) 289 : 0)) { 290 MOZ_ASSERT(mParent); 291 MOZ_ASSERT(mLock.Target().GetEventTarget()); 292 } 293 294 UniquePtr<TrackInfo> MediaSourceTrackDemuxer::GetInfo() const { 295 MutexAutoLock mon(mParent->mMutex); 296 return mParent->GetTrackInfo(mType)->Clone(); 297 } 298 299 RefPtr<MediaSourceTrackDemuxer::SeekPromise> MediaSourceTrackDemuxer::Seek( 300 const TimeUnit& aTime) { 301 MOZ_ASSERT(mParent, "Called after BreackCycle()"); 302 return InvokeAsync(mParent->GetTaskQueue(), this, __func__, 303 &MediaSourceTrackDemuxer::DoSeek, aTime); 304 } 305 306 RefPtr<MediaSourceTrackDemuxer::SamplesPromise> 307 MediaSourceTrackDemuxer::GetSamples(int32_t aNumSamples) { 308 MOZ_ASSERT(mParent, "Called after BreackCycle()"); 309 return InvokeAsync(mParent->GetTaskQueue(), this, __func__, 310 &MediaSourceTrackDemuxer::DoGetSamples, aNumSamples); 311 } 312 313 void MediaSourceTrackDemuxer::Reset() { 314 MOZ_ASSERT(mParent, "Called after BreackCycle()"); 315 RefPtr<MediaSourceTrackDemuxer> self = this; 316 nsCOMPtr<nsIRunnable> task = 317 NS_NewRunnableFunction("MediaSourceTrackDemuxer::Reset", [self]() { 318 self->TaskQueue().AssertOnCurrentThread(); 319 self->mLock.NoteOnTarget(); 320 321 self->mNextSample.reset(); 322 self->mReset = true; 323 if (!self->mManager) { 324 return; 325 } 326 self->mManager->Seek(self->mType, TimeUnit::Zero(), TimeUnit::Zero()); 327 { 328 MutexAutoLock lock(self->Mutex()); 329 self->mLock.ClearCurrentAccess(); 330 self->mLock.NoteExclusiveAccess(); 331 self->mNextRandomAccessPoint = 332 self->mManager->GetNextRandomAccessPoint( 333 self->mType, MediaSourceDemuxer::EOS_FUZZ); 334 } 335 }); 336 nsresult rv = mParent->GetTaskQueue()->Dispatch(task.forget()); 337 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 338 (void)rv; 339 } 340 341 nsresult MediaSourceTrackDemuxer::GetNextRandomAccessPoint(TimeUnit* aTime) { 342 MutexAutoLock lock(Mutex()); 343 mLock.NoteLockHeld(); 344 *aTime = mNextRandomAccessPoint; 345 return NS_OK; 346 } 347 348 RefPtr<MediaSourceTrackDemuxer::SkipAccessPointPromise> 349 MediaSourceTrackDemuxer::SkipToNextRandomAccessPoint( 350 const TimeUnit& aTimeThreshold) { 351 return InvokeAsync(mParent->GetTaskQueue(), this, __func__, 352 &MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint, 353 aTimeThreshold); 354 } 355 356 media::TimeIntervals MediaSourceTrackDemuxer::GetBuffered() { 357 MutexAutoLock lock(Mutex()); 358 mLock.NoteLockHeld(); 359 if (!mManager) { 360 return media::TimeIntervals(); 361 } 362 return mManager->Buffered(); 363 } 364 365 void MediaSourceTrackDemuxer::BreakCycles() { 366 RefPtr<MediaSourceTrackDemuxer> self = this; 367 nsCOMPtr<nsIRunnable> task = 368 NS_NewRunnableFunction("MediaSourceTrackDemuxer::BreakCycles", [self]() { 369 self->DetachManager(); 370 self->mParent = nullptr; 371 }); 372 nsresult rv = mParent->GetTaskQueue()->Dispatch(task.forget()); 373 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); 374 (void)rv; 375 } 376 377 RefPtr<MediaSourceTrackDemuxer::SeekPromise> MediaSourceTrackDemuxer::DoSeek( 378 const TimeUnit& aTime) { 379 TaskQueue().AssertOnCurrentThread(); 380 mLock.NoteOnTarget(); 381 382 if (!mManager) { 383 return SeekPromise::CreateAndReject( 384 MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, 385 RESULT_DETAIL("manager is detached.")), 386 __func__); 387 } 388 389 TimeIntervals buffered = mManager->Buffered(mType); 390 // Fuzz factor represents a +/- threshold. So when seeking it allows the gap 391 // to be twice as big as the fuzz value. We only want to allow EOS_FUZZ gap. 392 buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); 393 TimeUnit seekTime = std::max(aTime - mPreRoll, TimeUnit::Zero()); 394 395 if (mManager->HaveAllData() && seekTime >= buffered.GetEnd()) { 396 // We're attempting to seek past the end time. Cap seekTime so that we seek 397 // to the last sample instead. 398 seekTime = std::max(mManager->HighestStartTime(mType) - mPreRoll, 399 TimeUnit::Zero()); 400 } 401 402 MSE_DEBUG("DoSeek, original target=%" PRId64 "%s, seekTime=%" PRId64 403 "%s, buffered=%s", 404 aTime.ToMicroseconds(), aTime.ToString().get(), 405 seekTime.ToMicroseconds(), seekTime.ToString().get(), 406 DumpTimeRanges(buffered).get()); 407 if (!buffered.ContainsWithStrictEnd(seekTime)) { 408 if (!buffered.ContainsWithStrictEnd(aTime)) { 409 // Target isn't in the buffered range, so we can perform an eviction if 410 // needed. 411 mManager->EvictDataWithoutSize(mType, seekTime); 412 // We don't have the data to seek to. 413 return SeekPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, 414 __func__); 415 } 416 // Theoretically we should reject the promise with WAITING_FOR_DATA, 417 // however, to avoid unwanted regressions we assume that if at this time 418 // we don't have the wanted data it won't come later. 419 // Instead of using the pre-rolled time, use the earliest time available in 420 // the interval. 421 TimeIntervals::IndexType index = buffered.Find(aTime); 422 MOZ_ASSERT(index != TimeIntervals::NoIndex); 423 MSE_DEBUG("Can't find seekTime %" PRId64 424 " in the buffer range, use the earliest time %" PRId64, 425 seekTime.ToMicroseconds(), 426 buffered[index].mStart.ToMicroseconds()); 427 seekTime = buffered[index].mStart; 428 } 429 seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ); 430 MediaResult result = NS_OK; 431 RefPtr<MediaRawData> sample = 432 mManager->GetSample(mType, TimeUnit::Zero(), result); 433 MOZ_ASSERT(NS_SUCCEEDED(result) && sample); 434 if (sample) { 435 mNextSample = Some(sample); 436 } 437 mReset = false; 438 { 439 MutexAutoLock lock(Mutex()); 440 mLock.ClearCurrentAccess(); 441 mLock.NoteExclusiveAccess(); 442 mNextRandomAccessPoint = 443 mManager->GetNextRandomAccessPoint(mType, MediaSourceDemuxer::EOS_FUZZ); 444 } 445 return SeekPromise::CreateAndResolve(seekTime, __func__); 446 } 447 448 RefPtr<MediaSourceTrackDemuxer::SamplesPromise> 449 MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples) { 450 TaskQueue().AssertOnCurrentThread(); 451 mLock.NoteOnTarget(); 452 if (!mManager) { 453 return SamplesPromise::CreateAndReject( 454 MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, 455 RESULT_DETAIL("manager is detached.")), 456 __func__); 457 } 458 459 if (mReset) { 460 // If a reset was recently performed, we ensure that the data 461 // we are about to retrieve is still available. 462 TimeIntervals buffered = mManager->Buffered(mType); 463 if (buffered.IsEmpty() && mManager->HaveAllData()) { 464 return SamplesPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_END_OF_STREAM, 465 __func__); 466 } 467 468 // We use a larger fuzz to determine the presentation start 469 // time than the fuzz we use to determine acceptable gaps between 470 // frames. This is needed to fix embedded video issues as seen in the wild 471 // from different muxed stream start times. 472 // See: https://www.w3.org/TR/media-source-2/#presentation-start-time 473 buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ_START); 474 if (!buffered.ContainsWithStrictEnd(TimeUnit::Zero())) { 475 return SamplesPromise::CreateAndReject( 476 NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__); 477 } 478 mReset = false; 479 } 480 RefPtr<MediaRawData> sample; 481 if (mNextSample) { 482 sample = mNextSample.ref(); 483 mNextSample.reset(); 484 } else { 485 MediaResult result = NS_OK; 486 sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, result); 487 if (!sample) { 488 if (result == NS_ERROR_DOM_MEDIA_END_OF_STREAM || 489 result == NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) { 490 return SamplesPromise::CreateAndReject( 491 (result == NS_ERROR_DOM_MEDIA_END_OF_STREAM && 492 mManager->HaveAllData()) 493 ? NS_ERROR_DOM_MEDIA_END_OF_STREAM 494 : NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, 495 __func__); 496 } 497 return SamplesPromise::CreateAndReject(result, __func__); 498 } 499 } 500 MOZ_DIAGNOSTIC_ASSERT(sample); 501 { 502 MutexAutoLock lock(Mutex()); 503 mLock.ClearCurrentAccess(); 504 mLock.NoteExclusiveAccess(); 505 // Diagnostic asserts for bug 1810396 506 MOZ_DIAGNOSTIC_ASSERT(sample, "Invalid sample pointer found!"); 507 MOZ_DIAGNOSTIC_ASSERT(sample->HasValidTime(), "Invalid sample time found!"); 508 if (!sample) { 509 return SamplesPromise::CreateAndReject(NS_ERROR_NULL_POINTER, __func__); 510 } 511 if (mNextRandomAccessPoint <= sample->mTime) { 512 mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint( 513 mType, MediaSourceDemuxer::EOS_FUZZ); 514 } 515 } 516 RefPtr<SamplesHolder> samples = new SamplesHolder; 517 samples->AppendSample(std::move(sample)); 518 return SamplesPromise::CreateAndResolve(samples, __func__); 519 } 520 521 RefPtr<MediaSourceTrackDemuxer::SkipAccessPointPromise> 522 MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint( 523 const TimeUnit& aTimeThreadshold) { 524 TaskQueue().AssertOnCurrentThread(); 525 mLock.NoteOnTarget(); 526 527 if (!mManager) { 528 return SkipAccessPointPromise::CreateAndReject( 529 SkipFailureHolder(MediaResult(NS_ERROR_DOM_MEDIA_CANCELED, 530 RESULT_DETAIL("manager is detached.")), 531 0), 532 __func__); 533 } 534 535 uint32_t parsed = 0; 536 // Ensure that the data we are about to skip to is still available. 537 TimeIntervals buffered = mManager->Buffered(mType); 538 buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ / 2); 539 if (buffered.ContainsWithStrictEnd(aTimeThreadshold)) { 540 bool found; 541 parsed = mManager->SkipToNextRandomAccessPoint( 542 mType, aTimeThreadshold, MediaSourceDemuxer::EOS_FUZZ, found); 543 if (found) { 544 return SkipAccessPointPromise::CreateAndResolve(parsed, __func__); 545 } 546 } 547 SkipFailureHolder holder(mManager->HaveAllData() 548 ? NS_ERROR_DOM_MEDIA_END_OF_STREAM 549 : NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, 550 parsed); 551 return SkipAccessPointPromise::CreateAndReject(holder, __func__); 552 } 553 554 bool MediaSourceTrackDemuxer::HasManager(TrackBuffersManager* aManager) const { 555 TaskQueue().AssertOnCurrentThread(); 556 mLock.NoteOnTarget(); 557 return mManager == aManager; 558 } 559 560 void MediaSourceTrackDemuxer::DetachManager() { 561 TaskQueue().AssertOnCurrentThread(); 562 MutexAutoLock lock(Mutex()); 563 mLock.NoteExclusiveAccess(); 564 mManager = nullptr; 565 } 566 567 #undef MSE_DEBUG 568 569 } // namespace mozilla