From 226e580a30d64b6b36ef505257de1f438699a16d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 14 May 2022 20:08:41 -0500 Subject: [PATCH] Prevent microstutter in balanced mode when streaming at 60 FPS on a 120 Hz display --- .../video/MediaCodecDecoderRenderer.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java index 6fe54098..d424e980 100644 --- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java +++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java @@ -86,6 +86,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C private LinkedBlockingQueue outputBufferQueue = new LinkedBlockingQueue<>(); private static final int OUTPUT_BUFFER_QUEUE_LIMIT = 2; + private long lastRenderedFrameTimeNanos; private int numSpsIn; private int numPpsIn; @@ -419,21 +420,28 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C return; } - // Render up to one frame when in frame pacing mode. - // - // NB: Since the queue limit is 2, we won't starve the decoder of output buffers - // by holding onto them for too long. This also ensures we will have that 1 extra - // frame of buffer to smooth over network/rendering jitter. - Integer nextOutputBuffer = outputBufferQueue.poll(); - if (nextOutputBuffer != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - videoDecoder.releaseOutputBuffer(nextOutputBuffer, frameTimeNanos); - } - else { - videoDecoder.releaseOutputBuffer(nextOutputBuffer, true); - } + // Don't render unless a new frame is due. This prevents microstutter when streaming + // at a frame rate that doesn't match the display (such as 60 FPS on 120 Hz). + long actualFrameTimeDeltaNs = frameTimeNanos - lastRenderedFrameTimeNanos; + long expectedFrameTimeDeltaNs = 800000000 / refreshRate; // within 80% of the next frame + if (actualFrameTimeDeltaNs >= expectedFrameTimeDeltaNs) { + // Render up to one frame when in frame pacing mode. + // + // NB: Since the queue limit is 2, we won't starve the decoder of output buffers + // by holding onto them for too long. This also ensures we will have that 1 extra + // frame of buffer to smooth over network/rendering jitter. + Integer nextOutputBuffer = outputBufferQueue.poll(); + if (nextOutputBuffer != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + videoDecoder.releaseOutputBuffer(nextOutputBuffer, frameTimeNanos); + } + else { + videoDecoder.releaseOutputBuffer(nextOutputBuffer, true); + } - activeWindowVideoStats.totalFramesRendered++; + lastRenderedFrameTimeNanos = frameTimeNanos; + activeWindowVideoStats.totalFramesRendered++; + } } // Request another callback for next frame