Bug 1126465 - Cancel sample requests when seeks start, disallow them while seeks are happening, and assert against seeks when samples arrive. r=mattwoodrow
authorBobby Holley <bobbyholley@gmail.com>
Thu, 29 Jan 2015 22:11:11 -0800
changeset 253877 7e85ee706260cc79257cc60082626afbc2c42661
parent 253876 33588deed1313b43cdcf99ca6d54013697583e8b
child 253878 8b4f0dec6cdd21e666be1b0b521fcca7d99437cb
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1126465
milestone38.0a1
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
Bug 1126465 - Cancel sample requests when seeks start, disallow them while seeks are happening, and assert against seeks when samples arrive. r=mattwoodrow The duplication of the IsSeeking() checks before all the Request{Audio,Video}Data callsites is ugly. We'll fix this in the next patch by applying the same disconnect treatment to the seek promise.
dom/media/mediasource/MediaSourceReader.cpp
dom/media/mediasource/MediaSourceReader.h
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -115,17 +115,17 @@ MediaSourceReader::RequestAudioData()
 {
   nsRefPtr<AudioDataPromise> p = mAudioPromise.Ensure(__func__);
   MSE_DEBUGV("MediaSourceReader(%p)::RequestAudioData", this);
   if (!mAudioReader) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called with no audio reader", this);
     mAudioPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
-  if (mAudioIsSeeking) {
+  if (IsSeeking()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestAudioData called mid-seek. Rejecting.", this);
     mAudioPromise.Reject(CANCELED, __func__);
     return p;
   }
   SwitchReaderResult ret = SwitchAudioReader(mLastAudioTime);
   switch (ret) {
     case READER_NEW:
       mAudioReader->Seek(mLastAudioTime, 0)
@@ -144,31 +144,38 @@ MediaSourceReader::RequestAudioData()
       break;
   }
   return p;
 }
 
 void
 MediaSourceReader::RequestAudioDataComplete(int64_t aTime)
 {
+  if (IsSeeking()) {
+    MSE_DEBUG("MediaSourceReader(%p)::RequestAudioDataComplete called mid-seek. Rejecting.", this);
+    MOZ_RELEASE_ASSERT(mAudioPromise.IsEmpty()); // Already rejected in ::Seek().
+    return;
+  }
+
   mAudioRequest.Begin(mAudioReader->RequestAudioData()
                       ->RefableThen(GetTaskQueue(), __func__, this,
                                     &MediaSourceReader::OnAudioDecoded,
                                     &MediaSourceReader::OnAudioNotDecoded));
 }
 
 void
 MediaSourceReader::RequestAudioDataFailed(nsresult aResult)
 {
   mAudioPromise.Reject(DECODE_ERROR, __func__);
 }
 
 void
 MediaSourceReader::OnAudioDecoded(AudioData* aSample)
 {
+  MOZ_RELEASE_ASSERT(!IsSeeking());
   mAudioRequest.Complete();
 
   MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
              this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropAudioBeforeThreshold) {
     if (aSample->mTime < mTimeThreshold) {
       MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
                 this, aSample->mTime, mTimeThreshold);
@@ -176,22 +183,17 @@ MediaSourceReader::OnAudioDecoded(AudioD
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnAudioDecoded,
                                         &MediaSourceReader::OnAudioNotDecoded));
       return;
     }
     mDropAudioBeforeThreshold = false;
   }
 
-  // Any OnAudioDecoded callbacks received while mAudioIsSeeking must be not
-  // update our last used timestamp, as these are emitted by the reader we're
-  // switching away from.
-  if (!mAudioIsSeeking) {
-    mLastAudioTime = aSample->mTime + aSample->mDuration;
-  }
+  mLastAudioTime = aSample->mTime + aSample->mDuration;
 
   mAudioPromise.Resolve(aSample, __func__);
 }
 
 // Find the closest approximation to the end time for this stream.
 // mLast{Audio,Video}Time differs from the actual end time because of
 // Bug 1065207 - the duration of a WebM fragment is an estimate not the
 // actual duration. In the case of audio time an example of where they
@@ -215,16 +217,17 @@ AdjustEndTime(int64_t* aEndTime, MediaDe
       *aEndTime = std::max(*aEndTime, end);
     }
   }
 }
 
 void
 MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
 {
+  MOZ_RELEASE_ASSERT(!IsSeeking());
   mAudioRequest.Complete();
 
   MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mAudioPromise.Reject(aReason, __func__);
     return;
   }
 
@@ -261,17 +264,17 @@ MediaSourceReader::RequestVideoData(bool
     mVideoPromise.Reject(DECODE_ERROR, __func__);
     return p;
   }
   if (aSkipToNextKeyframe) {
     mTimeThreshold = aTimeThreshold;
     mDropAudioBeforeThreshold = true;
     mDropVideoBeforeThreshold = true;
   }
-  if (mVideoIsSeeking) {
+  if (IsSeeking()) {
     MSE_DEBUG("MediaSourceReader(%p)::RequestVideoData called mid-seek. Rejecting.", this);
     mVideoPromise.Reject(CANCELED, __func__);
     return p;
   }
   SwitchReaderResult ret = SwitchVideoReader(mLastVideoTime);
   switch (ret) {
     case READER_NEW:
       mVideoReader->Seek(mLastVideoTime, 0)
@@ -294,31 +297,37 @@ MediaSourceReader::RequestVideoData(bool
   }
 
   return p;
 }
 
 void
 MediaSourceReader::RequestVideoDataComplete(int64_t aTime)
 {
+  if (IsSeeking()) {
+    MSE_DEBUG("MediaSourceReader(%p)::RequestVideoDataComplete called mid-seek. Rejecting.", this);
+    MOZ_ASSERT(mVideoPromise.IsEmpty()); // Already rejected in ::Seek().
+    return;
+  }
   mVideoRequest.Begin(mVideoReader->RequestVideoData(false, 0)
                       ->RefableThen(GetTaskQueue(), __func__, this,
                                     &MediaSourceReader::OnVideoDecoded,
                                     &MediaSourceReader::OnVideoNotDecoded));
 }
 
 void
 MediaSourceReader::RequestVideoDataFailed(nsresult aResult)
 {
   mVideoPromise.Reject(DECODE_ERROR, __func__);
 }
 
 void
 MediaSourceReader::OnVideoDecoded(VideoData* aSample)
 {
+  MOZ_RELEASE_ASSERT(!IsSeeking());
   mVideoRequest.Complete();
 
   MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
              this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
   if (mDropVideoBeforeThreshold) {
     if (aSample->mTime < mTimeThreshold) {
       MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
                 this, aSample->mTime, mTimeThreshold);
@@ -326,29 +335,25 @@ MediaSourceReader::OnVideoDecoded(VideoD
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnVideoDecoded,
                                         &MediaSourceReader::OnVideoNotDecoded));
       return;
     }
     mDropVideoBeforeThreshold = false;
   }
 
-  // Any OnVideoDecoded callbacks received while mVideoIsSeeking must be not
-  // update our last used timestamp, as these are emitted by the reader we're
-  // switching away from.
-  if (!mVideoIsSeeking) {
-    mLastVideoTime = aSample->mTime + aSample->mDuration;
-  }
+  mLastVideoTime = aSample->mTime + aSample->mDuration;
 
   mVideoPromise.Resolve(aSample, __func__);
 }
 
 void
 MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
 {
+  MOZ_RELEASE_ASSERT(!IsSeeking());
   mVideoRequest.Complete();
 
   MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
   if (aReason == DECODE_ERROR || aReason == CANCELED) {
     mVideoPromise.Reject(aReason, __func__);
     return;
   }
 
@@ -684,16 +689,25 @@ MediaSourceReader::Seek(int64_t aTime, i
   MOZ_ASSERT(mSeekPromise.IsEmpty());
   nsRefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
 
   if (IsShutdown()) {
     mSeekPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
 
+  // Any previous requests we've been waiting on are now unwanted.
+  mAudioRequest.DisconnectIfExists();
+  mVideoRequest.DisconnectIfExists();
+
+  // Additionally, reject any outstanding promises _we_ made that we might have
+  // been waiting on the above to fulfill.
+  mAudioPromise.RejectIfExists(CANCELED, __func__);
+  mVideoPromise.RejectIfExists(CANCELED, __func__);
+
   // Store pending seek target in case the track buffers don't contain
   // the desired time and we delay doing the seek.
   mPendingSeekTime = aTime;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mWaitingForSeekData = true;
   }
@@ -859,21 +873,21 @@ MediaSourceReader::WaitForData(MediaData
   return p;
 }
 
 void
 MediaSourceReader::MaybeNotifyHaveData()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   bool haveAudio = false, haveVideo = false;
-  if (!mAudioIsSeeking && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
+  if (!IsSeeking() && 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)) {
+  if (!IsSeeking() && 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
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -172,16 +172,17 @@ private:
   // 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,
                                                     int64_t aError,
                                                     const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
 
   void AttemptSeek();
+  bool IsSeeking() { return mPendingSeekTime != -1; }
 
   nsRefPtr<MediaDecoderReader> mAudioReader;
   nsRefPtr<MediaDecoderReader> mVideoReader;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;