Bug 1126465 - Cancel sample requests when seeks start, disallow them while seeks are happening, and assert against seeks when samples arrive. r=mattwoodrow, a=sledru
authorBobby Holley <bobbyholley@gmail.com>
Thu, 29 Jan 2015 22:11:11 -0800
changeset 243645 26df0dd2cceb
parent 243644 29c741d65b11
child 243646 5daace5690d6
push id4421
push userryanvm@gmail.com
push date2015-02-02 19:52 +0000
treeherdermozilla-beta@08a02585bc60 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow, sledru
bugs1126465
milestone36.0
Bug 1126465 - Cancel sample requests when seeks start, disallow them while seeks are happening, and assert against seeks when samples arrive. r=mattwoodrow, a=sledru 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;
   }
 
@@ -259,17 +262,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)
@@ -292,31 +295,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);
@@ -324,29 +333,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;
   }
 
@@ -669,16 +674,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;
   }
@@ -844,21 +858,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
@@ -171,16 +171,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 aTolerance,
                                                     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;