Commit 58e5ed0a by claincly Committed by bachinger

Fix transcoding drops a few frames.

In the old version, the transcoder uses decoder.isEnded() alone as the criteria
to stop the encoding/muxing process. It's rectified to:

- On decoder ending, signal the encoder of EOS after writing all decoded frames to it.
- On encoder ending, write end track to muxer.

PiperOrigin-RevId: 393322114
parent 9c2b4b86
...@@ -303,6 +303,11 @@ import java.nio.ByteBuffer; ...@@ -303,6 +303,11 @@ import java.nio.ByteBuffer;
codec.setVideoScalingMode(scalingMode); codec.setVideoScalingMode(scalingMode);
} }
@Override
public void signalEndOfInputStream() {
codec.signalEndOfInputStream();
}
@VisibleForTesting @VisibleForTesting
/* package */ void onError(MediaCodec.CodecException error) { /* package */ void onError(MediaCodec.CodecException error) {
asynchronousMediaCodecCallback.onError(codec, error); asynchronousMediaCodecCallback.onError(codec, error);
......
...@@ -242,4 +242,13 @@ public interface MediaCodecAdapter { ...@@ -242,4 +242,13 @@ public interface MediaCodecAdapter {
/** Whether the adapter needs to be reconfigured before it is used. */ /** Whether the adapter needs to be reconfigured before it is used. */
boolean needsReconfiguration(); 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();
} }
...@@ -200,6 +200,12 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter { ...@@ -200,6 +200,12 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
} }
@Override @Override
@RequiresApi(18)
public void signalEndOfInputStream() {
Api18.signalEndOfInputStream(codec);
}
@Override
@RequiresApi(23) @RequiresApi(23)
public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) { public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) {
codec.setOnFrameRenderedListener( codec.setOnFrameRenderedListener(
...@@ -232,5 +238,10 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter { ...@@ -232,5 +238,10 @@ public class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
public static Surface createCodecInputSurface(MediaCodec codec) { public static Surface createCodecInputSurface(MediaCodec codec) {
return codec.createInputSurface(); return codec.createInputSurface();
} }
@DoNotInline
public static void signalEndOfInputStream(MediaCodec codec) {
codec.signalEndOfInputStream();
}
} }
} }
...@@ -311,6 +311,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -311,6 +311,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
inputBuffer.data = null; inputBuffer.data = null;
} }
@RequiresApi(18)
public void signalEndOfInputStream() {
codec.signalEndOfInputStream();
}
/** Returns the current output format, if available. */ /** Returns the current output format, if available. */
@Nullable @Nullable
public Format getOutputFormat() { public Format getOutputFormat() {
......
...@@ -175,8 +175,15 @@ import java.nio.ByteBuffer; ...@@ -175,8 +175,15 @@ import java.nio.ByteBuffer;
if (decoder.isEnded()) { if (decoder.isEnded()) {
return false; return false;
} }
// Rendering the decoder output queues input to the encoder because they share the same surface. // 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() { private boolean feedMuxerFromEncoder() {
...@@ -190,10 +197,7 @@ import java.nio.ByteBuffer; ...@@ -190,10 +197,7 @@ import java.nio.ByteBuffer;
muxerWrapper.addTrackFormat(encoderOutputFormat); muxerWrapper.addTrackFormat(encoderOutputFormat);
} }
// TODO(claincly) May have to use inputStreamBuffer.isEndOfStream result to call if (encoder.isEnded()) {
// decoder.signalEndOfInputStream().
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
if (decoder.isEnded()) {
muxerWrapper.endTrack(getTrackType()); muxerWrapper.endTrack(getTrackType());
muxerWrapperTrackEnded = true; muxerWrapperTrackEnded = true;
return false; return false;
......
...@@ -268,6 +268,12 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa ...@@ -268,6 +268,12 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
delegate.setVideoScalingMode(scalingMode); delegate.setVideoScalingMode(scalingMode);
} }
@RequiresApi(18)
@Override
public void signalEndOfInputStream() {
delegate.signalEndOfInputStream();
}
// Dumpable implementation // Dumpable implementation
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment