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
This commit is contained in:
claincly 2021-08-27 12:16:53 +01:00 committed by bachinger
parent 9c2b4b860b
commit 58e5ed0afb
6 changed files with 45 additions and 5 deletions

View file

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

View file

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

View file

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

View file

@ -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() {

View file

@ -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;

View file

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