|
39 | 39 | private static final int AUDIO_DECODER_ERROR_INVALID_DATA = -1; |
40 | 40 | private static final int AUDIO_DECODER_ERROR_OTHER = -2; |
41 | 41 |
|
| 42 | + // FLAC parsing constants |
| 43 | + private static final byte[] flacStreamMarker = {'f', 'L', 'a', 'C'}; |
| 44 | + private static final int FLAC_METADATA_TYPE_STREAM_INFO = 0; |
| 45 | + private static final int FLAC_METADATA_BLOCK_HEADER_SIZE = 4; |
| 46 | + private static final int FLAC_STREAM_INFO_DATA_SIZE = 34; |
| 47 | + |
42 | 48 | private final String codecName; |
43 | 49 | @Nullable private final byte[] extraData; |
44 | 50 | private final @C.PcmEncoding int encoding; |
@@ -190,6 +196,8 @@ private static byte[] getExtraData(String mimeType, List<byte[]> initializationD |
190 | 196 | return getAlacExtraData(initializationData); |
191 | 197 | case MimeTypes.AUDIO_VORBIS: |
192 | 198 | return getVorbisExtraData(initializationData); |
| 199 | + case MimeTypes.AUDIO_FLAC: |
| 200 | + return getFlacExtraData(initializationData); |
193 | 201 | default: |
194 | 202 | // Other codecs do not require extra data. |
195 | 203 | return null; |
@@ -227,6 +235,66 @@ private static byte[] getVorbisExtraData(List<byte[]> initializationData) { |
227 | 235 | return extraData; |
228 | 236 | } |
229 | 237 |
|
| 238 | + @Nullable |
| 239 | + private static byte[] getFlacExtraData(List<byte[]> initializationData) { |
| 240 | + for (int i = 0; i < initializationData.size(); i++) { |
| 241 | + byte[] out = extractFlacStreamInfo(initializationData.get(i)); |
| 242 | + if (out != null) { |
| 243 | + return out; |
| 244 | + } |
| 245 | + } |
| 246 | + return null; |
| 247 | + } |
| 248 | + |
| 249 | + @Nullable |
| 250 | + private static byte[] extractFlacStreamInfo(byte[] data) { |
| 251 | + int offset = 0; |
| 252 | + if (arrayStartsWith(data, flacStreamMarker)) { |
| 253 | + offset = flacStreamMarker.length; |
| 254 | + } |
| 255 | + |
| 256 | + if (data.length - offset == FLAC_STREAM_INFO_DATA_SIZE) { |
| 257 | + byte[] streamInfo = new byte[FLAC_STREAM_INFO_DATA_SIZE]; |
| 258 | + System.arraycopy(data, offset, streamInfo, 0, FLAC_STREAM_INFO_DATA_SIZE); |
| 259 | + return streamInfo; |
| 260 | + } |
| 261 | + |
| 262 | + if (data.length >= offset + FLAC_METADATA_BLOCK_HEADER_SIZE) { |
| 263 | + int type = data[offset] & 0x7F; |
| 264 | + int length = |
| 265 | + ((data[offset + 1] & 0xFF) << 16) |
| 266 | + | ((data[offset + 2] & 0xFF) << 8) |
| 267 | + | (data[offset + 3] & 0xFF); |
| 268 | + |
| 269 | + if (type == FLAC_METADATA_TYPE_STREAM_INFO |
| 270 | + && length == FLAC_STREAM_INFO_DATA_SIZE |
| 271 | + && data.length >= offset + FLAC_METADATA_BLOCK_HEADER_SIZE + FLAC_STREAM_INFO_DATA_SIZE) { |
| 272 | + byte[] streamInfo = new byte[FLAC_STREAM_INFO_DATA_SIZE]; |
| 273 | + System.arraycopy( |
| 274 | + data, |
| 275 | + offset + FLAC_METADATA_BLOCK_HEADER_SIZE, |
| 276 | + streamInfo, |
| 277 | + 0, |
| 278 | + FLAC_STREAM_INFO_DATA_SIZE); |
| 279 | + return streamInfo; |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + return null; |
| 284 | + } |
| 285 | + |
| 286 | + private static boolean arrayStartsWith(byte[] data, byte[] prefix) { |
| 287 | + if (data.length < prefix.length) { |
| 288 | + return false; |
| 289 | + } |
| 290 | + for (int i = 0; i < prefix.length; i++) { |
| 291 | + if (data[i] != prefix[i]) { |
| 292 | + return false; |
| 293 | + } |
| 294 | + } |
| 295 | + return true; |
| 296 | + } |
| 297 | + |
230 | 298 | private native long ffmpegInitialize( |
231 | 299 | String codecName, |
232 | 300 | @Nullable byte[] extraData, |
|
0 commit comments