From 011fc9d5d3ca25fcc4a75f2cf986552a06391bf8 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Fri, 14 Apr 2023 20:40:27 +0100 Subject: [PATCH] Fix possible lost end of stream notification In `ExternalTextureManager` in seemingly rare cases end of stream is signaled at the point where a frame is currently pending processing. In that case the video end of stream signal was lost. If the muxer timeout was enabled this case would result in throwing an exception, but otherwise the operation would get stuck Add code to signal end of stream in `onInputFrameProcessed` as well, so that we signal end of stream when the pending frame is handled. Tested by running `TransformerEndToEndTest.loopingTranscodedVideo_producesExpectedResult` several times. PiperOrigin-RevId: 524361069 --- RELEASENOTES.md | 3 +++ .../media3/effect/ExternalTextureManager.java | 21 +++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 21bfce104b..96d315ca65 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -89,6 +89,9 @@ instead. * Remove `Transformer.startTransformation(MediaItem, ParcelFileDescriptor)`. + * Fix a bug where transformation could get stuck (leading to muxer + timeout) if the end of the video stream was signaled at the moment when + an input frame was pending processing. * DASH: * Fix handling of empty segment timelines ([#11014](https://github.com/google/ExoPlayer/issues/11014)). diff --git a/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java b/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java index 359e15f5cf..1957d073fa 100644 --- a/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java +++ b/libraries/effect/src/main/java/androidx/media3/effect/ExternalTextureManager.java @@ -56,6 +56,12 @@ import java.util.concurrent.atomic.AtomicInteger; // Read and written on the GL thread only. private boolean inputStreamEnded; + + // TODO(b/278273122): Remove this flag and the signal end of input call after queueing a frame if + // all frames notify that they've been processed. + + // Read and written on the GL thread only. + private boolean hasSignaledEndOfInput; // The frame that is sent downstream and is not done processing yet. // Set to null on any thread. Read and set to non-null on the GL thread only. @Nullable private volatile FrameInfo currentFrame; @@ -128,7 +134,11 @@ import java.util.concurrent.atomic.AtomicInteger; videoFrameProcessingTaskExecutor.submit( () -> { currentFrame = null; - maybeQueueFrameToExternalShaderProgram(); + if (inputStreamEnded && pendingFrames.isEmpty()) { + maybeSignalEndOfInput(); + } else { + maybeQueueFrameToExternalShaderProgram(); + } }); } @@ -173,7 +183,7 @@ import java.util.concurrent.atomic.AtomicInteger; () -> { inputStreamEnded = true; if (pendingFrames.isEmpty() && currentFrame == null) { - externalShaderProgram.signalEndOfCurrentInputStream(); + maybeSignalEndOfInput(); } }); } @@ -235,6 +245,13 @@ import java.util.concurrent.atomic.AtomicInteger; checkStateNotNull(pendingFrames.remove()); if (inputStreamEnded && pendingFrames.isEmpty()) { + maybeSignalEndOfInput(); + } + } + + private void maybeSignalEndOfInput() { + if (!hasSignaledEndOfInput) { + hasSignaledEndOfInput = true; externalShaderProgram.signalEndOfCurrentInputStream(); } }