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;
}
/**
* 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. */
@Nullable
public Format getOutputFormat() {
// The format is updated when dequeueing a 'special' buffer index, so attempt to dequeue now.
maybeDequeueOutputBuffer();
return outputFormat;
}
/** Returns the current output {@link ByteBuffer}, if available. */
@Nullable
public ByteBuffer getOutputBuffer() {
return outputBuffer;
return maybeDequeueOutputBuffer() ? outputBuffer : null;
}
/** Returns the {@link BufferInfo} associated with the current output buffer, if available. */
@Nullable
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();
}
/**
* 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) {
ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>();
int csdIndex = 0;

View File

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