From 8e09cf45c0922579c994910d40780375ba2cce03 Mon Sep 17 00:00:00 2001 From: kimvde Date: Mon, 29 Jun 2020 08:22:40 +0100 Subject: [PATCH] Fix bug unseekable FMP4 The seek start position was set to the first mdat but this box was always skipped because the moof box was not read. PiperOrigin-RevId: 318762126 --- .../extractor/mp4/FragmentedMp4Extractor.java | 10 +++---- .../assets/mp4/sample_fragmented.mp4.0.dump | 2 +- .../sample_fragmented.mp4.unknown_length.dump | 2 +- .../mp4/sample_fragmented_sei.mp4.0.dump | 2 +- ...ple_fragmented_sei.mp4.unknown_length.dump | 2 +- .../exoplayer2/testutil/ExtractorAsserts.java | 30 +++++++++++-------- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java index 37df66ba2c..cf84eab20d 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java @@ -374,16 +374,16 @@ public class FragmentedMp4Extractor implements Extractor { fragment.auxiliaryDataPosition = atomPosition; fragment.dataPosition = atomPosition; } + if (!haveOutputSeekMap) { + // This must be the first moof in the stream. + extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition)); + haveOutputSeekMap = true; + } } if (atomType == Atom.TYPE_mdat) { currentTrackBundle = null; endOfMdatPosition = atomPosition + atomSize; - if (!haveOutputSeekMap) { - // This must be the first mdat in the stream. - extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition)); - haveOutputSeekMap = true; - } parserState = STATE_READING_ENCRYPTION_DATA; return true; } diff --git a/testdata/src/test/assets/mp4/sample_fragmented.mp4.0.dump b/testdata/src/test/assets/mp4/sample_fragmented.mp4.0.dump index 2a5848f5a4..001895e61e 100644 --- a/testdata/src/test/assets/mp4/sample_fragmented.mp4.0.dump +++ b/testdata/src/test/assets/mp4/sample_fragmented.mp4.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = [[timeUs=0, position=1828]] + getPosition(0) = [[timeUs=0, position=1244]] numberOfTracks = 2 track 0: total output bytes = 85933 diff --git a/testdata/src/test/assets/mp4/sample_fragmented.mp4.unknown_length.dump b/testdata/src/test/assets/mp4/sample_fragmented.mp4.unknown_length.dump index 2a5848f5a4..001895e61e 100644 --- a/testdata/src/test/assets/mp4/sample_fragmented.mp4.unknown_length.dump +++ b/testdata/src/test/assets/mp4/sample_fragmented.mp4.unknown_length.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = [[timeUs=0, position=1828]] + getPosition(0) = [[timeUs=0, position=1244]] numberOfTracks = 2 track 0: total output bytes = 85933 diff --git a/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.0.dump b/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.0.dump index 341fba46b9..f88092002b 100644 --- a/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.0.dump +++ b/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.0.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = [[timeUs=0, position=1828]] + getPosition(0) = [[timeUs=0, position=1244]] numberOfTracks = 3 track 0: total output bytes = 85933 diff --git a/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.unknown_length.dump b/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.unknown_length.dump index 341fba46b9..f88092002b 100644 --- a/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.unknown_length.dump +++ b/testdata/src/test/assets/mp4/sample_fragmented_sei.mp4.unknown_length.dump @@ -1,7 +1,7 @@ seekMap: isSeekable = false duration = UNSET TIME - getPosition(0) = [[timeUs=0, position=1828]] + getPosition(0) = [[timeUs=0, position=1244]] numberOfTracks = 3 track 0: total output bytes = 85933 diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java index da585ee4f0..7411016177 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExtractorAsserts.java @@ -341,21 +341,25 @@ public final class ExtractorAsserts { extractorOutput.assertOutput(context, dumpFilesPrefix + ".0" + DUMP_EXTENSION); } - // If the SeekMap is seekable, test seeking in the stream. SeekMap seekMap = Assertions.checkNotNull(extractorOutput.seekMap); - if (seekMap.isSeekable()) { - long durationUs = seekMap.getDurationUs(); - for (int j = 0; j < 4; j++) { - extractorOutput.clearTrackOutputs(); - long timeUs = durationUs == C.TIME_UNSET ? 0 : (durationUs * j) / 3; - long position = seekMap.getSeekPoints(timeUs).first.position; - input.reset(); - input.setPosition((int) position); - consumeTestData(extractor, input, timeUs, extractorOutput, false); + long durationUs = seekMap.getDurationUs(); + // Only seek to the timeUs=0 if the SeekMap is unseekable or the duration is unknown. + int numberSeekTests = seekMap.isSeekable() && durationUs != C.TIME_UNSET ? 4 : 1; + for (int j = 0; j < numberSeekTests; j++) { + long timeUs = durationUs * j / 3; + long position = seekMap.getSeekPoints(timeUs).first.position; + if (timeUs == 0 && position == 0) { + // Already tested. + continue; + } + input.reset(); + input.setPosition((int) position); + extractorOutput.clearTrackOutputs(); + consumeTestData(extractor, input, timeUs, extractorOutput, false); + if (simulateUnknownLength && timeUs == 0) { + extractorOutput.assertOutput(context, dumpFilesPrefix + UNKNOWN_LENGTH_EXTENSION); + } else { extractorOutput.assertOutput(context, dumpFilesPrefix + '.' + j + DUMP_EXTENSION); - if (durationUs == C.TIME_UNSET) { - break; - } } } }