diff --git a/dev/demux.html b/dev/demux.html
index fb721a9..46a85ea 100644
--- a/dev/demux.html
+++ b/dev/demux.html
@@ -51,7 +51,6 @@
console.log(await videoTrack.computeSampleStats())
document.body.textContent = performance.now() - start;
- return;
const decoderConfig = await videoTrack.getDecoderConfig();
const sampleSource = new Metamuxer.EncodedVideoSampleSource(await videoTrack.getCodec());
@@ -81,7 +80,7 @@
a.click();
URL.revokeObjectURL(url);
}
- //download(new Blob([target.buffer]), 'converted.mp4');
+ download(new Blob([target.buffer]), 'converted.mp4');
const input2 = new Metamuxer.Input({
formats: Metamuxer.ALL_FORMATS,
diff --git a/src/isobmff/isobmff-boxes.ts b/src/isobmff/isobmff-boxes.ts
index 49f3cbe..5dead68 100644
--- a/src/isobmff/isobmff-boxes.ts
+++ b/src/isobmff/isobmff-boxes.ts
@@ -152,6 +152,12 @@ const u64 = (value: number) => {
return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]] as number[];
};
+const i64 = (value: number) => {
+ view.setInt32(0, Math.floor(value / 2 ** 32), false);
+ view.setInt32(4, value, false);
+ return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]] as number[];
+};
+
const fixed_8_8 = (value: number) => {
view.setInt16(0, 2 ** 8 * value, false);
return [bytes[0], bytes[1]] as number[];
@@ -499,11 +505,12 @@ export const stbl = (trackData: IsobmffTrackData) => {
return box('stbl', undefined, [
stsd(trackData),
stts(trackData),
- stss(trackData),
+ needsCtts ? ctts(trackData) : null,
+ needsCtts ? cslg(trackData) : null,
stsc(trackData),
stsz(trackData),
stco(trackData),
- needsCtts ? ctts(trackData) : null,
+ stss(trackData),
]);
};
@@ -851,9 +858,11 @@ export const stco = (trackData: IsobmffTrackData) => {
]);
};
-/** Composition Time to Sample Box: Stores composition time offset information (PTS-DTS) for a
+/**
+ * Composition Time to Sample Box: Stores composition time offset information (PTS-DTS) for a
* media's samples. The table is compact, meaning that consecutive samples with the same time
- * composition time offset will be grouped. */
+ * composition time offset will be grouped.
+ */
export const ctts = (trackData: IsobmffTrackData) => {
return fullBox('ctts', 0, 0, [
u32(trackData.compositionTimeOffsetTable.length), // Number of entries
@@ -864,6 +873,41 @@ export const ctts = (trackData: IsobmffTrackData) => {
]);
};
+/**
+ * Composition to Decode Box: Stores information about the composition and display times of the media samples.
+ */
+export const cslg = (trackData: IsobmffTrackData) => {
+ let leastDecodeToDisplayDelta = Infinity;
+ let greatestDecodeToDisplayDelta = -Infinity;
+ let compositionStartTime = Infinity;
+ let compositionEndTime = -Infinity;
+
+ assert(trackData.compositionTimeOffsetTable.length > 0);
+ assert(trackData.samples.length > 0);
+
+ for (let i = 0; i < trackData.compositionTimeOffsetTable.length; i++) {
+ const entry = trackData.compositionTimeOffsetTable[i]!;
+ leastDecodeToDisplayDelta = Math.min(leastDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);
+ greatestDecodeToDisplayDelta = Math.max(greatestDecodeToDisplayDelta, entry.sampleCompositionTimeOffset);
+ }
+
+ for (let i = 0; i < trackData.samples.length; i++) {
+ const sample = trackData.samples[i]!;
+ compositionStartTime = Math.min(compositionStartTime, sample.timestamp);
+ compositionEndTime = Math.max(compositionEndTime, sample.timestamp + sample.duration);
+ }
+
+ const compositionToDtsShift = Math.max(-leastDecodeToDisplayDelta, 0);
+
+ return fullBox('cslg', 1, 0, [
+ i64(compositionToDtsShift), // Composition to DTS shift
+ i64(leastDecodeToDisplayDelta), // Least decode to display delta
+ i64(greatestDecodeToDisplayDelta), // Greatest decode to display delta
+ i64(compositionStartTime), // Composition start time
+ i64(compositionEndTime), // Composition end time
+ ]);
+};
+
/**
* Movie Extends Box: This box signals to readers that the file is fragmented. Contains a single Track Extends Box
* for each track in the movie.
diff --git a/todo.txt b/todo.txt
index cc92b54..1100ef6 100644
--- a/todo.txt
+++ b/todo.txt
@@ -5,5 +5,4 @@
- A stream source?? Or like a callback-driven source
- onRead callback for Source
- onHeader, etc callbacks for Matroska
-- Add cslg box
- https://github.com/Vanilagy/mp4-muxer/issues/83 tell him it's possible now
\ No newline at end of file