From 17ca191fb514f8f9211af6183b3d48462cdba821 Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 1 Feb 2021 09:44:22 +0000 Subject: [PATCH] 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 --- .../transformer/MediaCodecAdapterWrapper.java | 89 +++++++++---------- .../transformer/TransformerAudioRenderer.java | 74 +++++++-------- 2 files changed, 76 insertions(+), 87 deletions(-) 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 5cd9c624e3..240506a48e 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 @@ -182,66 +182,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; inputBuffer.data = null; } - /** - * Dequeues an output buffer, if available. - * - *

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 csdBuffers = new ImmutableList.Builder<>(); int csdIndex = 0; diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java index a4c275a3f4..627120acb4 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/TransformerAudioRenderer.java @@ -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; }