Bug 1498440 - part4 : discard redudant looping data when cancel looping. r=jya
authoralwu <alwu@mozilla.com>
Mon, 05 Nov 2018 22:04:45 +0000
changeset 444495 2e61ba5a50f21511c80536cf6c4f7ebde528fc78
parent 444494 c67e483b4ab49df78d2bae3b889275284d919a44
child 444496 1cd5c63b4182d2b9117812666d7f809e45a072e3
push id34996
push userrgurzau@mozilla.com
push dateTue, 06 Nov 2018 09:53:23 +0000
treeherdermozilla-central@e160f0a60e4f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1498440
milestone65.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 1498440 - part4 : discard redudant looping data when cancel looping. r=jya When we cancel seamless looping, we should discard audio data whose time are later than the last audio frame's time in the audio track. Differential Revision: https://phabricator.services.mozilla.com/D9189
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaQueue.h
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -148,16 +148,29 @@ static void InitVideoQueuePrefs()
       "media.video-queue.default-size", MAX_VIDEO_QUEUE_SIZE);
     sVideoQueueHWAccelSize = Preferences::GetUint(
       "media.video-queue.hw-accel-size", HW_VIDEO_QUEUE_SIZE);
     sVideoQueueSendToCompositorSize = Preferences::GetUint(
       "media.video-queue.send-to-compositor-size", VIDEO_QUEUE_SEND_TO_COMPOSITOR_SIZE);
   }
 }
 
+template <typename Type, typename Function>
+static void
+DiscardFramesFromTail(MediaQueue<Type>& aQueue, const Function&& aTest)
+{
+  while(aQueue.GetSize()) {
+    if (aTest(aQueue.PeekBack()->mTime.ToMicroseconds())) {
+      RefPtr<Type> releaseMe = aQueue.PopBack();
+      continue;
+    }
+    break;
+  }
+}
+
 // Delay, in milliseconds, that tabs needs to be in background before video
 // decoding is suspended.
 static TimeDuration
 SuspendBackgroundVideoDelay()
 {
   return TimeDuration::FromMilliseconds(
     StaticPrefs::MediaSuspendBkgndVideoDelayMs());
 }
@@ -840,16 +853,19 @@ public:
   {
     MOZ_ASSERT(mMaster->mLooping);
   }
 
   void Exit() override
   {
     mAudioDataRequest.DisconnectIfExists();
     mAudioSeekRequest.DisconnectIfExists();
+    if (ShouldDiscardLoopedAudioData()) {
+      DiscardLoopedAudioData();
+    }
     DecodingState::Exit();
   }
 
   State GetState() const override
   {
     return DECODER_STATE_LOOPING_DECODING;
   }
 
@@ -932,16 +948,47 @@ private:
       aAudio->mTime += mAudioLoopingOffset;
     }
     return aAudio->mTime.IsValid() ?
               MediaResult(NS_OK) :
               MediaResult(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
                           "Audio sample overflow during looping time adjustment");
   }
 
+  bool ShouldDiscardLoopedAudioData() const
+  {
+    /**
+     * If media cancels looping, we should check whether there are audio data
+     * whose time is later than EOS. If so, we should discard them because we
+     * won't have a chance to play them.
+     *
+     *    playback                     last decoded
+     *    position          EOS        data time
+     *   ----|---------------|------------|---------> (Increasing timeline)
+     *    mCurrent        mLooping      mMaster's
+     * PlaybackPosition    Offset      mDecodedAudioEndTime
+     *
+     */
+    return (mAudioLoopingOffset != media::TimeUnit::Zero() &&
+            mMaster->mCurrentPosition.Ref() < mAudioLoopingOffset &&
+            mAudioLoopingOffset < mMaster->mDecodedAudioEndTime);
+  }
+
+  void DiscardLoopedAudioData()
+  {
+    if (mAudioLoopingOffset == media::TimeUnit::Zero()) {
+        return;
+    }
+
+    SLOG("Discard frames after the time=%" PRId64, mAudioLoopingOffset.ToMicroseconds());
+    DiscardFramesFromTail(AudioQueue(), [&] (int64_t aSampleTime) {
+        return aSampleTime > mAudioLoopingOffset.ToMicroseconds();
+    });
+  }
+
   media::TimeUnit mAudioLoopingOffset = media::TimeUnit::Zero();
   MozPromiseRequestHolder<MediaFormatReader::SeekPromise> mAudioSeekRequest;
   MozPromiseRequestHolder<AudioDataPromise> mAudioDataRequest;
 };
 
 /**
  * Purpose: seek to a particular new playback position.
  *
@@ -2501,35 +2548,16 @@ DecodingState::Step()
   TimeUnit before = mMaster->GetMediaTime();
   mMaster->UpdatePlaybackPositionPeriodically();
 
   // Fire the `seeking` and `seeked` events to meet the HTML spec
   // when the media is looped back from the end to the beginning.
   if (before > mMaster->GetMediaTime()) {
     MOZ_ASSERT(mMaster->mLooping);
     mMaster->mOnPlaybackEvent.Notify(MediaPlaybackEvent::Loop);
-  // After looping is cancelled, the time won't be corrected, and therefore we
-  // can check it to see if the end of the media track is reached. Make sure
-  // the media is started before comparing the time, or it's meaningless.
-  // Without checking IsStarted(), the media will be terminated immediately
-  // after seeking forward. When the state is just transited from seeking state,
-  // GetClock() is smaller than GetMediaTime(), since GetMediaTime() is updated
-  // upon seek is completed while GetClock() will be updated after the media is
-  // started again.
-  } else if (mMaster->mMediaSink->IsStarted() && !mMaster->mLooping) {
-    TimeUnit adjusted = mMaster->GetClock();
-    Reader()->AdjustByLooping(adjusted);
-    if (adjusted < before) {
-      mMaster->StopPlayback();
-      mMaster->mAudioDataRequest.DisconnectIfExists();
-      AudioQueue().Finish();
-      mMaster->mAudioCompleted = true;
-      SetState<CompletedState>();
-      return;
-    }
   }
 
   MOZ_ASSERT(!mMaster->IsPlaying() || mMaster->IsStateMachineScheduled(),
              "Must have timer scheduled");
 
   MaybeStartBuffering();
 }
 
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -56,21 +56,32 @@ public:
     RecursiveMutexAutoLock lock(mRecursiveMutex);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopEvent.Notify(rv);
     }
     return rv.forget();
   }
 
+  inline already_AddRefed<T> PopBack() {
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
+    RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::Pop()));
+    return rv.forget();
+  }
+
   inline RefPtr<T> PeekFront() const {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
     return static_cast<T*>(nsDeque::PeekFront());
   }
 
+  inline RefPtr<T> PeekBack() const {
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
+    return static_cast<T*>(nsDeque::Peek());
+  }
+
   void Reset() {
     RecursiveMutexAutoLock lock(mRecursiveMutex);
     while (GetSize() > 0) {
       RefPtr<T> x = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     }
     mEndOfStream = false;
   }