Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
* Smooth Streaming extension:
* RTSP extension:
* Decoder extensions (FFmpeg, VP9, AV1, etc.):
* FFmpeg extension: Fix an issue that prevented some FLAC files from
playing by ensuring the `STREAMINFO` block is correctly parsed and
passed to the decoder
([#2887](https://github.com/androidx/media/issues/2887)).
* MIDI extension:
* Leanback extension:
* Cast extension:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
private static final int AUDIO_DECODER_ERROR_INVALID_DATA = -1;
private static final int AUDIO_DECODER_ERROR_OTHER = -2;

// FLAC parsing constants
private static final byte[] flacStreamMarker = {'f', 'L', 'a', 'C'};
private static final int FLAC_METADATA_TYPE_STREAM_INFO = 0;
private static final int FLAC_METADATA_BLOCK_HEADER_SIZE = 4;
private static final int FLAC_STREAM_INFO_DATA_SIZE = 34;

private final String codecName;
@Nullable private final byte[] extraData;
private final @C.PcmEncoding int encoding;
Expand Down Expand Up @@ -190,6 +196,8 @@ private static byte[] getExtraData(String mimeType, List<byte[]> initializationD
return getAlacExtraData(initializationData);
case MimeTypes.AUDIO_VORBIS:
return getVorbisExtraData(initializationData);
case MimeTypes.AUDIO_FLAC:
return getFlacExtraData(initializationData);
default:
// Other codecs do not require extra data.
return null;
Expand Down Expand Up @@ -227,6 +235,71 @@ private static byte[] getVorbisExtraData(List<byte[]> initializationData) {
return extraData;
}

@Nullable
private static byte[] getFlacExtraData(List<byte[]> initializationData) {
for (int i = 0; i < initializationData.size(); i++) {
byte[] out = extractFlacStreamInfo(initializationData.get(i));
if (out != null) {
return out;
}
}
return null;
}

@Nullable
private static byte[] extractFlacStreamInfo(byte[] data) {
int offset = 0;
if (arrayStartsWith(data, flacStreamMarker)) {
offset = flacStreamMarker.length;
}


if (data.length - offset == FLAC_STREAM_INFO_DATA_SIZE) {
byte[] streamInfo = new byte[FLAC_STREAM_INFO_DATA_SIZE];
System.arraycopy(data, offset, streamInfo, 0, FLAC_STREAM_INFO_DATA_SIZE);
return streamInfo;
}

if (data.length >= offset + FLAC_METADATA_BLOCK_HEADER_SIZE) {
int type = data[offset] & 0x7F;
int length =
((data[offset + 1] & 0xFF) << 16)
| ((data[offset + 2] & 0xFF) << 8)
| (data[offset + 3] & 0xFF);

if (type == FLAC_METADATA_TYPE_STREAM_INFO
&& length == FLAC_STREAM_INFO_DATA_SIZE
&& data.length
>= offset
+ FLAC_METADATA_BLOCK_HEADER_SIZE
+ FLAC_STREAM_INFO_DATA_SIZE) {
byte[] streamInfo = new byte[FLAC_STREAM_INFO_DATA_SIZE];
System.arraycopy(
data,
offset + FLAC_METADATA_BLOCK_HEADER_SIZE,
streamInfo,
0,
FLAC_STREAM_INFO_DATA_SIZE);
return streamInfo;
}
}

return null;
}

private static boolean arrayStartsWith(byte[] data, byte[] prefix) {
if (data.length < prefix.length) {
return false;
}
for (int i = 0; i < prefix.length; i++) {
if (data[i] != prefix[i]) {
return false;
}
}
return true;
}


private native long ffmpegInitialize(
String codecName,
@Nullable byte[] extraData,
Expand Down