From 955fa2f0df48da7afdd17d5e1fe4f0aeb18b5657 Mon Sep 17 00:00:00 2001 From: SylvainMorel <62572068+SylvainMorel@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:27:32 -0400 Subject: [PATCH] Fix potential video codec stall in tunneling (#27) In #26 I added an error when we detect a potential stall of the video renderer (probably caused by the codec). It wasn't working properly in tunneling, because we never handle the decoded buffer (that's pretty much what tunneling does). It was triggering 100% because of that. This PR disables the detection in tunneling. And also cleans up how it was done. --- .../mediacodec/MediaCodecRenderer.java | 31 ++++++------------- .../video/MediaCodecVideoRenderer.java | 26 ++++++++++++++++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index 3b9e840fa9f..9a23bbffad8 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -419,7 +419,7 @@ private static String buildCustomDiagnosticInfo(int errorCode) { // MIREGO: added the following field and 2 functions to log and debug a specific issue (rendering pipleine stall) // MIREGO: once the issue is solved, we should get rid of that code - private boolean hasReportedRenderingStall = false; + protected boolean hasReportedRenderingStall = false; void saveFeedInputBufferStep(int stepIndex) { if (getTrackType() == TRACK_TYPE_AUDIO) { @@ -2110,6 +2110,10 @@ private void drainAndReinitializeCodec() throws ExoPlaybackException { int dequeuedOutputCount = 0; // MIREGO for logging + protected void detectRendererStallMirego(boolean hasDequeuedBuffer) { + // NOOP + } + /** * @return Whether it may be possible to drain more output data. * @throws ExoPlaybackException If an error occurs draining the output buffer. @@ -2141,22 +2145,12 @@ private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) outputIndex = codec.dequeueOutputBufferIndex(outputBufferInfo); } - if (outputIndex < 0) { + // MIREGO video renderer stall detection + if (getTrackType() == TRACK_TYPE_VIDEO) { + detectRendererStallMirego(outputIndex >= 0); + } - // MIREGO video renderer stall detection - if (getTrackType() == TRACK_TYPE_VIDEO) { - - if (Util.currentProcessedOutputBuffers < Util.currentQueuedInputBuffers) { - // waiting for a decoded buffer to be available from the codec - long currentTimeMs = System.currentTimeMillis(); - if (Util.waitingForDecodedVideoBufferTimeMs == 0) { - Util.waitingForDecodedVideoBufferTimeMs = currentTimeMs; // starting to wait for the decoded buffer - } else if (!hasReportedRenderingStall && currentTimeMs > Util.waitingForDecodedVideoBufferTimeMs + 7000) { // been waiting for an arbitrary while, send an error to the app - Log.e(TAG, new PlaybackException("Video codec may be stalled error", new RuntimeException(), PlaybackException.ERROR_CODE_VIDEO_CODEC_STALLED)); - hasReportedRenderingStall = true; - } - } - } + if (outputIndex < 0) { // MIREGO Log.v(Log.LOG_LEVEL_VERBOSE2, TAG, "drainOutputBuffer(type:%d) Failed dequeueOutputBufferIndex res: %d (dequeuedOutputCount: %d)", @@ -2176,11 +2170,6 @@ private boolean drainOutputBuffer(long positionUs, long elapsedRealtimeUs) return false; } - // MIREGO video renderer stall detection - if (getTrackType() == TRACK_TYPE_VIDEO) { - Util.waitingForDecodedVideoBufferTimeMs = 0; // we got a decoded buffer, reset the wait time - } - // MIREGO START dequeuedOutputCount++; Log.v(Log.LOG_LEVEL_VERBOSE3, TAG, "drainOutputBuffer(type:%d) presentationTime: %d dequeuedOutputCount: %d outputIndex: %d", 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 a342daf67e1..36cef30e2fe 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 @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.video; import static android.view.Display.DEFAULT_DISPLAY; +import static androidx.media3.common.C.TRACK_TYPE_VIDEO; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkStateNotNull; @@ -1607,6 +1608,31 @@ private void detectTunnelingDroppedFrames(long presentationTimeUs) { lastRenderedTunneledBufferPresentationTimeUs = presentationTimeUs; } + protected void detectRendererStallMirego(boolean hasDequeuedBuffer) { + if (tunneling) { + return; + } + + if (hasDequeuedBuffer) { + Util.waitingForDecodedVideoBufferTimeMs = 0; // we got a decoded buffer, reset the wait time + } else { + if (Util.currentProcessedOutputBuffers < Util.currentQueuedInputBuffers) { + // waiting for a decoded buffer to be available from the codec + long currentTimeMs = System.currentTimeMillis(); + if (Util.waitingForDecodedVideoBufferTimeMs == 0) { + Util.waitingForDecodedVideoBufferTimeMs = currentTimeMs; // starting to wait for the decoded buffer + } else if (!hasReportedRenderingStall && currentTimeMs + > Util.waitingForDecodedVideoBufferTimeMs + + 7000) { // been waiting for an arbitrary while, send an error to the app + Log.e(TAG, + new PlaybackException("Video codec may be stalled error", new RuntimeException(), + PlaybackException.ERROR_CODE_VIDEO_CODEC_STALLED)); + hasReportedRenderingStall = true; + } + } + } + } + /** Called when a output EOS was received in tunneling mode. */ private void onProcessedTunneledEndOfStream() { setPendingOutputEndOfStream();