Bug 1269408: P3. Ensure a new seek request will cancel the previous internal seek. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 05 May 2016 15:01:51 +1000
changeset 364191 bcb643ca881e2ce0befa3c5140d7cd4934af3b79
parent 364190 b8ad0eee7056aa24a164c327089ce3aa634a7ce8
child 364192 f53990b76f46e9a34e93f9f1a6bb48c0a80f5ad2
child 364201 6fa970aefeb4360ec493302c5e5ae0c446c44ba1
child 364211 9f804366c446ca24bd5fb265096f12d85564e26e
push id17382
push userbmo:jyavenard@mozilla.com
push dateFri, 06 May 2016 05:24:48 +0000
reviewersgerald
bugs1269408
milestone49.0a1
Bug 1269408: P3. Ensure a new seek request will cancel the previous internal seek. r?gerald MozReview-Commit-ID: 3dR8JWt4KSN
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -65,16 +65,17 @@ MediaFormatReader::MediaFormatReader(Abs
   , mDemuxer(aDemuxer)
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(aLayersBackend)
   , mInitDone(false)
   , mIsEncrypted(false)
   , mTrackDemuxersMayBlock(false)
   , mDemuxOnly(false)
+  , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
@@ -824,38 +825,42 @@ MediaFormatReader::UpdateReceivedNewData
     decoder.mTimeThreshold.ref().mWaiting = false;
   }
   decoder.mWaitingForData = false;
 
   if (decoder.mError) {
     return false;
   }
 
+  if (!mSeekPromise.IsEmpty()) {
+    MOZ_ASSERT(!decoder.HasPromise());
+    MOZ_DIAGNOSTIC_ASSERT(!mAudio.mTimeThreshold && !mVideo.mTimeThreshold,
+                          "InternalSeek must have been aborted when Seek was first called");
+    MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasWaitingPromise() && !mVideo.HasWaitingPromise(),
+                          "Waiting promises must have been rejected when Seek was first called");
+    if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) {
+      // Already waiting for a seek to complete. Nothing more to do.
+      return true;
+    }
+    LOG("Attempting Seek");
+    ScheduleSeek();
+    return true;
+  }
   if (decoder.HasInternalSeekPending() || decoder.HasWaitingPromise()) {
     if (decoder.HasInternalSeekPending()) {
       LOG("Attempting Internal Seek");
       InternalSeek(aTrack, decoder.mTimeThreshold.ref());
     }
     if (decoder.HasWaitingPromise()) {
       MOZ_ASSERT(!decoder.HasPromise());
       LOG("We have new data. Resolving WaitingPromise");
       decoder.mWaitingPromise.Resolve(decoder.mType, __func__);
     }
     return true;
   }
-  if (!mSeekPromise.IsEmpty()) {
-    MOZ_ASSERT(!decoder.HasPromise());
-    if (mVideo.mSeekRequest.Exists() || mAudio.mSeekRequest.Exists()) {
-      // Already waiting for a seek to complete. Nothing more to do.
-      return true;
-    }
-    LOG("Attempting Seek");
-    AttemptSeek();
-    return true;
-  }
   return false;
 }
 
 void
 MediaFormatReader::RequestDemuxSamples(TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
@@ -1296,34 +1301,47 @@ MediaFormatReader::ResetDecode()
 
   // Do the same for any data wait promises.
   mAudio.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::CANCELED), __func__);
   mVideo.mWaitingPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::CANCELED), __func__);
 
   // Reset miscellaneous seeking state.
   mPendingSeekTime.reset();
 
+  ResetDemuxers();
+
   if (HasVideo()) {
-    mVideo.ResetDemuxer();
     Flush(TrackInfo::kVideoTrack);
     if (mVideo.HasPromise()) {
       mVideo.RejectPromise(CANCELED, __func__);
     }
   }
   if (HasAudio()) {
-    mAudio.ResetDemuxer();
     Flush(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
       mAudio.RejectPromise(CANCELED, __func__);
     }
   }
   return MediaDecoderReader::ResetDecode();
 }
 
 void
+MediaFormatReader::ResetDemuxers()
+{
+  if (HasVideo()) {
+    mVideo.ResetDemuxer();
+    mVideo.ResetState();
+  }
+  if (HasAudio()) {
+    mAudio.ResetDemuxer();
+    mAudio.ResetState();
+  }
+}
+
+void
 MediaFormatReader::Output(TrackType aTrack, MediaData* aSample)
 {
   LOGV("Decoded %s sample time=%lld timecode=%lld kf=%d dur=%lld",
        TrackTypeToStr(aTrack), aSample->mTime, aSample->mTimecode,
        aSample->mKeyframe, aSample->mDuration);
 
   if (!aSample) {
     NS_WARNING("MediaFormatReader::Output() passed a null sample");
@@ -1474,32 +1492,42 @@ MediaFormatReader::Seek(SeekTarget aTarg
     return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   mOriginalSeekTarget = Some(aTarget);
   mPendingSeekTime = Some(aTarget.GetTime());
 
   RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
 
-  OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaFormatReader::AttemptSeek));
+  ScheduleSeek();
 
   return p;
 }
 
 void
+MediaFormatReader::ScheduleSeek()
+{
+  if (mSeekScheduled) {
+    return;
+  }
+  mSeekScheduled = true;
+  OwnerThread()->Dispatch(NewRunnableMethod(this, &MediaFormatReader::AttemptSeek));
+}
+
+void
 MediaFormatReader::AttemptSeek()
 {
   MOZ_ASSERT(OnTaskQueue());
+
+  mSeekScheduled = false;
+
   if (mPendingSeekTime.isNothing()) {
     return;
   }
-  // An internal seek may be pending due to Seek queueing multiple tasks calling
-  // AttemptSeek ; we can ignore those by resetting any pending demuxer's seek.
-  mAudio.mSeekRequest.DisconnectIfExists();
-  mVideo.mSeekRequest.DisconnectIfExists();
+  ResetDemuxers();
   if (HasVideo()) {
     DoVideoSeek();
   } else if (HasAudio()) {
     DoAudioSeek();
   } else {
     MOZ_CRASH();
   }
 }
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -169,16 +169,17 @@ private:
   // functions.
   void Output(TrackType aType, MediaData* aSample);
   void InputExhausted(TrackType aTrack);
   void Error(TrackType aTrack);
   void Flush(TrackType aTrack);
   void DrainComplete(TrackType aTrack);
 
   bool ShouldSkip(bool aSkipToNextKeyframe, media::TimeUnit aTimeThreshold);
+  void ResetDemuxers();
 
   size_t SizeOfQueue(TrackType aTrack);
 
   RefPtr<PDMFactory> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
   public:
     DecoderCallback(MediaFormatReader* aReader, TrackType aType)
@@ -457,24 +458,26 @@ private:
   // Set to true if any of our track buffers may be blocking.
   bool mTrackDemuxersMayBlock;
 
   // Set the demuxed-only flag.
   Atomic<bool> mDemuxOnly;
 
   // Seeking objects.
   bool IsSeeking() const { return mPendingSeekTime.isSome(); }
+  void ScheduleSeek();
   void AttemptSeek();
   void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
   void DoVideoSeek();
   void OnVideoSeekCompleted(media::TimeUnit aTime);
   void OnVideoSeekFailed(DemuxerFailureReason aFailure)
   {
     OnSeekFailed(TrackType::kVideoTrack, aFailure);
   }
+  bool mSeekScheduled;
 
   void DoAudioSeek();
   void OnAudioSeekCompleted(media::TimeUnit aTime);
   void OnAudioSeekFailed(DemuxerFailureReason aFailure)
   {
     OnSeekFailed(TrackType::kAudioTrack, aFailure);
   }