From 58a977e0c048012936633dbdfb581269edb68c65 Mon Sep 17 00:00:00 2001 From: christosts Date: Mon, 20 Feb 2023 12:38:42 +0000 Subject: [PATCH] Skip rendering multiple frames on the same vsync When rendering frames at a rate higher than the screen refresh rate, e.g. playing at 8x, the player is releasing multiple frames at the same release time (nanos) which are then dropped by the platform. The output buffers are available later and as a result MediaCodec cannot keep up decoding fast enough. This change skips releasing multiple video frames on the same vsync period and proactivelly drops the frame. The frame is counted as skipped rather than dropped to differentiate with frames dropped due to slow decoding. PiperOrigin-RevId: 510964976 (cherry picked from commit ab7e84fb34b7ef4b13e492e1f8918345c712ec30) --- .../exoplayer/video/MediaCodecVideoRenderer.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 1bd45fc24a..6b9d4b567d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -153,6 +153,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private long lastRenderRealtimeUs; private long totalVideoFrameProcessingOffsetUs; private int videoFrameProcessingOffsetCount; + private long lastFrameReleaseTimeNs; private int currentWidth; private int currentHeight; @@ -1128,9 +1129,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (Util.SDK_INT >= 21) { // Let the underlying framework time the release. if (earlyUs < 50000) { - notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format); - renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs); + if (adjustedReleaseTimeNs == lastFrameReleaseTimeNs) { + // This frame should be displayed on the same vsync with the previous released frame. We + // are likely rendering frames at a rate higher than the screen refresh rate. Skip + // this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not + // be able to keep decoding with this rate [b/263454203]. + skipOutputBuffer(codec, bufferIndex, presentationTimeUs); + } else { + notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format); + renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs); + } updateVideoFrameProcessingOffsetCounters(earlyUs); + lastFrameReleaseTimeNs = adjustedReleaseTimeNs; return true; } } else {