mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add support for partially fragmented MP4s
ISSUE: #7308 PiperOrigin-RevId: 319541273
This commit is contained in:
parent
0ee3a35ade
commit
6872910d5a
7 changed files with 770 additions and 46 deletions
|
|
@ -203,6 +203,7 @@
|
||||||
([#6410](https://github.com/google/ExoPlayer/issues/6410)).
|
([#6410](https://github.com/google/ExoPlayer/issues/6410)).
|
||||||
* Select first extractors based on the filename extension and the response
|
* Select first extractors based on the filename extension and the response
|
||||||
headers mime type in `DefaultExtractorsFactory`.
|
headers mime type in `DefaultExtractorsFactory`.
|
||||||
|
* Add support for partially fragmented MP4s.
|
||||||
* Testing
|
* Testing
|
||||||
* Add `TestExoPlayer`, a utility class with APIs to create
|
* Add `TestExoPlayer`, a utility class with APIs to create
|
||||||
`SimpleExoPlayer` instances with fake components for testing.
|
`SimpleExoPlayer` instances with fake components for testing.
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,17 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
extractorOutput = output;
|
extractorOutput = output;
|
||||||
if (sideloadedTrack != null) {
|
if (sideloadedTrack != null) {
|
||||||
TrackBundle bundle = new TrackBundle(output.track(0, sideloadedTrack.type));
|
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);
|
trackBundles.put(0, bundle);
|
||||||
maybeInitExtraTracks();
|
maybeInitExtraTracks();
|
||||||
extractorOutput.endTracks();
|
extractorOutput.endTracks();
|
||||||
|
|
@ -368,6 +378,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
long atomPosition = input.getPosition() - atomHeaderBytesRead;
|
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) {
|
if (atomType == Atom.TYPE_moof) {
|
||||||
// The data positions may be updated when parsing the tfhd/trun.
|
// The data positions may be updated when parsing the tfhd/trun.
|
||||||
int trackCount = trackBundles.size();
|
int trackCount = trackBundles.size();
|
||||||
|
|
@ -377,11 +395,6 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
fragment.auxiliaryDataPosition = atomPosition;
|
fragment.auxiliaryDataPosition = atomPosition;
|
||||||
fragment.dataPosition = 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) {
|
if (atomType == Atom.TYPE_mdat) {
|
||||||
|
|
@ -483,7 +496,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construction of tracks and sample tables.
|
// Construction of tracks and sample tables.
|
||||||
List<TrackSampleTable> trackSampleTables =
|
List<TrackSampleTable> sampleTables =
|
||||||
parseTraks(
|
parseTraks(
|
||||||
moov,
|
moov,
|
||||||
new GaplessInfoHolder(),
|
new GaplessInfoHolder(),
|
||||||
|
|
@ -493,13 +506,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
/* isQuickTime= */ false,
|
/* isQuickTime= */ false,
|
||||||
this::modifyTrack);
|
this::modifyTrack);
|
||||||
|
|
||||||
int trackCount = trackSampleTables.size();
|
int trackCount = sampleTables.size();
|
||||||
if (trackBundles.size() == 0) {
|
if (trackBundles.size() == 0) {
|
||||||
// We need to create the track bundles.
|
// We need to create the track bundles.
|
||||||
for (int i = 0; i < trackCount; i++) {
|
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 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);
|
trackBundles.put(track.id, trackBundle);
|
||||||
durationUs = Math.max(durationUs, track.durationUs);
|
durationUs = Math.max(durationUs, track.durationUs);
|
||||||
}
|
}
|
||||||
|
|
@ -508,10 +522,11 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
} else {
|
} else {
|
||||||
Assertions.checkState(trackBundles.size() == trackCount);
|
Assertions.checkState(trackBundles.size() == trackCount);
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
Track track = trackSampleTables.get(i).track;
|
TrackSampleTable sampleTable = sampleTables.get(i);
|
||||||
|
Track track = sampleTable.track;
|
||||||
trackBundles
|
trackBundles
|
||||||
.get(track.id)
|
.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;
|
TrackFragment fragment = trackBundle.fragment;
|
||||||
long decodeTime = fragment.nextFragmentDecodeTime;
|
long decodeTime = fragment.nextFragmentDecodeTime;
|
||||||
trackBundle.reset();
|
trackBundle.reset();
|
||||||
|
trackBundle.currentlyInFragment = true;
|
||||||
|
|
||||||
@Nullable LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
|
@Nullable LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
|
||||||
if (tfdtAtom != null && (flags & FLAG_WORKAROUND_IGNORE_TFDT_BOX) == 0) {
|
if (tfdtAtom != null && (flags & FLAG_WORKAROUND_IGNORE_TFDT_BOX) == 0) {
|
||||||
|
|
@ -712,7 +728,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
trackBundle.track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
|
trackBundle.moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
||||||
|
fragment.header.sampleDescriptionIndex);
|
||||||
|
|
||||||
@Nullable LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
|
@Nullable LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
|
||||||
if (saiz != null) {
|
if (saiz != null) {
|
||||||
|
|
@ -933,7 +950,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
int fullAtom = trun.readInt();
|
int fullAtom = trun.readInt();
|
||||||
int atomFlags = Atom.parseFullAtomFlags(fullAtom);
|
int atomFlags = Atom.parseFullAtomFlags(fullAtom);
|
||||||
|
|
||||||
Track track = trackBundle.track;
|
Track track = trackBundle.moovSampleTable.track;
|
||||||
TrackFragment fragment = trackBundle.fragment;
|
TrackFragment fragment = trackBundle.fragment;
|
||||||
DefaultSampleValues defaultSampleValues = fragment.header;
|
DefaultSampleValues defaultSampleValues = fragment.header;
|
||||||
|
|
||||||
|
|
@ -1001,6 +1018,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
sampleDecodingTimeUsTable[i] =
|
sampleDecodingTimeUsTable[i] =
|
||||||
Util.scaleLargeTimestamp(cumulativeTime, C.MICROS_PER_SECOND, timescale) - edtsOffsetUs;
|
Util.scaleLargeTimestamp(cumulativeTime, C.MICROS_PER_SECOND, timescale) - edtsOffsetUs;
|
||||||
|
sampleDecodingTimeUsTable[i] += trackBundle.moovSampleTable.durationUs;
|
||||||
sampleSizeTable[i] = sampleSize;
|
sampleSizeTable[i] = sampleSize;
|
||||||
sampleIsSyncFrameTable[i] = ((sampleFlags >> 16) & 0x1) == 0
|
sampleIsSyncFrameTable[i] = ((sampleFlags >> 16) & 0x1) == 0
|
||||||
&& (!workaroundEveryVideoFrameIsSyncFrame || i == 0);
|
&& (!workaroundEveryVideoFrameIsSyncFrame || i == 0);
|
||||||
|
|
@ -1225,7 +1243,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
private boolean readSample(ExtractorInput input) throws IOException {
|
private boolean readSample(ExtractorInput input) throws IOException {
|
||||||
if (parserState == STATE_READING_SAMPLE_START) {
|
if (parserState == STATE_READING_SAMPLE_START) {
|
||||||
if (currentTrackBundle == null) {
|
if (currentTrackBundle == null) {
|
||||||
@Nullable TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles);
|
@Nullable TrackBundle currentTrackBundle = getNextTrackBundle(trackBundles);
|
||||||
if (currentTrackBundle == null) {
|
if (currentTrackBundle == null) {
|
||||||
// We've run out of samples in the current mdat. Discard any trailing data and prepare to
|
// We've run out of samples in the current mdat. Discard any trailing data and prepare to
|
||||||
// read the header of the next atom.
|
// read the header of the next atom.
|
||||||
|
|
@ -1262,12 +1280,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTrackBundle.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
|
if (currentTrackBundle.moovSampleTable.track.sampleTransformation
|
||||||
|
== Track.TRANSFORMATION_CEA608_CDAT) {
|
||||||
sampleSize -= Atom.HEADER_SIZE;
|
sampleSize -= Atom.HEADER_SIZE;
|
||||||
input.skipFully(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.
|
// AC4 samples need to be prefixed with a clear sample header.
|
||||||
sampleBytesWritten =
|
sampleBytesWritten =
|
||||||
currentTrackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
|
currentTrackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
|
||||||
|
|
@ -1283,7 +1303,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
sampleCurrentNalBytesRemaining = 0;
|
sampleCurrentNalBytesRemaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Track track = currentTrackBundle.track;
|
Track track = currentTrackBundle.moovSampleTable.track;
|
||||||
TrackOutput output = currentTrackBundle.output;
|
TrackOutput output = currentTrackBundle.output;
|
||||||
long sampleTimeUs = currentTrackBundle.getCurrentSamplePresentationTimeUs();
|
long sampleTimeUs = currentTrackBundle.getCurrentSamplePresentationTimeUs();
|
||||||
if (timestampAdjuster != null) {
|
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
|
* Returns the {@link TrackBundle} whose sample has the earliest file position out of those yet to
|
||||||
* yet to be consumed, or null if all have been consumed.
|
* be consumed, or null if all have been consumed.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static TrackBundle getNextFragmentRun(SparseArray<TrackBundle> trackBundles) {
|
private static TrackBundle getNextTrackBundle(SparseArray<TrackBundle> trackBundles) {
|
||||||
TrackBundle nextTrackBundle = null;
|
TrackBundle nextTrackBundle = null;
|
||||||
long nextTrackRunOffset = Long.MAX_VALUE;
|
long nextSampleOffset = Long.MAX_VALUE;
|
||||||
|
|
||||||
int trackBundlesSize = trackBundles.size();
|
int trackBundlesSize = trackBundles.size();
|
||||||
for (int i = 0; i < trackBundlesSize; i++) {
|
for (int i = 0; i < trackBundlesSize; i++) {
|
||||||
TrackBundle trackBundle = trackBundles.valueAt(i);
|
TrackBundle trackBundle = trackBundles.valueAt(i);
|
||||||
if (trackBundle.currentTrackRunIndex == trackBundle.fragment.trunCount) {
|
if ((!trackBundle.currentlyInFragment
|
||||||
// This track fragment contains no more runs in the next mdat box.
|
&& 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 {
|
} else {
|
||||||
long trunOffset = trackBundle.getCurrentSampleOffset();
|
long sampleOffset = trackBundle.getCurrentSampleOffset();
|
||||||
if (trunOffset < nextTrackRunOffset) {
|
if (sampleOffset < nextSampleOffset) {
|
||||||
nextTrackBundle = trackBundle;
|
nextTrackBundle = trackBundle;
|
||||||
nextTrackRunOffset = trunOffset;
|
nextSampleOffset = sampleOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1502,7 +1525,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
public final TrackFragment fragment;
|
public final TrackFragment fragment;
|
||||||
public final ParsableByteArray scratch;
|
public final ParsableByteArray scratch;
|
||||||
|
|
||||||
public Track track;
|
public TrackSampleTable moovSampleTable;
|
||||||
public DefaultSampleValues defaultSampleValues;
|
public DefaultSampleValues defaultSampleValues;
|
||||||
public int currentSampleIndex;
|
public int currentSampleIndex;
|
||||||
public int currentSampleInTrackRun;
|
public int currentSampleInTrackRun;
|
||||||
|
|
@ -1512,6 +1535,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
private final ParsableByteArray encryptionSignalByte;
|
private final ParsableByteArray encryptionSignalByte;
|
||||||
private final ParsableByteArray defaultInitializationVector;
|
private final ParsableByteArray defaultInitializationVector;
|
||||||
|
|
||||||
|
private boolean currentlyInFragment;
|
||||||
|
|
||||||
public TrackBundle(TrackOutput output) {
|
public TrackBundle(TrackOutput output) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
fragment = new TrackFragment();
|
fragment = new TrackFragment();
|
||||||
|
|
@ -1520,30 +1545,34 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
defaultInitializationVector = new ParsableByteArray();
|
defaultInitializationVector = new ParsableByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(Track track, DefaultSampleValues defaultSampleValues) {
|
public void init(TrackSampleTable moovSampleTable, DefaultSampleValues defaultSampleValues) {
|
||||||
this.track = Assertions.checkNotNull(track);
|
Assertions.checkNotNull(moovSampleTable.track);
|
||||||
|
this.moovSampleTable = moovSampleTable;
|
||||||
this.defaultSampleValues = Assertions.checkNotNull(defaultSampleValues);
|
this.defaultSampleValues = Assertions.checkNotNull(defaultSampleValues);
|
||||||
output.format(track.format);
|
output.format(moovSampleTable.track.format);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateDrmInitData(DrmInitData drmInitData) {
|
public void updateDrmInitData(DrmInitData drmInitData) {
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
|
moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
||||||
|
fragment.header.sampleDescriptionIndex);
|
||||||
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
||||||
DrmInitData updatedDrmInitData = drmInitData.copyWithSchemeType(schemeType);
|
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);
|
output.format(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resets the current fragment and sample indices. */
|
/** Resets the current fragment, sample indices and {@code isInFragment} boolean. */
|
||||||
public void reset() {
|
public void reset() {
|
||||||
fragment.reset();
|
fragment.reset();
|
||||||
currentSampleIndex = 0;
|
currentSampleIndex = 0;
|
||||||
currentTrackRunIndex = 0;
|
currentTrackRunIndex = 0;
|
||||||
currentSampleInTrackRun = 0;
|
currentSampleInTrackRun = 0;
|
||||||
firstSampleToOutputIndex = 0;
|
firstSampleToOutputIndex = 0;
|
||||||
|
currentlyInFragment = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1565,23 +1594,32 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
/** Returns the presentation time of the current sample in microseconds. */
|
/** Returns the presentation time of the current sample in microseconds. */
|
||||||
public long getCurrentSamplePresentationTimeUs() {
|
public long getCurrentSamplePresentationTimeUs() {
|
||||||
return fragment.getSamplePresentationTimeUs(currentSampleIndex);
|
return !currentlyInFragment
|
||||||
|
? moovSampleTable.timestampsUs[currentSampleIndex]
|
||||||
|
: fragment.getSamplePresentationTimeUs(currentSampleIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the byte offset of the current sample. */
|
/** Returns the byte offset of the current sample. */
|
||||||
public long getCurrentSampleOffset() {
|
public long getCurrentSampleOffset() {
|
||||||
return fragment.trunDataPosition[currentTrackRunIndex];
|
return !currentlyInFragment
|
||||||
|
? moovSampleTable.offsets[currentSampleIndex]
|
||||||
|
: fragment.trunDataPosition[currentTrackRunIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the size of the current sample in bytes. */
|
/** Returns the size of the current sample in bytes. */
|
||||||
public int getCurrentSampleSize() {
|
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. */
|
/** Returns the {@link C.BufferFlags} corresponding to the the current sample. */
|
||||||
@C.BufferFlags
|
@C.BufferFlags
|
||||||
public int getCurrentSampleFlags() {
|
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) {
|
if (getEncryptionBoxIfEncrypted() != null) {
|
||||||
flags |= C.BUFFER_FLAG_ENCRYPTED;
|
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
|
* Advances the indices in the bundle to point to the next sample in the sample table (if it has
|
||||||
* the current sample is the last one in the current fragment, then the advanced state will be
|
* not reached the fragments yet) or in the current fragment.
|
||||||
* {@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.
|
* <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 this {@link TrackBundle} can be used to read the next sample without
|
||||||
|
* recomputing the next {@link TrackBundle}.
|
||||||
*/
|
*/
|
||||||
public boolean next() {
|
public boolean next() {
|
||||||
currentSampleIndex++;
|
currentSampleIndex++;
|
||||||
|
if (!currentlyInFragment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
currentSampleInTrackRun++;
|
currentSampleInTrackRun++;
|
||||||
if (currentSampleInTrackRun == fragment.trunLength[currentTrackRunIndex]) {
|
if (currentSampleInTrackRun == fragment.trunLength[currentTrackRunIndex]) {
|
||||||
currentTrackRunIndex++;
|
currentTrackRunIndex++;
|
||||||
|
|
@ -1610,6 +1656,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
/**
|
/**
|
||||||
* Outputs the encryption data for the current sample.
|
* 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
|
* @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.
|
* 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
|
* @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;
|
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() {
|
public void skipSampleEncryptionData() {
|
||||||
@Nullable TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
@Nullable TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
||||||
if (encryptionBox == null) {
|
if (encryptionBox == null) {
|
||||||
|
|
@ -1717,12 +1769,16 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TrackEncryptionBox getEncryptionBoxIfEncrypted() {
|
public TrackEncryptionBox getEncryptionBoxIfEncrypted() {
|
||||||
|
if (!currentlyInFragment) {
|
||||||
|
// Encryption is not supported yet for samples specified in the sample table.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
|
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
fragment.trackEncryptionBox != null
|
fragment.trackEncryptionBox != null
|
||||||
? fragment.trackEncryptionBox
|
? fragment.trackEncryptionBox
|
||||||
: track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex);
|
: moovSampleTable.track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex);
|
||||||
return encryptionBox != null && encryptionBox.isEncrypted ? encryptionBox : null;
|
return encryptionBox != null && encryptionBox.isEncrypted ? encryptionBox : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
*/
|
*/
|
||||||
public boolean sampleEncryptionDataNeedsFill;
|
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;
|
public long nextFragmentDecodeTime;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,14 @@ public final class FragmentedMp4ExtractorTest {
|
||||||
simulationConfig);
|
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) {
|
private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
|
||||||
return () ->
|
return () ->
|
||||||
new FragmentedMp4Extractor(
|
new FragmentedMp4Extractor(
|
||||||
|
|
|
||||||
BIN
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4
vendored
Normal file
BIN
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4
vendored
Normal file
Binary file not shown.
329
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.0.dump
vendored
Normal file
329
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.0.dump
vendored
Normal file
|
|
@ -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
|
||||||
329
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.unknown_length.dump
vendored
Normal file
329
testdata/src/test/assets/mp4/sample_partially_fragmented.mp4.unknown_length.dump
vendored
Normal file
|
|
@ -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
|
||||||
Loading…
Reference in a new issue