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