Bug 1326372 - P2. Backed out changeset 19c468c32d03 bug 1319995-P1 - r=jya a=abillings
authorGerald Squelart <gsquelart@squelart.com>
Wed, 15 Feb 2017 09:23:15 +1100
changeset 378467 b58ee06413e1a365c40f90ab1d1ca7d5db1916c0
parent 378466 0dab61b1573fd15a2a6fc80cd773cd1dc5cd0915
child 378468 df0a213dad6c60547835b26e980deb2e4ad8c250
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, abillings
bugs1326372, 1319995
milestone53.0a2
Bug 1326372 - P2. Backed out changeset 19c468c32d03 bug 1319995-P1 - r=jya a=abillings Original commit description: Run demuxing operations on its own task queue. We runs all demuxing operations on a dedicated task queue. MediaDataDemuxer's members using a synchronous API are handled via thread-safe copy that are updated along the operations. The buffered range calculation is now handled separately and the entire operation is made asynchronous.
dom/media/MediaDecoderReader.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -190,20 +190,21 @@ public:
   // the audio queue.
   size_t SizeOfAudioQueueInBytes() const;
 
   virtual size_t SizeOfVideoQueueInFrames();
   virtual size_t SizeOfAudioQueueInFrames();
 
   // Called once new data has been cached by the MediaResource.
   // mBuffered should be recalculated and updated accordingly.
-  virtual void NotifyDataArrived()
+  void NotifyDataArrived()
   {
     MOZ_ASSERT(OnTaskQueue());
     NS_ENSURE_TRUE_VOID(!mShutdown);
+    NotifyDataArrivedInternal();
     UpdateBuffered();
   }
 
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
   {
@@ -249,16 +250,34 @@ public:
   virtual void SetVideoBlankDecode(bool aIsBlankDecode) {}
 
 protected:
   virtual ~MediaDecoderReader();
 
   // Recomputes mBuffered.
   virtual void UpdateBuffered();
 
+  // Populates aBuffered with the time ranges which are buffered. This may only
+  // be called on the decode task queue, and should only be used internally by
+  // UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
+  // else.
+  //
+  // This base implementation in MediaDecoderReader estimates the time ranges
+  // buffered by interpolating the cached byte ranges with the duration
+  // of the media. Reader subclasses should override this method if they
+  // can quickly calculate the buffered ranges more accurately.
+  //
+  // The primary advantage of this implementation in the reader base class
+  // is that it's a fast approximation, which does not perform any I/O.
+  //
+  // The OggReader relies on this base implementation not performing I/O,
+  // since in FirefoxOS we can't do I/O on the main thread, where this is
+  // called.
+  virtual media::TimeIntervals GetBuffered();
+
   RefPtr<MediaDataPromise> DecodeToFirstVideoData();
 
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<AudioData> mAudioQueue;
 
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
@@ -321,16 +340,18 @@ private:
   // Returns NS_OK on success, or NS_ERROR_FAILURE on failure.
   virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
   {
     MOZ_CRASH();
   }
 
   virtual void VisibilityChanged();
 
+  virtual void NotifyDataArrivedInternal() {}
+
   // Overrides of this function should decodes an unspecified amount of
   // audio data, enqueuing the audio data in mAudioQueue. Returns true
   // when there's more audio to decode, false if the audio is finished,
   // end of file has been reached, or an un-recoverable read error has
   // occured. This function blocks until the decode is complete.
   virtual bool DecodeAudioData()
   {
     return false;
@@ -341,25 +362,16 @@ private:
   // (unless they're not keyframes and aKeyframeSkip is true), but will
   // not be added to the queue. This function blocks until the decode
   // is complete.
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold)
   {
     return false;
   }
 
-  // GetBuffered estimates the time ranges buffered by interpolating the cached
-  // byte ranges with the duration of the media. Reader subclasses should
-  // override this method if they can quickly calculate the buffered ranges more
-  // accurately.
-  //
-  // The primary advantage of this implementation in the reader base class is
-  // that it's a fast approximation, which does not perform any I/O.
-  media::TimeIntervals GetBuffered();
-
   // Promises used only for the base-class (sync->async adapter) implementation
   // of Request{Audio,Video}Data.
   MozPromiseHolder<MediaDataPromise> mBaseAudioPromise;
   MozPromiseHolder<MediaDataPromise> mBaseVideoPromise;
 
   MediaEventListener mDataArrivedListener;
 };
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1,36 +1,32 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "AutoTaskQueue.h"
+#include "mozilla/CDMProxy.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "nsContentUtils.h"
+#include "nsPrintfCString.h"
+#include "nsSize.h"
 #include "Layers.h"
 #include "MediaData.h"
 #include "MediaInfo.h"
 #include "MediaFormatReader.h"
 #include "MediaPrefs.h"
 #include "MediaResource.h"
+#include "mozilla/SharedThreadPool.h"
 #include "VideoUtils.h"
 #include "VideoFrameContainer.h"
-#include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/layers/ShadowLayers.h"
-#include "mozilla/AbstractThread.h"
-#include "mozilla/CDMProxy.h"
-#include "mozilla/ClearOnShutdown.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/Telemetry.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/SharedThreadPool.h"
-#include "mozilla/SyncRunnable.h"
-#include "nsContentUtils.h"
-#include "nsPrintfCString.h"
-#include "nsSize.h"
 
 #include <algorithm>
 #include <queue>
 
 using namespace mozilla::media;
 
 using mozilla::layers::Image;
 using mozilla::layers::LayerManager;
@@ -427,350 +423,16 @@ MediaFormatReader::DecoderFactory::DoIni
       data.mInitPromise.Complete();
       data.mStage = Stage::None;
       data.mDecoder->Shutdown();
       data.mDecoder = nullptr;
       mOwner->NotifyError(aTrack, aError);
     })->Track(data.mInitPromise);
 }
 
-// DemuxerProxy ensures that the original main demuxer is only ever accessed
-// via its own dedicated task queue.
-// This ensure that the reader's taskqueue will never blocked while a demuxer
-// is itself blocked attempting to access the MediaCache or the MediaResource.
-class MediaFormatReader::DemuxerProxy
-{
-  using TrackType = TrackInfo::TrackType;
-  class Wrapper;
-
-public:
-  explicit DemuxerProxy(MediaDataDemuxer* aDemuxer, AbstractThread* mainThread)
-    : mTaskQueue(new AutoTaskQueue(
-                   GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
-                   mainThread))
-    , mData(new Data(aDemuxer))
-  {
-    MOZ_COUNT_CTOR(DemuxerProxy);
-  }
-
-  ~DemuxerProxy()
-  {
-    MOZ_COUNT_DTOR(DemuxerProxy);
-    mData->mAudioDemuxer = nullptr;
-    mData->mVideoDemuxer = nullptr;
-    RefPtr<Data> data = mData.forget();
-    mTaskQueue->Dispatch(
-      // We need to clear our reference to the demuxer now. So that in the event
-      // the init promise wasn't resolved, such as what can happen with the
-      // mediasource demuxer that is waiting on more data, it will force the
-      // init promise to be rejected.
-      NS_NewRunnableFunction([data]() { data->mDemuxer = nullptr; }));
-  }
-
-  RefPtr<MediaDataDemuxer::InitPromise> Init();
-
-  Wrapper*
-  GetTrackDemuxer(TrackType aTrack, uint32_t aTrackNumber)
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    switch (aTrack) {
-      case TrackInfo::kAudioTrack:
-        return mData->mAudioDemuxer;
-      case TrackInfo::kVideoTrack:
-        return mData->mVideoDemuxer;
-      default:
-        return nullptr;
-    }
-  }
-
-  uint32_t GetNumberTracks(TrackType aTrack) const
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    switch (aTrack) {
-      case TrackInfo::kAudioTrack:
-        return mData->mNumAudioTrack;
-      case TrackInfo::kVideoTrack:
-        return mData->mNumVideoTrack;
-      default:
-        return 0;
-    }
-  }
-
-  bool IsSeekable() const
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    return mData->mSeekable;
-  }
-
-  bool IsSeekableOnlyInBufferedRanges() const
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    return mData->mSeekableOnlyInBufferedRange;
-  }
-
-  UniquePtr<EncryptionInfo> GetCrypto() const
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    if (!mData->mCrypto) {
-      return nullptr;
-    }
-    auto crypto = MakeUnique<EncryptionInfo>();
-    *crypto = *mData->mCrypto;
-    return crypto;
-  }
-
-  RefPtr<NotifyDataArrivedPromise> NotifyDataArrived();
-
-  bool ShouldComputeStartTime() const
-  {
-    MOZ_RELEASE_ASSERT(mData && mData->mInitDone);
-
-    return mData->mShouldComputeStartTime;
-  }
-
-private:
-  const RefPtr<AutoTaskQueue> mTaskQueue;
-  struct Data
-  {
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Data)
-
-    explicit Data(MediaDataDemuxer* aDemuxer)
-      : mInitDone(false)
-      , mDemuxer(aDemuxer)
-    { }
-
-    Atomic<bool> mInitDone;
-    // Only ever accessed over mTaskQueue once.
-    RefPtr<MediaDataDemuxer> mDemuxer;
-    // Only accessed once InitPromise has been resolved and immutable after.
-    // So we can safely access them without the use of the mutex.
-    uint32_t mNumAudioTrack = 0;
-    RefPtr<Wrapper> mAudioDemuxer;
-    uint32_t mNumVideoTrack = 0;
-    RefPtr<Wrapper> mVideoDemuxer;
-    bool mSeekable = false;
-    bool mSeekableOnlyInBufferedRange = false;
-    bool mShouldComputeStartTime = true;
-    UniquePtr<EncryptionInfo> mCrypto;
-  private:
-    ~Data() { }
-  };
-  RefPtr<Data> mData;
-};
-
-class MediaFormatReader::DemuxerProxy::Wrapper : public MediaTrackDemuxer
-{
-public:
-  Wrapper(MediaTrackDemuxer* aTrackDemuxer, AutoTaskQueue* aTaskQueue)
-    : mMutex("TrackDemuxer Mutex")
-    , mTaskQueue(aTaskQueue)
-    , mGetSamplesMayBlock(aTrackDemuxer->GetSamplesMayBlock())
-    , mInfo(aTrackDemuxer->GetInfo())
-    , mTrackDemuxer(aTrackDemuxer)
-  { }
-
-  UniquePtr<TrackInfo> GetInfo() const override
-  {
-    if (!mInfo) {
-      return nullptr;
-    }
-    return mInfo->Clone();
-  }
-
-  RefPtr<SeekPromise> Seek(const media::TimeUnit& aTime) override
-  {
-    RefPtr<Wrapper> self = this;
-    return InvokeAsync(
-             mTaskQueue, __func__,
-             [self, aTime]() { return self->mTrackDemuxer->Seek(aTime); })
-      ->Then(mTaskQueue, __func__,
-             [self]() { self->UpdateRandomAccessPoint(); },
-             [self]() { self->UpdateRandomAccessPoint(); });
-  }
-
-  RefPtr<SamplesPromise> GetSamples(int32_t aNumSamples) override
-  {
-    RefPtr<Wrapper> self = this;
-    return InvokeAsync(mTaskQueue, __func__,
-                       [self, aNumSamples]() {
-                         return self->mTrackDemuxer->GetSamples(aNumSamples);
-                       })
-      ->Then(mTaskQueue, __func__,
-             [self]() { self->UpdateRandomAccessPoint(); },
-             [self]() { self->UpdateRandomAccessPoint(); });
-  }
-
-  bool GetSamplesMayBlock() const override
-  {
-    return mGetSamplesMayBlock;
-  }
-
-  void Reset() override
-  {
-    RefPtr<Wrapper> self = this;
-    mTaskQueue->Dispatch(NS_NewRunnableFunction([self]() {
-      self->mTrackDemuxer->Reset();
-    }));
-  }
-
-  nsresult GetNextRandomAccessPoint(TimeUnit* aTime) override
-  {
-    MutexAutoLock lock(mMutex);
-    if (NS_SUCCEEDED(mNextRandomAccessPointResult)) {
-      *aTime = mNextRandomAccessPoint;
-    }
-    return mNextRandomAccessPointResult;
-  }
-
-  RefPtr<SkipAccessPointPromise>
-  SkipToNextRandomAccessPoint(const media::TimeUnit& aTimeThreshold) override
-  {
-    RefPtr<Wrapper> self = this;
-    return InvokeAsync(
-             mTaskQueue, __func__,
-             [self, aTimeThreshold]()  {
-               return self->mTrackDemuxer->SkipToNextRandomAccessPoint(
-                 aTimeThreshold);
-             })
-      ->Then(mTaskQueue, __func__,
-             [self]() { self->UpdateRandomAccessPoint(); },
-             [self]() { self->UpdateRandomAccessPoint(); });
-  }
-
-  TimeIntervals GetBuffered() override
-  {
-    MutexAutoLock lock(mMutex);
-    return mBuffered;
-  }
-
-  void BreakCycles() override { }
-
-private:
-  Mutex mMutex;
-  const RefPtr<AutoTaskQueue> mTaskQueue;
-  const bool mGetSamplesMayBlock;
-  const UniquePtr<TrackInfo> mInfo;
-  // mTrackDemuxer is only ever accessed on demuxer's task queue.
-  RefPtr<MediaTrackDemuxer> mTrackDemuxer;
-  // All following members are protected by mMutex
-  nsresult mNextRandomAccessPointResult = NS_OK;
-  TimeUnit mNextRandomAccessPoint;
-  TimeIntervals mBuffered;
-  friend class DemuxerProxy;
-
-  ~Wrapper()
-  {
-    RefPtr<MediaTrackDemuxer> trackDemuxer = mTrackDemuxer.forget();
-    mTaskQueue->Dispatch(NS_NewRunnableFunction(
-      [trackDemuxer]() { trackDemuxer->BreakCycles(); }));
-  }
-
-  void UpdateRandomAccessPoint()
-  {
-    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-    if (!mTrackDemuxer) {
-      // Detached.
-      return;
-    }
-    MutexAutoLock lock(mMutex);
-    mNextRandomAccessPointResult =
-      mTrackDemuxer->GetNextRandomAccessPoint(&mNextRandomAccessPoint);
-  }
-
-  void UpdateBuffered()
-  {
-    MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-    if (!mTrackDemuxer) {
-      // Detached.
-      return;
-    }
-    MutexAutoLock lock(mMutex);
-    mBuffered = mTrackDemuxer->GetBuffered();
-  }
-};
-
-RefPtr<MediaDataDemuxer::InitPromise>
-MediaFormatReader::DemuxerProxy::Init()
-{
-  RefPtr<Data> data = mData;
-  RefPtr<AutoTaskQueue> taskQueue = mTaskQueue;
-  return InvokeAsync(mTaskQueue, __func__,
-                     [data, taskQueue]() {
-                       if (!data->mDemuxer) {
-                         return MediaDataDemuxer::InitPromise::CreateAndReject(
-                           NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-                       }
-                       return data->mDemuxer->Init();
-                     })
-    ->Then(taskQueue, __func__,
-           [data, taskQueue]() {
-             if (!data->mDemuxer) { // Was shutdown.
-               return;
-             }
-             data->mNumAudioTrack =
-               data->mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
-             if (data->mNumAudioTrack) {
-               RefPtr<MediaTrackDemuxer> d =
-                 data->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
-               if (d) {
-                 RefPtr<Wrapper> wrapper =
-                   new DemuxerProxy::Wrapper(d, taskQueue);
-                 wrapper->UpdateBuffered();
-                 data->mAudioDemuxer = wrapper;
-               }
-             }
-             data->mNumVideoTrack =
-               data->mDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
-             if (data->mNumVideoTrack) {
-               RefPtr<MediaTrackDemuxer> d =
-                 data->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
-               if (d) {
-                 RefPtr<Wrapper> wrapper =
-                   new DemuxerProxy::Wrapper(d, taskQueue);
-                 wrapper->UpdateBuffered();
-                 data->mVideoDemuxer = wrapper;
-               }
-             }
-             data->mCrypto = data->mDemuxer->GetCrypto();
-             data->mSeekable = data->mDemuxer->IsSeekable();
-             data->mSeekableOnlyInBufferedRange =
-               data->mDemuxer->IsSeekableOnlyInBufferedRanges();
-             data->mShouldComputeStartTime =
-               data->mDemuxer->ShouldComputeStartTime();
-             data->mInitDone = true;
-           },
-           []() {});
-}
-
-RefPtr<MediaFormatReader::NotifyDataArrivedPromise>
-MediaFormatReader::DemuxerProxy::NotifyDataArrived()
-{
-  RefPtr<Data> data = mData;
-  return InvokeAsync(mTaskQueue, __func__, [data]() {
-    if (!data->mDemuxer) {
-      // Was shutdown.
-      return NotifyDataArrivedPromise::CreateAndReject(
-        NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-    }
-    data->mDemuxer->NotifyDataArrived();
-    if (data->mAudioDemuxer) {
-      data->mAudioDemuxer->UpdateBuffered();
-    }
-    if (data->mVideoDemuxer) {
-      data->mVideoDemuxer->UpdateBuffered();
-    }
-    return NotifyDataArrivedPromise::CreateAndResolve(true, __func__);
-  });
-}
-
 static const char*
 TrackTypeToStr(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
              aTrack == TrackInfo::kVideoTrack ||
              aTrack == TrackInfo::kTextTrack);
   switch (aTrack) {
   case TrackInfo::kAudioTrack:
@@ -787,17 +449,17 @@ TrackTypeToStr(TrackInfo::TrackType aTra
 MediaFormatReader::MediaFormatReader(AbstractMediaDecoder* aDecoder,
                                      MediaDataDemuxer* aDemuxer,
                                      VideoFrameContainer* aVideoFrameContainer)
   : MediaDecoderReader(aDecoder)
   , mAudio(this, MediaData::AUDIO_DATA,
            Preferences::GetUint("media.audio-max-decode-error", 3))
   , mVideo(this, MediaData::VIDEO_DATA,
            Preferences::GetUint("media.video-max-decode-error", 2))
-  , mDemuxer(new DemuxerProxy(aDemuxer, aDecoder->AbstractMainThread()))
+  , mDemuxer(aDemuxer)
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mInitDone(false)
   , mTrackDemuxersMayBlock(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
   , mDecoderFactory(new DecoderFactory(this))
@@ -819,17 +481,16 @@ MediaFormatReader::~MediaFormatReader()
 
 RefPtr<ShutdownPromise>
 MediaFormatReader::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   mDecoderFactory = nullptr;
   mDemuxerInitRequest.DisconnectIfExists();
-  mNotifyDataArrivedPromise.DisconnectIfExists();
   mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSkipRequest.DisconnectIfExists();
 
   if (mAudio.mDecoder) {
     Reset(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
       mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
@@ -1025,16 +686,17 @@ MediaFormatReader::OnDemuxerInitDone(nsr
         return;
       }
       mInfo.mVideo = *videoInfo->GetAsVideoInfo();
       for (const MetadataTag& tag : videoInfo->mTags) {
         tags->Put(tag.mKey, tag.mValue);
       }
       mVideo.mOriginalInfo = Move(videoInfo);
       mVideo.mCallback = new DecoderCallback(this, TrackInfo::kVideoTrack);
+      mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered();
       mTrackDemuxersMayBlock |= mVideo.mTrackDemuxer->GetSamplesMayBlock();
     } else {
       mVideo.mTrackDemuxer->BreakCycles();
       mVideo.mTrackDemuxer = nullptr;
     }
   }
 
   bool audioActive = !!mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
@@ -1053,16 +715,17 @@ MediaFormatReader::OnDemuxerInitDone(nsr
 
     if (audioActive) {
       mInfo.mAudio = *audioInfo->GetAsAudioInfo();
       for (const MetadataTag& tag : audioInfo->mTags) {
         tags->Put(tag.mKey, tag.mValue);
       }
       mAudio.mOriginalInfo = Move(audioInfo);
       mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack);
+      mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
       mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
     } else {
       mAudio.mTrackDemuxer->BreakCycles();
       mAudio.mTrackDemuxer = nullptr;
     }
   }
 
   UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto();
@@ -1128,25 +791,22 @@ MediaFormatReader::MaybeResolveMetadataP
   TimeUnit startTime =
     std::min(mAudio.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity()),
              mVideo.mFirstDemuxedSampleTime.refOr(TimeUnit::FromInfinity()));
 
   if (!startTime.IsInfinite()) {
     mInfo.mStartTime = startTime; // mInfo.mStartTime is initialized to 0.
   }
 
+  mHasStartTime = true;
+  UpdateBuffered();
+
   RefPtr<MetadataHolder> metadata = new MetadataHolder();
   metadata->mInfo = mInfo;
   metadata->mTags = mTags->Count() ? mTags.release() : nullptr;
-
-  // We now have all the informations required to calculate the initial buffered
-  // range.
-  mHasStartTime = true;
-  UpdateBuffered();
-
   mMetadataPromise.Resolve(metadata, __func__);
 }
 
 bool
 MediaFormatReader::IsEncrypted() const
 {
   return (HasAudio() && mInfo.mAudio.mCrypto.mValid) ||
          (HasVideo() && mInfo.mVideo.mCrypto.mValid);
@@ -1494,16 +1154,19 @@ MediaFormatReader::UpdateReceivedNewData
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
 
   if (!decoder.mReceivedNewData) {
     return false;
   }
 
+  // Update our cached TimeRange.
+  decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
+
   // We do not want to clear mWaitingForData while there are pending
   // demuxing or seeking operations that could affect the value of this flag.
   // This is in order to ensure that we will retry once they complete as we may
   // now have new data that could potentially allow those operations to
   // successfully complete if tried again.
   if (decoder.mSeekRequest.Exists()) {
     // Nothing more to do until this operation complete.
     return true;
@@ -1521,16 +1184,26 @@ MediaFormatReader::UpdateReceivedNewData
   }
 
   if (decoder.HasPendingDrain()) {
     // We do not want to clear mWaitingForData or mDemuxEOS while
     // a drain is in progress in order to properly complete the operation.
     return false;
   }
 
+  bool hasLastEnd;
+  media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
+  if (hasLastEnd) {
+    if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() < lastEnd) {
+      // New data was added after our previous end, we can clear the EOS flag.
+      decoder.mDemuxEOS = false;
+    }
+    decoder.mLastTimeRangesEnd = Some(lastEnd);
+  }
+
   decoder.mReceivedNewData = false;
   if (decoder.mTimeThreshold) {
     decoder.mTimeThreshold.ref().mWaiting = false;
   }
   decoder.mWaitingForData = false;
 
   if (decoder.HasFatalError()) {
     return false;
@@ -2518,139 +2191,101 @@ MediaFormatReader::OnAudioSeekCompleted(
 }
 
 void
 MediaFormatReader::OnAudioSeekFailed(const MediaResult& aError)
 {
   OnSeekFailed(TrackType::kAudioTrack, aError);
 }
 
+media::TimeIntervals
+MediaFormatReader::GetBuffered()
+{
+  MOZ_ASSERT(OnTaskQueue());
+  media::TimeIntervals videoti;
+  media::TimeIntervals audioti;
+  media::TimeIntervals intervals;
+
+  if (!mInitDone || !mHasStartTime) {
+    return intervals;
+  }
+
+  // Ensure we have up to date buffered time range.
+  if (HasVideo()) {
+    UpdateReceivedNewData(TrackType::kVideoTrack);
+  }
+  if (HasAudio()) {
+    UpdateReceivedNewData(TrackType::kAudioTrack);
+  }
+  if (HasVideo()) {
+    videoti = mVideo.mTimeRanges;
+  }
+  if (HasAudio()) {
+    audioti = mAudio.mTimeRanges;
+  }
+  if (HasAudio() && HasVideo()) {
+    intervals = media::Intersection(Move(videoti), Move(audioti));
+  } else if (HasAudio()) {
+    intervals = Move(audioti);
+  } else if (HasVideo()) {
+    intervals = Move(videoti);
+  }
+
+  if (!intervals.Length() ||
+      intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
+    // IntervalSet already starts at 0 or is empty, nothing to shift.
+    return intervals;
+  }
+  return intervals.Shift(media::TimeUnit() - mInfo.mStartTime);
+}
+
 void MediaFormatReader::ReleaseResources()
 {
   mVideo.ShutdownDecoder();
   mAudio.ShutdownDecoder();
 }
 
 bool
 MediaFormatReader::VideoIsHardwareAccelerated() const
 {
   return mVideo.mIsHardwareAccelerated;
 }
 
 void
-MediaFormatReader::NotifyTrackDemuxers()
+MediaFormatReader::NotifyDemuxer()
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  if (mShutdown || !mDemuxer ||
+      (!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
+    return;
+  }
+
   LOGV("");
 
+  mDemuxer->NotifyDataArrived();
+
   if (!mInitDone) {
     return;
   }
-
   if (HasVideo()) {
     mVideo.mReceivedNewData = true;
     ScheduleUpdate(TrackType::kVideoTrack);
   }
   if (HasAudio()) {
     mAudio.mReceivedNewData = true;
     ScheduleUpdate(TrackType::kAudioTrack);
   }
 }
 
 void
-MediaFormatReader::NotifyDataArrived()
-{
-  MOZ_ASSERT(OnTaskQueue());
-
-  if (mShutdown || !mDemuxer ||
-      (!mDemuxerInitDone && !mDemuxerInitRequest.Exists())) {
-    return;
-  }
-
-  if (mNotifyDataArrivedPromise.Exists()) {
-    // Already one in progress. Reschedule for later.
-    RefPtr<nsIRunnable> task(
-        NewRunnableMethod(this, &MediaFormatReader::NotifyDataArrived));
-    OwnerThread()->Dispatch(task.forget());
-    return;
-  }
-
-  RefPtr<MediaFormatReader> self = this;
-  mDemuxer->NotifyDataArrived()
-    ->Then(OwnerThread(), __func__,
-           [self]() {
-             self->mNotifyDataArrivedPromise.Complete();
-             self->UpdateBuffered();
-             self->NotifyTrackDemuxers();
-           },
-           [self]() { self->mNotifyDataArrivedPromise.Complete(); })
-    ->Track(mNotifyDataArrivedPromise);
-}
-
-void
-MediaFormatReader::UpdateBuffered()
+MediaFormatReader::NotifyDataArrivedInternal()
 {
   MOZ_ASSERT(OnTaskQueue());
-
-  if (mShutdown) {
-    return;
-  }
-
-  if (!mInitDone || !mHasStartTime) {
-    mBuffered = TimeIntervals();
-    return;
-  }
-
-  if (HasVideo()) {
-    mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered();
-    bool hasLastEnd;
-    media::TimeUnit lastEnd = mVideo.mTimeRanges.GetEnd(&hasLastEnd);
-    if (hasLastEnd) {
-      if (mVideo.mLastTimeRangesEnd
-          && mVideo.mLastTimeRangesEnd.ref() < lastEnd) {
-        // New data was added after our previous end, we can clear the EOS flag.
-        mVideo.mDemuxEOS = false;
-        ScheduleUpdate(TrackInfo::kVideoTrack);
-      }
-      mVideo.mLastTimeRangesEnd = Some(lastEnd);
-    }
-  }
-  if (HasAudio()) {
-    mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered();
-    bool hasLastEnd;
-    media::TimeUnit lastEnd = mAudio.mTimeRanges.GetEnd(&hasLastEnd);
-    if (hasLastEnd) {
-      if (mAudio.mLastTimeRangesEnd
-          && mAudio.mLastTimeRangesEnd.ref() < lastEnd) {
-        // New data was added after our previous end, we can clear the EOS flag.
-        mAudio.mDemuxEOS = false;
-        ScheduleUpdate(TrackInfo::kAudioTrack);
-      }
-      mAudio.mLastTimeRangesEnd = Some(lastEnd);
-    }
-  }
-
-  media::TimeIntervals intervals;
-  if (HasAudio() && HasVideo()) {
-    intervals = media::Intersection(mVideo.mTimeRanges, mAudio.mTimeRanges);
-  } else if (HasAudio()) {
-    intervals = mAudio.mTimeRanges;
-  } else if (HasVideo()) {
-    intervals = mVideo.mTimeRanges;
-  }
-
-  if (!intervals.Length() ||
-      intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
-    // IntervalSet already starts at 0 or is empty, nothing to shift.
-    mBuffered = intervals;
-  } else {
-    mBuffered =
-      intervals.Shift(media::TimeUnit() - mInfo.mStartTime);
-  }
+  NotifyDemuxer();
 }
 
 layers::ImageContainer*
 MediaFormatReader::GetImageContainer()
 {
   return mVideoFrameContainer
     ? mVideoFrameContainer->GetImageContainer() : nullptr;
 }
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -20,17 +20,16 @@
 
 namespace mozilla {
 
 class CDMProxy;
 
 class MediaFormatReader final : public MediaDecoderReader
 {
   typedef TrackInfo::TrackType TrackType;
-  typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> NotifyDataArrivedPromise;
 
 public:
   MediaFormatReader(AbstractMediaDecoder* aDecoder,
                     MediaDataDemuxer* aDemuxer,
                     VideoFrameContainer* aVideoFrameContainer = nullptr);
 
   virtual ~MediaFormatReader();
 
@@ -44,20 +43,21 @@ public:
 
   RefPtr<MetadataPromise> AsyncReadMetadata() override;
 
   void ReadUpdatedMetadata(MediaInfo* aInfo) override;
 
   RefPtr<SeekPromise> Seek(const SeekTarget& aTarget) override;
 
 protected:
-  void NotifyDataArrived() override;
-  void UpdateBuffered() override;
+  void NotifyDataArrivedInternal() override;
 
 public:
+  media::TimeIntervals GetBuffered() override;
+
   // For Media Resource Management
   void ReleaseResources() override;
 
   nsresult ResetDecode(TrackSet aTracks) override;
 
   RefPtr<ShutdownPromise> Shutdown() override;
 
   bool IsAsync() const override { return true; }
@@ -84,18 +84,20 @@ private:
   nsresult InitInternal() override;
 
   bool HasVideo() const { return mVideo.mTrackDemuxer; }
   bool HasAudio() const { return mAudio.mTrackDemuxer; }
 
   bool IsWaitingOnCDMResource();
 
   bool InitDemuxer();
-  // Notify the track demuxers that new data has been received.
-  void NotifyTrackDemuxers();
+  // Notify the demuxer that new data has been received.
+  // The next queued task calling GetBuffered() is guaranteed to have up to date
+  // buffered ranges.
+  void NotifyDemuxer();
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
   // Enqueues a task to call Update(aTrack) on the decoder task queue.
   // Lock for corresponding track must be held.
   void ScheduleUpdate(TrackType aTrack);
   void Update(TrackType aTrack);
   // Handle actions should more data be received.
   // Returns true if no more action is required.
@@ -472,24 +474,22 @@ private:
   DecoderDataWithPromise mVideo;
 
   // Returns true when the decoder for this track needs input.
   bool NeedInput(DecoderData& aDecoder);
 
   DecoderData& GetDecoderData(TrackType aTrack);
 
   // Demuxer objects.
-  class DemuxerProxy;
-  UniquePtr<DemuxerProxy> mDemuxer;
+  RefPtr<MediaDataDemuxer> mDemuxer;
   bool mDemuxerInitDone;
   void OnDemuxerInitDone(nsresult);
   void OnDemuxerInitFailed(const MediaResult& aError);
   MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
-  MozPromiseRequestHolder<NotifyDataArrivedPromise> mNotifyDataArrivedPromise;
-  void OnDemuxFailed(TrackType aTrack, const MediaResult &aError);
+  void OnDemuxFailed(TrackType aTrack, const MediaResult& aError);
 
   void DoDemuxVideo();
   void OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
   void OnVideoDemuxFailed(const MediaResult& aError)
   {
     OnDemuxFailed(TrackType::kVideoTrack, aError);
   }