@invintusmedia/tomp4 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tomp4.js +9 -5
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/muxers/mp4.js +8 -4
- package/src/ts-to-mp4.js +7 -3
package/dist/tomp4.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* toMp4.js v1.0.
|
|
2
|
+
* toMp4.js v1.0.8
|
|
3
3
|
* Convert MPEG-TS and fMP4 to standard MP4
|
|
4
4
|
* https://github.com/TVWIT/toMp4.js
|
|
5
5
|
* MIT License
|
|
@@ -114,17 +114,21 @@
|
|
|
114
114
|
// Clip audio to the REQUESTED time range (not from keyframe)
|
|
115
115
|
// Audio doesn't need keyframe pre-roll
|
|
116
116
|
const audioStartPts = startPts;
|
|
117
|
-
const audioEndPts = Math.min(endPts, lastFramePts);
|
|
117
|
+
const audioEndPts = Math.min(endPts, lastFramePts + 90000); // Include audio slightly past last video
|
|
118
118
|
const clippedAudio = audioAUs.filter(au => au.pts >= audioStartPts && au.pts < audioEndPts);
|
|
119
119
|
|
|
120
|
-
// Normalize
|
|
120
|
+
// Normalize video timestamps so keyframe starts at 0
|
|
121
121
|
const offset = keyframePts;
|
|
122
122
|
for (const au of clippedVideo) {
|
|
123
123
|
au.pts -= offset;
|
|
124
124
|
au.dts -= offset;
|
|
125
125
|
}
|
|
126
|
+
|
|
127
|
+
// Normalize audio timestamps so it starts at 0 (matching video playback start after preroll)
|
|
128
|
+
// Audio doesn't have preroll, so it should start at PTS 0 to sync with video after edit list
|
|
129
|
+
const audioOffset = audioStartPts; // Use requested start, not keyframe
|
|
126
130
|
for (const au of clippedAudio) {
|
|
127
|
-
au.pts -=
|
|
131
|
+
au.pts -= audioOffset;
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
return {
|
|
@@ -752,7 +756,7 @@
|
|
|
752
756
|
toMp4.isMpegTs = isMpegTs;
|
|
753
757
|
toMp4.isFmp4 = isFmp4;
|
|
754
758
|
toMp4.isStandardMp4 = isStandardMp4;
|
|
755
|
-
toMp4.version = '1.0.
|
|
759
|
+
toMp4.version = '1.0.8';
|
|
756
760
|
|
|
757
761
|
return toMp4;
|
|
758
762
|
});
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -316,7 +316,7 @@ toMp4.transcode = transcode;
|
|
|
316
316
|
toMp4.isWebCodecsSupported = isWebCodecsSupported;
|
|
317
317
|
|
|
318
318
|
// Version (injected at build time for dist, read from package.json for ESM)
|
|
319
|
-
toMp4.version = '1.0.
|
|
319
|
+
toMp4.version = '1.0.8';
|
|
320
320
|
|
|
321
321
|
// Export
|
|
322
322
|
export {
|
package/src/muxers/mp4.js
CHANGED
|
@@ -513,15 +513,19 @@ export class MP4Muxer {
|
|
|
513
513
|
if (this.parser.audioPts.length === 0) return null;
|
|
514
514
|
|
|
515
515
|
const firstAudioPts = this.parser.audioPts[0];
|
|
516
|
+
|
|
517
|
+
// When clipping with preroll, audio is normalized to start at PTS 0
|
|
518
|
+
// (matching video playback start after edit list), so no edit list needed
|
|
516
519
|
if (firstAudioPts === 0) return null;
|
|
517
520
|
|
|
521
|
+
// For non-clipped content, handle any timestamp offset
|
|
518
522
|
const mediaTime = Math.round(firstAudioPts * this.audioTimescale / 90000);
|
|
519
|
-
const
|
|
523
|
+
const audioDuration = this.audioSampleSizes.length * this.audioSampleDuration;
|
|
520
524
|
|
|
521
525
|
const elstData = new Uint8Array(16);
|
|
522
526
|
const view = new DataView(elstData.buffer);
|
|
523
527
|
view.setUint32(0, 1);
|
|
524
|
-
view.setUint32(4, Math.round(
|
|
528
|
+
view.setUint32(4, Math.round(audioDuration * this.videoTimescale / this.audioTimescale));
|
|
525
529
|
view.setInt32(8, mediaTime);
|
|
526
530
|
view.setUint16(12, 1);
|
|
527
531
|
view.setUint16(14, 0);
|
|
@@ -534,8 +538,8 @@ export class MP4Muxer {
|
|
|
534
538
|
const data = new Uint8Array(80);
|
|
535
539
|
const view = new DataView(data.buffer);
|
|
536
540
|
view.setUint32(8, 257);
|
|
537
|
-
|
|
538
|
-
view.setUint32(16,
|
|
541
|
+
// Use playback duration to match video track (for proper sync with preroll)
|
|
542
|
+
view.setUint32(16, this.calculatePlaybackDuration());
|
|
539
543
|
view.setUint16(32, 0x0100);
|
|
540
544
|
view.setUint32(36, 0x00010000); view.setUint32(52, 0x00010000); view.setUint32(68, 0x40000000);
|
|
541
545
|
return createFullBox('tkhd', 0, 3, data);
|
package/src/ts-to-mp4.js
CHANGED
|
@@ -93,17 +93,21 @@ function clipAccessUnits(videoAUs, audioAUs, startTime, endTime) {
|
|
|
93
93
|
// Clip audio to the REQUESTED time range (not from keyframe)
|
|
94
94
|
// Audio doesn't need keyframe pre-roll
|
|
95
95
|
const audioStartPts = startPts;
|
|
96
|
-
const audioEndPts = Math.min(endPts, lastFramePts);
|
|
96
|
+
const audioEndPts = Math.min(endPts, lastFramePts + 90000); // Include audio slightly past last video
|
|
97
97
|
const clippedAudio = audioAUs.filter(au => au.pts >= audioStartPts && au.pts < audioEndPts);
|
|
98
98
|
|
|
99
|
-
// Normalize
|
|
99
|
+
// Normalize video timestamps so keyframe starts at 0
|
|
100
100
|
const offset = keyframePts;
|
|
101
101
|
for (const au of clippedVideo) {
|
|
102
102
|
au.pts -= offset;
|
|
103
103
|
au.dts -= offset;
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
// Normalize audio timestamps so it starts at 0 (matching video playback start after preroll)
|
|
107
|
+
// Audio doesn't have preroll, so it should start at PTS 0 to sync with video after edit list
|
|
108
|
+
const audioOffset = audioStartPts; // Use requested start, not keyframe
|
|
105
109
|
for (const au of clippedAudio) {
|
|
106
|
-
au.pts -=
|
|
110
|
+
au.pts -= audioOffset;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
return {
|