@invintusmedia/tomp4 1.3.0 → 1.3.1

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * toMp4.js v1.3.0
2
+ * toMp4.js v1.3.1
3
3
  * Convert MPEG-TS and fMP4 to standard MP4
4
4
  * https://github.com/TVWIT/toMp4.js
5
5
  * MIT License
@@ -1186,7 +1186,7 @@
1186
1186
  toMp4.isMpegTs = isMpegTs;
1187
1187
  toMp4.isFmp4 = isFmp4;
1188
1188
  toMp4.isStandardMp4 = isStandardMp4;
1189
- toMp4.version = '1.3.0';
1189
+ toMp4.version = '1.3.1';
1190
1190
 
1191
1191
  return toMp4;
1192
1192
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invintusmedia/tomp4",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Convert MPEG-TS, fMP4, and HLS streams to MP4 with clipping support - pure JavaScript, zero dependencies",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
package/src/hls-clip.js CHANGED
@@ -78,8 +78,13 @@ function remuxToFragment(parser, sequenceNumber, videoBaseTime, audioBaseTime, a
78
78
  }
79
79
 
80
80
  /**
81
- * Clip a parsed TS segment at the start (frame-accurate with preroll)
82
- * and/or at the end. Returns clipped access units + timing metadata.
81
+ * Clip a parsed TS segment at the start and/or end.
82
+ *
83
+ * Starts at the nearest keyframe at or before startTime (required for
84
+ * decoding). No preroll/edit-list — hls.js doesn't read edit lists, so
85
+ * every frame in the fMP4 gets played. The EXTINF duration matches the
86
+ * actual content, which means the clip may start slightly before the
87
+ * requested time (at the keyframe).
83
88
  */
84
89
  function clipSegment(parser, startTime, endTime) {
85
90
  const startPts = (startTime !== undefined ? startTime : 0) * PTS_PER_SECOND;
@@ -104,9 +109,8 @@ function clipSegment(parser, startTime, endTime) {
104
109
  if (clippedVideo.length === 0) return null;
105
110
 
106
111
  const keyframePts = clippedVideo[0].pts;
107
- const prerollPts = Math.max(0, startPts - keyframePts);
108
112
 
109
- // Clip audio from keyframe (for A/V sync, matching the fix in ts-to-mp4.js)
113
+ // Clip audio from keyframe (same start as video for A/V sync)
110
114
  const lastVideoPts = clippedVideo[clippedVideo.length - 1].pts;
111
115
  const audioEndPts = Math.min(endPts, lastVideoPts + PTS_PER_SECOND);
112
116
  const clippedAudio = audioAUs.filter(au => au.pts >= keyframePts && au.pts < audioEndPts);
@@ -116,19 +120,16 @@ function clipSegment(parser, startTime, endTime) {
116
120
  for (const au of clippedVideo) { au.pts -= offset; au.dts -= offset; }
117
121
  for (const au of clippedAudio) { au.pts -= offset; }
118
122
 
119
- // Calculate durations
120
- const videoDuration = clippedVideo.length > 1
123
+ // Duration = full content from keyframe (no preroll subtraction)
124
+ const duration = clippedVideo.length > 1
121
125
  ? clippedVideo[clippedVideo.length - 1].dts - clippedVideo[0].dts +
122
- (clippedVideo[1].dts - clippedVideo[0].dts) // add one frame for last
126
+ (clippedVideo[1].dts - clippedVideo[0].dts)
123
127
  : 3003;
124
- const playbackDuration = (videoDuration - prerollPts) / PTS_PER_SECOND;
125
128
 
126
129
  return {
127
130
  videoSamples: clippedVideo,
128
131
  audioSamples: clippedAudio,
129
- prerollPts,
130
- playbackDuration: Math.max(0, playbackDuration),
131
- mediaDuration: videoDuration / PTS_PER_SECOND,
132
+ duration: duration / PTS_PER_SECOND,
132
133
  };
133
134
  }
134
135
 
@@ -383,13 +384,13 @@ export async function clipHls(source, options = {}) {
383
384
  });
384
385
 
385
386
  clipSegments.push({
386
- duration: firstClipped.playbackDuration,
387
+ duration: firstClipped.duration,
387
388
  data: firstFragment, // pre-clipped, in memory
388
389
  originalUrl: null,
389
390
  timelineOffset: 0,
390
391
  isBoundary: true,
391
392
  });
392
- timelineOffset += firstClipped.mediaDuration;
393
+ timelineOffset += firstClipped.duration;
393
394
 
394
395
  // ── Middle segments (pass-through, remuxed on demand) ──
395
396
  for (let i = 1; i < overlapping.length - 1; i++) {
@@ -410,6 +411,7 @@ export async function clipHls(source, options = {}) {
410
411
  const lastRelEnd = endTime - lastSeg.startTime;
411
412
  const lastClipped = clipSegment(lastParser, undefined, lastRelEnd);
412
413
  if (lastClipped && lastClipped.videoSamples.length > 0) {
414
+ const lastDuration = lastClipped.duration;
413
415
  const lastSeqNum = overlapping.length;
414
416
  const lastVideoBaseTime = Math.round(timelineOffset * PTS_PER_SECOND);
415
417
  const lastAudioBaseTime = Math.round(timelineOffset * audioTimescale);
@@ -426,7 +428,7 @@ export async function clipHls(source, options = {}) {
426
428
  });
427
429
 
428
430
  clipSegments.push({
429
- duration: lastClipped.playbackDuration,
431
+ duration: lastClipped.duration,
430
432
  data: lastFragment,
431
433
  originalUrl: null,
432
434
  timelineOffset,
package/src/index.js CHANGED
@@ -342,7 +342,7 @@ toMp4.TSParser = TSParser;
342
342
  toMp4.RemoteMp4 = RemoteMp4;
343
343
 
344
344
  // Version (injected at build time for dist, read from package.json for ESM)
345
- toMp4.version = '1.3.0';
345
+ toMp4.version = '1.3.1';
346
346
 
347
347
  // Export
348
348
  export {