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
This commit is contained in:
andrewlewis 2023-04-14 20:40:27 +01:00 committed by Rohit Singh
parent 0e85491d4e
commit 011fc9d5d3
2 changed files with 22 additions and 2 deletions

View File

@ -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)).

View File

@ -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();
}
}