Bug 1309516 part 7 - modify the seek operation;r?jwwang draft
authorKaku Kuo <kaku@mozilla.com>
Sun, 16 Oct 2016 22:15:29 +0800
changeset 441573 ad72bba42fdf1506f6abcdc0d1b3bfb48b1c2b37
parent 441572 060d81a458805c1d61a141a3cb2c77b135d71385
child 441574 25d91eb128bc173839d3022987b192b39cb21323
push id36449
push userbmo:kaku@mozilla.com
push dateSat, 19 Nov 2016 07:05:35 +0000
reviewersjwwang
bugs1309516
milestone53.0a1
Bug 1309516 part 7 - modify the seek operation;r?jwwang MozReview-Commit-ID: AZ9yK050ElM
dom/media/AccurateSeekTask.cpp
dom/media/AccurateSeekTask.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaFormatReader.cpp
dom/media/NextFrameSeekTask.cpp
dom/media/NextFrameSeekTask.h
dom/media/SeekTask.h
--- a/dom/media/AccurateSeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -72,16 +72,46 @@ AccurateSeekTask::Discard()
 
 bool
 AccurateSeekTask::NeedToResetMDSM() const
 {
   AssertOwnerThread();
   return true;
 }
 
+int64_t
+AccurateSeekTask::CalculateNewCurrentTime() const
+{
+  AssertOwnerThread();
+
+  const int64_t seekTime = mTarget.GetTime().ToMicroseconds();
+
+  // For the accurate seek, we always set the newCurrentTime = seekTime so that
+  // the updated HTMLMediaElement.currentTime will always be the seek target;
+  // we rely on the MediaSink to handles the gap between the newCurrentTime and
+  // the real decoded samples' start time.
+  if (mTarget.IsAccurate()) {
+    return seekTime;
+  }
+
+  // For the fast seek, we update the newCurrentTime with the decoded audio and
+  // video samples, set it to be the one which is closet to the seekTime.
+  if (mTarget.IsFast()) {
+    MOZ_ASSERT(mSeekedAudioData || mSeekedVideoData);
+    const int64_t audioStart = mSeekedAudioData ? mSeekedAudioData->mTime : INT64_MAX;
+    const int64_t videoStart = mSeekedVideoData ? mSeekedVideoData->mTime : INT64_MAX;
+    const int64_t audioGap = std::abs(audioStart - seekTime);
+    const int64_t videoGap = std::abs(videoStart - seekTime);
+    return audioGap <= videoGap ? audioStart : videoStart;
+  }
+
+  MOZ_ASSERT(false, "AccurateSeekTask doesn't handle other seek types.");
+  return 0;
+}
+
 RefPtr<AccurateSeekTask::SeekTaskPromise>
 AccurateSeekTask::Seek(const media::TimeUnit& aDuration)
 {
   AssertOwnerThread();
 
   // Do the seek.
   mSeekRequest.Begin(mReader->Seek(mTarget, aDuration)
     ->Then(OwnerThread(), __func__, this,
--- a/dom/media/AccurateSeekTask.h
+++ b/dom/media/AccurateSeekTask.h
@@ -24,16 +24,18 @@ public:
                    int64_t aCurrentMediaTime);
 
   void Discard() override;
 
   RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
 
   bool NeedToResetMDSM() const override;
 
+  int64_t CalculateNewCurrentTime() const override;
+
 private:
   ~AccurateSeekTask();
 
   void RequestVideoData();
 
   void RequestAudioData();
 
   nsresult DropAudioUpToSeekTarget(MediaData* aSample);
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1446,42 +1446,17 @@ DecodingState::MaybeStartBuffering()
     SetState<BufferingState>();
   }
 }
 
 void
 MediaDecoderStateMachine::
 SeekingState::SeekCompleted()
 {
-  int64_t seekTime = mSeekTask->GetSeekTarget().GetTime().ToMicroseconds();
-  int64_t newCurrentTime = seekTime;
-
-  // Setup timestamp state.
-  RefPtr<MediaData> video = mMaster->VideoQueue().PeekFront();
-  if (seekTime == mMaster->Duration().ToMicroseconds()) {
-    newCurrentTime = seekTime;
-  } else if (mMaster->HasAudio()) {
-    RefPtr<MediaData> audio = AudioQueue().PeekFront();
-    // Though we adjust the newCurrentTime in audio-based, and supplemented
-    // by video. For better UX, should NOT bind the slide position to
-    // the first audio data timestamp directly.
-    // While seeking to a position where there's only either audio or video, or
-    // seeking to a position lies before audio or video, we need to check if
-    // seekTime is bounded in suitable duration. See Bug 1112438.
-    int64_t audioStart = audio ? audio->mTime : seekTime;
-    // We only pin the seek time to the video start time if the video frame
-    // contains the seek time.
-    if (video && video->mTime <= seekTime && video->GetEndTime() > seekTime) {
-      newCurrentTime = std::min(audioStart, video->mTime);
-    } else {
-      newCurrentTime = audioStart;
-    }
-  } else {
-    newCurrentTime = video ? video->mTime : seekTime;
-  }
+  const int64_t newCurrentTime = mSeekTask->CalculateNewCurrentTime();
 
   bool isLiveStream = Resource()->IsLiveStream();
   if (newCurrentTime == mMaster->Duration().ToMicroseconds() && !isLiveStream) {
     // Seeked to end of media. Explicitly finish the queues so DECODING
     // will transition to COMPLETED immediately. Note we don't do
     // this when playing a live stream, since the end of media will advance
     // once we download more data!
     AudioQueue().Finish();
@@ -1512,17 +1487,17 @@ SeekingState::SeekCompleted()
     // Otherwise we might have |newCurrentTime > mMediaSink->GetPosition()|
     // and fail the assertion in GetClock() since we didn't stop MediaSink.
     mMaster->UpdatePlaybackPositionInternal(newCurrentTime);
   }
 
   // Try to decode another frame to detect if we're at the end...
   SLOG("Seek completed, mCurrentPosition=%lld", mMaster->mCurrentPosition.Ref());
 
-  if (video) {
+  if (mMaster->VideoQueue().PeekFront()) {
     mMaster->mMediaSink->Redraw(Info().mVideo);
     mMaster->mOnPlaybackEvent.Notify(MediaEventType::Invalidate);
   }
 
   if (mVisibility == EventVisibility::Observable) {
     mMaster->UpdateNextFrameStatus();
   }
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1955,26 +1955,18 @@ MediaFormatReader::Seek(SeekTarget aTarg
   return p;
 }
 
 void
 MediaFormatReader::SetSeekTarget(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  SeekTarget target = aTarget;
-
-  // Transform the seek target time to the demuxer timeline.
-  if (!ForceZeroStartTime()) {
-    target.SetTime(aTarget.GetTime() - TimeUnit::FromMicroseconds(StartTime())
-                   + mInfo.mStartTime);
-  }
-
-  mOriginalSeekTarget = target;
-  mFallbackSeekTime = mPendingSeekTime = Some(target.GetTime());
+  mOriginalSeekTarget = aTarget;
+  mFallbackSeekTime = mPendingSeekTime = Some(aTarget.GetTime());
 }
 
 void
 MediaFormatReader::ScheduleSeek()
 {
   if (mSeekScheduled) {
     return;
   }
--- a/dom/media/NextFrameSeekTask.cpp
+++ b/dom/media/NextFrameSeekTask.cpp
@@ -63,16 +63,26 @@ NextFrameSeekTask::Discard()
 
 bool
 NextFrameSeekTask::NeedToResetMDSM() const
 {
   AssertOwnerThread();
   return false;
 }
 
+int64_t
+NextFrameSeekTask::CalculateNewCurrentTime() const
+{
+  AssertOwnerThread();
+
+  // The HTMLMediaElement.currentTime should be updated to the seek target
+  // which has been updated to the next frame's time.
+  return mTarget.GetTime().ToMicroseconds();
+}
+
 /*
  * Remove samples from the queue until aCompare() returns false.
  * aCompare A function object with the signature bool(int64_t) which returns
  *          true for samples that should be removed.
  */
 template <typename Function> static void
 DiscardFrames(MediaQueue<MediaData>& aQueue, const Function& aCompare)
 {
--- a/dom/media/NextFrameSeekTask.h
+++ b/dom/media/NextFrameSeekTask.h
@@ -35,16 +35,18 @@ public:
                    MediaQueue<MediaData>& aVideoQueue);
 
   void Discard() override;
 
   RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) override;
 
   bool NeedToResetMDSM() const override;
 
+  int64_t CalculateNewCurrentTime() const override;
+
 private:
   ~NextFrameSeekTask();
 
   void RequestVideoData();
 
   bool NeedMoreVideo() const;
 
   bool IsVideoRequestPending() const;
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -53,16 +53,18 @@ public:
     MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
 
   virtual void Discard() = 0;
 
   virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration) = 0;
 
   virtual bool NeedToResetMDSM() const = 0;
 
+  virtual int64_t CalculateNewCurrentTime() const = 0;
+
   const SeekTarget& GetSeekTarget();
 
 protected:
   SeekTask(const void* aDecoderID,
            AbstractThread* aThread,
            MediaDecoderReaderWrapper* aReader,
            const SeekTarget& aTarget);