@invintusmedia/tomp4 1.1.0 → 1.1.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 +2 -2
- package/package.json +1 -1
- package/src/fmp4/stitcher.js +53 -3
- package/src/index.js +1 -1
package/dist/tomp4.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* toMp4.js v1.1.
|
|
2
|
+
* toMp4.js v1.1.1
|
|
3
3
|
* Convert MPEG-TS and fMP4 to standard MP4
|
|
4
4
|
* https://github.com/TVWIT/toMp4.js
|
|
5
5
|
* MIT License
|
|
@@ -705,7 +705,7 @@
|
|
|
705
705
|
toMp4.isMpegTs = isMpegTs;
|
|
706
706
|
toMp4.isFmp4 = isFmp4;
|
|
707
707
|
toMp4.isStandardMp4 = isStandardMp4;
|
|
708
|
-
toMp4.version = '1.1.
|
|
708
|
+
toMp4.version = '1.1.1';
|
|
709
709
|
|
|
710
710
|
return toMp4;
|
|
711
711
|
});
|
package/package.json
CHANGED
package/src/fmp4/stitcher.js
CHANGED
|
@@ -47,12 +47,17 @@ function createBox(type, ...payloads) {
|
|
|
47
47
|
// Fragment Parsing
|
|
48
48
|
// ============================================
|
|
49
49
|
|
|
50
|
-
function parseTfhd(tfhdData) {
|
|
50
|
+
function parseTfhd(tfhdData, trexDefaults = {}) {
|
|
51
51
|
const view = new DataView(tfhdData.buffer, tfhdData.byteOffset, tfhdData.byteLength);
|
|
52
52
|
const flags = (tfhdData[9] << 16) | (tfhdData[10] << 8) | tfhdData[11];
|
|
53
53
|
const trackId = view.getUint32(12);
|
|
54
54
|
let offset = 16;
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
// Start with trex defaults, override with tfhd values if present
|
|
57
|
+
let baseDataOffset = 0;
|
|
58
|
+
let defaultSampleDuration = trexDefaults.defaultSampleDuration || 0;
|
|
59
|
+
let defaultSampleSize = trexDefaults.defaultSampleSize || 0;
|
|
60
|
+
let defaultSampleFlags = trexDefaults.defaultSampleFlags || 0;
|
|
56
61
|
|
|
57
62
|
if (flags & 0x1) { baseDataOffset = Number(view.getBigUint64(offset)); offset += 8; }
|
|
58
63
|
if (flags & 0x2) offset += 4; // sample description index
|
|
@@ -63,6 +68,43 @@ function parseTfhd(tfhdData) {
|
|
|
63
68
|
return { trackId, flags, baseDataOffset, defaultSampleDuration, defaultSampleSize, defaultSampleFlags };
|
|
64
69
|
}
|
|
65
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Parse trex (Track Extends) box from mvex
|
|
73
|
+
* This contains default sample values for all fragments
|
|
74
|
+
*/
|
|
75
|
+
function parseTrex(trexData) {
|
|
76
|
+
const view = new DataView(trexData.buffer, trexData.byteOffset, trexData.byteLength);
|
|
77
|
+
// Skip box header (8) + version/flags (4)
|
|
78
|
+
const trackId = view.getUint32(12);
|
|
79
|
+
const defaultSampleDescriptionIndex = view.getUint32(16);
|
|
80
|
+
const defaultSampleDuration = view.getUint32(20);
|
|
81
|
+
const defaultSampleSize = view.getUint32(24);
|
|
82
|
+
const defaultSampleFlags = view.getUint32(28);
|
|
83
|
+
|
|
84
|
+
return { trackId, defaultSampleDescriptionIndex, defaultSampleDuration, defaultSampleSize, defaultSampleFlags };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Extract trex defaults from moov's mvex box
|
|
89
|
+
*/
|
|
90
|
+
function extractTrexDefaults(moovBox) {
|
|
91
|
+
const defaults = new Map(); // trackId -> { defaultSampleDuration, ... }
|
|
92
|
+
const moovChildren = parseChildBoxes(moovBox);
|
|
93
|
+
const mvex = findBox(moovChildren, 'mvex');
|
|
94
|
+
|
|
95
|
+
if (mvex) {
|
|
96
|
+
const mvexChildren = parseChildBoxes(mvex);
|
|
97
|
+
for (const child of mvexChildren) {
|
|
98
|
+
if (child.type === 'trex') {
|
|
99
|
+
const trex = parseTrex(child.data);
|
|
100
|
+
defaults.set(trex.trackId, trex);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return defaults;
|
|
106
|
+
}
|
|
107
|
+
|
|
66
108
|
function parseTfdt(tfdtData) {
|
|
67
109
|
const view = new DataView(tfdtData.buffer, tfdtData.byteOffset, tfdtData.byteLength);
|
|
68
110
|
const version = tfdtData[8];
|
|
@@ -433,6 +475,7 @@ export function stitchFmp4(segments, options = {}) {
|
|
|
433
475
|
let ftyp = null;
|
|
434
476
|
let moov = null;
|
|
435
477
|
let originalTrackIds = [];
|
|
478
|
+
let trexDefaults = new Map(); // trackId -> default sample values from trex
|
|
436
479
|
|
|
437
480
|
// Process init segment if provided separately
|
|
438
481
|
if (initData) {
|
|
@@ -443,6 +486,7 @@ export function stitchFmp4(segments, options = {}) {
|
|
|
443
486
|
throw new Error('stitchFmp4: Init segment missing ftyp or moov');
|
|
444
487
|
}
|
|
445
488
|
originalTrackIds = extractTrackIds(moov);
|
|
489
|
+
trexDefaults = extractTrexDefaults(moov);
|
|
446
490
|
}
|
|
447
491
|
|
|
448
492
|
// Process each segment
|
|
@@ -461,6 +505,7 @@ export function stitchFmp4(segments, options = {}) {
|
|
|
461
505
|
if (!moov && segMoov) {
|
|
462
506
|
moov = segMoov;
|
|
463
507
|
originalTrackIds = extractTrackIds(moov);
|
|
508
|
+
trexDefaults = extractTrexDefaults(moov);
|
|
464
509
|
}
|
|
465
510
|
|
|
466
511
|
// Process fragment boxes (moof + mdat pairs)
|
|
@@ -492,7 +537,12 @@ export function stitchFmp4(segments, options = {}) {
|
|
|
492
537
|
const tfdtBox = findBox(trafChildren, 'tfdt');
|
|
493
538
|
|
|
494
539
|
if (tfhdBox && trunBox) {
|
|
495
|
-
|
|
540
|
+
// Get trackId first to look up trex defaults
|
|
541
|
+
const tfhdView = new DataView(tfhdBox.data.buffer, tfhdBox.data.byteOffset, tfhdBox.data.byteLength);
|
|
542
|
+
const trackId = tfhdView.getUint32(12);
|
|
543
|
+
const trackTrexDefaults = trexDefaults.get(trackId) || {};
|
|
544
|
+
|
|
545
|
+
const tfhd = parseTfhd(tfhdBox.data, trackTrexDefaults);
|
|
496
546
|
const { samples, dataOffset } = parseTrun(trunBox.data, tfhd);
|
|
497
547
|
|
|
498
548
|
if (!tracks.has(tfhd.trackId)) {
|
package/src/index.js
CHANGED