tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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