Skip to content

Commit a878b72

Browse files
committed
Add detection for DTS-HD audio tracks in MatroskaExtractor
Distinguish between DTS and DTS-HD formats by analyzing the frame data in matroska containers. This enables accurate MIME type assignment for DTS-HD content.
1 parent 258a3e0 commit a878b72

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

libraries/extractor/src/main/java/androidx/media3/extractor/mkv/MatroskaExtractor.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import androidx.media3.extractor.AacUtil;
4747
import androidx.media3.extractor.AvcConfig;
4848
import androidx.media3.extractor.ChunkIndex;
49+
import androidx.media3.extractor.DtsUtil;
4950
import androidx.media3.extractor.Extractor;
5051
import androidx.media3.extractor.ExtractorInput;
5152
import androidx.media3.extractor.ExtractorOutput;
@@ -1564,6 +1565,53 @@ private int writeSampleData(ExtractorInput input, Track track, int size, boolean
15641565
return finishWriteSampleData();
15651566
}
15661567

1568+
if (CODEC_ID_DTS.equals(track.codecId) && !track.dtsAnalyzed) {
1569+
long remaining = input.getLength() - input.getPosition();
1570+
// Limit the peek ahead to be up to the max frame size (16383) plus the
1571+
// sync word of the second frame
1572+
int scanLength = (int)Math.min(16383 + 95, remaining);
1573+
byte[] buf = new byte[scanLength];
1574+
track.dtsAnalyzed = true;
1575+
1576+
input.advancePeekPosition(0);
1577+
input.peekFully(buf, 0, buf.length);
1578+
input.resetPeekPosition();
1579+
1580+
final ByteBuffer bb = ByteBuffer.wrap(buf);
1581+
for (int idx = 0; idx + 4 <= buf.length; idx += 4) {
1582+
int word = bb.getInt(idx);
1583+
1584+
if (DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_CORE) {
1585+
// header starts immediately after the 4‐byte sync
1586+
int headerStart = idx + 4;
1587+
if (headerStart + 10 > buf.length) {
1588+
break;
1589+
}
1590+
1591+
bb.mark();
1592+
bb.position(headerStart);
1593+
byte[] header = new byte[10];
1594+
bb.get(header);
1595+
bb.reset();
1596+
int fsize = DtsUtil.getDtsFrameSize(header);
1597+
if (fsize <= 0 || idx + fsize + 4 > buf.length) {
1598+
break;
1599+
}
1600+
1601+
word = bb.getInt(idx + fsize);
1602+
1603+
if (DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM) {
1604+
track.formatBuilder.setSampleMimeType(MimeTypes.AUDIO_DTS_HD);
1605+
track.output.format(track.formatBuilder.build());
1606+
}
1607+
1608+
// After finding a valid DTS core frame we can break the loop, there is no
1609+
// need to evaluate the rest of the buffer.
1610+
break;
1611+
}
1612+
}
1613+
}
1614+
15671615
TrackOutput output = track.output;
15681616
if (!sampleEncodingHandled) {
15691617
if (track.hasContentEncryption) {
@@ -2135,6 +2183,7 @@ protected static final class Track {
21352183
public long codecDelayNs = 0;
21362184
public long seekPreRollNs = 0;
21372185
public @MonotonicNonNull TrueHdSampleRechunker trueHdSampleRechunker;
2186+
public boolean dtsAnalyzed = false;
21382187

21392188
// Text elements.
21402189
public boolean flagForced;
@@ -2143,6 +2192,7 @@ protected static final class Track {
21432192

21442193
// Set when the output is initialized. nalUnitLengthFieldLength is only set for H264/H265.
21452194
public @MonotonicNonNull TrackOutput output;
2195+
public Format.Builder formatBuilder;
21462196
public int nalUnitLengthFieldLength;
21472197

21482198
/** Initializes the track with an output. */
@@ -2369,7 +2419,7 @@ public void initializeOutput(ExtractorOutput output, int trackId) throws ParserE
23692419
selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0;
23702420

23712421
int type;
2372-
Format.Builder formatBuilder = new Format.Builder();
2422+
formatBuilder = new Format.Builder();
23732423
// TODO: Consider reading the name elements of the tracks and, if present, incorporating them
23742424
// into the trackId passed when creating the formats.
23752425
if (MimeTypes.isAudio(mimeType)) {

0 commit comments

Comments
 (0)