Skip to content

Add detection for DTS-HD audio tracks in MatroskaExtractor #2485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import androidx.media3.extractor.AacUtil;
import androidx.media3.extractor.AvcConfig;
import androidx.media3.extractor.ChunkIndex;
import androidx.media3.extractor.DtsUtil;
import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.ExtractorInput;
import androidx.media3.extractor.ExtractorOutput;
Expand Down Expand Up @@ -444,6 +445,7 @@ public static ExtractorsFactory newFactory(SubtitleParser.Factory subtitleParser
private long durationTimecode = C.TIME_UNSET;
private long durationUs = C.TIME_UNSET;
private boolean isWebm;
private boolean pendingEndTracks = true;

// The track corresponding to the current TrackEntry element, or null.
@Nullable private Track currentTrack;
Expand Down Expand Up @@ -795,6 +797,23 @@ protected void startMasterElement(int id, long contentPosition, long contentSize
}
}

/**
* Ensures `extractorOutput.endTracks()` gets called only once, and only
* if we don't have any pending audio analysis.
*/
private void maybeEndTracks() {
if (!pendingEndTracks) {
return;
}
for (int i = 0; i < tracks.size(); i++) {
if (tracks.valueAt(i).waitingForDtsAnalysis) {
return;
}
}
extractorOutput.endTracks();
pendingEndTracks = false;
}

/**
* Called when the end of a master element is encountered.
*
Expand Down Expand Up @@ -905,7 +924,7 @@ protected void endMasterElement(int id) throws ParserException {
throw ParserException.createForMalformedContainer(
"No valid tracks were found", /* cause= */ null);
}
extractorOutput.endTracks();
maybeEndTracks();
break;
default:
break;
Expand Down Expand Up @@ -1564,6 +1583,53 @@ private int writeSampleData(ExtractorInput input, Track track, int size, boolean
return finishWriteSampleData();
}

if (track.waitingForDtsAnalysis) {
// The format for this DTS track as not been determined yet
long remaining = input.getLength() - input.getPosition();
// Limit the peek ahead to be up to the max frame size (16383) plus the
// sync word of the second frame
int scanLength = (int)Math.min(16383 + 95, remaining);
byte[] buf = new byte[scanLength];

input.advancePeekPosition(0);
input.peekFully(buf, 0, buf.length);
input.resetPeekPosition();

final ByteBuffer bb = ByteBuffer.wrap(buf);
for (int idx = 0; idx + 4 <= buf.length; idx += 4) {
int word = bb.getInt(idx);

if (DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_CORE) {
if (idx + 10 > buf.length) {
break;
}

bb.mark();
bb.position(idx);
byte[] header = new byte[10];
bb.get(header);
bb.reset();
int fsize = DtsUtil.getDtsFrameSize(header);
if (fsize <= 0 || idx + fsize + 4 > buf.length) {
break;
}

word = bb.getInt(idx + fsize);

if (DtsUtil.getFrameType(word) == DtsUtil.FRAME_TYPE_EXTENSION_SUBSTREAM) {
track.formatBuilder.setSampleMimeType(MimeTypes.AUDIO_DTS_HD);
track.output.format(track.formatBuilder.build());
}

// After finding a valid DTS core frame we can break the loop, there is no
// need to evaluate the rest of the buffer.
break;
}
}
track.waitingForDtsAnalysis = false;
maybeEndTracks();
}

TrackOutput output = track.output;
if (!sampleEncodingHandled) {
if (track.hasContentEncryption) {
Expand Down Expand Up @@ -2135,6 +2201,7 @@ protected static final class Track {
public long codecDelayNs = 0;
public long seekPreRollNs = 0;
public @MonotonicNonNull TrueHdSampleRechunker trueHdSampleRechunker;
public boolean waitingForDtsAnalysis = false;

// Text elements.
public boolean flagForced;
Expand All @@ -2143,6 +2210,7 @@ protected static final class Track {

// Set when the output is initialized. nalUnitLengthFieldLength is only set for H264/H265.
public @MonotonicNonNull TrackOutput output;
public Format.Builder formatBuilder;
public int nalUnitLengthFieldLength;

/** Initializes the track with an output. */
Expand Down Expand Up @@ -2246,7 +2314,8 @@ public void initializeOutput(ExtractorOutput output, int trackId) throws ParserE
break;
case CODEC_ID_DTS:
case CODEC_ID_DTS_EXPRESS:
mimeType = MimeTypes.AUDIO_DTS;
mimeType = MimeTypes.AUDIO_DTS; // temporary
waitingForDtsAnalysis = true;
break;
case CODEC_ID_DTS_LOSSLESS:
mimeType = MimeTypes.AUDIO_DTS_HD;
Expand Down Expand Up @@ -2369,7 +2438,7 @@ public void initializeOutput(ExtractorOutput output, int trackId) throws ParserE
selectionFlags |= flagForced ? C.SELECTION_FLAG_FORCED : 0;

int type;
Format.Builder formatBuilder = new Format.Builder();
formatBuilder = new Format.Builder();
// TODO: Consider reading the name elements of the tracks and, if present, incorporating them
// into the trackId passed when creating the formats.
if (MimeTypes.isAudio(mimeType)) {
Expand Down