diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java index 5c0dbec2184..0fcc37d90c3 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java @@ -37,6 +37,7 @@ import java.lang.annotation.Target; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** Utility methods for parsing DTS frames. */ @@ -875,31 +876,64 @@ public static int parseDtsUhdHeaderSize(byte[] headerPrefix) { + 1; } - /** Returns whether the sample data at the current {@link ExtractorInput} is a DTS-HD sample. */ - public static boolean isSampleDtsHd(ExtractorInput input, int sampleSize) throws IOException { + /** + * Returns a {@link Format} with an adjusted MIME type if the sample data at the current {@link + * ExtractorInput} is a DTS-HD sample with static fields, or the unmodified {@code format} if it + * is not. + * + * @param input The {@link ExtractorInput} to read from. + * @param sampleSize The size of the sample data. + * @param format The {@link Format} to build upon. + * @return The updated {@link Format}. + * @throws IOException If an error occurs reading from the input. + */ + public static Format updateFormatWithDtsHdInfo( + ExtractorInput input, int sampleSize, Format format) throws IOException { ParsableByteArray sampleData = new ParsableByteArray(sampleSize); if (!input.peekFully( sampleData.getData(), /* offset= */ 0, sampleSize, /* allowEndOfInput= */ true)) { - return false; + return format; } input.resetPeekPosition(); int word = sampleData.peekInt(); + // Skip the core frame if present (it doesn't have to be). if (DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_CORE) { if (sampleData.bytesLeft() < 10) { - return false; + return format; } byte[] header = new byte[10]; sampleData.readBytes(header, /* offset= */ 0, /* length= */ 10); - sampleData.setPosition(0); int frameSize = DtsUtil.getDtsFrameSize(header); - if (frameSize <= 0 || sampleData.bytesLeft() < frameSize + 4) { - return false; + if (frameSize <= 0 || sampleData.limit() < frameSize + 4) { + return format; } - sampleData.skipBytes(frameSize); - word = sampleData.readInt(); - return DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM; - } - return false; + sampleData.setPosition(frameSize); + word = sampleData.peekInt(); + } + if (DtsUtil.getFrameType(word) != DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM) { + return format; + } + if (sampleData.bytesLeft() < 7) { + return format; + } + int extHeaderOffset = sampleData.getPosition(); + byte[] headerPrefix = new byte[7]; + sampleData.readBytes(headerPrefix, /* offset= */ 0, /* length= */ 7); + sampleData.setPosition(extHeaderOffset); + int frameSize = parseDtsHdHeaderSize(headerPrefix); + if (frameSize <= 0 || sampleData.bytesLeft() < frameSize) { + return format; + } + byte[] header = new byte[frameSize]; + sampleData.readBytes(header, /* offset= */ 0, /* length= */ frameSize); + DtsHeader dtsHeader = parseDtsHdHeader(header); + // If the MIME type was parsed successfully, use it. If it is null (e.g. because static + // fields were missing), there's nothing we can do other than assume it is DTS-HD. + String mimeType = dtsHeader.mimeType != null ? dtsHeader.mimeType : MimeTypes.AUDIO_DTS_HD; + if (Objects.equals(format.sampleMimeType, mimeType)) { + return format; + } + return format.buildUpon().setSampleMimeType(mimeType).build(); } /** diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java index 4f4a08a1a6c..0683fca4525 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java @@ -1846,9 +1846,7 @@ private int writeSampleData(ExtractorInput input, Track track, int size, boolean if (track.waitingForDtsAnalysis) { checkNotNull(track.format); - if (DtsUtil.isSampleDtsHd(input, size)) { - track.format = track.format.buildUpon().setSampleMimeType(MimeTypes.AUDIO_DTS_HD).build(); - } + track.format = DtsUtil.updateFormatWithDtsHdInfo(input, size, track.format); track.output.format(track.format); track.waitingForDtsAnalysis = false; maybeEndTracks(); @@ -2544,10 +2542,12 @@ public void initializeFormat(int trackId) throws ParserException { trueHdSampleRechunker = new TrueHdSampleRechunker(); break; case CODEC_ID_DTS: - case CODEC_ID_DTS_EXPRESS: mimeType = MimeTypes.AUDIO_DTS; // temporary waitingForDtsAnalysis = true; break; + case CODEC_ID_DTS_EXPRESS: + mimeType = MimeTypes.AUDIO_DTS_EXPRESS; + break; case CODEC_ID_DTS_LOSSLESS: mimeType = MimeTypes.AUDIO_DTS_HD; break; diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java index 756b9b6fdb2..82cd4ec168c 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java @@ -258,6 +258,14 @@ public void mkvSample_withDts() throws Exception { simulationConfig); } + @Test + public void mkaSample_withDtsExpress() throws Exception { + ExtractorAsserts.assertBehavior( + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/mka/sample_with_dts_express.mka", + simulationConfig); + } + @Test public void mkvSample_withDtsHdMa() throws Exception { ExtractorAsserts.assertBehavior( diff --git a/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.0.dump b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.0.dump new file mode 100644 index 00000000000..0976403f6fe --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.0.dump @@ -0,0 +1,63 @@ +seekMap: + isSeekable = true + duration = 938000 + getPosition(0) = [[timeUs=0, position=469]] + getPosition(1) = [[timeUs=0, position=469]] + getPosition(469000) = [[timeUs=0, position=469]] + getPosition(938000) = [[timeUs=0, position=469]] +numberOfTracks = 1 +track 1: + total output bytes = 45056 + sample count = 11 + format 0: + id = 1 + containerMimeType = video/x-matroska + sampleMimeType = audio/vnd.dts.hd;profile=lbr + channelCount = 6 + sampleRate = 48000 + language = und + sample 0: + time = 0 + flags = 1 + data = length 4096, hash 1F0B79C5 + sample 1: + time = 85000 + flags = 1 + data = length 4096, hash 2EC282A1 + sample 2: + time = 171000 + flags = 1 + data = length 4096, hash 6B7902F0 + sample 3: + time = 256000 + flags = 1 + data = length 4096, hash 8FC4EE2C + sample 4: + time = 341000 + flags = 1 + data = length 4096, hash 67899547 + sample 5: + time = 427000 + flags = 1 + data = length 4096, hash 1BE4CF1C + sample 6: + time = 512000 + flags = 1 + data = length 4096, hash 620F5E51 + sample 7: + time = 597000 + flags = 1 + data = length 4096, hash 4D3E0644 + sample 8: + time = 683000 + flags = 1 + data = length 4096, hash F69B5FED + sample 9: + time = 768000 + flags = 1 + data = length 4096, hash 93D31EA7 + sample 10: + time = 853000 + flags = 1 + data = length 4096, hash 41F1D921 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.1.dump b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.1.dump new file mode 100644 index 00000000000..0976403f6fe --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.1.dump @@ -0,0 +1,63 @@ +seekMap: + isSeekable = true + duration = 938000 + getPosition(0) = [[timeUs=0, position=469]] + getPosition(1) = [[timeUs=0, position=469]] + getPosition(469000) = [[timeUs=0, position=469]] + getPosition(938000) = [[timeUs=0, position=469]] +numberOfTracks = 1 +track 1: + total output bytes = 45056 + sample count = 11 + format 0: + id = 1 + containerMimeType = video/x-matroska + sampleMimeType = audio/vnd.dts.hd;profile=lbr + channelCount = 6 + sampleRate = 48000 + language = und + sample 0: + time = 0 + flags = 1 + data = length 4096, hash 1F0B79C5 + sample 1: + time = 85000 + flags = 1 + data = length 4096, hash 2EC282A1 + sample 2: + time = 171000 + flags = 1 + data = length 4096, hash 6B7902F0 + sample 3: + time = 256000 + flags = 1 + data = length 4096, hash 8FC4EE2C + sample 4: + time = 341000 + flags = 1 + data = length 4096, hash 67899547 + sample 5: + time = 427000 + flags = 1 + data = length 4096, hash 1BE4CF1C + sample 6: + time = 512000 + flags = 1 + data = length 4096, hash 620F5E51 + sample 7: + time = 597000 + flags = 1 + data = length 4096, hash 4D3E0644 + sample 8: + time = 683000 + flags = 1 + data = length 4096, hash F69B5FED + sample 9: + time = 768000 + flags = 1 + data = length 4096, hash 93D31EA7 + sample 10: + time = 853000 + flags = 1 + data = length 4096, hash 41F1D921 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.2.dump b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.2.dump new file mode 100644 index 00000000000..0976403f6fe --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.2.dump @@ -0,0 +1,63 @@ +seekMap: + isSeekable = true + duration = 938000 + getPosition(0) = [[timeUs=0, position=469]] + getPosition(1) = [[timeUs=0, position=469]] + getPosition(469000) = [[timeUs=0, position=469]] + getPosition(938000) = [[timeUs=0, position=469]] +numberOfTracks = 1 +track 1: + total output bytes = 45056 + sample count = 11 + format 0: + id = 1 + containerMimeType = video/x-matroska + sampleMimeType = audio/vnd.dts.hd;profile=lbr + channelCount = 6 + sampleRate = 48000 + language = und + sample 0: + time = 0 + flags = 1 + data = length 4096, hash 1F0B79C5 + sample 1: + time = 85000 + flags = 1 + data = length 4096, hash 2EC282A1 + sample 2: + time = 171000 + flags = 1 + data = length 4096, hash 6B7902F0 + sample 3: + time = 256000 + flags = 1 + data = length 4096, hash 8FC4EE2C + sample 4: + time = 341000 + flags = 1 + data = length 4096, hash 67899547 + sample 5: + time = 427000 + flags = 1 + data = length 4096, hash 1BE4CF1C + sample 6: + time = 512000 + flags = 1 + data = length 4096, hash 620F5E51 + sample 7: + time = 597000 + flags = 1 + data = length 4096, hash 4D3E0644 + sample 8: + time = 683000 + flags = 1 + data = length 4096, hash F69B5FED + sample 9: + time = 768000 + flags = 1 + data = length 4096, hash 93D31EA7 + sample 10: + time = 853000 + flags = 1 + data = length 4096, hash 41F1D921 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.3.dump b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.3.dump new file mode 100644 index 00000000000..0976403f6fe --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.3.dump @@ -0,0 +1,63 @@ +seekMap: + isSeekable = true + duration = 938000 + getPosition(0) = [[timeUs=0, position=469]] + getPosition(1) = [[timeUs=0, position=469]] + getPosition(469000) = [[timeUs=0, position=469]] + getPosition(938000) = [[timeUs=0, position=469]] +numberOfTracks = 1 +track 1: + total output bytes = 45056 + sample count = 11 + format 0: + id = 1 + containerMimeType = video/x-matroska + sampleMimeType = audio/vnd.dts.hd;profile=lbr + channelCount = 6 + sampleRate = 48000 + language = und + sample 0: + time = 0 + flags = 1 + data = length 4096, hash 1F0B79C5 + sample 1: + time = 85000 + flags = 1 + data = length 4096, hash 2EC282A1 + sample 2: + time = 171000 + flags = 1 + data = length 4096, hash 6B7902F0 + sample 3: + time = 256000 + flags = 1 + data = length 4096, hash 8FC4EE2C + sample 4: + time = 341000 + flags = 1 + data = length 4096, hash 67899547 + sample 5: + time = 427000 + flags = 1 + data = length 4096, hash 1BE4CF1C + sample 6: + time = 512000 + flags = 1 + data = length 4096, hash 620F5E51 + sample 7: + time = 597000 + flags = 1 + data = length 4096, hash 4D3E0644 + sample 8: + time = 683000 + flags = 1 + data = length 4096, hash F69B5FED + sample 9: + time = 768000 + flags = 1 + data = length 4096, hash 93D31EA7 + sample 10: + time = 853000 + flags = 1 + data = length 4096, hash 41F1D921 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.unknown_length.dump new file mode 100644 index 00000000000..0976403f6fe --- /dev/null +++ b/libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.unknown_length.dump @@ -0,0 +1,63 @@ +seekMap: + isSeekable = true + duration = 938000 + getPosition(0) = [[timeUs=0, position=469]] + getPosition(1) = [[timeUs=0, position=469]] + getPosition(469000) = [[timeUs=0, position=469]] + getPosition(938000) = [[timeUs=0, position=469]] +numberOfTracks = 1 +track 1: + total output bytes = 45056 + sample count = 11 + format 0: + id = 1 + containerMimeType = video/x-matroska + sampleMimeType = audio/vnd.dts.hd;profile=lbr + channelCount = 6 + sampleRate = 48000 + language = und + sample 0: + time = 0 + flags = 1 + data = length 4096, hash 1F0B79C5 + sample 1: + time = 85000 + flags = 1 + data = length 4096, hash 2EC282A1 + sample 2: + time = 171000 + flags = 1 + data = length 4096, hash 6B7902F0 + sample 3: + time = 256000 + flags = 1 + data = length 4096, hash 8FC4EE2C + sample 4: + time = 341000 + flags = 1 + data = length 4096, hash 67899547 + sample 5: + time = 427000 + flags = 1 + data = length 4096, hash 1BE4CF1C + sample 6: + time = 512000 + flags = 1 + data = length 4096, hash 620F5E51 + sample 7: + time = 597000 + flags = 1 + data = length 4096, hash 4D3E0644 + sample 8: + time = 683000 + flags = 1 + data = length 4096, hash F69B5FED + sample 9: + time = 768000 + flags = 1 + data = length 4096, hash 93D31EA7 + sample 10: + time = 853000 + flags = 1 + data = length 4096, hash 41F1D921 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/media/mka/sample_with_dts_express.mka b/libraries/test_data/src/test/assets/media/mka/sample_with_dts_express.mka new file mode 100644 index 00000000000..59257c26af9 Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mka/sample_with_dts_express.mka differ