Skip to content

Commit ffca82f

Browse files
committed
Bugfix: Handle seekhead for cues on mkv files.
1 parent 0ad7cea commit ffca82f

File tree

5 files changed

+570
-3
lines changed

5 files changed

+570
-3
lines changed

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/MkvPlaybackTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ public static ImmutableList<String> mediaSamples() {
5757
"sample_with_overlapping_srt.mkv",
5858
"sample_with_vtt_subtitles.mkv",
5959
"sample_with_null_terminated_vtt_subtitles.mkv",
60-
"sample_with_vobsub.mkv");
60+
"sample_with_vobsub.mkv",
61+
"sample_recursive_seekhead.mkv");
6162
}
6263

6364
@ParameterizedRobolectricTestRunner.Parameter public String inputFile;

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

+33-2
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,9 @@ public static ExtractorsFactory newFactory(SubtitleParser.Factory subtitleParser
456456

457457
// Cue related elements.
458458
private boolean seekForCues;
459+
private boolean seekForSeekContent;
460+
private long seekPositionAfterSeekingForHead = C.INDEX_UNSET;
461+
private long seekHeadContentPosition = C.INDEX_UNSET;
459462
private long cuesContentPosition = C.INDEX_UNSET;
460463
private long seekPositionAfterBuildingCues = C.INDEX_UNSET;
461464
private long clusterTimecodeUs = C.TIME_UNSET;
@@ -764,6 +767,10 @@ protected void startMasterElement(int id, long contentPosition, long contentSize
764767
if (seekForCuesEnabled && cuesContentPosition != C.INDEX_UNSET) {
765768
// We know where the Cues element is located. Seek to request it.
766769
seekForCues = true;
770+
} else if (seekForCuesEnabled && seekHeadContentPosition != C.INDEX_UNSET) {
771+
// We do not know where the cues are located, however we have a seek-head entry
772+
// we have not yet visited
773+
seekForSeekContent = true;
767774
} else {
768775
// We don't know where the Cues element is located. It's most likely omitted. Allow
769776
// playback, but disable seeking.
@@ -816,9 +823,16 @@ protected void endMasterElement(int id) throws ParserException {
816823
if (seekEntryId == UNSET_ENTRY_ID || seekEntryPosition == C.INDEX_UNSET) {
817824
throw ParserException.createForMalformedContainer(
818825
"Mandatory element SeekID or SeekPosition not found", /* cause= */ null);
819-
}
820-
if (seekEntryId == ID_CUES) {
826+
} else if (seekEntryId == ID_SEEK_HEAD) {
827+
seekHeadContentPosition = seekEntryPosition;
828+
} else if (seekEntryId == ID_CUES) {
821829
cuesContentPosition = seekEntryPosition;
830+
831+
// We are currently seeking from the seek-head, so we seek again to get to the cues
832+
// instead of waiting for the cluster
833+
if (seekForCuesEnabled && seekPositionAfterSeekingForHead != C.INDEX_UNSET) {
834+
seekForCues = true;
835+
}
822836
}
823837
break;
824838
case ID_CUES:
@@ -1936,6 +1950,13 @@ private SeekMap buildSeekMap(
19361950
* @return Whether the seek position was updated.
19371951
*/
19381952
private boolean maybeSeekForCues(PositionHolder seekPosition, long currentPosition) {
1953+
if (seekForSeekContent) {
1954+
seekPositionAfterSeekingForHead = currentPosition;
1955+
seekPosition.position = seekHeadContentPosition;
1956+
seekForSeekContent = false;
1957+
return true;
1958+
}
1959+
19391960
if (seekForCues) {
19401961
seekPositionAfterBuildingCues = currentPosition;
19411962
seekPosition.position = cuesContentPosition;
@@ -1949,6 +1970,16 @@ private boolean maybeSeekForCues(PositionHolder seekPosition, long currentPositi
19491970
seekPositionAfterBuildingCues = C.INDEX_UNSET;
19501971
return true;
19511972
}
1973+
1974+
// After we have seeked back from seekPositionAfterBuildingCues seek back again to parse the
1975+
// rest of the file. This ends the double jump that is preformed when the beginning metadata
1976+
// only contains a ID_SEEK_HEAD without a ID_CUES.
1977+
if (sentSeekMap && seekPositionAfterSeekingForHead != C.INDEX_UNSET) {
1978+
seekPosition.position = seekPositionAfterSeekingForHead;
1979+
seekPositionAfterSeekingForHead = C.INDEX_UNSET;
1980+
return true;
1981+
}
1982+
19521983
return false;
19531984
}
19541985

libraries/extractor/src/test/java/androidx/media3/extractor/mkv/MatroskaExtractorTest.java

+11
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ public void mkvSample_withNullTerminatedVttSubtitles() throws Exception {
140140
simulationConfig);
141141
}
142142

143+
// https://github.com/androidx/media/issues/1143
144+
@Test
145+
public void mkvSample_withRecursiveSeekHead() throws Exception {
146+
ExtractorAsserts.assertBehavior(
147+
getExtractorFactory(subtitlesParsedDuringExtraction),
148+
"media/mkv/sample_recursive_seekhead.mkv",
149+
getAssertionConfigWithPrefix(
150+
"media/mkv/sample_recursive_seekhead.mkv", subtitlesParsedDuringExtraction),
151+
simulationConfig);
152+
}
153+
143154
@Test
144155
public void mkvSample_withVorbisAudio() throws Exception {
145156
ExtractorAsserts.assertBehavior(
Binary file not shown.

0 commit comments

Comments
 (0)