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
--- 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();