diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ad8395d024c..422d5bfe76d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -203,6 +203,7 @@ ([#6410](https://github.com/google/ExoPlayer/issues/6410)). * Select first extractors based on the filename extension and the response headers mime type in `DefaultExtractorsFactory`. + * Add support for partially fragmented MP4s. * Testing * Add `TestExoPlayer`, a utility class with APIs to create `SimpleExoPlayer` instances with fake components for testing. 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 30a1de8988b..3cfc68cbe23 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 @@ -280,7 +280,17 @@ public void init(ExtractorOutput output) { extractorOutput = output; if (sideloadedTrack != null) { TrackBundle bundle = new TrackBundle(output.track(0, sideloadedTrack.type)); - bundle.init(sideloadedTrack, new DefaultSampleValues(0, 0, 0, 0)); + bundle.init( + new TrackSampleTable( + sideloadedTrack, + /* offsets= */ new long[0], + /* sizes= */ new int[0], + /* maximumSize= */ 0, + /* timestampsUs= */ new long[0], + /* flags= */ new int[0], + /* durationUs= */ C.TIME_UNSET), + new DefaultSampleValues( + /* sampleDescriptionIndex= */ 0, /* duration= */ 0, /* size= */ 0, /* flags= */ 0)); trackBundles.put(0, bundle); maybeInitExtraTracks(); extractorOutput.endTracks(); @@ -368,6 +378,14 @@ private boolean readAtomHeader(ExtractorInput input) throws IOException { } long atomPosition = input.getPosition() - atomHeaderBytesRead; + if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mdat) { + if (!haveOutputSeekMap) { + // This must be the first moof or mdat in the stream. + extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition)); + haveOutputSeekMap = true; + } + } + if (atomType == Atom.TYPE_moof) { // The data positions may be updated when parsing the tfhd/trun. int trackCount = trackBundles.size(); @@ -377,11 +395,6 @@ private boolean readAtomHeader(ExtractorInput input) throws IOException { 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) { @@ -483,7 +496,7 @@ private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException } // Construction of tracks and sample tables. - List trackSampleTables = + List sampleTables = parseTraks( moov, new GaplessInfoHolder(), @@ -493,13 +506,14 @@ private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException /* isQuickTime= */ false, this::modifyTrack); - int trackCount = trackSampleTables.size(); + int trackCount = sampleTables.size(); if (trackBundles.size() == 0) { // We need to create the track bundles. for (int i = 0; i < trackCount; i++) { - Track track = trackSampleTables.get(i).track; + TrackSampleTable sampleTable = sampleTables.get(i); + Track track = sampleTable.track; TrackBundle trackBundle = new TrackBundle(extractorOutput.track(i, track.type)); - trackBundle.init(track, getDefaultSampleValues(defaultSampleValuesArray, track.id)); + trackBundle.init(sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id)); trackBundles.put(track.id, trackBundle); durationUs = Math.max(durationUs, track.durationUs); } @@ -508,10 +522,11 @@ private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException } else { Assertions.checkState(trackBundles.size() == trackCount); for (int i = 0; i < trackCount; i++) { - Track track = trackSampleTables.get(i).track; + TrackSampleTable sampleTable = sampleTables.get(i); + Track track = sampleTable.track; trackBundles .get(track.id) - .init(track, getDefaultSampleValues(defaultSampleValuesArray, track.id)); + .init(sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id)); } } } @@ -702,6 +717,7 @@ private static void parseTraf(ContainerAtom traf, SparseArray track TrackFragment fragment = trackBundle.fragment; long decodeTime = fragment.nextFragmentDecodeTime; trackBundle.reset(); + trackBundle.currentlyInFragment = true; @Nullable LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt); if (tfdtAtom != null && (flags & FLAG_WORKAROUND_IGNORE_TFDT_BOX) == 0) { @@ -712,7 +728,8 @@ private static void parseTraf(ContainerAtom traf, SparseArray track @Nullable TrackEncryptionBox encryptionBox = - trackBundle.track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex); + trackBundle.moovSampleTable.track.getSampleDescriptionEncryptionBox( + fragment.header.sampleDescriptionIndex); @Nullable LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz); if (saiz != null) { @@ -933,7 +950,7 @@ private static int parseTrun( int fullAtom = trun.readInt(); int atomFlags = Atom.parseFullAtomFlags(fullAtom); - Track track = trackBundle.track; + Track track = trackBundle.moovSampleTable.track; TrackFragment fragment = trackBundle.fragment; DefaultSampleValues defaultSampleValues = fragment.header; @@ -1001,6 +1018,7 @@ private static int parseTrun( } sampleDecodingTimeUsTable[i] = Util.scaleLargeTimestamp(cumulativeTime, C.MICROS_PER_SECOND, timescale) - edtsOffsetUs; + sampleDecodingTimeUsTable[i] += trackBundle.moovSampleTable.durationUs; sampleSizeTable[i] = sampleSize; sampleIsSyncFrameTable[i] = ((sampleFlags >> 16) & 0x1) == 0 && (!workaroundEveryVideoFrameIsSyncFrame || i == 0); @@ -1225,7 +1243,7 @@ private void readEncryptionData(ExtractorInput input) throws IOException { private boolean readSample(ExtractorInput input) throws IOException { if (parserState == STATE_READING_SAMPLE_START) { if (currentTrackBundle == null) { - @Nullable TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles); + @Nullable TrackBundle currentTrackBundle = getNextTrackBundle(trackBundles); if (currentTrackBundle == null) { // We've run out of samples in the current mdat. Discard any trailing data and prepare to // read the header of the next atom. @@ -1262,12 +1280,14 @@ private boolean readSample(ExtractorInput input) throws IOException { return true; } - if (currentTrackBundle.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) { + if (currentTrackBundle.moovSampleTable.track.sampleTransformation + == Track.TRANSFORMATION_CEA608_CDAT) { sampleSize -= Atom.HEADER_SIZE; input.skipFully(Atom.HEADER_SIZE); } - if (MimeTypes.AUDIO_AC4.equals(currentTrackBundle.track.format.sampleMimeType)) { + if (MimeTypes.AUDIO_AC4.equals( + currentTrackBundle.moovSampleTable.track.format.sampleMimeType)) { // AC4 samples need to be prefixed with a clear sample header. sampleBytesWritten = currentTrackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE); @@ -1283,7 +1303,7 @@ private boolean readSample(ExtractorInput input) throws IOException { sampleCurrentNalBytesRemaining = 0; } - Track track = currentTrackBundle.track; + Track track = currentTrackBundle.moovSampleTable.track; TrackOutput output = currentTrackBundle.output; long sampleTimeUs = currentTrackBundle.getCurrentSamplePresentationTimeUs(); if (timestampAdjuster != null) { @@ -1390,24 +1410,27 @@ private void outputPendingMetadataSamples(long sampleTimeUs) { } /** - * Returns the {@link TrackBundle} whose fragment run has the earliest file position out of those - * yet to be consumed, or null if all have been consumed. + * Returns the {@link TrackBundle} whose sample has the earliest file position out of those yet to + * be consumed, or null if all have been consumed. */ @Nullable - private static TrackBundle getNextFragmentRun(SparseArray trackBundles) { + private static TrackBundle getNextTrackBundle(SparseArray trackBundles) { TrackBundle nextTrackBundle = null; - long nextTrackRunOffset = Long.MAX_VALUE; + long nextSampleOffset = Long.MAX_VALUE; int trackBundlesSize = trackBundles.size(); for (int i = 0; i < trackBundlesSize; i++) { TrackBundle trackBundle = trackBundles.valueAt(i); - if (trackBundle.currentTrackRunIndex == trackBundle.fragment.trunCount) { - // This track fragment contains no more runs in the next mdat box. + if ((!trackBundle.currentlyInFragment + && trackBundle.currentSampleIndex == trackBundle.moovSampleTable.sampleCount) + || (trackBundle.currentlyInFragment + && trackBundle.currentTrackRunIndex == trackBundle.fragment.trunCount)) { + // This track sample table or fragment contains no more runs in the next mdat box. } else { - long trunOffset = trackBundle.getCurrentSampleOffset(); - if (trunOffset < nextTrackRunOffset) { + long sampleOffset = trackBundle.getCurrentSampleOffset(); + if (sampleOffset < nextSampleOffset) { nextTrackBundle = trackBundle; - nextTrackRunOffset = trunOffset; + nextSampleOffset = sampleOffset; } } } @@ -1502,7 +1525,7 @@ private static final class TrackBundle { public final TrackFragment fragment; public final ParsableByteArray scratch; - public Track track; + public TrackSampleTable moovSampleTable; public DefaultSampleValues defaultSampleValues; public int currentSampleIndex; public int currentSampleInTrackRun; @@ -1512,6 +1535,8 @@ private static final class TrackBundle { private final ParsableByteArray encryptionSignalByte; private final ParsableByteArray defaultInitializationVector; + private boolean currentlyInFragment; + public TrackBundle(TrackOutput output) { this.output = output; fragment = new TrackFragment(); @@ -1520,30 +1545,34 @@ public TrackBundle(TrackOutput output) { defaultInitializationVector = new ParsableByteArray(); } - public void init(Track track, DefaultSampleValues defaultSampleValues) { - this.track = Assertions.checkNotNull(track); + public void init(TrackSampleTable moovSampleTable, DefaultSampleValues defaultSampleValues) { + Assertions.checkNotNull(moovSampleTable.track); + this.moovSampleTable = moovSampleTable; this.defaultSampleValues = Assertions.checkNotNull(defaultSampleValues); - output.format(track.format); + output.format(moovSampleTable.track.format); reset(); } public void updateDrmInitData(DrmInitData drmInitData) { @Nullable TrackEncryptionBox encryptionBox = - track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex); + moovSampleTable.track.getSampleDescriptionEncryptionBox( + fragment.header.sampleDescriptionIndex); @Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null; DrmInitData updatedDrmInitData = drmInitData.copyWithSchemeType(schemeType); - Format format = track.format.buildUpon().setDrmInitData(updatedDrmInitData).build(); + Format format = + moovSampleTable.track.format.buildUpon().setDrmInitData(updatedDrmInitData).build(); output.format(format); } - /** Resets the current fragment and sample indices. */ + /** Resets the current fragment, sample indices and {@code isInFragment} boolean. */ public void reset() { fragment.reset(); currentSampleIndex = 0; currentTrackRunIndex = 0; currentSampleInTrackRun = 0; firstSampleToOutputIndex = 0; + currentlyInFragment = false; } /** @@ -1565,23 +1594,32 @@ public void seek(long timeUs) { /** Returns the presentation time of the current sample in microseconds. */ public long getCurrentSamplePresentationTimeUs() { - return fragment.getSamplePresentationTimeUs(currentSampleIndex); + return !currentlyInFragment + ? moovSampleTable.timestampsUs[currentSampleIndex] + : fragment.getSamplePresentationTimeUs(currentSampleIndex); } /** Returns the byte offset of the current sample. */ public long getCurrentSampleOffset() { - return fragment.trunDataPosition[currentTrackRunIndex]; + return !currentlyInFragment + ? moovSampleTable.offsets[currentSampleIndex] + : fragment.trunDataPosition[currentTrackRunIndex]; } /** Returns the size of the current sample in bytes. */ public int getCurrentSampleSize() { - return fragment.sampleSizeTable[currentSampleIndex]; + return !currentlyInFragment + ? moovSampleTable.sizes[currentSampleIndex] + : fragment.sampleSizeTable[currentSampleIndex]; } /** Returns the {@link C.BufferFlags} corresponding to the the current sample. */ @C.BufferFlags public int getCurrentSampleFlags() { - int flags = fragment.sampleIsSyncFrameTable[currentSampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0; + int flags = + !currentlyInFragment + ? moovSampleTable.flags[currentSampleIndex] + : (fragment.sampleIsSyncFrameTable[currentSampleIndex] ? C.BUFFER_FLAG_KEY_FRAME : 0); if (getEncryptionBoxIfEncrypted() != null) { flags |= C.BUFFER_FLAG_ENCRYPTED; } @@ -1589,15 +1627,23 @@ public int getCurrentSampleFlags() { } /** - * Advances the indices in the bundle to point to the next sample in the current fragment. If - * the current sample is the last one in the current fragment, then the advanced state will be - * {@code currentSampleIndex == fragment.sampleCount}, {@code currentTrackRunIndex == - * fragment.trunCount} and {@code #currentSampleInTrackRun == 0}. + * Advances the indices in the bundle to point to the next sample in the sample table (if it has + * not reached the fragments yet) or in the current fragment. + * + *

If the current sample is the last one in the sample table, then the advanced state will be + * {@code currentSampleIndex == moovSampleTable.sampleCount}. If the current sample is the last + * one in the current fragment, then the advanced state will be {@code currentSampleIndex == + * fragment.sampleCount}, {@code currentTrackRunIndex == fragment.trunCount} and {@code + * #currentSampleInTrackRun == 0}. * - * @return Whether the next sample is in the same track run as the previous one. + * @return Whether this {@link TrackBundle} can be used to read the next sample without + * recomputing the next {@link TrackBundle}. */ public boolean next() { currentSampleIndex++; + if (!currentlyInFragment) { + return false; + } currentSampleInTrackRun++; if (currentSampleInTrackRun == fragment.trunLength[currentTrackRunIndex]) { currentTrackRunIndex++; @@ -1610,6 +1656,8 @@ public boolean next() { /** * Outputs the encryption data for the current sample. * + *

This is not supported yet for samples specified in the sample table. + * * @param sampleSize The size of the current sample in bytes, excluding any additional clear * header that will be prefixed to the sample by the extractor. * @param clearHeaderSize The size of a clear header that will be prefixed to the sample by the @@ -1699,7 +1747,11 @@ public int outputSampleEncryptionData(int sampleSize, int clearHeaderSize) { return 1 + vectorSize + subsampleDataLength; } - /** Skips the encryption data for the current sample. */ + /** + * Skips the encryption data for the current sample. + * + *

This is not supported yet for samples specified in the sample table. + */ public void skipSampleEncryptionData() { @Nullable TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted(); if (encryptionBox == null) { @@ -1717,12 +1769,16 @@ public void skipSampleEncryptionData() { @Nullable public TrackEncryptionBox getEncryptionBoxIfEncrypted() { + if (!currentlyInFragment) { + // Encryption is not supported yet for samples specified in the sample table. + return null; + } int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex; @Nullable TrackEncryptionBox encryptionBox = fragment.trackEncryptionBox != null ? fragment.trackEncryptionBox - : track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex); + : moovSampleTable.track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex); return encryptionBox != null && encryptionBox.isEncrypted ? encryptionBox : null; } diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java index b2141143404..7362af0ea60 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackFragment.java @@ -89,7 +89,8 @@ */ public boolean sampleEncryptionDataNeedsFill; /** - * The absolute decode time of the start of the next fragment. + * The absolute decode time of the start of the next fragment, excluding the samples outside + * fragments. */ public long nextFragmentDecodeTime; diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java index af31d429bc1..3cac224b918 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4ExtractorTest.java @@ -98,6 +98,14 @@ public void sampleWithEac3jocTrack() throws Exception { simulationConfig); } + @Test + public void samplePartiallyFragmented() throws Exception { + ExtractorAsserts.assertBehavior( + getExtractorFactory(ImmutableList.of()), + "mp4/sample_partially_fragmented.mp4", + simulationConfig); + } + private static ExtractorFactory getExtractorFactory(final List closedCaptionFormats) { return () -> new FragmentedMp4Extractor( diff --git a/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4 b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4 new file mode 100644 index 00000000000..f1ea81269c2 Binary files /dev/null and b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4 differ diff --git a/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.0.dump b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.0.dump new file mode 100644 index 00000000000..7ee9341b9c2 --- /dev/null +++ b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.0.dump @@ -0,0 +1,329 @@ +seekMap: + isSeekable = false + duration = 134000 + getPosition(0) = [[timeUs=0, position=1428]] +numberOfTracks = 2 +track 0: + total output bytes = 87715 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + sample 0: + time = 66733 + flags = 1 + data = length 37655, hash 265F7BA7 + sample 1: + time = 133466 + flags = 0 + data = length 5023, hash 30768D40 + sample 2: + time = 100100 + flags = 0 + data = length 497, hash 9E536CA2 + sample 3: + time = 200200 + flags = 536870912 + data = length 5867, hash 56F9EE87 + sample 4: + time = 400398 + flags = 0 + data = length 570, hash 984421BD + sample 5: + time = 500499 + flags = 0 + data = length 3406, hash 9A33201E + sample 6: + time = 467132 + flags = 0 + data = length 476, hash C59620F3 + sample 7: + time = 567232 + flags = 0 + data = length 4310, hash 291E6161 + sample 8: + time = 533865 + flags = 0 + data = length 497, hash 398CBFAA + sample 9: + time = 700699 + flags = 0 + data = length 4449, hash 322CAA2B + sample 10: + time = 633965 + flags = 0 + data = length 1076, hash B479B634 + sample 11: + time = 600599 + flags = 0 + data = length 365, hash 68C7D4C2 + sample 12: + time = 667332 + flags = 0 + data = length 463, hash A85F9769 + sample 13: + time = 834165 + flags = 0 + data = length 5339, hash F232195D + sample 14: + time = 767432 + flags = 0 + data = length 1085, hash 47AFB6FE + sample 15: + time = 734066 + flags = 0 + data = length 689, hash 3EB753A3 + sample 16: + time = 800798 + flags = 0 + data = length 516, hash E6DF9C1C + sample 17: + time = 967632 + flags = 0 + data = length 4899, hash A9A8F4B7 + sample 18: + time = 900899 + flags = 0 + data = length 963, hash 684782FB + sample 19: + time = 867532 + flags = 0 + data = length 625, hash ED1C8EF1 + sample 20: + time = 934265 + flags = 0 + data = length 492, hash E6E066EA + sample 21: + time = 1101099 + flags = 0 + data = length 2973, hash A3C54C3B + sample 22: + time = 1034365 + flags = 0 + data = length 833, hash 41CA807D + sample 23: + time = 1000999 + flags = 0 + data = length 516, hash 5B21BB11 + sample 24: + time = 1067732 + flags = 0 + data = length 384, hash A0E8FA50 + sample 25: + time = 1234565 + flags = 0 + data = length 1450, hash 92741C3B + sample 26: + time = 1167832 + flags = 0 + data = length 831, hash DDA0685B + sample 27: + time = 1134466 + flags = 0 + data = length 413, hash 886904C + sample 28: + time = 1201198 + flags = 0 + data = length 427, hash FC2FA8CC + sample 29: + time = 1267932 + flags = 0 + data = length 626, hash DCE82342 +track 1: + total output bytes = 10107 + sample count = 45 + format 0: + id = 2 + sampleMimeType = audio/mp4a-latm + codecs = mp4a.40.2 + channelCount = 1 + sampleRate = 44100 + language = und + initializationData: + data = length 5, hash 2B7623A + sample 0: + time = 0 + flags = 536870913 + data = length 21, hash D57A2CCC + sample 1: + time = 267890 + flags = 1 + data = length 6, hash 336D5819 + sample 2: + time = 291110 + flags = 1 + data = length 279, hash 6E3E48B0 + sample 3: + time = 314330 + flags = 1 + data = length 286, hash 5AABFF + sample 4: + time = 337550 + flags = 1 + data = length 275, hash D3109764 + sample 5: + time = 360770 + flags = 1 + data = length 284, hash 154B6E9 + sample 6: + time = 383990 + flags = 1 + data = length 273, hash 40C8A066 + sample 7: + time = 407210 + flags = 1 + data = length 272, hash 4211880F + sample 8: + time = 430430 + flags = 1 + data = length 281, hash 1F534130 + sample 9: + time = 453650 + flags = 1 + data = length 279, hash F5B3EE5F + sample 10: + time = 476870 + flags = 1 + data = length 282, hash 6CDF1B54 + sample 11: + time = 500090 + flags = 1 + data = length 291, hash 6EC03046 + sample 12: + time = 523310 + flags = 1 + data = length 296, hash 9C7F2E6A + sample 13: + time = 546530 + flags = 1 + data = length 282, hash 584ABD5E + sample 14: + time = 569749 + flags = 1 + data = length 283, hash 38CB1734 + sample 15: + time = 592969 + flags = 1 + data = length 274, hash 648EC8BD + sample 16: + time = 616189 + flags = 1 + data = length 274, hash E8FE0F68 + sample 17: + time = 639409 + flags = 1 + data = length 277, hash 2E1B8A11 + sample 18: + time = 662629 + flags = 1 + data = length 282, hash FB6ACCED + sample 19: + time = 685849 + flags = 1 + data = length 283, hash 152D69D + sample 20: + time = 709069 + flags = 1 + data = length 274, hash 45F44C4B + sample 21: + time = 732289 + flags = 1 + data = length 242, hash F9225BB7 + sample 22: + time = 755509 + flags = 1 + data = length 207, hash F5DFB6B2 + sample 23: + time = 778729 + flags = 1 + data = length 226, hash 41DC63E1 + sample 24: + time = 801949 + flags = 1 + data = length 218, hash A82772CF + sample 25: + time = 825169 + flags = 1 + data = length 223, hash 861AB80 + sample 26: + time = 848389 + flags = 1 + data = length 220, hash F1CBA15E + sample 27: + time = 871609 + flags = 1 + data = length 203, hash CB57EEF7 + sample 28: + time = 894829 + flags = 1 + data = length 206, hash 766F4D9E + sample 29: + time = 918049 + flags = 1 + data = length 210, hash FE2A2935 + sample 30: + time = 941269 + flags = 1 + data = length 207, hash A06A178D + sample 31: + time = 964489 + flags = 1 + data = length 206, hash 1ABD9A5F + sample 32: + time = 987709 + flags = 1 + data = length 209, hash 69D7E5F3 + sample 33: + time = 1010929 + flags = 1 + data = length 173, hash 7CE0FDCA + sample 34: + time = 1034149 + flags = 1 + data = length 208, hash 21D67E09 + sample 35: + time = 1057369 + flags = 1 + data = length 207, hash C7187D46 + sample 36: + time = 1080588 + flags = 1 + data = length 180, hash D17CFAF8 + sample 37: + time = 1103808 + flags = 1 + data = length 206, hash C58FD669 + sample 38: + time = 1127028 + flags = 1 + data = length 212, hash 27E2F2C4 + sample 39: + time = 1150248 + flags = 1 + data = length 190, hash 534CC89E + sample 40: + time = 1173468 + flags = 1 + data = length 180, hash 1C58DF95 + sample 41: + time = 1196688 + flags = 1 + data = length 213, hash 24FBF10A + sample 42: + time = 1219908 + flags = 1 + data = length 186, hash EFC31805 + sample 43: + time = 1243128 + flags = 1 + data = length 208, hash 4A050A0D + sample 44: + time = 1266348 + flags = 1 + data = length 13, hash 2555A7DC +tracksEnded = true diff --git a/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.unknown_length.dump b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.unknown_length.dump new file mode 100644 index 00000000000..7ee9341b9c2 --- /dev/null +++ b/testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.unknown_length.dump @@ -0,0 +1,329 @@ +seekMap: + isSeekable = false + duration = 134000 + getPosition(0) = [[timeUs=0, position=1428]] +numberOfTracks = 2 +track 0: + total output bytes = 87715 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + initializationData: + data = length 29, hash 4746B5D9 + data = length 10, hash 7A0D0F2B + sample 0: + time = 66733 + flags = 1 + data = length 37655, hash 265F7BA7 + sample 1: + time = 133466 + flags = 0 + data = length 5023, hash 30768D40 + sample 2: + time = 100100 + flags = 0 + data = length 497, hash 9E536CA2 + sample 3: + time = 200200 + flags = 536870912 + data = length 5867, hash 56F9EE87 + sample 4: + time = 400398 + flags = 0 + data = length 570, hash 984421BD + sample 5: + time = 500499 + flags = 0 + data = length 3406, hash 9A33201E + sample 6: + time = 467132 + flags = 0 + data = length 476, hash C59620F3 + sample 7: + time = 567232 + flags = 0 + data = length 4310, hash 291E6161 + sample 8: + time = 533865 + flags = 0 + data = length 497, hash 398CBFAA + sample 9: + time = 700699 + flags = 0 + data = length 4449, hash 322CAA2B + sample 10: + time = 633965 + flags = 0 + data = length 1076, hash B479B634 + sample 11: + time = 600599 + flags = 0 + data = length 365, hash 68C7D4C2 + sample 12: + time = 667332 + flags = 0 + data = length 463, hash A85F9769 + sample 13: + time = 834165 + flags = 0 + data = length 5339, hash F232195D + sample 14: + time = 767432 + flags = 0 + data = length 1085, hash 47AFB6FE + sample 15: + time = 734066 + flags = 0 + data = length 689, hash 3EB753A3 + sample 16: + time = 800798 + flags = 0 + data = length 516, hash E6DF9C1C + sample 17: + time = 967632 + flags = 0 + data = length 4899, hash A9A8F4B7 + sample 18: + time = 900899 + flags = 0 + data = length 963, hash 684782FB + sample 19: + time = 867532 + flags = 0 + data = length 625, hash ED1C8EF1 + sample 20: + time = 934265 + flags = 0 + data = length 492, hash E6E066EA + sample 21: + time = 1101099 + flags = 0 + data = length 2973, hash A3C54C3B + sample 22: + time = 1034365 + flags = 0 + data = length 833, hash 41CA807D + sample 23: + time = 1000999 + flags = 0 + data = length 516, hash 5B21BB11 + sample 24: + time = 1067732 + flags = 0 + data = length 384, hash A0E8FA50 + sample 25: + time = 1234565 + flags = 0 + data = length 1450, hash 92741C3B + sample 26: + time = 1167832 + flags = 0 + data = length 831, hash DDA0685B + sample 27: + time = 1134466 + flags = 0 + data = length 413, hash 886904C + sample 28: + time = 1201198 + flags = 0 + data = length 427, hash FC2FA8CC + sample 29: + time = 1267932 + flags = 0 + data = length 626, hash DCE82342 +track 1: + total output bytes = 10107 + sample count = 45 + format 0: + id = 2 + sampleMimeType = audio/mp4a-latm + codecs = mp4a.40.2 + channelCount = 1 + sampleRate = 44100 + language = und + initializationData: + data = length 5, hash 2B7623A + sample 0: + time = 0 + flags = 536870913 + data = length 21, hash D57A2CCC + sample 1: + time = 267890 + flags = 1 + data = length 6, hash 336D5819 + sample 2: + time = 291110 + flags = 1 + data = length 279, hash 6E3E48B0 + sample 3: + time = 314330 + flags = 1 + data = length 286, hash 5AABFF + sample 4: + time = 337550 + flags = 1 + data = length 275, hash D3109764 + sample 5: + time = 360770 + flags = 1 + data = length 284, hash 154B6E9 + sample 6: + time = 383990 + flags = 1 + data = length 273, hash 40C8A066 + sample 7: + time = 407210 + flags = 1 + data = length 272, hash 4211880F + sample 8: + time = 430430 + flags = 1 + data = length 281, hash 1F534130 + sample 9: + time = 453650 + flags = 1 + data = length 279, hash F5B3EE5F + sample 10: + time = 476870 + flags = 1 + data = length 282, hash 6CDF1B54 + sample 11: + time = 500090 + flags = 1 + data = length 291, hash 6EC03046 + sample 12: + time = 523310 + flags = 1 + data = length 296, hash 9C7F2E6A + sample 13: + time = 546530 + flags = 1 + data = length 282, hash 584ABD5E + sample 14: + time = 569749 + flags = 1 + data = length 283, hash 38CB1734 + sample 15: + time = 592969 + flags = 1 + data = length 274, hash 648EC8BD + sample 16: + time = 616189 + flags = 1 + data = length 274, hash E8FE0F68 + sample 17: + time = 639409 + flags = 1 + data = length 277, hash 2E1B8A11 + sample 18: + time = 662629 + flags = 1 + data = length 282, hash FB6ACCED + sample 19: + time = 685849 + flags = 1 + data = length 283, hash 152D69D + sample 20: + time = 709069 + flags = 1 + data = length 274, hash 45F44C4B + sample 21: + time = 732289 + flags = 1 + data = length 242, hash F9225BB7 + sample 22: + time = 755509 + flags = 1 + data = length 207, hash F5DFB6B2 + sample 23: + time = 778729 + flags = 1 + data = length 226, hash 41DC63E1 + sample 24: + time = 801949 + flags = 1 + data = length 218, hash A82772CF + sample 25: + time = 825169 + flags = 1 + data = length 223, hash 861AB80 + sample 26: + time = 848389 + flags = 1 + data = length 220, hash F1CBA15E + sample 27: + time = 871609 + flags = 1 + data = length 203, hash CB57EEF7 + sample 28: + time = 894829 + flags = 1 + data = length 206, hash 766F4D9E + sample 29: + time = 918049 + flags = 1 + data = length 210, hash FE2A2935 + sample 30: + time = 941269 + flags = 1 + data = length 207, hash A06A178D + sample 31: + time = 964489 + flags = 1 + data = length 206, hash 1ABD9A5F + sample 32: + time = 987709 + flags = 1 + data = length 209, hash 69D7E5F3 + sample 33: + time = 1010929 + flags = 1 + data = length 173, hash 7CE0FDCA + sample 34: + time = 1034149 + flags = 1 + data = length 208, hash 21D67E09 + sample 35: + time = 1057369 + flags = 1 + data = length 207, hash C7187D46 + sample 36: + time = 1080588 + flags = 1 + data = length 180, hash D17CFAF8 + sample 37: + time = 1103808 + flags = 1 + data = length 206, hash C58FD669 + sample 38: + time = 1127028 + flags = 1 + data = length 212, hash 27E2F2C4 + sample 39: + time = 1150248 + flags = 1 + data = length 190, hash 534CC89E + sample 40: + time = 1173468 + flags = 1 + data = length 180, hash 1C58DF95 + sample 41: + time = 1196688 + flags = 1 + data = length 213, hash 24FBF10A + sample 42: + time = 1219908 + flags = 1 + data = length 186, hash EFC31805 + sample 43: + time = 1243128 + flags = 1 + data = length 208, hash 4A050A0D + sample 44: + time = 1266348 + flags = 1 + data = length 13, hash 2555A7DC +tracksEnded = true