From 68f18c88480f1c9c0eae551bd830e504908809f4 Mon Sep 17 00:00:00 2001 From: nift4 Date: Sat, 28 Mar 2026 20:08:54 +0100 Subject: [PATCH 1/2] Support DTS Express in Matroska FFmpeg outputs DTS Express files with "DTS" codec ID, so we need to add it to the detection. That however is free because we must do the detection anyway for DTS-HD, and we can even reuse all the code needed for TS logic. This is also a building block for a future commit detecting DTS-HD MA, and the util method is useful for a future commit for the MP4 extractor. --- .../androidx/media3/extractor/DtsUtil.java | 40 ++++++++--- .../extractor/mkv/MatroskaExtractor.java | 8 +-- .../extractor/mkv/MatroskaExtractorTest.java | 8 +++ .../mka/sample_with_dts_express.mka.0.dump | 63 ++++++++++++++++++ .../mka/sample_with_dts_express.mka.1.dump | 63 ++++++++++++++++++ .../mka/sample_with_dts_express.mka.2.dump | 63 ++++++++++++++++++ .../mka/sample_with_dts_express.mka.3.dump | 63 ++++++++++++++++++ ...e_with_dts_express.mka.unknown_length.dump | 63 ++++++++++++++++++ .../media/mka/sample_with_dts_express.mka | Bin 0 -> 45646 bytes 9 files changed, 359 insertions(+), 12 deletions(-) create mode 100644 libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.0.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.1.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.2.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.3.dump create mode 100644 libraries/test_data/src/test/assets/extractordumps/mka/sample_with_dts_express.mka.unknown_length.dump create mode 100644 libraries/test_data/src/test/assets/media/mka/sample_with_dts_express.mka 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..0daa9aae44e 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/DtsUtil.java @@ -875,31 +875,55 @@ 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 format with adjusted mime type if the sample data at the current {@link + * ExtractorInput} is a DTS-HD sample with static fields, or the unmodified format if it is not. + */ + public static Format setDtsHdInfoToFormat(Format baseFormat, ExtractorInput input, int sampleSize) + throws IOException { ParsableByteArray sampleData = new ParsableByteArray(sampleSize); if (!input.peekFully( sampleData.getData(), /* offset= */ 0, sampleSize, /* allowEndOfInput= */ true)) { - return false; + return baseFormat; } 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 baseFormat; } 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; + return baseFormat; } sampleData.skipBytes(frameSize); - word = sampleData.readInt(); - return DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM; + word = sampleData.peekInt(); + } + if (DtsUtil.getFrameType(word) != DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM) { + return baseFormat; + } + if (sampleData.bytesLeft() < 7) { + return baseFormat; + } + byte[] headerPrefix = new byte[7]; + sampleData.readBytes(headerPrefix, /* offset= */ 0, /* length= */ 7); + sampleData.skipBytes(-7); + int frameSize = parseDtsHdHeaderSize(headerPrefix); + if (frameSize <= 0 || sampleData.bytesLeft() < frameSize) { + return baseFormat; + } + byte[] header = new byte[frameSize]; + sampleData.readBytes(header, /* offset= */ 0, /* length= */ frameSize); + DtsHeader dtsHeader = parseDtsHdHeader(header); + if (dtsHeader.mimeType == null) { + // Missing static fields, there's nothing we can do other than assuming it is DTS-HD + return baseFormat.buildUpon().setSampleMimeType(MimeTypes.AUDIO_DTS_HD).build(); } - return false; + return baseFormat.buildUpon().setSampleMimeType(dtsHeader.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..828f705ab0a 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.setDtsHdInfoToFormat(track.format, input, size); 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 0000000000000000000000000000000000000000..59257c26af975ba02b9b07a7f018d90cc5f388a0 GIT binary patch literal 45646 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 literal 0 HcmV?d00001 From a9753b879b890af21256a360ac84d072c7e1cf26 Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Fri, 15 May 2026 00:27:05 +0100 Subject: [PATCH 2/2] Refactor and clean up DTS-HD format extraction in DtsUtil --- .../androidx/media3/extractor/DtsUtil.java | 46 +++++++++++-------- .../extractor/mkv/MatroskaExtractor.java | 2 +- 2 files changed, 29 insertions(+), 19 deletions(-) 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 0daa9aae44e..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. */ @@ -876,54 +877,63 @@ public static int parseDtsUhdHeaderSize(byte[] headerPrefix) { } /** - * Returns a format with adjusted mime type if the sample data at the current {@link - * ExtractorInput} is a DTS-HD sample with static fields, or the unmodified format if it is not. + * 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 setDtsHdInfoToFormat(Format baseFormat, ExtractorInput input, int sampleSize) - throws IOException { + 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 baseFormat; + 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 baseFormat; + 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 baseFormat; + if (frameSize <= 0 || sampleData.limit() < frameSize + 4) { + return format; } - sampleData.skipBytes(frameSize); + sampleData.setPosition(frameSize); word = sampleData.peekInt(); } if (DtsUtil.getFrameType(word) != DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM) { - return baseFormat; + return format; } if (sampleData.bytesLeft() < 7) { - return baseFormat; + return format; } + int extHeaderOffset = sampleData.getPosition(); byte[] headerPrefix = new byte[7]; sampleData.readBytes(headerPrefix, /* offset= */ 0, /* length= */ 7); - sampleData.skipBytes(-7); + sampleData.setPosition(extHeaderOffset); int frameSize = parseDtsHdHeaderSize(headerPrefix); if (frameSize <= 0 || sampleData.bytesLeft() < frameSize) { - return baseFormat; + return format; } byte[] header = new byte[frameSize]; sampleData.readBytes(header, /* offset= */ 0, /* length= */ frameSize); DtsHeader dtsHeader = parseDtsHdHeader(header); - if (dtsHeader.mimeType == null) { - // Missing static fields, there's nothing we can do other than assuming it is DTS-HD - return baseFormat.buildUpon().setSampleMimeType(MimeTypes.AUDIO_DTS_HD).build(); + // 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 baseFormat.buildUpon().setSampleMimeType(dtsHeader.mimeType).build(); + 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 828f705ab0a..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,7 +1846,7 @@ private int writeSampleData(ExtractorInput input, Track track, int size, boolean if (track.waitingForDtsAnalysis) { checkNotNull(track.format); - track.format = DtsUtil.setDtsHdInfoToFormat(track.format, input, size); + track.format = DtsUtil.updateFormatWithDtsHdInfo(input, size, track.format); track.output.format(track.format); track.waitingForDtsAnalysis = false; maybeEndTracks();