Write moov box at the start of the file if possible

This is done by reserving some space for moov box at the start of the file and writing it there if it fits. If it doesn't fit, then it is written at the end of the file.

PiperOrigin-RevId: 643944698
This commit is contained in:
sheenachhabra 2024-06-17 03:33:46 -07:00 committed by Copybara-Service
parent 4109e2edd9
commit aeb8fd134b
31 changed files with 445 additions and 113 deletions

View File

@ -70,7 +70,7 @@ public class Mp4MuxerEndToEndNonParameterizedAndroidTest {
// Muxer not closed. // Muxer not closed.
// Audio sample written = 192 out of 195. // Audio sample written = 192 out of 195.
// Video sample written = 94 out of 127. // Video sample written = 125 out of 127.
// Output is still a valid MP4 file. // Output is still a valid MP4 file.
FakeExtractorOutput fakeExtractorOutput = FakeExtractorOutput fakeExtractorOutput =
TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), checkNotNull(outputPath)); TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), checkNotNull(outputPath));

View File

@ -95,6 +95,7 @@ public final class Mp4Muxer implements Muxer {
private @LastFrameDurationBehavior int lastFrameDurationBehavior; private @LastFrameDurationBehavior int lastFrameDurationBehavior;
@Nullable private AnnexBToAvccConverter annexBToAvccConverter; @Nullable private AnnexBToAvccConverter annexBToAvccConverter;
private boolean sampleCopyEnabled; private boolean sampleCopyEnabled;
private boolean attemptStreamableOutputEnabled;
/** /**
* Creates a {@link Builder} instance with default values. * Creates a {@link Builder} instance with default values.
@ -105,6 +106,7 @@ public final class Mp4Muxer implements Muxer {
this.fileOutputStream = checkNotNull(fileOutputStream); this.fileOutputStream = checkNotNull(fileOutputStream);
lastFrameDurationBehavior = LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME; lastFrameDurationBehavior = LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME;
sampleCopyEnabled = true; sampleCopyEnabled = true;
attemptStreamableOutputEnabled = true;
} }
/** /**
@ -148,6 +150,21 @@ public final class Mp4Muxer implements Muxer {
return this; return this;
} }
/**
* Sets whether to attempt to write a file where the metadata is stored at the start, which can
* make the file more efficient to read sequentially.
*
* <p>Setting to {@code true} does not guarantee a streamable MP4 output.
*
* <p>The default value is {@code true}.
*/
@CanIgnoreReturnValue
public Mp4Muxer.Builder setAttemptStreamableOutputEnabled(
boolean attemptStreamableOutputEnabled) {
this.attemptStreamableOutputEnabled = attemptStreamableOutputEnabled;
return this;
}
/** Builds an {@link Mp4Muxer} instance. */ /** Builds an {@link Mp4Muxer} instance. */
public Mp4Muxer build() { public Mp4Muxer build() {
MetadataCollector metadataCollector = new MetadataCollector(); MetadataCollector metadataCollector = new MetadataCollector();
@ -158,7 +175,8 @@ public final class Mp4Muxer implements Muxer {
fileOutputStream, fileOutputStream,
moovStructure, moovStructure,
annexBToAvccConverter == null ? AnnexBToAvccConverter.DEFAULT : annexBToAvccConverter, annexBToAvccConverter == null ? AnnexBToAvccConverter.DEFAULT : annexBToAvccConverter,
sampleCopyEnabled); sampleCopyEnabled,
attemptStreamableOutputEnabled);
return new Mp4Muxer(mp4Writer, metadataCollector); return new Mp4Muxer(mp4Writer, metadataCollector);
} }

View File

@ -40,6 +40,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** Writes all media samples into a single mdat box. */ /** Writes all media samples into a single mdat box. */
/* package */ final class Mp4Writer { /* package */ final class Mp4Writer {
private static final long INTERLEAVE_DURATION_US = 1_000_000L; private static final long INTERLEAVE_DURATION_US = 1_000_000L;
private static final int DEFAULT_MOOV_BOX_SIZE_BYTES = 400_000;
private static final String FREE_BOX_TYPE = "free";
private final FileOutputStream outputStream; private final FileOutputStream outputStream;
private final FileChannel output; private final FileChannel output;
@ -49,6 +51,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
private final AtomicBoolean hasWrittenSamples; private final AtomicBoolean hasWrittenSamples;
private final boolean sampleCopyEnabled; private final boolean sampleCopyEnabled;
// Stores location of the space reserved for the moov box at the beginning of the file (after ftyp
// box)
private long reservedMoovSpaceStart;
private long reservedMoovSpaceEnd;
private boolean canWriteMoovAtStart;
private long mdatStart; private long mdatStart;
private long mdatEnd; private long mdatEnd;
private long mdatDataEnd; // Always <= mdatEnd private long mdatDataEnd; // Always <= mdatEnd
@ -65,17 +72,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
* H.265 NAL units from the Annex-B format (using start codes to delineate NAL units) to the * H.265 NAL units from the Annex-B format (using start codes to delineate NAL units) to the
* AVCC format (which uses length prefixes). * AVCC format (which uses length prefixes).
* @param sampleCopyEnabled Whether sample copying is enabled. * @param sampleCopyEnabled Whether sample copying is enabled.
* @param attemptStreamableOutputEnabled Whether to attempt to write a streamable output.
*/ */
public Mp4Writer( public Mp4Writer(
FileOutputStream outputStream, FileOutputStream outputStream,
Mp4MoovStructure moovGenerator, Mp4MoovStructure moovGenerator,
AnnexBToAvccConverter annexBToAvccConverter, AnnexBToAvccConverter annexBToAvccConverter,
boolean sampleCopyEnabled) { boolean sampleCopyEnabled,
boolean attemptStreamableOutputEnabled) {
this.outputStream = outputStream; this.outputStream = outputStream;
this.output = outputStream.getChannel(); this.output = outputStream.getChannel();
this.moovGenerator = moovGenerator; this.moovGenerator = moovGenerator;
this.annexBToAvccConverter = annexBToAvccConverter; this.annexBToAvccConverter = annexBToAvccConverter;
this.sampleCopyEnabled = sampleCopyEnabled; this.sampleCopyEnabled = sampleCopyEnabled;
canWriteMoovAtStart = attemptStreamableOutputEnabled;
tracks = new ArrayList<>(); tracks = new ArrayList<>();
hasWrittenSamples = new AtomicBoolean(false); hasWrittenSamples = new AtomicBoolean(false);
lastMoovWritten = Range.closed(0L, 0L); lastMoovWritten = Range.closed(0L, 0L);
@ -115,9 +125,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
output.position(0L); output.position(0L);
output.write(Boxes.ftyp()); output.write(Boxes.ftyp());
if (canWriteMoovAtStart) {
// Reserve some space for moov box by adding a free box.
reservedMoovSpaceStart = output.position();
output.write(
BoxUtils.wrapIntoBox(FREE_BOX_TYPE, ByteBuffer.allocate(DEFAULT_MOOV_BOX_SIZE_BYTES)));
reservedMoovSpaceEnd = output.position();
}
// Start with an empty mdat box. // Start with an empty mdat box.
mdatStart = output.position(); mdatStart = output.position();
ByteBuffer header = ByteBuffer.allocate(4 + 4 + 8); ByteBuffer header = ByteBuffer.allocate(4 + 4 + 8);
header.putInt(1); // 4 bytes, indicating a 64-bit length field header.putInt(1); // 4 bytes, indicating a 64-bit length field
header.put(Util.getUtf8Bytes("mdat")); // 4 bytes header.put(Util.getUtf8Bytes("mdat")); // 4 bytes
@ -127,7 +144,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// The box includes only its type and length. // The box includes only its type and length.
mdatDataEnd = mdatStart + 16; mdatDataEnd = mdatStart + 16;
mdatEnd = mdatDataEnd; mdatEnd = canWriteMoovAtStart ? Long.MAX_VALUE : mdatDataEnd;
} }
private ByteBuffer assembleCurrentMoovData() { private ByteBuffer assembleCurrentMoovData() {
@ -174,7 +191,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Write a free box to the end of the file, with the new moov box wrapped into it. // Write a free box to the end of the file, with the new moov box wrapped into it.
output.position(newMoovBoxPosition); output.position(newMoovBoxPosition);
output.write(BoxUtils.wrapIntoBox("free", newMoovBoxData.duplicate())); output.write(BoxUtils.wrapIntoBox(FREE_BOX_TYPE, newMoovBoxData.duplicate()));
// The current state is: // The current state is:
// | ftyp | mdat .. .. .. | previous moov | free (new moov)| // | ftyp | mdat .. .. .. | previous moov | free (new moov)|
@ -188,6 +205,35 @@ import java.util.concurrent.atomic.AtomicBoolean;
Range.closed(newMoovBoxPosition, newMoovBoxPosition + newMoovBoxData.remaining()); Range.closed(newMoovBoxPosition, newMoovBoxPosition + newMoovBoxData.remaining());
} }
/**
* Attempts to write moov box at the start (after the ftyp box). If this is not possible, the moov
* box is written at the end of the file (after the mdat box).
*/
private void maybeWriteMoovAtStart() throws IOException {
ByteBuffer moovBox = assembleCurrentMoovData();
int moovBoxSize = moovBox.remaining();
// Keep some space for free box to fill the remaining space.
if (moovBox.remaining() + BOX_HEADER_SIZE <= reservedMoovSpaceEnd - reservedMoovSpaceStart) {
output.position(reservedMoovSpaceStart);
output.write(moovBox);
// Write free box in the remaining space.
int freeSpace = (int) (reservedMoovSpaceEnd - output.position() - BOX_HEADER_SIZE);
output.write(BoxUtils.wrapIntoBox(FREE_BOX_TYPE, ByteBuffer.allocate(freeSpace)));
} else {
// Write moov at the end (after mdat).
canWriteMoovAtStart = false;
mdatEnd = mdatDataEnd;
output.position(mdatEnd);
output.write(moovBox);
lastMoovWritten = Range.closed(mdatEnd, mdatEnd + moovBoxSize);
// Replace previously written moov box (after ftyp box) with a free box.
int freeSpace = (int) (reservedMoovSpaceEnd - reservedMoovSpaceStart - BOX_HEADER_SIZE);
ByteBuffer freeBox = BoxUtils.wrapIntoBox(FREE_BOX_TYPE, ByteBuffer.allocate(freeSpace));
output.write(freeBox, reservedMoovSpaceStart);
}
updateMdatSize(mdatDataEnd - mdatStart);
}
/** /**
* Writes the final moov box and trims extra space from the mdat box. * Writes the final moov box and trims extra space from the mdat box.
* *
@ -196,6 +242,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @throws IOException If there is any error while writing data to the disk. * @throws IOException If there is any error while writing data to the disk.
*/ */
private void writeMoovAndTrim() throws IOException { private void writeMoovAndTrim() throws IOException {
if (canWriteMoovAtStart) {
maybeWriteMoovAtStart();
return;
}
// The current state is: // The current state is:
// | ftyp | mdat .. .. .. (00 00 00) | moov | // | ftyp | mdat .. .. .. (00 00 00) | moov |
@ -229,10 +280,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
ByteBuffer freeHeader = ByteBuffer.allocate(4 + 4); ByteBuffer freeHeader = ByteBuffer.allocate(4 + 4);
freeHeader.putInt((int) remainingLength); freeHeader.putInt((int) remainingLength);
freeHeader.put((byte) 'f'); freeHeader.put(Util.getUtf8Bytes(FREE_BOX_TYPE));
freeHeader.put((byte) 'r');
freeHeader.put((byte) 'e');
freeHeader.put((byte) 'e');
freeHeader.flip(); freeHeader.flip();
output.write(freeHeader); output.write(freeHeader);
@ -307,11 +355,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
mdatDataEnd += output.write(currentSampleByteBuffer, mdatDataEnd); mdatDataEnd += output.write(currentSampleByteBuffer, mdatDataEnd);
track.writtenSamples.add(currentSampleBufferInfo); track.writtenSamples.add(currentSampleBufferInfo);
} while (!track.pendingSamplesBufferInfo.isEmpty()); } while (!track.pendingSamplesBufferInfo.isEmpty());
checkState(mdatDataEnd <= mdatEnd); checkState(mdatDataEnd <= mdatEnd);
} }
private void maybeExtendMdatAndRewriteMoov(long additionalBytesNeeded) throws IOException { private void maybeExtendMdatAndRewriteMoov(long additionalBytesNeeded) throws IOException {
// The mdat box can be written till the end of the file.
if (canWriteMoovAtStart) {
return;
}
// If the required number of bytes doesn't fit in the gap between the actual data and the moov // If the required number of bytes doesn't fit in the gap between the actual data and the moov
// box, extend the file and write out the moov box to the end again. // box, extend the file and write out the moov box to the end again.
if (mdatDataEnd + additionalBytesNeeded >= mdatEnd) { if (mdatDataEnd + additionalBytesNeeded >= mdatEnd) {
@ -332,6 +383,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
private void doInterleave() throws IOException { private void doInterleave() throws IOException {
boolean newSamplesWritten = false;
for (int i = 0; i < tracks.size(); i++) { for (int i = 0; i < tracks.size(); i++) {
Track track = tracks.get(i); Track track = tracks.get(i);
// TODO: b/270583563 - Check if we need to consider the global timestamp instead. // TODO: b/270583563 - Check if we need to consider the global timestamp instead.
@ -341,10 +393,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (lastSampleInfo.presentationTimeUs - firstSampleInfo.presentationTimeUs if (lastSampleInfo.presentationTimeUs - firstSampleInfo.presentationTimeUs
> INTERLEAVE_DURATION_US) { > INTERLEAVE_DURATION_US) {
newSamplesWritten = true;
flushPending(track); flushPending(track);
} }
} }
} }
if (newSamplesWritten && canWriteMoovAtStart) {
maybeWriteMoovAtStart();
}
} }
/** /**

View File

@ -300,4 +300,64 @@ public class Mp4MuxerEndToEndTest {
DumpFileAsserts.assertOutput( DumpFileAsserts.assertOutput(
context, dumpableBox, MuxerTestUtil.getExpectedDumpFilePath("mp4_without_empty_track.mp4")); context, dumpableBox, MuxerTestUtil.getExpectedDumpFilePath("mp4_without_empty_track.mp4"));
} }
@Test
public void writeMp4File_withLargeNumberOfSamples_writesMoovBoxAtTheEndAndFreeBoxAtStart()
throws Exception {
String outputFilePath = temporaryFolder.newFile().getPath();
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try {
muxer.addMetadataEntry(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
for (int i = 0; i < 50_000; i++) {
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
}
} finally {
muxer.close();
}
DumpableMp4Box dumpableBox =
new DumpableMp4Box(ByteBuffer.wrap(TestUtil.getByteArrayFromFilePath(outputFilePath)));
DumpFileAsserts.assertOutput(
context,
dumpableBox,
MuxerTestUtil.getExpectedDumpFilePath(
"mp4_with_moov_at_the_end_and_free_box_at_start.mp4"));
}
@Test
public void writeMp4File_withAttemptStreamableMp4SetToFalse_writesMoovBoxAtTheEndAndNoFreeBox()
throws Exception {
String outputFilePath = temporaryFolder.newFile().getPath();
Mp4Muxer muxer =
new Mp4Muxer.Builder(new FileOutputStream(outputFilePath))
.setAttemptStreamableOutputEnabled(false)
.build();
try {
muxer.addMetadataEntry(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
for (int i = 0; i < 1_000; i++) {
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
}
} finally {
muxer.close();
}
DumpableMp4Box dumpableBox =
new DumpableMp4Box(ByteBuffer.wrap(TestUtil.getByteArrayFromFilePath(outputFilePath)));
DumpFileAsserts.assertOutput(
context,
dumpableBox,
MuxerTestUtil.getExpectedDumpFilePath("mp4_with_moov_at_the_end_and_no_free_box.mp4"));
}
} }

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 3966600 duration = 3966600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=1000000, position=152546]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=1000000, position=552554]]
getPosition(1983300) = [[timeUs=1000000, position=152546], [timeUs=2000000, position=238309]] getPosition(1983300) = [[timeUs=1000000, position=552554], [timeUs=2000000, position=638317]]
getPosition(3966600) = [[timeUs=3000000, position=353603]] getPosition(3966600) = [[timeUs=3000000, position=753611]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 416333 total output bytes = 416333

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 3966600 duration = 3966600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=1000000, position=147439]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=1000000, position=547447]]
getPosition(1983300) = [[timeUs=1000000, position=147439], [timeUs=2000000, position=231666]] getPosition(1983300) = [[timeUs=1000000, position=547447], [timeUs=2000000, position=631674]]
getPosition(3966600) = [[timeUs=3000000, position=343992]] getPosition(3966600) = [[timeUs=3000000, position=744000]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 406132 total output bytes = 406132

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 66700 duration = 66700
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(33350) = [[timeUs=0, position=44]] getPosition(33350) = [[timeUs=0, position=400052]]
getPosition(66700) = [[timeUs=0, position=44]] getPosition(66700) = [[timeUs=0, position=400052]]
numberOfTracks = 3 numberOfTracks = 3
track 0: track 0:
total output bytes = 387 total output bytes = 387

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 4203200 duration = 4203200
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=1002944, position=1883462]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=1002944, position=2283470]]
getPosition(2101600) = [[timeUs=2003555, position=3784641], [timeUs=3003433, position=4026908]] getPosition(2101600) = [[timeUs=2003555, position=4184649], [timeUs=3003433, position=4426916]]
getPosition(4203200) = [[timeUs=4003266, position=6038533]] getPosition(4203200) = [[timeUs=4003266, position=6438541]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 7944083 total output bytes = 7944083

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 3000 duration = 3000
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=3000, position=100]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=3000, position=400108]]
getPosition(1500) = [[timeUs=0, position=44], [timeUs=3000, position=100]] getPosition(1500) = [[timeUs=0, position=400052], [timeUs=3000, position=400108]]
getPosition(3000) = [[timeUs=3000, position=100]] getPosition(3000) = [[timeUs=3000, position=400108]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 224 total output bytes = 224

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 273900 duration = 273900
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=273900, position=100]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=273900, position=400108]]
getPosition(136950) = [[timeUs=0, position=44], [timeUs=273900, position=100]] getPosition(136950) = [[timeUs=0, position=400052], [timeUs=273900, position=400108]]
getPosition(273900) = [[timeUs=273900, position=100]] getPosition(273900) = [[timeUs=273900, position=400108]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 224 total output bytes = 224

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 200 duration = 200
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=100]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=100, position=400108]]
getPosition(100) = [[timeUs=100, position=100]] getPosition(100) = [[timeUs=100, position=400108]]
getPosition(200) = [[timeUs=100, position=100]] getPosition(200) = [[timeUs=100, position=400108]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 112 total output bytes = 112

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -0,0 +1,35 @@
ftyp (28 bytes):
Data = length 20, hash EF896440
free (400008 bytes):
Data = length 400000, hash BBC7B001
mdat (2800016 bytes):
Data = length 2800000, hash 7A1CC21
moov (472634 bytes):
mvhd (108 bytes):
Data = length 100, hash CAE9466E
trak (472518 bytes):
tkhd (92 bytes):
Data = length 84, hash 59BEBEA1
mdia (472418 bytes):
mdhd (32 bytes):
Data = length 24, hash 58568E23
hdlr (44 bytes):
Data = length 36, hash A0852FF2
minf (472334 bytes):
vmhd (20 bytes):
Data = length 12, hash EE830681
dinf (36 bytes):
Data = length 28, hash D535436B
stbl (472270 bytes):
stsd (166 bytes):
Data = length 158, hash 11532063
stts (72008 bytes):
Data = length 72000, hash FA47C748
stsz (200020 bytes):
Data = length 200012, hash 719AE8EE
stsc (28 bytes):
Data = length 20, hash 2BB02571
co64 (24 bytes):
Data = length 16, hash E4EE6662
stss (200016 bytes):
Data = length 200008, hash 8ABF61C3

View File

@ -0,0 +1,33 @@
ftyp (28 bytes):
Data = length 20, hash EF896440
mdat (56016 bytes):
Data = length 56000, hash F535891
moov (10074 bytes):
mvhd (108 bytes):
Data = length 100, hash E6A54065
trak (9958 bytes):
tkhd (92 bytes):
Data = length 84, hash CB9C1A18
mdia (9858 bytes):
mdhd (32 bytes):
Data = length 24, hash 463A595A
hdlr (44 bytes):
Data = length 36, hash A0852FF2
minf (9774 bytes):
vmhd (20 bytes):
Data = length 12, hash EE830681
dinf (36 bytes):
Data = length 28, hash D535436B
stbl (9710 bytes):
stsd (166 bytes):
Data = length 158, hash 11532063
stts (1448 bytes):
Data = length 1440, hash 4C9792F5
stsz (4020 bytes):
Data = length 4012, hash 537BD986
stsc (28 bytes):
Data = length 20, hash 932CC0C9
co64 (24 bytes):
Data = length 16, hash E4EE4D2E
stss (4016 bytes):
Data = length 4008, hash 729103FF

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 200 duration = 200
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=100, position=100]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=100, position=400108]]
getPosition(100) = [[timeUs=100, position=100]] getPosition(100) = [[timeUs=100, position=400108]]
getPosition(200) = [[timeUs=100, position=100]] getPosition(200) = [[timeUs=100, position=400108]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 112 total output bytes = 112

View File

@ -1,7 +1,5 @@
ftyp (28 bytes): ftyp (28 bytes):
Data = length 20, hash EF896440 Data = length 20, hash EF896440
mdat (72 bytes):
Data = length 56, hash DB5662FB
moov (873 bytes): moov (873 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 2613A5C Data = length 100, hash 2613A5C
@ -37,8 +35,12 @@ moov (873 bytes):
stsc (28 bytes): stsc (28 bytes):
Data = length 20, hash 8F6E8285 Data = length 20, hash 8F6E8285
co64 (24 bytes): co64 (24 bytes):
Data = length 16, hash E4EE4D2E Data = length 16, hash E4EE6662
stss (20 bytes): stss (20 bytes):
Data = length 12, hash EE911E03 Data = length 12, hash EE911E03
uuid (2853 bytes): uuid (2853 bytes):
Data = length 2845, hash 52AF0F9D Data = length 2845, hash 52AF0F9D
free (396282 bytes):
Data = length 396274, hash 946025C1
mdat (72 bytes):
Data = length 56, hash DB5662FB

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 0 duration = 0
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 56 total output bytes = 56

View File

@ -1,7 +1,5 @@
ftyp (28 bytes): ftyp (28 bytes):
Data = length 20, hash EF896440 Data = length 20, hash EF896440
mdat (72 bytes):
Data = length 56, hash DB5662FB
moov (658 bytes): moov (658 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 2613A5C Data = length 100, hash 2613A5C
@ -28,8 +26,12 @@ moov (658 bytes):
stsc (28 bytes): stsc (28 bytes):
Data = length 20, hash 8F6E8285 Data = length 20, hash 8F6E8285
co64 (24 bytes): co64 (24 bytes):
Data = length 16, hash E4EE4D2E Data = length 16, hash E4EE6662
stss (20 bytes): stss (20 bytes):
Data = length 12, hash EE911E03 Data = length 12, hash EE911E03
uuid (2853 bytes): uuid (2853 bytes):
Data = length 2845, hash 52AF0F9D Data = length 2845, hash 52AF0F9D
free (396497 bytes):
Data = length 396489, hash 429891F
mdat (72 bytes):
Data = length 56, hash DB5662FB

View File

@ -1,7 +1,5 @@
ftyp (28 bytes): ftyp (28 bytes):
Data = length 20, hash EF896440 Data = length 20, hash EF896440
mdat (128 bytes):
Data = length 112, hash 1AAF8FF5
moov (674 bytes): moov (674 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 105FA889 Data = length 100, hash 105FA889
@ -28,6 +26,10 @@ moov (674 bytes):
stsc (28 bytes): stsc (28 bytes):
Data = length 20, hash 8F7C9A06 Data = length 20, hash 8F7C9A06
co64 (24 bytes): co64 (24 bytes):
Data = length 16, hash E4EE4D2E Data = length 16, hash E4EE6662
stss (24 bytes): stss (24 bytes):
Data = length 16, hash 7940D386 Data = length 16, hash 7940D386
free (399334 bytes):
Data = length 399326, hash 3C185041
mdat (128 bytes):
Data = length 112, hash 1AAF8FF5

View File

@ -1,14 +1,14 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 4117500 duration = 4137400
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=1002944, position=1883462]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=1002944, position=2283470]]
getPosition(2058750) = [[timeUs=2003555, position=3784641], [timeUs=3003433, position=4026908]] getPosition(2068700) = [[timeUs=2003555, position=4184649], [timeUs=3003433, position=4426916]]
getPosition(4117500) = [[timeUs=3003433, position=4026908]] getPosition(4137400) = [[timeUs=4003266, position=6438541]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 5911788 total output bytes = 7822354
sample count = 94 sample count = 125
format 0: format 0:
id = 1 id = 1
sampleMimeType = video/hevc sampleMimeType = video/hevc
@ -17,7 +17,7 @@ track 0:
maxNumReorderSamples = 0 maxNumReorderSamples = 0
width = 1280 width = 1280
height = 720 height = 720
frameRate = 30.28936 frameRate = 30.21221
colorInfo: colorInfo:
colorSpace = 6 colorSpace = 6
colorRange = 2 colorRange = 2
@ -401,8 +401,132 @@ track 0:
data = length 54110, hash 7B27C656 data = length 54110, hash 7B27C656
sample 93: sample 93:
time = 3103411 time = 3103411
flags = 536870912 flags = 0
data = length 68308, hash C7F4AE80 data = length 68308, hash C7F4AE80
sample 94:
time = 3136744
flags = 0
data = length 67629, hash 48E625B6
sample 95:
time = 3170100
flags = 0
data = length 66968, hash D46F0E01
sample 96:
time = 3203400
flags = 0
data = length 53022, hash 91852F32
sample 97:
time = 3236722
flags = 0
data = length 66729, hash 12CA7617
sample 98:
time = 3270055
flags = 0
data = length 53556, hash 904B00CF
sample 99:
time = 3303377
flags = 0
data = length 63459, hash AB813676
sample 100:
time = 3336711
flags = 0
data = length 63637, hash 8B0750F6
sample 101:
time = 3370033
flags = 0
data = length 64700, hash 8922E5BE
sample 102:
time = 3403366
flags = 0
data = length 54680, hash 4F49EB3D
sample 103:
time = 3436700
flags = 0
data = length 62600, hash 9DF2F9F5
sample 104:
time = 3470022
flags = 0
data = length 69506, hash DB702311
sample 105:
time = 3503355
flags = 0
data = length 50277, hash 1034F0A6
sample 106:
time = 3536711
flags = 0
data = length 52100, hash 33745B51
sample 107:
time = 3570000
flags = 0
data = length 65067, hash F73FE2C7
sample 108:
time = 3603333
flags = 0
data = length 68940, hash 4331DA16
sample 109:
time = 3636666
flags = 0
data = length 55215, hash 68087A40
sample 110:
time = 3670000
flags = 0
data = length 56090, hash 2F483911
sample 111:
time = 3703322
flags = 0
data = length 58356, hash D7D190C6
sample 112:
time = 3736644
flags = 0
data = length 57368, hash 2F6B7918
sample 113:
time = 3769977
flags = 0
data = length 56591, hash 19B696D2
sample 114:
time = 3803300
flags = 0
data = length 54748, hash 7E0CE70E
sample 115:
time = 3836622
flags = 0
data = length 73133, hash CFA46EE4
sample 116:
time = 3869955
flags = 0
data = length 52577, hash 5E174B5
sample 117:
time = 3903300
flags = 0
data = length 74756, hash 5A571060
sample 118:
time = 3936611
flags = 0
data = length 67171, hash 6F13FB2C
sample 119:
time = 3969933
flags = 0
data = length 42130, hash DAE94DB2
sample 120:
time = 4003266
flags = 1
data = length 120199, hash 32DEA792
sample 121:
time = 4036600
flags = 0
data = length 51028, hash FC7AF64F
sample 122:
time = 4069911
flags = 0
data = length 51879, hash 74852E0E
sample 123:
time = 4103244
flags = 0
data = length 51443, hash B735512A
sample 124:
time = 4137444
flags = 536870912
data = length 65012, hash DB29A2CD
track 1: track 1:
total output bytes = 131233 total output bytes = 131233
sample count = 192 sample count = 192

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1064600 duration = 1064600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(532300) = [[timeUs=0, position=44]] getPosition(532300) = [[timeUs=0, position=400052]]
getPosition(1064600) = [[timeUs=0, position=44]] getPosition(1064600) = [[timeUs=0, position=400052]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 69084 total output bytes = 69084

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1065600 duration = 1065600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(532800) = [[timeUs=0, position=44]] getPosition(532800) = [[timeUs=0, position=400052]]
getPosition(1065600) = [[timeUs=0, position=44]] getPosition(1065600) = [[timeUs=0, position=400052]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 9529 total output bytes = 9529

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 1065600 duration = 1065600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44]] getPosition(1) = [[timeUs=0, position=400052]]
getPosition(532800) = [[timeUs=0, position=44]] getPosition(532800) = [[timeUs=0, position=400052]]
getPosition(1065600) = [[timeUs=0, position=44]] getPosition(1065600) = [[timeUs=0, position=400052]]
numberOfTracks = 2 numberOfTracks = 2
track 0: track 0:
total output bytes = 301222 total output bytes = 301222

View File

@ -1,10 +1,10 @@
seekMap: seekMap:
isSeekable = true isSeekable = true
duration = 966600 duration = 966600
getPosition(0) = [[timeUs=0, position=44]] getPosition(0) = [[timeUs=0, position=400052]]
getPosition(1) = [[timeUs=0, position=44], [timeUs=333333, position=16035]] getPosition(1) = [[timeUs=0, position=400052], [timeUs=333333, position=416043]]
getPosition(483300) = [[timeUs=333333, position=16035], [timeUs=666666, position=28785]] getPosition(483300) = [[timeUs=333333, position=416043], [timeUs=666666, position=428793]]
getPosition(966600) = [[timeUs=666666, position=28785]] getPosition(966600) = [[timeUs=666666, position=428793]]
numberOfTracks = 1 numberOfTracks = 1
track 0: track 0:
total output bytes = 41647 total output bytes = 41647