Fix extension FLAC decoder to correctly handle non-16-bit depths
PiperOrigin-RevId: 288860159
This commit is contained in:
parent
017a7cf38c
commit
1a9b301f52
@ -56,6 +56,10 @@
|
||||
([#4078](https://github.com/google/ExoPlayer/issues/4078)).
|
||||
* Don't use notification chronometer if playback speed is != 1.0
|
||||
([#6816](https://github.com/google/ExoPlayer/issues/6816)).
|
||||
* FLAC extension: Fix handling of bit depths other than 16 in `FLACDecoder`.
|
||||
This issue caused FLAC streams with other bit depths to sound like white noise
|
||||
on earlier releases, but only when embedded in a non-FLAC container such as
|
||||
Matroska or MP4.
|
||||
|
||||
### 2.11.1 (2019-12-20) ###
|
||||
|
||||
@ -222,7 +226,7 @@
|
||||
`C.MSG_SET_OUTPUT_BUFFER_RENDERER`.
|
||||
* Use `VideoDecoderRenderer` as an implementation of
|
||||
`VideoDecoderOutputBufferRenderer`, instead of `VideoDecoderSurfaceView`.
|
||||
* Flac extension: Update to use NDK r20.
|
||||
* FLAC extension: Update to use NDK r20.
|
||||
* Opus extension: Update to use NDK r20.
|
||||
* FFmpeg extension:
|
||||
* Update to use NDK r20.
|
||||
@ -359,7 +363,7 @@
|
||||
([#6241](https://github.com/google/ExoPlayer/issues/6241)).
|
||||
* MP3: Use CBR header bitrate, not calculated bitrate. This reverts a change
|
||||
from 2.9.3 ([#6238](https://github.com/google/ExoPlayer/issues/6238)).
|
||||
* Flac extension: Parse `VORBIS_COMMENT` and `PICTURE` metadata
|
||||
* FLAC extension: Parse `VORBIS_COMMENT` and `PICTURE` metadata
|
||||
([#5527](https://github.com/google/ExoPlayer/issues/5527)).
|
||||
* Fix issue where initial seek positions get ignored when playing a preroll ad
|
||||
([#6201](https://github.com/google/ExoPlayer/issues/6201)).
|
||||
@ -368,7 +372,7 @@
|
||||
([#6153](https://github.com/google/ExoPlayer/issues/6153)).
|
||||
* Fix `DataSchemeDataSource` re-opening and range requests
|
||||
([#6192](https://github.com/google/ExoPlayer/issues/6192)).
|
||||
* Fix Flac and ALAC playback on some LG devices
|
||||
* Fix FLAC and ALAC playback on some LG devices
|
||||
([#5938](https://github.com/google/ExoPlayer/issues/5938)).
|
||||
* Fix issue when calling `performClick` on `PlayerView` without
|
||||
`PlayerControlView`
|
||||
|
@ -33,7 +33,7 @@ import java.util.List;
|
||||
/* package */ final class FlacDecoder extends
|
||||
SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, FlacDecoderException> {
|
||||
|
||||
private final int maxOutputBufferSize;
|
||||
private final FlacStreamMetadata streamMetadata;
|
||||
private final FlacDecoderJni decoderJni;
|
||||
|
||||
/**
|
||||
@ -59,7 +59,6 @@ import java.util.List;
|
||||
}
|
||||
decoderJni = new FlacDecoderJni();
|
||||
decoderJni.setData(ByteBuffer.wrap(initializationData.get(0)));
|
||||
FlacStreamMetadata streamMetadata;
|
||||
try {
|
||||
streamMetadata = decoderJni.decodeStreamMetadata();
|
||||
} catch (ParserException e) {
|
||||
@ -72,7 +71,6 @@ import java.util.List;
|
||||
int initialInputBufferSize =
|
||||
maxInputBufferSize != Format.NO_VALUE ? maxInputBufferSize : streamMetadata.maxFrameSize;
|
||||
setInitialInputBufferSize(initialInputBufferSize);
|
||||
maxOutputBufferSize = streamMetadata.getMaxDecodedFrameSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -103,7 +101,8 @@ import java.util.List;
|
||||
decoderJni.flush();
|
||||
}
|
||||
decoderJni.setData(Util.castNonNull(inputBuffer.data));
|
||||
ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, maxOutputBufferSize);
|
||||
ByteBuffer outputData =
|
||||
outputBuffer.init(inputBuffer.timeUs, streamMetadata.getMaxDecodedFrameSize());
|
||||
try {
|
||||
decoderJni.decodeSample(outputData);
|
||||
} catch (FlacDecoderJni.FlacFrameDecodeException e) {
|
||||
@ -121,4 +120,8 @@ import java.util.List;
|
||||
decoderJni.release();
|
||||
}
|
||||
|
||||
/** Returns the {@link FlacStreamMetadata} decoded from the initialization data. */
|
||||
public FlacStreamMetadata getStreamMetadata() {
|
||||
return streamMetadata;
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +24,20 @@ import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||
import com.google.android.exoplayer2.audio.SimpleDecoderAudioRenderer;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.ExoMediaCrypto;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.FlacConstants;
|
||||
import com.google.android.exoplayer2.util.FlacStreamMetadata;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* Decodes and renders audio using the native Flac decoder.
|
||||
*/
|
||||
public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
/** Decodes and renders audio using the native Flac decoder. */
|
||||
public final class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
|
||||
@MonotonicNonNull private FlacStreamMetadata streamMetadata;
|
||||
|
||||
public LibflacAudioRenderer() {
|
||||
this(/* eventHandler= */ null, /* eventListener= */ null);
|
||||
}
|
||||
@ -57,7 +62,23 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
if (!FlacLibrary.isAvailable()
|
||||
|| !MimeTypes.AUDIO_FLAC.equalsIgnoreCase(format.sampleMimeType)) {
|
||||
return FORMAT_UNSUPPORTED_TYPE;
|
||||
} else if (!supportsOutput(format.channelCount, C.ENCODING_PCM_16BIT)) {
|
||||
}
|
||||
// Compute the PCM encoding that the FLAC decoder will output.
|
||||
@C.PcmEncoding int pcmEncoding;
|
||||
if (format.initializationData.isEmpty()) {
|
||||
// The initialization data might not be set if the format was obtained from a manifest (e.g.
|
||||
// for DASH playbacks) rather than directly from the media. In this case we assume
|
||||
// ENCODING_PCM_16BIT. If the actual encoding is different, playback will still succeed as
|
||||
// long as the AudioSink supports it (which will always be true when using DefaultAudioSink).
|
||||
pcmEncoding = C.ENCODING_PCM_16BIT;
|
||||
} else {
|
||||
int streamMetadataOffset =
|
||||
FlacConstants.STREAM_MARKER_SIZE + FlacConstants.METADATA_BLOCK_HEADER_SIZE;
|
||||
FlacStreamMetadata streamMetadata =
|
||||
new FlacStreamMetadata(format.initializationData.get(0), streamMetadataOffset);
|
||||
pcmEncoding = Util.getPcmEncoding(streamMetadata.bitsPerSample);
|
||||
}
|
||||
if (!supportsOutput(format.channelCount, pcmEncoding)) {
|
||||
return FORMAT_UNSUPPORTED_SUBTYPE;
|
||||
} else if (!supportsFormatDrm(drmSessionManager, format.drmInitData)) {
|
||||
return FORMAT_UNSUPPORTED_DRM;
|
||||
@ -69,8 +90,27 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
||||
@Override
|
||||
protected FlacDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
|
||||
throws FlacDecoderException {
|
||||
return new FlacDecoder(
|
||||
NUM_BUFFERS, NUM_BUFFERS, format.maxInputSize, format.initializationData);
|
||||
FlacDecoder decoder =
|
||||
new FlacDecoder(NUM_BUFFERS, NUM_BUFFERS, format.maxInputSize, format.initializationData);
|
||||
streamMetadata = decoder.getStreamMetadata();
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Format getOutputFormat() {
|
||||
Assertions.checkNotNull(streamMetadata);
|
||||
return Format.createAudioSampleFormat(
|
||||
/* id= */ null,
|
||||
MimeTypes.AUDIO_RAW,
|
||||
/* codecs= */ null,
|
||||
/* bitrate= */ Format.NO_VALUE,
|
||||
/* maxInputSize= */ Format.NO_VALUE,
|
||||
streamMetadata.channels,
|
||||
streamMetadata.sampleRate,
|
||||
Util.getPcmEncoding(streamMetadata.bitsPerSample),
|
||||
/* initializationData= */ null,
|
||||
/* drmInitData= */ null,
|
||||
/* selectionFlags= */ 0,
|
||||
/* language= */ null);
|
||||
}
|
||||
}
|
||||
|
@ -351,15 +351,8 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
|
||||
/**
|
||||
* 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.
|
||||
* <p>
|
||||
* The default implementation returns a 16-bit PCM format with the same channel count and sample
|
||||
* rate as the input.
|
||||
*/
|
||||
protected Format getOutputFormat() {
|
||||
return Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW, null, Format.NO_VALUE,
|
||||
Format.NO_VALUE, inputFormat.channelCount, inputFormat.sampleRate, C.ENCODING_PCM_16BIT,
|
||||
null, null, 0, null);
|
||||
}
|
||||
protected abstract Format getOutputFormat();
|
||||
|
||||
/**
|
||||
* Returns whether the existing decoder can be kept for a new format.
|
||||
|
@ -102,8 +102,9 @@ public final class FlacStreamMetadata {
|
||||
/**
|
||||
* Parses binary FLAC stream info metadata.
|
||||
*
|
||||
* @param data An array containing binary FLAC stream info block (with or without header).
|
||||
* @param offset The offset of the stream info block in {@code data} (header excluded).
|
||||
* @param data An array containing binary FLAC stream info block.
|
||||
* @param offset The offset of the stream info block in {@code data}, excluding the header (i.e.
|
||||
* the offset points to the first byte of the minimum block size).
|
||||
*/
|
||||
public FlacStreamMetadata(byte[] data, int offset) {
|
||||
ParsableBitArray scratch = new ParsableBitArray(data);
|
||||
|
@ -67,10 +67,14 @@ public class SimpleDecoderAudioRendererTest {
|
||||
@Override
|
||||
protected SimpleDecoder<
|
||||
DecoderInputBuffer, ? extends SimpleOutputBuffer, ? extends AudioDecoderException>
|
||||
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto)
|
||||
throws AudioDecoderException {
|
||||
createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) {
|
||||
return new FakeDecoder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Format getOutputFormat() {
|
||||
return FORMAT;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user