mirror of
https://github.com/samsonjs/media.git
synced 2026-04-07 11:35:46 +00:00
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
This commit is contained in:
parent
a6be8eeb6b
commit
4d03d30890
6 changed files with 32 additions and 54 deletions
|
|
@ -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<FfmpegAudioDecoder> {
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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<FlacDecoder> {
|
||||
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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<OpusDecoder> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,10 @@ import java.lang.annotation.RetentionPolicy;
|
|||
* underlying audio track.
|
||||
* </ul>
|
||||
*/
|
||||
public abstract class DecoderAudioRenderer extends BaseRenderer implements MediaClock {
|
||||
public abstract class DecoderAudioRenderer<
|
||||
T extends
|
||||
Decoder<DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends DecoderException>>
|
||||
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<DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends DecoderException>
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@ public class DecoderAudioRendererTest {
|
|||
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_RAW).build();
|
||||
|
||||
@Mock private AudioSink mockAudioSink;
|
||||
private DecoderAudioRenderer audioRenderer;
|
||||
private DecoderAudioRenderer<FakeDecoder> audioRenderer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
audioRenderer =
|
||||
new DecoderAudioRenderer(null, null, mockAudioSink) {
|
||||
new DecoderAudioRenderer<FakeDecoder>(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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue