Bug 1365190 P1 - NextFrameSeekingState should be aware of the existing video data request; r?jwwang draft
authorKaku Kuo <kaku@mozilla.com>
Thu, 25 May 2017 12:06:34 +0800
changeset 584342 9eba20b237515c269071a326d1f59490e1b7b76c
parent 584216 f81bcc23d37d7bec48f08b19a9327e93c54d37b5
child 584343 a92adcc3f5afd50f83eca34257151722f4d26ef1
push id60698
push userbmo:kaku@mozilla.com
push dateThu, 25 May 2017 09:22:08 +0000
reviewersjwwang
bugs1365190
milestone55.0a1
Bug 1365190 P1 - NextFrameSeekingState should be aware of the existing video data request; r?jwwang MozReview-Commit-ID: 3deIwYbQ3BM
dom/media/MediaDecoderStateMachine.cpp
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -985,19 +985,21 @@ public:
     if (aVisibility == EventVisibility::Observable) {
       mMaster->mOnPlaybackEvent.Notify(MediaEventType::SeekStarted);
       // We want dormant actions to be transparent to the user.
       // So we only notify the change when the seek request is from the user.
       mMaster->UpdateNextFrameStatus(
         MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING);
     }
 
+    RefPtr<MediaDecoder::SeekPromise> p = mSeekJob.mPromise.Ensure(__func__);
+
     DoSeek();
 
-    return mSeekJob.mPromise.Ensure(__func__);
+    return p;
   }
 
   virtual void Exit() override = 0;
 
   State GetState() const override
   {
     return DECODER_STATE_SEEKING;
   }
@@ -1512,17 +1514,17 @@ public:
     mCurrentTime = mMaster->GetMediaTime();
     mDuration = mMaster->Duration();
     return SeekingState::Enter(Move(aSeekJob), aVisibility);
   }
 
   void Exit() override
   {
     // Disconnect my async seek operation.
-    mAsyncSeekTask->Cancel();
+    if (mAsyncSeekTask) { mAsyncSeekTask->Cancel(); }
 
     // Disconnect MediaDecoder.
     mSeekJob.RejectIfExists(__func__);
   }
 
   void HandleAudioDecoded(AudioData* aAudio) override
   {
     mMaster->PushAudio(aAudio);
@@ -1604,17 +1606,31 @@ public:
   {
     // The HTMLMediaElement.currentTime should be updated to the seek target
     // which has been updated to the next frame's time.
     return mSeekJob.mTarget->GetTime();
   }
 
   void DoSeek() override
   {
-    // We need to do the seek operation asynchronously. Because for a special
+    auto currentTime = mCurrentTime;
+    DiscardFrames(VideoQueue(), [currentTime] (int64_t aSampleTime) {
+      return aSampleTime <= currentTime.ToMicroseconds();
+    });
+
+    // If there is a pending video request, finish the seeking if we don't need
+    // more data, or wait for HandleVideoDecoded() to finish seeking.
+    if (mMaster->IsRequestingVideoData()) {
+      if (!NeedMoreVideo()) {
+        FinishSeek();
+      }
+      return;
+    }
+
+    // Otherwise, we need to do the seek operation asynchronously for a special
     // case (bug504613.ogv) which has no data at all, the 1st seekToNextFrame()
     // operation reaches the end of the media. If we did the seek operation
     // synchronously, we immediately resolve the SeekPromise in mSeekJob and
     // then switch to the CompletedState which dispatches an "ended" event.
     // However, the ThenValue of the SeekPromise has not yet been set, so the
     // promise resolving is postponed and then the JS developer receives the
     // "ended" event before the seek promise is resolved.
     // An asynchronous seek operation helps to solve this issue since while the
@@ -1622,20 +1638,19 @@ public:
     // been set so that it won't be postponed.
     RefPtr<Runnable> r = mAsyncSeekTask = new AysncNextFrameSeekTask(this);
     OwnerThread()->Dispatch(r.forget());
   }
 
 private:
   void DoSeekInternal()
   {
-    auto currentTime = mCurrentTime;
-    DiscardFrames(VideoQueue(), [currentTime] (int64_t aSampleTime) {
-      return aSampleTime <= currentTime.ToMicroseconds();
-    });
+    // We don't need to discard frames to the mCurrentTime here because we have
+    // done it at DoSeek() and any video data received in between either
+    // finishes the seek operation or be discarded, see HandleVideoDecoded().
 
     if (!NeedMoreVideo()) {
       FinishSeek();
     } else if (!mMaster->IsRequestingVideoData()
                && !mMaster->IsWaitingVideoData()) {
       RequestVideoData();
     }
   }