Simplify output buffer handling in transformer

We can dequeue as part of getting output buffers (or output buffer info) in
`MediaCodecAdapterWrapper`, which simplifies the caller slightly.

Also try to make minor clarifications in method naming in
`TransformerAudioRenderer`.

#minor-release

PiperOrigin-RevId: 354890796
This commit is contained in:
andrewlewis 2021-02-01 09:44:22 +00:00 committed by Oliver Woodman
parent 50344e2703
commit 17ca191fb5
2 changed files with 76 additions and 87 deletions

View File

@ -182,66 +182,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
inputBuffer.data = null; inputBuffer.data = null;
} }
/**
* Dequeues an output buffer, if available.
*
* <p>Once this method returns {@code true}, call {@link #getOutputBuffer()} to access the
* dequeued buffer.
*
* @return Whether an output buffer is available.
*/
public boolean maybeDequeueOutputBuffer() {
if (outputBufferIndex >= 0) {
return true;
}
if (outputStreamEnded) {
return false;
}
outputBufferIndex = codec.dequeueOutputBufferIndex(outputBufferInfo);
if (outputBufferIndex < 0) {
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = getFormat(codec.getOutputFormat());
}
return false;
}
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputStreamEnded = true;
if (outputBufferInfo.size == 0) {
releaseOutputBuffer();
return false;
}
}
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// Encountered a CSD buffer, skip it.
releaseOutputBuffer();
return false;
}
outputBuffer = checkNotNull(codec.getOutputBuffer(outputBufferIndex));
outputBuffer.position(outputBufferInfo.offset);
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
return true;
}
/** Returns the current output format, if available. */ /** Returns the current output format, if available. */
@Nullable @Nullable
public Format getOutputFormat() { public Format getOutputFormat() {
// The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now.
maybeDequeueOutputBuffer();
return outputFormat; return outputFormat;
} }
/** Returns the current output {@link ByteBuffer}, if available. */ /** Returns the current output {@link ByteBuffer}, if available. */
@Nullable @Nullable
public ByteBuffer getOutputBuffer() { public ByteBuffer getOutputBuffer() {
return outputBuffer; return maybeDequeueOutputBuffer() ? outputBuffer : null;
} }
/** Returns the {@link BufferInfo} associated with the current output buffer, if available. */ /** Returns the {@link BufferInfo} associated with the current output buffer, if available. */
@Nullable @Nullable
public BufferInfo getOutputBufferInfo() { public BufferInfo getOutputBufferInfo() {
return outputBuffer == null ? null : outputBufferInfo; return maybeDequeueOutputBuffer() ? outputBufferInfo : null;
} }
/** /**
@ -267,6 +225,45 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
codec.release(); codec.release();
} }
/**
* Returns true if there is already an output buffer pending. Otherwise attempts to dequeue an
* output buffer and returns whether there is a new output buffer.
*/
private boolean maybeDequeueOutputBuffer() {
if (outputBufferIndex >= 0) {
return true;
}
if (outputStreamEnded) {
return false;
}
outputBufferIndex = codec.dequeueOutputBufferIndex(outputBufferInfo);
if (outputBufferIndex < 0) {
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = getFormat(codec.getOutputFormat());
}
return false;
}
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
outputStreamEnded = true;
if (outputBufferInfo.size == 0) {
releaseOutputBuffer();
return false;
}
}
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
// Encountered a CSD buffer, skip it.
releaseOutputBuffer();
return false;
}
outputBuffer = checkNotNull(codec.getOutputBuffer(outputBufferIndex));
outputBuffer.position(outputBufferInfo.offset);
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
return true;
}
private static Format getFormat(MediaFormat mediaFormat) { private static Format getFormat(MediaFormat mediaFormat) {
ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>(); ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>();
int csdIndex = 0; int csdIndex = 0;

View File

@ -114,19 +114,18 @@ import java.nio.ByteBuffer;
return; return;
} }
if (!setupDecoder()) { if (ensureDecoderConfigured()) {
return; if (ensureEncoderAndAudioProcessingConfigured()) {
while (drainEncoderToFeedMuxer()) {}
if (sonicAudioProcessor.isActive()) {
while (drainSonicToFeedEncoder()) {}
while (drainDecoderToFeedSonic()) {}
} else {
while (drainDecoderToFeedEncoder()) {}
}
}
while (feedDecoderInputFromSource()) {}
} }
setupEncoderAndMaybeSonic();
while (drainEncoderToFeedMuxer()) {}
if (sonicAudioProcessor.isActive()) {
while (drainSonicToFeedEncoder()) {}
while (drainDecoderToFeedSonic()) {}
} else {
while (drainDecoderToFeedEncoder()) {}
}
while (feedDecoderInputFromSource()) {}
} }
/** /**
@ -136,8 +135,6 @@ import java.nio.ByteBuffer;
private boolean drainEncoderToFeedMuxer() { private boolean drainEncoderToFeedMuxer() {
MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder); MediaCodecAdapterWrapper encoder = checkNotNull(this.encoder);
if (!hasEncoderOutputFormat) { if (!hasEncoderOutputFormat) {
// Dequeue output format change.
encoder.maybeDequeueOutputBuffer();
@Nullable Format encoderOutputFormat = encoder.getOutputFormat(); @Nullable Format encoderOutputFormat = encoder.getOutputFormat();
if (encoderOutputFormat == null) { if (encoderOutputFormat == null) {
return false; return false;
@ -147,19 +144,15 @@ import java.nio.ByteBuffer;
} }
if (encoder.isEnded()) { if (encoder.isEnded()) {
// Encoder output stream ended and output is empty or null so end muxer track.
muxerWrapper.endTrack(getTrackType()); muxerWrapper.endTrack(getTrackType());
muxerWrapperTrackEnded = true; muxerWrapperTrackEnded = true;
return false; return false;
} }
@Nullable ByteBuffer encoderOutputBuffer = encoder.getOutputBuffer();
if (!encoder.maybeDequeueOutputBuffer()) { if (encoderOutputBuffer == null) {
return false; return false;
} }
ByteBuffer encoderOutputBuffer = checkNotNull(encoder.getOutputBuffer());
BufferInfo encoderOutputBufferInfo = checkNotNull(encoder.getOutputBufferInfo()); BufferInfo encoderOutputBufferInfo = checkNotNull(encoder.getOutputBufferInfo());
if (!muxerWrapper.writeSample( if (!muxerWrapper.writeSample(
getTrackType(), getTrackType(),
encoderOutputBuffer, encoderOutputBuffer,
@ -187,19 +180,15 @@ import java.nio.ByteBuffer;
return false; return false;
} }
if (!decoder.maybeDequeueOutputBuffer()) { @Nullable ByteBuffer decoderOutputBuffer = decoder.getOutputBuffer();
if (decoderOutputBuffer == null) {
return false; return false;
} }
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) { if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
flushSonicAndSetSpeed(currentSpeed); flushSonicAndSetSpeed(currentSpeed);
return false; return false;
} }
ByteBuffer decoderOutputBuffer = checkNotNull(decoder.getOutputBuffer());
feedEncoder(decoderOutputBuffer); feedEncoder(decoderOutputBuffer);
if (!decoderOutputBuffer.hasRemaining()) { if (!decoderOutputBuffer.hasRemaining()) {
decoder.releaseOutputBuffer(); decoder.releaseOutputBuffer();
} }
@ -246,8 +235,8 @@ import java.nio.ByteBuffer;
drainingSonicForSpeedChange = false; drainingSonicForSpeedChange = false;
} }
// Sonic invalidates the output buffer when more input is queued, so we don't queue if there is // Sonic invalidates any previous output buffer when more input is queued, so we don't queue if
// output still to be processed. // there is output still to be processed.
if (sonicOutputBuffer.hasRemaining()) { if (sonicOutputBuffer.hasRemaining()) {
return false; return false;
} }
@ -256,20 +245,17 @@ import java.nio.ByteBuffer;
sonicAudioProcessor.queueEndOfStream(); sonicAudioProcessor.queueEndOfStream();
return false; return false;
} }
checkState(!sonicAudioProcessor.isEnded()); checkState(!sonicAudioProcessor.isEnded());
if (!decoder.maybeDequeueOutputBuffer()) { @Nullable ByteBuffer decoderOutputBuffer = decoder.getOutputBuffer();
if (decoderOutputBuffer == null) {
return false; return false;
} }
if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) { if (isSpeedChanging(checkNotNull(decoder.getOutputBufferInfo()))) {
sonicAudioProcessor.queueEndOfStream(); sonicAudioProcessor.queueEndOfStream();
drainingSonicForSpeedChange = true; drainingSonicForSpeedChange = true;
return false; return false;
} }
ByteBuffer decoderOutputBuffer = checkNotNull(decoder.getOutputBuffer());
sonicAudioProcessor.queueInput(decoderOutputBuffer); sonicAudioProcessor.queueInput(decoderOutputBuffer);
if (!decoderOutputBuffer.hasRemaining()) { if (!decoderOutputBuffer.hasRemaining()) {
decoder.releaseOutputBuffer(); decoder.releaseOutputBuffer();
@ -337,18 +323,23 @@ import java.nio.ByteBuffer;
} }
/** /**
* Configures the {@link #encoder} and Sonic (if applicable), if they have not been configured * Attempts to configure the {@link #encoder} and Sonic (if applicable), if they have not been
* yet. * configured yet, and returns whether they have been configured.
*/ */
private void setupEncoderAndMaybeSonic() throws ExoPlaybackException { private boolean ensureEncoderAndAudioProcessingConfigured() throws ExoPlaybackException {
if (encoder != null) { if (encoder != null) {
return; return true;
}
MediaCodecAdapterWrapper decoder = checkNotNull(this.decoder);
@Nullable Format decoderOutputFormat = decoder.getOutputFormat();
if (decoderOutputFormat == null) {
return false;
} }
// TODO(b/161127201): Use the decoder output format once the decoder is fed before setting up
// the encoder.
AudioFormat outputAudioFormat = AudioFormat outputAudioFormat =
new AudioFormat( new AudioFormat(
checkNotNull(inputFormat).sampleRate, inputFormat.channelCount, C.ENCODING_PCM_16BIT); decoderOutputFormat.sampleRate,
decoderOutputFormat.channelCount,
decoderOutputFormat.pcmEncoding);
if (transformation.flattenForSlowMotion) { if (transformation.flattenForSlowMotion) {
try { try {
outputAudioFormat = sonicAudioProcessor.configure(outputAudioFormat); outputAudioFormat = sonicAudioProcessor.configure(outputAudioFormat);
@ -370,13 +361,14 @@ import java.nio.ByteBuffer;
throw createRendererException(e); throw createRendererException(e);
} }
encoderInputAudioFormat = outputAudioFormat; encoderInputAudioFormat = outputAudioFormat;
return true;
} }
/** /**
* Attempts to configure the {@link #decoder} if it has not been configured yet, and returns * Attempts to configure the {@link #decoder} if it has not been configured yet, and returns
* whether the decoder has been configured. * whether the decoder has been configured.
*/ */
private boolean setupDecoder() throws ExoPlaybackException { private boolean ensureDecoderConfigured() throws ExoPlaybackException {
if (decoder != null) { if (decoder != null) {
return true; return true;
} }