From 4d03d3089000a3a29119bf83aada99c82049d6c9 Mon Sep 17 00:00:00 2001 From: olly Date: Tue, 4 Aug 2020 14:25:39 +0100 Subject: [PATCH] Audio extension decoders: Pass decoder to getOutputFormat It seems generally useful to have access to the decoder in getOutputFormat. We're currently working around lack of access by using member variables in the concrete audio extension renderers. In the case of the Ffmpeg extension, holding a reference to the decoder is preventing it from being garbage collected when the decoder is released by the base class. PiperOrigin-RevId: 324799670 --- .../ext/ffmpeg/FfmpegAudioRenderer.java | 9 +++----- .../ext/flac/LibflacAudioRenderer.java | 11 +++------- .../ext/opus/LibopusAudioRenderer.java | 17 +++++--------- .../exoplayer2/ext/opus/OpusDecoder.java | 17 ++++---------- .../audio/DecoderAudioRenderer.java | 22 ++++++++++--------- .../audio/DecoderAudioRendererTest.java | 10 ++++----- 6 files changed, 32 insertions(+), 54 deletions(-) diff --git a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java index e4b889c174..0718dc2c5c 100644 --- a/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java +++ b/extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java @@ -34,10 +34,9 @@ import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.Util; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Decodes and renders audio using FFmpeg. */ -public final class FfmpegAudioRenderer extends DecoderAudioRenderer { +public final class FfmpegAudioRenderer extends DecoderAudioRenderer { private static final String TAG = "FfmpegAudioRenderer"; @@ -46,8 +45,6 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { /** The default input buffer size. */ private static final int DEFAULT_INPUT_BUFFER_SIZE = 960 * 6; - private @MonotonicNonNull FfmpegAudioDecoder decoder; - public FfmpegAudioRenderer() { this(/* eventHandler= */ null, /* eventListener= */ null); } @@ -122,7 +119,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { TraceUtil.beginSection("createFfmpegAudioDecoder"); int initialInputBufferSize = format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE; - decoder = + FfmpegAudioDecoder decoder = new FfmpegAudioDecoder( format, NUM_BUFFERS, NUM_BUFFERS, initialInputBufferSize, shouldOutputFloat(format)); TraceUtil.endSection(); @@ -130,7 +127,7 @@ public final class FfmpegAudioRenderer extends DecoderAudioRenderer { } @Override - public Format getOutputFormat() { + public Format getOutputFormat(FfmpegAudioDecoder decoder) { Assertions.checkNotNull(decoder); return new Format.Builder() .setSampleMimeType(MimeTypes.AUDIO_RAW) diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java index 3ff9a5c275..df511866a3 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java @@ -25,21 +25,17 @@ import com.google.android.exoplayer2.audio.AudioSink; import com.google.android.exoplayer2.audio.DecoderAudioRenderer; import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.extractor.FlacStreamMetadata; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.FlacConstants; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.Util; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Decodes and renders audio using the native Flac decoder. */ -public final class LibflacAudioRenderer extends DecoderAudioRenderer { +public final class LibflacAudioRenderer extends DecoderAudioRenderer { private static final String TAG = "LibflacAudioRenderer"; private static final int NUM_BUFFERS = 16; - private @MonotonicNonNull FlacStreamMetadata streamMetadata; - public LibflacAudioRenderer() { this(/* eventHandler= */ null, /* eventListener= */ null); } @@ -120,14 +116,13 @@ public final class LibflacAudioRenderer extends DecoderAudioRenderer { TraceUtil.beginSection("createFlacDecoder"); FlacDecoder decoder = new FlacDecoder(NUM_BUFFERS, NUM_BUFFERS, format.maxInputSize, format.initializationData); - streamMetadata = decoder.getStreamMetadata(); TraceUtil.endSection(); return decoder; } @Override - protected Format getOutputFormat() { - return getOutputFormat(Assertions.checkNotNull(streamMetadata)); + protected Format getOutputFormat(FlacDecoder decoder) { + return getOutputFormat(decoder.getStreamMetadata()); } private static Format getOutputFormat(FlacStreamMetadata streamMetadata) { diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java index 0678a107c4..5a9c131c51 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java @@ -30,7 +30,7 @@ import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.Util; /** Decodes and renders audio using the native Opus decoder. */ -public class LibopusAudioRenderer extends DecoderAudioRenderer { +public class LibopusAudioRenderer extends DecoderAudioRenderer { private static final String TAG = "LibopusAudioRenderer"; /** The number of input and output buffers. */ @@ -38,10 +38,6 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { /** The default input buffer size. */ private static final int DEFAULT_INPUT_BUFFER_SIZE = 960 * 6; - private int channelCount; - private int sampleRate; - private boolean outputFloat; - public LibopusAudioRenderer() { this(/* eventHandler= */ null, /* eventListener= */ null); } @@ -108,7 +104,7 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { int formatSupport = getSinkFormatSupport( Util.getPcmFormat(C.ENCODING_PCM_FLOAT, format.channelCount, format.sampleRate)); - outputFloat = formatSupport == AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY; + boolean outputFloat = formatSupport == AudioSink.SINK_FORMAT_SUPPORTED_DIRECTLY; int initialInputBufferSize = format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE; @@ -120,16 +116,15 @@ public class LibopusAudioRenderer extends DecoderAudioRenderer { format.initializationData, mediaCrypto, outputFloat); - channelCount = decoder.getChannelCount(); - sampleRate = decoder.getSampleRate(); TraceUtil.endSection(); return decoder; } @Override - protected Format getOutputFormat() { - @C.PcmEncoding int pcmEncoding = outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT; - return Util.getPcmFormat(pcmEncoding, channelCount, sampleRate); + protected Format getOutputFormat(OpusDecoder decoder) { + @C.PcmEncoding + int pcmEncoding = decoder.outputFloat ? C.ENCODING_PCM_FLOAT : C.ENCODING_PCM_16BIT; + return Util.getPcmFormat(pcmEncoding, decoder.channelCount, OpusDecoder.SAMPLE_RATE); } } diff --git a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java index 4d9bedcfbe..23dd312116 100644 --- a/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java +++ b/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java @@ -36,21 +36,22 @@ import java.util.List; private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840; /** Opus streams are always decoded at 48000 Hz. */ - private static final int SAMPLE_RATE = 48_000; + public static final int SAMPLE_RATE = 48_000; private static final int NO_ERROR = 0; private static final int DECODE_ERROR = -1; private static final int DRM_ERROR = -2; + public final boolean outputFloat; + public final int channelCount; + @Nullable private final ExoMediaCrypto exoMediaCrypto; - private final int channelCount; private final int headerSkipSamples; private final int headerSeekPreRollSamples; private final long nativeDecoderContext; private int skipSamples; - private final boolean outputFloat; /** * Creates an Opus decoder. @@ -230,16 +231,6 @@ import java.util.List; opusClose(nativeDecoderContext); } - /** Returns the channel count of output audio. */ - public int getChannelCount() { - return channelCount; - } - - /** Returns the sample rate of output audio. */ - public int getSampleRate() { - return SAMPLE_RATE; - } - private static int nsToSamples(long ns) { return (int) (ns * SAMPLE_RATE / 1000000000); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java index f8af058545..c20de49f06 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DecoderAudioRenderer.java @@ -73,7 +73,10 @@ import java.lang.annotation.RetentionPolicy; * underlying audio track. * */ -public abstract class DecoderAudioRenderer extends BaseRenderer implements MediaClock { +public abstract class DecoderAudioRenderer< + T extends + Decoder> + extends BaseRenderer implements MediaClock { @Documented @Retention(RetentionPolicy.SOURCE) @@ -109,9 +112,7 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media private int encoderDelay; private int encoderPadding; - @Nullable - private Decoder - decoder; + @Nullable private T decoder; @Nullable private DecoderInputBuffer inputBuffer; @Nullable private SimpleOutputBuffer outputBuffer; @@ -317,15 +318,16 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media * @return The decoder. * @throws DecoderException If an error occurred creating a suitable decoder. */ - protected abstract Decoder< - DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends DecoderException> - createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) throws DecoderException; + protected abstract T createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) + throws DecoderException; /** * Returns the format of audio buffers output by the decoder. Will not be called until the first * output buffer has been dequeued, so the decoder may use input data to determine the format. + * + * @param decoder The decoder. */ - protected abstract Format getOutputFormat(); + protected abstract Format getOutputFormat(T decoder); /** * Returns whether the existing decoder can be kept for a new format. @@ -365,7 +367,7 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media try { processEndOfStream(); } catch (AudioSink.WriteException e) { - throw createRendererException(e, getOutputFormat()); + throw createRendererException(e, getOutputFormat(decoder)); } } return false; @@ -373,7 +375,7 @@ public abstract class DecoderAudioRenderer extends BaseRenderer implements Media if (audioTrackNeedsConfigure) { Format outputFormat = - getOutputFormat() + getOutputFormat(decoder) .buildUpon() .setEncoderDelay(encoderDelay) .setEncoderPadding(encoderPadding) diff --git a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java index f6e3ac941d..adcc4bc0cf 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/audio/DecoderAudioRendererTest.java @@ -55,13 +55,13 @@ public class DecoderAudioRendererTest { new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_RAW).build(); @Mock private AudioSink mockAudioSink; - private DecoderAudioRenderer audioRenderer; + private DecoderAudioRenderer audioRenderer; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); audioRenderer = - new DecoderAudioRenderer(null, null, mockAudioSink) { + new DecoderAudioRenderer(null, null, mockAudioSink) { @Override public String getName() { return "TestAudioRenderer"; @@ -74,14 +74,12 @@ public class DecoderAudioRendererTest { } @Override - protected SimpleDecoder< - DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends DecoderException> - createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) { + protected FakeDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) { return new FakeDecoder(); } @Override - protected Format getOutputFormat() { + protected Format getOutputFormat(FakeDecoder decoder) { return FORMAT; } };