Backed out 7 changesets (bug 1109437) for suspicious-looking crashes on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 19 Dec 2014 17:02:27 -0500
changeset 246543 b6db7735f698a0265bc27d8db94a864e5c451a7b
parent 246542 3c190cc7328c9cf03eeb1dc3d59ea4348d6eca73
child 246544 b1f73c9ed680752b6b8c9330150bf6dc289cd505
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1109437
milestone37.0a1
backs outdc45e97d102d2f814b7a8da84ac251ef32991cce
e6350e4fb18ba8198a7f4913954a0e4b2b8d3782
4c38dda0688052e7074c04a7a25065cb7e3246c6
72f171ec04bad38d43a39545fe7804dbf42fd94d
ec275f65c67632ed65305cae41b9b26dacdd747b
65d172e7a1cfed8669fceb219ad959ffd26de107
0b8a156dcff0dc8aaeffabc1fe7780ac3605052c
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 7 changesets (bug 1109437) for suspicious-looking crashes on a CLOSED TREE. Backed out changeset dc45e97d102d (bug 1109437) Backed out changeset e6350e4fb18b (bug 1109437) Backed out changeset 4c38dda06880 (bug 1109437) Backed out changeset 72f171ec04ba (bug 1109437) Backed out changeset ec275f65c676 (bug 1109437) Backed out changeset 65d172e7a1cf (bug 1109437) Backed out changeset 0b8a156dcff0 (bug 1109437)
dom/media/MediaDecoderReader.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaDecoderStateMachine.h
dom/media/MediaPromise.h
dom/media/mediasource/MediaSourceDecoder.cpp
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/MediaSourceReader.h
dom/media/mediasource/TrackBuffer.cpp
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -8,38 +8,25 @@
 
 #include "AbstractMediaDecoder.h"
 #include "MediaInfo.h"
 #include "MediaData.h"
 #include "MediaPromise.h"
 #include "MediaQueue.h"
 #include "AudioCompactor.h"
 
-#include "mozilla/TypedEnum.h"
-
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
 
 class MediaDecoderReader;
 class SharedDecoderManager;
 
-struct WaitForDataRejectValue {
-  enum Reason {
-    SHUTDOWN
-  };
-
-  WaitForDataRejectValue(MediaData::Type aType, Reason aReason)
-    :mType(aType), mReason(aReason) {}
-  MediaData::Type mType;
-  Reason mReason;
-};
-
 // Encapsulates the decoding and reading of media data. Reading can either
 // synchronous and done on the calling "decode" thread, or asynchronous and
 // performed on a background thread, with the result being returned by
 // callback. Never hold the decoder monitor when calling into this class.
 // Unless otherwise specified, methods and fields of this class can only
 // be accessed on the decode task queue.
 class MediaDecoderReader {
 public:
@@ -48,17 +35,16 @@ public:
     DECODE_ERROR,
     WAITING_FOR_DATA,
     CANCELED
   };
 
   typedef MediaPromise<nsRefPtr<AudioData>, NotDecodedReason> AudioDataPromise;
   typedef MediaPromise<nsRefPtr<VideoData>, NotDecodedReason> VideoDataPromise;
   typedef MediaPromise<bool, nsresult> SeekPromise;
-  typedef MediaPromise<MediaData::Type, WaitForDataRejectValue> WaitForDataPromise;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
 
   explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
   virtual nsresult Init(MediaDecoderReader* aCloneDonor) = 0;
@@ -122,22 +108,16 @@ public:
   //
   // Don't hold the decoder monitor while calling this, as the implementation
   // may try to wait on something that needs the monitor and deadlock.
   // If aSkipToKeyframe is true, the decode should skip ahead to the
   // the next keyframe at or after aTimeThreshold microseconds.
   virtual nsRefPtr<VideoDataPromise>
   RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold);
 
-  // By default, the state machine polls the reader once per second when it's
-  // in buffering mode. Some readers support a promise-based mechanism by which
-  // they notify the state machine when the data arrives.
-  virtual bool IsWaitForDataSupported() { return false; }
-  virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
-
   virtual bool HasAudio() = 0;
   virtual bool HasVideo() = 0;
 
   // A function that is called before ReadMetadata() call.
   virtual void PreReadMetadata() {};
 
   // Read header data for all bitstreams in the file. Fills aInfo with
   // the data required to present the media, and optionally fills *aTags
@@ -190,21 +170,20 @@ public:
   //
   // 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 nsresult GetBuffered(dom::TimeRanges* aBuffered);
 
   virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio);
 
-  // The MediaDecoderStateMachine uses various heuristics that assume that
-  // raw media data is arriving sequentially from a network channel. This
-  // makes sense in the <video src="foo"> case, but not for more advanced use
-  // cases like MSE.
-  virtual bool UseBufferingHeuristics() { return true; }
+  // Wait this number of seconds when buffering, then leave and play
+  // as best as we can if the required amount of data hasn't been
+  // retrieved.
+  virtual uint32_t GetBufferingWait() { return 30; }
 
   // Returns the number of bytes of memory allocated by structures/frames in
   // the video queue.
   size_t SizeOfVideoQueueInBytes() const;
 
   // Returns the number of bytes of memory allocated by structures/frames in
   // the audio queue.
   size_t SizeOfAudioQueueInBytes() const;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -190,18 +190,18 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDecodedAudioEndTime(-1),
   mVideoFrameEndTime(-1),
   mVolume(1.0),
   mPlaybackRate(1.0),
   mPreservesPitch(true),
   mAmpleVideoFrames(2),
   mLowAudioThresholdUsecs(LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(AMPLE_AUDIO_USECS),
-  mAudioRequestStatus(RequestStatus::Idle),
-  mVideoRequestStatus(RequestStatus::Idle),
+  mAudioRequestPending(false),
+  mVideoRequestPending(false),
   mAudioCaptured(false),
   mPositionChangeQueued(false),
   mAudioCompleted(false),
   mGotDurationFromMetaData(false),
   mDispatchedEventToDecode(false),
   mStopAudioThread(true),
   mQuickBuffering(false),
   mMinimizePreroll(false),
@@ -216,17 +216,17 @@ MediaDecoderStateMachine::MediaDecoderSt
   mDecodingFrozenAtStateDecoding(false)
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   mAmpleVideoFrames =
     std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
 
-  mBufferingWait = mScheduler->IsRealTime() ? 0 : 30;
+  mBufferingWait = mScheduler->IsRealTime() ? 0 : mReader->GetBufferingWait();
   mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS;
 
   mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2;
   mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine thread isn't woken up at reliable intervals to set the next frame,
@@ -574,17 +574,17 @@ MediaDecoderStateMachine::DecodeVideo()
   bool skipToNextKeyFrame = false;
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
     if (mState != DECODER_STATE_DECODING &&
         mState != DECODER_STATE_BUFFERING &&
         mState != DECODER_STATE_SEEKING) {
-      mVideoRequestStatus = RequestStatus::Idle;
+      mVideoRequestPending = false;
       DispatchDecodeTasksIfNeeded();
       return;
     }
 
     // We don't want to consider skipping to the next keyframe if we've
     // only just started up the decode loop, so wait until we've decoded
     // some frames before enabling the keyframe skip logic on video.
     if (mIsVideoPrerolling &&
@@ -653,17 +653,17 @@ MediaDecoderStateMachine::DecodeAudio()
 {
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
 
     if (mState != DECODER_STATE_DECODING &&
         mState != DECODER_STATE_BUFFERING &&
         mState != DECODER_STATE_SEEKING) {
-      mAudioRequestStatus = RequestStatus::Idle;
+      mAudioRequestPending = false;
       DispatchDecodeTasksIfNeeded();
       mon.NotifyAll();
       return;
     }
 
     // We don't want to consider skipping to the next keyframe if we've
     // only just started up the decode loop, so wait until we've decoded
     // some audio data before enabling the keyframe skip logic on audio.
@@ -708,36 +708,34 @@ MediaDecoderStateMachine::IsVideoSeekCom
 }
 
 void
 MediaDecoderStateMachine::OnAudioDecoded(AudioData* aAudioSample)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   nsRefPtr<AudioData> audio(aAudioSample);
   MOZ_ASSERT(audio);
-  mAudioRequestStatus = RequestStatus::Idle;
+  mAudioRequestPending = false;
   mDecodedAudioEndTime = audio->GetEndTime();
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
              (audio ? audio->mTime : -1),
              (audio ? audio->GetEndTime() : -1),
              (audio ? audio->mDiscontinuity : 0));
 
   switch (mState) {
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       Push(audio);
       MaybeFinishDecodeFirstFrame();
       return;
     }
 
     case DECODER_STATE_BUFFERING:
-      // If we're buffering, this may be the sample we need to stop buffering.
-      // Schedule the state machine and then fall through.
-      ScheduleStateMachine();
     case DECODER_STATE_DECODING: {
+      // In buffering and decoding state, we simply enqueue samples.
       Push(audio);
       return;
     }
 
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeekTarget.IsValid()) {
         // We've received a sample from a previous decode. Discard it.
         return;
@@ -822,33 +820,37 @@ MediaDecoderStateMachine::OnNotDecoded(M
                                        MediaDecoderReader::NotDecodedReason aReason)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   SAMPLE_LOG("OnNotDecoded (aType=%u, aReason=%u)", aType, aReason);
   bool isAudio = aType == MediaData::AUDIO_DATA;
   MOZ_ASSERT_IF(!isAudio, aType == MediaData::VIDEO_DATA);
 
   // This callback means that the pending request is dead.
-  RequestStatusRef(aType) = RequestStatus::Idle;
+  if (isAudio) {
+    mAudioRequestPending = false;
+  } else {
+    mVideoRequestPending = false;
+  }
 
   // If this is a decode error, delegate to the generic error path.
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     DecodeError();
     return;
   }
 
-  // If the decoder is waiting for data, we tell it to call us back when the
-  // data arrives.
+  // If the decoder is waiting for data, we need to make sure that the requests
+  // are cleared, which happened above. Additionally, if we're out of decoded
+  // samples, we need to switch to buffering mode.
   if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
-    MOZ_ASSERT(mReader->IsWaitForDataSupported(),
-               "Readers that send WAITING_FOR_DATA need to implement WaitForData");
-    RequestStatusRef(aType) = RequestStatus::Waiting;
-    mReader->WaitForData(aType)->Then(DecodeTaskQueue(), __func__, this,
-                                      &MediaDecoderStateMachine::OnWaitForDataResolved,
-                                      &MediaDecoderStateMachine::OnWaitForDataRejected);
+    bool outOfSamples = isAudio ? !AudioQueue().GetSize() : !VideoQueue().GetSize();
+    if (outOfSamples) {
+      StartBuffering();
+    }
+
     return;
   }
 
   if (aReason == MediaDecoderReader::CANCELED) {
     DispatchDecodeTasksIfNeeded();
     return;
   }
 
@@ -923,34 +925,31 @@ MediaDecoderStateMachine::MaybeFinishDec
 }
 
 void
 MediaDecoderStateMachine::OnVideoDecoded(VideoData* aVideoSample)
 {
   MOZ_ASSERT(OnDecodeThread());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   nsRefPtr<VideoData> video(aVideoSample);
-  mVideoRequestStatus = RequestStatus::Idle;
+  mVideoRequestPending = false;
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
              (video ? video->mTime : -1),
              (video ? video->GetEndTime() : -1),
              (video ? video->mDiscontinuity : 0));
 
   switch (mState) {
     case DECODER_STATE_DECODING_FIRSTFRAME: {
       Push(video);
       MaybeFinishDecodeFirstFrame();
       return;
     }
 
     case DECODER_STATE_BUFFERING:
-      // If we're buffering, this may be the sample we need to stop buffering.
-      // Schedule the state machine and then fall through.
-      ScheduleStateMachine();
     case DECODER_STATE_DECODING: {
       Push(video);
       // If the requested video sample was slow to arrive, increase the
       // amount of audio we buffer to ensure that we don't run out of audio.
       // TODO: Detect when we're truly async, and don't do this if so, as
       // it's not necessary.
       TimeDuration decodeTime = TimeStamp::Now() - mVideoDecodeStartTime;
       if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
@@ -1741,19 +1740,19 @@ MediaDecoderStateMachine::DispatchDecode
              (!needToDecodeAudio && !needToDecodeVideo));
 
   bool needIdle = !mDecoder->IsLogicallyPlaying() &&
                   mState != DECODER_STATE_SEEKING &&
                   !needToDecodeAudio &&
                   !needToDecodeVideo &&
                   !IsPlaying();
 
-  SAMPLE_LOG("DispatchDecodeTasksIfNeeded needAudio=%d audioStatus=%d needVideo=%d videoStatus=%d needIdle=%d",
-             needToDecodeAudio, mAudioRequestStatus,
-             needToDecodeVideo, mVideoRequestStatus,
+  SAMPLE_LOG("DispatchDecodeTasksIfNeeded needAudio=%d dispAudio=%d needVideo=%d dispVid=%d needIdle=%d",
+             needToDecodeAudio, mAudioRequestPending,
+             needToDecodeVideo, mVideoRequestPending,
              needIdle);
 
   if (needToDecodeAudio) {
     EnsureAudioDecodeTaskQueued();
   }
   if (needToDecodeVideo) {
     EnsureVideoDecodeTaskQueued();
   }
@@ -1813,31 +1812,31 @@ MediaDecoderStateMachine::DispatchAudioD
 
 nsresult
 MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
 {
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
-  SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%d",
-              IsAudioDecoding(), mAudioRequestStatus);
+  SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d dispatched=%d",
+              IsAudioDecoding(), mAudioRequestPending);
 
   if (mState >= DECODER_STATE_COMPLETED) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
 
-  if (IsAudioDecoding() && mAudioRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
+  if (IsAudioDecoding() && !mAudioRequestPending && !mWaitingForDecoderSeek) {
     RefPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeAudio));
     nsresult rv = DecodeTaskQueue()->Dispatch(task);
     if (NS_SUCCEEDED(rv)) {
-      mAudioRequestStatus = RequestStatus::Pending;
+      mAudioRequestPending = true;
     } else {
       DECODER_WARN("Failed to dispatch task to decode audio");
     }
   }
 
   return NS_OK;
 }
 
@@ -1855,34 +1854,34 @@ MediaDecoderStateMachine::DispatchVideoD
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
 {
   AssertCurrentThreadInMonitor();
 
-  SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%d",
-             IsVideoDecoding(), mVideoRequestStatus);
+  SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d dispatched=%d",
+             IsVideoDecoding(), mVideoRequestPending);
 
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
   if (mState >= DECODER_STATE_COMPLETED) {
     return NS_OK;
   }
 
   MOZ_ASSERT(mState > DECODER_STATE_DECODING_FIRSTFRAME);
 
-  if (IsVideoDecoding() && mVideoRequestStatus == RequestStatus::Idle && !mWaitingForDecoderSeek) {
+  if (IsVideoDecoding() && !mVideoRequestPending && !mWaitingForDecoderSeek) {
     RefPtr<nsIRunnable> task(
       NS_NewRunnableMethod(this, &MediaDecoderStateMachine::DecodeVideo));
     nsresult rv = DecodeTaskQueue()->Dispatch(task);
     if (NS_SUCCEEDED(rv)) {
-      mVideoRequestStatus = RequestStatus::Pending;
+      mVideoRequestPending = true;
     } else {
       DECODER_WARN("Failed to dispatch task to decode video");
     }
   }
 
   return NS_OK;
 }
 
@@ -1926,17 +1925,16 @@ int64_t MediaDecoderStateMachine::AudioD
   // data waiting to be pushed to the hardware.
   int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0;
   return pushed + AudioQueue().Duration();
 }
 
 bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs)
 {
   AssertCurrentThreadInMonitor();
-  MOZ_ASSERT(mReader->UseBufferingHeuristics());
   // We consider ourselves low on decoded data if we're low on audio,
   // provided we've not decoded to the end of the audio stream, or
   // if we're low on video frames, provided
   // we've not decoded to the end of the video stream.
   return ((IsAudioDecoding() && AudioDecodedUsecs() < aAudioUsecs) ||
          (IsVideoDecoding() &&
           static_cast<uint32_t>(VideoQueue().GetSize()) < LOW_VIDEO_FRAMES));
 }
@@ -2623,51 +2621,38 @@ nsresult MediaDecoderStateMachine::RunSt
                    mPlaybackRate == 0.0, "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_BUFFERING: {
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
-      // With buffering heuristics we will remain in the buffering state if
-      // we've not decoded enough data to begin playback, or if we've not
-      // downloaded a reasonable amount of data inside our buffering time.
-      if (mReader->UseBufferingHeuristics()) {
-        TimeDuration elapsed = now - mBufferingStart;
-        bool isLiveStream = resource->GetLength() == -1;
-        if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
-              elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
-              (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
-                               : HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
-              mDecoder->IsExpectingMoreData())
-        {
-          DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
-                      mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
-                      (mQuickBuffering ? "(quick exit)" : ""));
-          ScheduleStateMachine(USECS_PER_S);
-          return NS_OK;
-        }
-      } else if (OutOfDecodedAudio() || OutOfDecodedVideo()) {
-        MOZ_ASSERT(mReader->IsWaitForDataSupported(),
-                   "Don't yet have a strategy for non-heuristic + non-WaitForData");
-        DispatchDecodeTasksIfNeeded();
-        MOZ_ASSERT_IF(OutOfDecodedAudio(), mAudioRequestStatus != RequestStatus::Idle);
-        MOZ_ASSERT_IF(OutOfDecodedVideo(), mVideoRequestStatus != RequestStatus::Idle);
-        DECODER_LOG("In buffering mode, waiting to be notified: outOfAudio: %d, "
-                    "mAudioStatus: %d, outOfVideo: %d, mVideoStatus: %d",
-                    OutOfDecodedAudio(), mAudioRequestStatus,
-                    OutOfDecodedVideo(), mVideoRequestStatus);
+      // We will remain in the buffering state if we've not decoded enough
+      // data to begin playback, or if we've not downloaded a reasonable
+      // amount of data inside our buffering time.
+      TimeDuration elapsed = now - mBufferingStart;
+      bool isLiveStream = resource->GetLength() == -1;
+      if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
+            elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
+            (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
+                             : HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
+            mDecoder->IsExpectingMoreData())
+      {
+        DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
+                    mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
+                    (mQuickBuffering ? "(quick exit)" : ""));
+        ScheduleStateMachine(USECS_PER_S);
         return NS_OK;
+      } else {
+        DECODER_LOG("Changed state from BUFFERING to DECODING");
+        DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
+        StartDecoding();
       }
 
-      DECODER_LOG("Changed state from BUFFERING to DECODING");
-      DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
-      StartDecoding();
-
       // Notify to allow blocked decoder thread to continue
       mDecoder->GetReentrantMonitor().NotifyAll();
       UpdateReadyState();
       if (mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
           !IsPlaying())
       {
         StartPlayback();
       }
@@ -2757,18 +2742,18 @@ MediaDecoderStateMachine::FlushDecoding(
     // alive.
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     DecodeTaskQueue()->FlushAndDispatch(task);
   }
 
   // These flags will be reset when the decoded data returned in OnAudioDecoded()
   // and OnVideoDecoded(). Because the decode tasks are flushed, these flags need
   // to be reset here.
-  mAudioRequestStatus = RequestStatus::Idle;
-  mVideoRequestStatus = RequestStatus::Idle;
+  mAudioRequestPending = false;
+  mVideoRequestPending = false;
 
   // We must reset playback so that all references to frames queued
   // in the state machine are dropped, else subsequent calls to Shutdown()
   // or ReleaseMediaResources() can fail on B2G.
   ResetPlayback();
 }
 
 void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
@@ -2914,26 +2899,19 @@ void MediaDecoderStateMachine::AdvanceFr
       remainingTime = frame->mTime - now;
     }
   }
 
   // Check to see if we don't have enough data to play up to the next frame.
   // If we don't, switch to buffering mode.
   if (mState == DECODER_STATE_DECODING &&
       mDecoder->GetState() == MediaDecoder::PLAY_STATE_PLAYING &&
+      HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
       mDecoder->IsExpectingMoreData()) {
-    bool shouldBuffer;
-    if (mReader->UseBufferingHeuristics()) {
-      shouldBuffer = HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
-                     (JustExitedQuickBuffering() || HasLowUndecodedData());
-    } else {
-      MOZ_ASSERT(mReader->IsWaitForDataSupported());
-      shouldBuffer = OutOfDecodedAudio() || OutOfDecodedVideo();
-    }
-    if (shouldBuffer) {
+    if (JustExitedQuickBuffering() || HasLowUndecodedData()) {
       if (currentFrame) {
         VideoQueue().PushFront(currentFrame);
       }
       StartBuffering();
       // Don't go straight back to the state machine loop since that might
       // cause us to start decoding again and we could flip-flop between
       // decoding and quick-buffering.
       ScheduleStateMachine(USECS_PER_S);
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -384,33 +384,16 @@ public:
   {
     MOZ_ASSERT(OnDecodeThread());
     OnNotDecoded(MediaData::VIDEO_DATA, aReason);
   }
 
   void OnSeekCompleted();
   void OnSeekFailed(nsresult aResult);
 
-  void OnWaitForDataResolved(MediaData::Type aType)
-  {
-    ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-    if (RequestStatusRef(aType) == RequestStatus::Waiting) {
-      RequestStatusRef(aType) = RequestStatus::Idle;
-      DispatchDecodeTasksIfNeeded();
-    }
-  }
-
-  void OnWaitForDataRejected(WaitForDataRejectValue aRejection)
-  {
-    MOZ_ASSERT(aRejection.mReason == WaitForDataRejectValue::SHUTDOWN);
-    if (RequestStatusRef(aRejection.mType) == RequestStatus::Waiting) {
-      RequestStatusRef(aRejection.mType) = RequestStatus::Idle;
-    }
-  }
-
 private:
   void AcquireMonitorAndInvokeDecodeError();
 
 protected:
   virtual ~MediaDecoderStateMachine();
 
   void AssertCurrentThreadInMonitor() const { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); }
 
@@ -474,33 +457,18 @@ protected:
   // decode more.
   bool NeedToDecodeVideo();
 
   // Decodes some video. This should be run on the decode task queue.
   void DecodeVideo();
 
   // Returns true if we've got less than aAudioUsecs microseconds of decoded
   // and playable data. The decoder monitor must be held.
-  //
-  // May not be invoked when mReader->UseBufferingHeuristics() is false.
   bool HasLowDecodedData(int64_t aAudioUsecs);
 
-  bool OutOfDecodedAudio()
-  {
-    return IsAudioDecoding() && !AudioQueue().IsFinished() && AudioQueue().GetSize() == 0;
-  }
-
-  bool OutOfDecodedVideo()
-  {
-    // In buffering mode, we keep the last already-played frame in the queue.
-    int emptyVideoSize = mState == DECODER_STATE_BUFFERING ? 1 : 0;
-    return IsVideoDecoding() && !VideoQueue().IsFinished() && VideoQueue().GetSize() <= emptyVideoSize;
-  }
-
-
   // Returns true if we're running low on data which is not yet decoded.
   // The decoder monitor must be held.
   bool HasLowUndecodedData();
 
   // Returns true if we have less than aUsecs of undecoded data available.
   bool HasLowUndecodedData(int64_t aUsecs);
 
   // Returns the number of unplayed usecs of audio we've got decoded and/or
@@ -938,32 +906,21 @@ protected:
   // can't keep up with the decode, and cause us to pause playback. So we
   // have a "preroll" stage, where we ignore the results of our "low data"
   // logic during the first few frames of our decode. This occurs during
   // playback. The flags below are true when the corresponding stream is
   // being "prerolled".
   bool mIsAudioPrerolling;
   bool mIsVideoPrerolling;
 
-  MOZ_BEGIN_NESTED_ENUM_CLASS(RequestStatus)
-    Idle,
-    Pending,
-    Waiting
-  MOZ_END_NESTED_ENUM_CLASS(RequestStatus)
-
   // True when we have dispatched a task to the decode task queue to request
   // decoded audio/video, and/or we are waiting for the requested sample to be
   // returned by callback from the Reader.
-  RequestStatus mAudioRequestStatus;
-  RequestStatus mVideoRequestStatus;
-
-  RequestStatus& RequestStatusRef(MediaData::Type aType)
-  {
-    return aType == MediaData::AUDIO_DATA ? mAudioRequestStatus : mVideoRequestStatus;
-  }
+  bool mAudioRequestPending;
+  bool mVideoRequestPending;
 
   // True if we shouldn't play our audio (but still write it to any capturing
   // streams). When this is true, mStopAudioThread is always true and
   // the audio thread will never start again after it has stopped.
   bool mAudioCaptured;
 
   // True if an event to notify about a change in the playback
   // position has been queued, but not yet run. It is set to false when
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -56,16 +56,17 @@ public:
   typedef ResolveValueT ResolveValueType;
   typedef RejectValueT RejectValueType;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise)
   explicit MediaPromise(const char* aCreationSite)
     : mCreationSite(aCreationSite)
     , mMutex("MediaPromise Mutex")
   {
+    MOZ_COUNT_CTOR(MediaPromise);
     PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
   }
 
   static nsRefPtr<MediaPromise<ResolveValueT, RejectValueT>>
   CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite)
   {
     nsRefPtr<MediaPromise<ResolveValueT, RejectValueT>> p =
       new MediaPromise<ResolveValueT, RejectValueT>(aResolveSite);
@@ -93,20 +94,24 @@ protected:
   class ThenValueBase
   {
   public:
     class ResolveRunnable : public nsRunnable
     {
     public:
       ResolveRunnable(ThenValueBase* aThenValue, ResolveValueType aResolveValue)
         : mThenValue(aThenValue)
-        , mResolveValue(aResolveValue) {}
+        , mResolveValue(aResolveValue)
+      {
+        MOZ_COUNT_CTOR(ResolveRunnable);
+      }
 
       ~ResolveRunnable()
       {
+        MOZ_COUNT_DTOR(ResolveRunnable);
         MOZ_ASSERT(!mThenValue);
       }
 
       NS_IMETHODIMP Run()
       {
         PROMISE_LOG("ResolveRunnable::Run() [this=%p]", this);
         mThenValue->DoResolve(mResolveValue);
 
@@ -120,20 +125,24 @@ protected:
       ResolveValueType mResolveValue;
     };
 
     class RejectRunnable : public nsRunnable
     {
     public:
       RejectRunnable(ThenValueBase* aThenValue, RejectValueType aRejectValue)
         : mThenValue(aThenValue)
-        , mRejectValue(aRejectValue) {}
+        , mRejectValue(aRejectValue)
+      {
+        MOZ_COUNT_CTOR(RejectRunnable);
+      }
 
       ~RejectRunnable()
       {
+        MOZ_COUNT_DTOR(RejectRunnable);
         MOZ_ASSERT(!mThenValue);
       }
 
       NS_IMETHODIMP Run()
       {
         PROMISE_LOG("RejectRunnable::Run() [this=%p]", this);
         mThenValue->DoReject(mRejectValue);
 
@@ -313,16 +322,17 @@ protected:
       aOther->Resolve(mResolveValue.ref(), "<chained promise>");
     } else {
       aOther->Reject(mRejectValue.ref(), "<chained promise>");
     }
   }
 
   ~MediaPromise()
   {
+    MOZ_COUNT_DTOR(MediaPromise);
     PROMISE_LOG("MediaPromise::~MediaPromise [this=%p]", this);
     MOZ_ASSERT(!IsPending());
     MOZ_ASSERT(mThenValues.IsEmpty());
     MOZ_ASSERT(mChainedPromises.IsEmpty());
   };
 
   const char* mCreationSite; // For logging
   Mutex mMutex;
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -61,17 +61,18 @@ MediaSourceDecoder::Load(nsIStreamListen
     NS_WARNING("Failed to create state machine!");
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mDecoderStateMachine->Init(nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetStateMachineParameters();
-  return ScheduleStateMachineThread();
+
+  return NS_OK;
 }
 
 nsresult
 MediaSourceDecoder::GetSeekable(dom::TimeRanges* aSeekable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mMediaSource) {
     return NS_ERROR_FAILURE;
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -340,19 +340,16 @@ MediaSourceReader::ContinueShutdown()
   mAudioTrack = nullptr;
   mAudioReader = nullptr;
   mVideoTrack = nullptr;
   mVideoReader = nullptr;
 
   MOZ_ASSERT(mAudioPromise.IsEmpty());
   MOZ_ASSERT(mVideoPromise.IsEmpty());
 
-  mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
-  mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
-
   MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
 }
 
 void
 MediaSourceReader::BreakCycles()
 {
   MediaDecoderReader::BreakCycles();
 
@@ -390,25 +387,16 @@ MediaSourceReader::SelectReader(int64_t 
 
     return newReader.forget();
   }
 
   return nullptr;
 }
 
 bool
-MediaSourceReader::HaveData(int64_t aTarget, MediaData::Type aType)
-{
-  TrackBuffer* trackBuffer = aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
-  MOZ_ASSERT(trackBuffer);
-  nsRefPtr<MediaDecoderReader> reader = SelectReader(aTarget, trackBuffer->Decoders());
-  return !!reader;
-}
-
-bool
 MediaSourceReader::SwitchAudioReader(int64_t aTarget)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   // XXX: Can't handle adding an audio track after ReadMetadata.
   if (!mAudioTrack) {
     return false;
   }
   nsRefPtr<MediaDecoderReader> newReader = SelectReader(aTarget, mAudioTrack->Decoders());
@@ -727,42 +715,16 @@ MediaSourceReader::GetBuffered(dom::Time
 
     intersectionRanges->Intersection(sourceRanges);
   }
 
   MSE_DEBUG("MediaSourceReader(%p)::GetBuffered ranges=%s", this, DumpTimeRanges(intersectionRanges).get());
   return NS_OK;
 }
 
-nsRefPtr<MediaDecoderReader::WaitForDataPromise>
-MediaSourceReader::WaitForData(MediaData::Type aType)
-{
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  nsRefPtr<WaitForDataPromise> p = WaitPromise(aType).Ensure(__func__);
-  MaybeNotifyHaveData();
-  return p;
-}
-
-void
-MediaSourceReader::MaybeNotifyHaveData()
-{
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  bool haveAudio = false, haveVideo = false;
-  if (!mAudioIsSeeking && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
-    haveAudio = true;
-    WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
-  }
-  if (!mVideoIsSeeking && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) {
-    haveVideo = true;
-    WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
-  }
-  MSE_DEBUG("MediaSourceReader(%p)::MaybeNotifyHaveData haveAudio=%d, haveVideo=%d", this,
-            haveAudio, haveVideo);
-}
-
 nsresult
 MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
 {
   MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata tracks=%u/%u audio=%p video=%p",
             this, mEssentialTrackBuffers.Length(), mTrackBuffers.Length(),
             mAudioTrack.get(), mVideoTrack.get());
 
   {
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -56,20 +56,16 @@ public:
   void OnAudioDecoded(AudioData* aSample);
   void OnAudioNotDecoded(NotDecodedReason aReason);
   void OnVideoDecoded(VideoData* aSample);
   void OnVideoNotDecoded(NotDecodedReason aReason);
 
   void OnSeekCompleted();
   void OnSeekFailed(nsresult aResult);
 
-  virtual bool IsWaitForDataSupported() MOZ_OVERRIDE { return true; }
-  virtual nsRefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) MOZ_OVERRIDE;
-  void MaybeNotifyHaveData();
-
   bool HasVideo() MOZ_OVERRIDE
   {
     return mInfo.HasVideo();
   }
 
   bool HasAudio() MOZ_OVERRIDE
   {
     return mInfo.HasAudio();
@@ -77,21 +73,21 @@ public:
 
   void NotifyTimeRangesChanged();
 
   // We can't compute a proper start time since we won't necessarily
   // have the first frame of the resource available. This does the same
   // as chrome/blink and assumes that we always start at t=0.
   virtual int64_t ComputeStartTime(const VideoData* aVideo, const AudioData* aAudio) MOZ_OVERRIDE { return 0; }
 
-  // Buffering heuristics don't make sense for MSE, because the arrival of data
-  // is at least partly controlled by javascript, and javascript does not expect
-  // us to sit on unplayed data just because it may not be enough to play
-  // through.
-  bool UseBufferingHeuristics() MOZ_OVERRIDE { return false; }
+  // Buffering waits (in which we decline to present decoded frames because we
+  // "don't have enough") don't really make sense for MSE. The delay is
+  // essentially a streaming heuristic, but JS is supposed to take care of that
+  // in the MSE world. Avoid injecting inexplicable delays.
+  virtual uint32_t GetBufferingWait() { return 0; }
 
   bool IsMediaSeekable() { return true; }
 
   nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) MOZ_OVERRIDE;
   void ReadUpdatedMetadata(MediaInfo* aInfo) MOZ_OVERRIDE;
   nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime,
        int64_t aCurrentTime) MOZ_OVERRIDE;
@@ -131,40 +127,32 @@ public:
 private:
   bool SwitchAudioReader(int64_t aTarget);
   bool SwitchVideoReader(int64_t aTarget);
 
   // Return a reader from the set available in aTrackDecoders that has data
   // available in the range requested by aTarget.
   already_AddRefed<MediaDecoderReader> SelectReader(int64_t aTarget,
                                                     const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
-  bool HaveData(int64_t aTarget, MediaData::Type aType);
 
   void AttemptSeek();
   void FinalizeSeek();
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;
 
   MediaPromiseHolder<AudioDataPromise> mAudioPromise;
   MediaPromiseHolder<VideoDataPromise> mVideoPromise;
 
-  MediaPromiseHolder<WaitForDataPromise> mAudioWaitPromise;
-  MediaPromiseHolder<WaitForDataPromise> mVideoWaitPromise;
-  MediaPromiseHolder<WaitForDataPromise>& WaitPromise(MediaData::Type aType)
-  {
-    return aType == MediaData::AUDIO_DATA ? mAudioWaitPromise : mVideoWaitPromise;
-  }
-
 #ifdef MOZ_EME
   nsRefPtr<CDMProxy> mCDMProxy;
 #endif
 
   // These are read and written on the decode task queue threads.
   int64_t mLastAudioTime;
   int64_t mLastVideoTime;
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -178,19 +178,19 @@ TrackBuffer::AppendData(const uint8_t* a
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
   if (!AppendDataToCurrentResource(aData, aLength)) {
     return false;
   }
 
-  // Tell our reader that we have more data to ensure that playback starts if
-  // required when data is appended.
-  mParentDecoder->GetReader()->MaybeNotifyHaveData();
+  // Schedule the state machine thread to ensure playback starts if required
+  // when data is appended.
+  mParentDecoder->ScheduleStateMachineThread();
   return true;
 }
 
 bool
 TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mCurrentDecoder) {
@@ -435,21 +435,16 @@ TrackBuffer::InitializeDecoder(SourceBuf
   }
 
   if (!RegisterDecoder(aDecoder)) {
     // XXX: Need to signal error back to owning SourceBuffer.
     MSE_DEBUG("TrackBuffer(%p): Reader %p not activated", this, reader);
     RemoveDecoder(aDecoder);
     return;
   }
-
-  // Tell our reader that we have more data to ensure that playback starts if
-  // required when data is appended.
-  mParentDecoder->GetReader()->MaybeNotifyHaveData();
-
   MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
 }
 
 bool
 TrackBuffer::ValidateTrackFormats(const MediaInfo& aInfo)
 {
   if (mInfo.HasAudio() != aInfo.HasAudio() ||
       mInfo.HasVideo() != aInfo.HasVideo()) {