diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java index 9cc9382238..eef4441c92 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/AsynchronousMediaCodecAdapter.java @@ -303,6 +303,11 @@ import java.nio.ByteBuffer; codec.setVideoScalingMode(scalingMode); } + @Override + public void signalEndOfInputStream() { + codec.signalEndOfInputStream(); + } + @VisibleForTesting /* package */ void onError(MediaCodec.CodecException error) { asynchronousMediaCodecCallback.onError(codec, error); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java index c620e0e4a2..7dd15954b1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecAdapter.java @@ -242,4 +242,13 @@ public interface MediaCodecAdapter { /** Whether the adapter needs to be reconfigured before it is used. */ boolean needsReconfiguration(); + + /** + * Signals the encoder of end-of-stream on input. The call can only be used when the encoder + * receives its input from a {@link Surface surface}. + * + * @see MediaCodec#signalEndOfInputStream() + */ + @RequiresApi(18) + void signalEndOfInputStream(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java index 4150592a39..1af545c6d0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/SynchronousMediaCodecAdapter.java @@ -199,6 +199,12 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter { codec.release(); } + @Override + @RequiresApi(18) + public void signalEndOfInputStream() { + Api18.signalEndOfInputStream(codec); + } + @Override @RequiresApi(23) public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) { @@ -232,5 +238,10 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter { public static Surface createCodecInputSurface(MediaCodec codec) { return codec.createInputSurface(); } + + @DoNotInline + public static void signalEndOfInputStream(MediaCodec codec) { + codec.signalEndOfInputStream(); + } } } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java index 30a2f94ff2..abe338cef0 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/MediaCodecAdapterWrapper.java @@ -311,6 +311,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; inputBuffer.data = null; } + @RequiresApi(18) + public void signalEndOfInputStream() { + codec.signalEndOfInputStream(); + } + /** Returns the current output format, if available. */ @Nullable public Format getOutputFormat() { diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java index 064142186d..545bbc6fc7 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerTranscodingVideoRenderer.java @@ -175,8 +175,15 @@ import java.nio.ByteBuffer; if (decoder.isEnded()) { return false; } + // Rendering the decoder output queues input to the encoder because they share the same surface. - return decoder.maybeDequeueRenderAndReleaseOutputBuffer(); + boolean hasProcessedOutputBuffer = decoder.maybeDequeueRenderAndReleaseOutputBuffer(); + if (decoder.isEnded()) { + checkNotNull(encoder).signalEndOfInputStream(); + // All decoded frames have been rendered to the encoder's input surface. + return false; + } + return hasProcessedOutputBuffer; } private boolean feedMuxerFromEncoder() { @@ -190,10 +197,7 @@ import java.nio.ByteBuffer; muxerWrapper.addTrackFormat(encoderOutputFormat); } - // TODO(claincly) May have to use inputStreamBuffer.isEndOfStream result to call - // decoder.signalEndOfInputStream(). - MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder); - if (decoder.isEnded()) { + if (encoder.isEnded()) { muxerWrapper.endTrack(getTrackType()); muxerWrapperTrackEnded = true; return false; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingRenderersFactory.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingRenderersFactory.java index 0a2f5832ac..edb07348f3 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingRenderersFactory.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/CapturingRenderersFactory.java @@ -268,6 +268,12 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa delegate.setVideoScalingMode(scalingMode); } + @RequiresApi(18) + @Override + public void signalEndOfInputStream() { + delegate.signalEndOfInputStream(); + } + // Dumpable implementation @Override