diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java b/library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java index 208989124a1..ff8f5175bf3 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/audio/WavUtil.java @@ -22,6 +22,10 @@ /** Utilities for handling WAVE files. */ public final class WavUtil { + /** Four character code for "RF64". */ + public static final int RF64_FOURCC = 0x52463634; + /** Four character code for "ds64". */ + public static final int DS64_FOURCC = 0x64733634; /** Four character code for "RIFF". */ public static final int RIFF_FOURCC = 0x52494646; /** Four character code for "WAVE". */ diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java index f794933d16c..27bd28ad5eb 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java @@ -48,9 +48,9 @@ public static WavHeader peek(ExtractorInput input) throws IOException { // Allocate a scratch buffer large enough to store the format chunk. ParsableByteArray scratch = new ParsableByteArray(16); - // Attempt to read the RIFF chunk. + // Attempt to read the RIFF or RF64 chunk. ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); - if (chunkHeader.id != WavUtil.RIFF_FOURCC) { + if (chunkHeader.id != WavUtil.RIFF_FOURCC && chunkHeader.id != WavUtil.RF64_FOURCC) { return null; } @@ -117,14 +117,27 @@ public static Pair skipToData(ExtractorInput input) throws IOExcepti ParsableByteArray scratch = new ParsableByteArray(ChunkHeader.SIZE_IN_BYTES); // Skip all chunks until we find the data header. ChunkHeader chunkHeader = ChunkHeader.peek(input, scratch); + + // Data size holder. To be determined from data chunk or ds64 chunk in case of RF64. + long dataSize = -1; + while (chunkHeader.id != WavUtil.DATA_FOURCC) { - if (chunkHeader.id != WavUtil.RIFF_FOURCC && chunkHeader.id != WavUtil.FMT_FOURCC) { + if (chunkHeader.id != WavUtil.RIFF_FOURCC && chunkHeader.id != WavUtil.FMT_FOURCC + && chunkHeader.id != WavUtil. RF64_FOURCC && chunkHeader.id != WavUtil. DS64_FOURCC) { Log.w(TAG, "Ignoring unknown WAV chunk: " + chunkHeader.id); } long bytesToSkip = ChunkHeader.SIZE_IN_BYTES + chunkHeader.size; // Override size of RIFF chunk, since it describes its size as the entire file. - if (chunkHeader.id == WavUtil.RIFF_FOURCC) { + // Also, ignore the size of RF64 chunk, since its always going to be 0xFFFFFFFF + if (chunkHeader.id == WavUtil.RIFF_FOURCC || chunkHeader.id == WavUtil.RF64_FOURCC) { bytesToSkip = ChunkHeader.SIZE_IN_BYTES + 4; + } else if (chunkHeader.id == WavUtil.DS64_FOURCC) { + int ds64Size = (int) chunkHeader.size; + ParsableByteArray ds64Bytes = new ParsableByteArray(ds64Size); + input.peekFully(ds64Bytes.getData(), 0, ds64Size); + // ds64 chunk contains 64bit sizes. From position 8 to 16 is the data size + ds64Bytes.setPosition(8); + dataSize = ds64Bytes.readLittleEndianLong(); } if (bytesToSkip > Integer.MAX_VALUE) { throw ParserException.createForUnsupportedContainerFeature( @@ -133,11 +146,15 @@ public static Pair skipToData(ExtractorInput input) throws IOExcepti input.skipFully((int) bytesToSkip); chunkHeader = ChunkHeader.peek(input, scratch); } + // Use size from data chunk if it wasn't determined from ds64 chunk + if (dataSize == -1) { + dataSize = chunkHeader.size; + } // Skip past the "data" header. input.skipFully(ChunkHeader.SIZE_IN_BYTES); long dataStartPosition = input.getPosition(); - long dataEndPosition = dataStartPosition + chunkHeader.size; + long dataEndPosition = dataStartPosition + dataSize; long inputLength = input.getLength(); if (inputLength != C.LENGTH_UNSET && dataEndPosition > inputLength) { Log.w(TAG, "Data exceeds input length: " + dataEndPosition + ", " + inputLength); diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java index 4217a1528a3..211380463d3 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/wav/WavExtractorTest.java @@ -53,4 +53,10 @@ public void sample_imaAdpcm() throws Exception { ExtractorAsserts.assertBehavior( WavExtractor::new, "media/wav/sample_ima_adpcm.wav", simulationConfig); } + + @Test + public void sample_RF64() throws Exception { + ExtractorAsserts + .assertBehavior(WavExtractor::new, "media/wav/sample_rf64.wav", simulationConfig); + } } diff --git a/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.0.dump b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.0.dump new file mode 100644 index 00000000000..1e129acd707 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.0.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 348625 + getPosition(0) = [[timeUs=0, position=80]] + getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]] + getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]] + getPosition(348625) = [[timeUs=348604, position=67012]] +numberOfTracks = 1 +track 0: + total output bytes = 66936 + sample count = 4 + format 0: + averageBitrate = 1536000 + peakBitrate = 1536000 + sampleMimeType = audio/raw + maxInputSize = 19200 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = 2 + sample 0: + time = 0 + flags = 1 + data = length 19200, hash EF6C7C27 + sample 1: + time = 100000 + flags = 1 + data = length 19200, hash 5AB97AFC + sample 2: + time = 200000 + flags = 1 + data = length 19200, hash 37920F33 + sample 3: + time = 300000 + flags = 1 + data = length 9336, hash 135F1C30 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.1.dump b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.1.dump new file mode 100644 index 00000000000..f4d925bad31 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.1.dump @@ -0,0 +1,32 @@ +seekMap: + isSeekable = true + duration = 348625 + getPosition(0) = [[timeUs=0, position=80]] + getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]] + getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]] + getPosition(348625) = [[timeUs=348604, position=67012]] +numberOfTracks = 1 +track 0: + total output bytes = 44628 + sample count = 3 + format 0: + averageBitrate = 1536000 + peakBitrate = 1536000 + sampleMimeType = audio/raw + maxInputSize = 19200 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = 2 + sample 0: + time = 116208 + flags = 1 + data = length 19200, hash E4B962ED + sample 1: + time = 216208 + flags = 1 + data = length 19200, hash 4F13D6CF + sample 2: + time = 316208 + flags = 1 + data = length 6228, hash 3FB5F446 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.2.dump b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.2.dump new file mode 100644 index 00000000000..2b42e93b5a9 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.2.dump @@ -0,0 +1,28 @@ +seekMap: + isSeekable = true + duration = 348625 + getPosition(0) = [[timeUs=0, position=80]] + getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]] + getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]] + getPosition(348625) = [[timeUs=348604, position=67012]] +numberOfTracks = 1 +track 0: + total output bytes = 22316 + sample count = 2 + format 0: + averageBitrate = 1536000 + peakBitrate = 1536000 + sampleMimeType = audio/raw + maxInputSize = 19200 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = 2 + sample 0: + time = 232416 + flags = 1 + data = length 19200, hash F82E494B + sample 1: + time = 332416 + flags = 1 + data = length 3116, hash 93C99CFD +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.3.dump b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.3.dump new file mode 100644 index 00000000000..2a6345d4a8f --- /dev/null +++ b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.3.dump @@ -0,0 +1,24 @@ +seekMap: + isSeekable = true + duration = 348625 + getPosition(0) = [[timeUs=0, position=80]] + getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]] + getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]] + getPosition(348625) = [[timeUs=348604, position=67012]] +numberOfTracks = 1 +track 0: + total output bytes = 4 + sample count = 1 + format 0: + averageBitrate = 1536000 + peakBitrate = 1536000 + sampleMimeType = audio/raw + maxInputSize = 19200 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = 2 + sample 0: + time = 348625 + flags = 1 + data = length 4, hash FFD4C53F +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.unknown_length.dump b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.unknown_length.dump new file mode 100644 index 00000000000..1e129acd707 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/wav/sample_rf64.wav.unknown_length.dump @@ -0,0 +1,36 @@ +seekMap: + isSeekable = true + duration = 348625 + getPosition(0) = [[timeUs=0, position=80]] + getPosition(1) = [[timeUs=0, position=80], [timeUs=20, position=84]] + getPosition(174312) = [[timeUs=174291, position=33544], [timeUs=174312, position=33548]] + getPosition(348625) = [[timeUs=348604, position=67012]] +numberOfTracks = 1 +track 0: + total output bytes = 66936 + sample count = 4 + format 0: + averageBitrate = 1536000 + peakBitrate = 1536000 + sampleMimeType = audio/raw + maxInputSize = 19200 + channelCount = 2 + sampleRate = 48000 + pcmEncoding = 2 + sample 0: + time = 0 + flags = 1 + data = length 19200, hash EF6C7C27 + sample 1: + time = 100000 + flags = 1 + data = length 19200, hash 5AB97AFC + sample 2: + time = 200000 + flags = 1 + data = length 19200, hash 37920F33 + sample 3: + time = 300000 + flags = 1 + data = length 9336, hash 135F1C30 +tracksEnded = true diff --git a/testdata/src/test/assets/media/wav/sample_rf64.wav b/testdata/src/test/assets/media/wav/sample_rf64.wav new file mode 100644 index 00000000000..b2dd53c6872 Binary files /dev/null and b/testdata/src/test/assets/media/wav/sample_rf64.wav differ