From 8d47209a46d53b4cf4728a037bc41d2f954e4062 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Thu, 29 Mar 2018 09:01:35 -0700 Subject: [PATCH] Make video playback smoother at the beginning Video renderers assume that the player position is advancing linearly while in the started state. MediaCodecVideoRenderer schedules frames for rendering in the future in the expectation that the player position is advancing. This assumption is not currently true when using a position from the AudioTrack: the player position can be fixed for (in the worst case) up to about 100 ms before it starts increasing. This leads to an effect where the first frame is rendered then a few other frames are rendered, then there's a pause before frames start being rendered smoothly. Work around this issue by checking whether the position has started advancing before scheduling frames to be rendered in the future. It might be preferable to make the audio renderer only become ready when its timestamp can start advancing, but this is likely to be complicated. Issue: #3841 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=190937429 --- .../android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java | 8 +++++++- .../android/exoplayer2/video/MediaCodecVideoRenderer.java | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index 4d75f6076b9..f3720fee0ad 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -127,6 +127,7 @@ public class LibvpxVideoRenderer extends BaseRenderer { private Bitmap bitmap; private boolean renderedFirstFrame; + private long initialPositionUs; private long joiningDeadlineMs; private Surface surface; private VpxOutputBufferRenderer outputBufferRenderer; @@ -303,6 +304,7 @@ protected void onPositionReset(long positionUs, boolean joining) throws ExoPlayb inputStreamEnded = false; outputStreamEnded = false; clearRenderedFirstFrame(); + initialPositionUs = C.TIME_UNSET; consecutiveDroppedFrameCount = 0; if (decoder != null) { flushDecoder(); @@ -809,6 +811,10 @@ private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) */ private boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException { + if (initialPositionUs == C.TIME_UNSET) { + initialPositionUs = positionUs; + } + long earlyUs = outputBuffer.timeUs - positionUs; if (outputMode == VpxDecoder.OUTPUT_MODE_NONE) { // Skip frames in sync with playback, so we'll be at the right frame if the mode changes. @@ -828,7 +834,7 @@ && shouldForceRenderOutputBuffer(earlyUs, elapsedRealtimeNowUs - lastRenderTimeU return true; } - if (!isStarted) { + if (!isStarted || positionUs == initialPositionUs) { return false; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 7a75e5d352e..979088f4215 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -100,6 +100,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { @C.VideoScalingMode private int scalingMode; private boolean renderedFirstFrame; + private long initialPositionUs; private long joiningDeadlineMs; private long droppedFrameAccumulationStartTimeMs; private int droppedFrames; @@ -276,6 +277,7 @@ protected void onStreamChanged(Format[] formats, long offsetUs) throws ExoPlayba protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { super.onPositionReset(positionUs, joining); clearRenderedFirstFrame(); + initialPositionUs = C.TIME_UNSET; consecutiveDroppedFrameCount = 0; if (pendingOutputStreamOffsetCount != 0) { outputStreamOffsetUs = pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]; @@ -532,6 +534,10 @@ protected void onOutputFormatChanged(MediaCodec codec, MediaFormat outputFormat) protected boolean processOutputBuffer(long positionUs, long elapsedRealtimeUs, MediaCodec codec, ByteBuffer buffer, int bufferIndex, int bufferFlags, long bufferPresentationTimeUs, boolean shouldSkip) throws ExoPlaybackException { + if (initialPositionUs == C.TIME_UNSET) { + initialPositionUs = positionUs; + } + while (pendingOutputStreamOffsetCount != 0 && bufferPresentationTimeUs >= pendingOutputStreamOffsetsUs[0]) { outputStreamOffsetUs = pendingOutputStreamOffsetsUs[0]; @@ -569,7 +575,7 @@ && shouldForceRenderOutputBuffer(earlyUs, elapsedRealtimeNowUs - lastRenderTimeU return true; } - if (!isStarted) { + if (!isStarted || positionUs == initialPositionUs) { return false; }