Commit 6872910d by kimvde Committed by Ian Baker

Add support for partially fragmented MP4s

ISSUE: #7308
PiperOrigin-RevId: 319541273
parent 0ee3a35a
......@@ -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.
......
......@@ -280,7 +280,17 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
}
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 @@ 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) {
......@@ -483,7 +496,7 @@ public class FragmentedMp4Extractor implements Extractor {
}
// Construction of tracks and sample tables.
List<TrackSampleTable> trackSampleTables =
List<TrackSampleTable> sampleTables =
parseTraks(
moov,
new GaplessInfoHolder(),
......@@ -493,13 +506,14 @@ public class FragmentedMp4Extractor implements Extractor {
/* 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 @@ public class FragmentedMp4Extractor implements Extractor {
} 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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
@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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
}
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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
}
/**
* 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<TrackBundle> trackBundles) {
private static TrackBundle getNextTrackBundle(SparseArray<TrackBundle> 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 @@ public class FragmentedMp4Extractor implements Extractor {
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 @@ public class FragmentedMp4Extractor implements Extractor {
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 class FragmentedMp4Extractor implements Extractor {
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 class FragmentedMp4Extractor implements Extractor {
/** 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 class FragmentedMp4Extractor implements Extractor {
}
/**
* 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.
*
* <p>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 class FragmentedMp4Extractor implements Extractor {
/**
* Outputs the encryption data for the current sample.
*
* <p>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 class FragmentedMp4Extractor implements Extractor {
return 1 + vectorSize + subsampleDataLength;
}
/** Skips the encryption data for the current sample. */
/**
* Skips the encryption data for the current sample.
*
* <p>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 class FragmentedMp4Extractor implements Extractor {
@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;
}
......
......@@ -89,7 +89,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*/
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;
......
......@@ -98,6 +98,14 @@ public final class FragmentedMp4ExtractorTest {
simulationConfig);
}
@Test
public void samplePartiallyFragmented() throws Exception {
ExtractorAsserts.assertBehavior(
getExtractorFactory(ImmutableList.of()),
"mp4/sample_partially_fragmented.mp4",
simulationConfig);
}
private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
return () ->
new FragmentedMp4Extractor(
......
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
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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment