Bug 1375922 - don't send frames to the compositor if the 1st one is a future frame. draft
authorJW Wang <jwwang@mozilla.com>
Mon, 03 Jul 2017 11:51:39 +0800
changeset 603942 333bb4fec5b3bb1f4e2e12f212f2ddfc4df1f11e
parent 603940 1b9922ef568da217dcffe8f4b845cd21e4af0eab
child 636045 969cd16fd24052b6148d41488c1d5fd8dfbeae2e
push id66910
push userjwwang@mozilla.com
push dateWed, 05 Jul 2017 02:27:55 +0000
bugs1375922
milestone56.0a1
Bug 1375922 - don't send frames to the compositor if the 1st one is a future frame. A/V sync will be broken for the compositor will render the 1st frame immediately without considering its timestamp. MozReview-Commit-ID: 6j7GLccrFcX
dom/media/mediasink/VideoSink.cpp
--- a/dom/media/mediasink/VideoSink.cpp
+++ b/dom/media/mediasink/VideoSink.cpp
@@ -302,20 +302,44 @@ VideoSink::Redraw(const VideoInfo& aInfo
     mContainer->GetImageContainer()->CreatePlanarYCbCrImage();
   mContainer->SetCurrentFrame(aInfo.mDisplay, blank, TimeStamp::Now());
 }
 
 void
 VideoSink::TryUpdateRenderedVideoFrames()
 {
   AssertOwnerThread();
-  if (!mUpdateScheduler.IsScheduled() && VideoQueue().GetSize() >= 1 &&
-      mAudioSink->IsPlaying()) {
+  if (mUpdateScheduler.IsScheduled() || !mAudioSink->IsPlaying()) {
+    return;
+  }
+  RefPtr<VideoData> v = VideoQueue().PeekFront();
+  if (!v) {
+    // No frames to render.
+    return;
+  }
+
+  TimeStamp nowTime;
+  const TimeUnit clockTime = mAudioSink->GetPosition(&nowTime);
+  if (clockTime >= v->mTime) {
+    // Time to render this frame.
     UpdateRenderedVideoFrames();
+    return;
   }
+
+  // If we send this future frame to the compositor now, it will be rendered
+  // immediately and break A/V sync. Instead, we schedule a timer to send it
+  // later.
+  int64_t delta = (v->mTime - clockTime).ToMicroseconds() /
+                  mAudioSink->GetPlaybackParams().mPlaybackRate;
+  TimeStamp target = nowTime + TimeDuration::FromMicroseconds(delta);
+  RefPtr<VideoSink> self = this;
+  mUpdateScheduler.Ensure(
+    target,
+    [self]() { self->UpdateRenderedVideoFramesByTimer(); },
+    [self]() { self->UpdateRenderedVideoFramesByTimer(); });
 }
 
 void
 VideoSink::UpdateRenderedVideoFramesByTimer()
 {
   AssertOwnerThread();
   mUpdateScheduler.CompleteRequest();
   UpdateRenderedVideoFrames();