Skip to content

Commit 83779b9

Browse files
feat: use bigint for 64 bit ints if needed and available. (#383)
Read values as BigInt when appropriate. Potentially, cases where this happens already results in broken playback, but out of abundance of caution we're marking this as a breaking change. BREAKING CHANGE: In some cases, mux.js will now be returning a BigInt rather than a regular Number value. This means that consumers of this library will need to add checks for BigInt for optimal operation.
1 parent 87f777f commit 83779b9

File tree

12 files changed

+1144
-752
lines changed

12 files changed

+1144
-752
lines changed

lib/mp4/caption-parser.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var findBox = require('../mp4/find-box.js');
1515
var parseTfdt = require('../tools/parse-tfdt.js');
1616
var parseTrun = require('../tools/parse-trun.js');
1717
var parseTfhd = require('../tools/parse-tfhd.js');
18+
var window = require('global/window');
1819

1920
/**
2021
* Maps an offset in the mdat to a sample based on the the size of the samples.
@@ -122,7 +123,7 @@ var findSeiNals = function(avcStream, samples, trackId) {
122123
* the absolute presentation and decode timestamps of each sample.
123124
*
124125
* @param {Array<Uint8Array>} truns - The Trun Run boxes to be parsed
125-
* @param {Number} baseMediaDecodeTime - base media decode time from tfdt
126+
* @param {Number|BigInt} baseMediaDecodeTime - base media decode time from tfdt
126127
@see ISO-BMFF-12/2015, Section 8.8.12
127128
* @param {Object} tfhd - The parsed Track Fragment Header
128129
* @see inspect.parseTfhd
@@ -156,9 +157,15 @@ var parseSamples = function(truns, baseMediaDecodeTime, tfhd) {
156157
if (sample.compositionTimeOffset === undefined) {
157158
sample.compositionTimeOffset = 0;
158159
}
159-
sample.pts = currentDts + sample.compositionTimeOffset;
160160

161-
currentDts += sample.duration;
161+
if (typeof currentDts === 'bigint') {
162+
sample.pts = currentDts + window.BigInt(sample.compositionTimeOffset);
163+
currentDts += window.BigInt(sample.duration);
164+
165+
} else {
166+
sample.pts = currentDts + sample.compositionTimeOffset;
167+
currentDts += sample.duration;
168+
}
162169
});
163170

164171
allSamples = allSamples.concat(samples);

lib/mp4/mp4-generator.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
*/
1010
'use strict';
1111

12-
var UINT32_MAX = Math.pow(2, 32) - 1;
12+
var MAX_UINT32 = require('../utils/numbers.js').MAX_UINT32;
13+
1314

1415
var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,
1516
trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,
@@ -581,8 +582,8 @@ traf = function(track) {
581582
0x00, 0x00, 0x00, 0x00 // default_sample_flags
582583
]));
583584

584-
upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
585-
lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
585+
upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (MAX_UINT32));
586+
lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (MAX_UINT32));
586587

587588
trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
588589
0x01, // version 1

lib/mp4/probe.js

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ var parseType = require('../mp4/parse-type.js');
1515
var parseTfhd = require('../tools/parse-tfhd.js');
1616
var parseTrun = require('../tools/parse-trun.js');
1717
var parseTfdt = require('../tools/parse-tfdt.js');
18+
var getUint64 = require('../utils/numbers.js').getUint64;
1819
var timescale, startTime, compositionStartTime, getVideoTrackIds, getTracks,
1920
getTimescaleFromMediaHeader;
21+
var window = require('global/window');
22+
2023

2124
/**
2225
* Parses an MP4 initialization segment and extracts the timescale
@@ -87,52 +90,55 @@ timescale = function(init) {
8790
* fragment, in seconds
8891
*/
8992
startTime = function(timescale, fragment) {
90-
var trafs, baseTimes, result;
93+
var trafs, result;
9194

9295
// we need info from two childrend of each track fragment box
9396
trafs = findBox(fragment, ['moof', 'traf']);
9497

9598
// determine the start times for each track
96-
baseTimes = [].concat.apply([], trafs.map(function(traf) {
97-
return findBox(traf, ['tfhd']).map(function(tfhd) {
98-
var id, scale, baseTime;
99-
100-
// get the track id from the tfhd
101-
id = toUnsigned(tfhd[4] << 24 |
102-
tfhd[5] << 16 |
103-
tfhd[6] << 8 |
104-
tfhd[7]);
105-
// assume a 90kHz clock if no timescale was specified
106-
scale = timescale[id] || 90e3;
107-
108-
// get the base media decode time from the tfdt
109-
baseTime = findBox(traf, ['tfdt']).map(function(tfdt) {
110-
var version, result;
111-
112-
version = tfdt[0];
113-
result = toUnsigned(tfdt[4] << 24 |
114-
tfdt[5] << 16 |
115-
tfdt[6] << 8 |
116-
tfdt[7]);
117-
if (version === 1) {
118-
result *= Math.pow(2, 32);
119-
result += toUnsigned(tfdt[8] << 24 |
120-
tfdt[9] << 16 |
121-
tfdt[10] << 8 |
122-
tfdt[11]);
123-
}
124-
return result;
125-
})[0];
126-
baseTime = typeof baseTime === 'number' && !isNaN(baseTime) ? baseTime : Infinity;
99+
var lowestTime = trafs.reduce(function(acc, traf) {
100+
var tfhd = findBox(traf, ['tfhd'])[0];
101+
102+
// get the track id from the tfhd
103+
var id = toUnsigned(tfhd[4] << 24 |
104+
tfhd[5] << 16 |
105+
tfhd[6] << 8 |
106+
tfhd[7]);
107+
// assume a 90kHz clock if no timescale was specified
108+
var scale = timescale[id] || 90e3;
109+
110+
// get the base media decode time from the tfdt
111+
var tfdt = findBox(traf, ['tfdt'])[0];
112+
var dv = new DataView(tfdt.buffer, tfdt.byteOffset, tfdt.byteLength);
113+
var baseTime;
114+
115+
// version 1 is 64 bit
116+
if (tfdt[0] === 1) {
117+
baseTime = getUint64(tfdt.subarray(4, 12));
118+
} else {
119+
baseTime = dv.getUint32(4);
120+
}
127121

128-
// convert base time to seconds
129-
return baseTime / scale;
130-
});
131-
}));
122+
// convert base time to seconds if it is a valid number.
123+
let seconds;
124+
if (typeof baseTime === 'bigint') {
125+
seconds = baseTime / window.BigInt(scale);
126+
} else if (typeof baseTime === 'number' && !isNaN(baseTime)) {
127+
seconds = baseTime / scale;
128+
}
129+
130+
if (seconds < Number.MAX_SAFE_INTEGER) {
131+
seconds = Number(seconds);
132+
}
132133

133-
// return the minimum
134-
result = Math.min.apply(null, baseTimes);
135-
return isFinite(result) ? result : 0;
134+
if (seconds < acc) {
135+
acc = seconds;
136+
}
137+
138+
return acc;
139+
}, Infinity);
140+
141+
return typeof lowestTime === 'bigint' || isFinite(lowestTime) ? lowestTime : 0;
136142
};
137143

138144
/**
@@ -194,7 +200,18 @@ compositionStartTime = function(timescales, fragment) {
194200
var timescale = timescales[trackId] || 90e3;
195201

196202
// return the composition start time, in seconds
197-
return (baseMediaDecodeTime + compositionTimeOffset) / timescale;
203+
if (typeof baseMediaDecodeTime === 'bigint') {
204+
compositionTimeOffset = window.BigInt(compositionTimeOffset);
205+
timescale = window.BigInt(timescale);
206+
}
207+
208+
var result = (baseMediaDecodeTime + compositionTimeOffset) / timescale;
209+
210+
if (typeof result === 'bigint' && result < Number.MAX_SAFE_INTEGER) {
211+
result = Number(result);
212+
}
213+
214+
return result;
198215
};
199216

200217
/**

lib/tools/mp4-inspector.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
*/
1010
'use strict';
1111

12-
var MAX_UINT32 = Math.pow(2, 32);
12+
var numberHelpers = require('../utils/numbers.js');
13+
var MAX_UINT32 = numberHelpers.MAX_UINT32;
14+
var getUint64 = numberHelpers.getUint64;
1315

1416
var
1517
inspectMp4,
@@ -150,8 +152,8 @@ var
150152
i += 12;
151153
} else {
152154
result.edits.push({
153-
segmentDuration: (view.getUint32(i) * MAX_UINT32) + view.getUint32(i + 4),
154-
mediaTime: (view.getUint32(i + 8) * MAX_UINT32) + view.getUint32(i + 12),
155+
segmentDuration: getUint64(data.subarray(i)),
156+
mediaTime: getUint64(data.subarray(i + 8)),
155157
mediaRate: view.getUint16(i + 16) + view.getUint16(i + 18) / (256 * 256)
156158
});
157159
i += 20;

lib/tools/parse-sidx.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var MAX_UINT32 = Math.pow(2, 32);
1+
var getUint64 = require('../utils/numbers.js').getUint64;
22

33
var parseSidx = function(data) {
44
var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
@@ -17,8 +17,8 @@ var parseSidx = function(data) {
1717
i += 8;
1818
} else {
1919
// read 64 bits
20-
result.earliestPresentationTime = (view.getUint32(i) * MAX_UINT32) + view.getUint32(i + 4);
21-
result.firstOffset = (view.getUint32(i + 8) * MAX_UINT32) + view.getUint32(i + 12);
20+
result.earliestPresentationTime = getUint64(data.subarray(i));
21+
result.firstOffset = getUint64(data.subarray(i + 8));
2222
i += 16;
2323
}
2424

lib/tools/parse-tfdt.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
var toUnsigned = require('../utils/bin').toUnsigned;
2+
var getUint64 = require('../utils/numbers.js').getUint64;
23

34
var tfdt = function(data) {
45
var result = {
56
version: data[0],
67
flags: new Uint8Array(data.subarray(1, 4)),
7-
baseMediaDecodeTime: toUnsigned(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7])
88
};
9+
10+
911
if (result.version === 1) {
10-
result.baseMediaDecodeTime *= Math.pow(2, 32);
11-
result.baseMediaDecodeTime += toUnsigned(data[8] << 24 | data[9] << 16 | data[10] << 8 | data[11]);
12+
result.baseMediaDecodeTime = getUint64(data.subarray(4));
13+
} else {
14+
result.baseMediaDecodeTime = toUnsigned(data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7])
1215
}
1316
return result;
1417
};

lib/utils/numbers.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var MAX_UINT32 = Math.pow(2, 32);
2+
3+
var getUint64 = (uint8) => {
4+
var dv = new DataView(uint8.buffer, uint8.byteOffset, uint8.byteLength);
5+
var value;
6+
7+
if (dv.getBigUint64) {
8+
value = dv.getBigUint64(0);
9+
10+
if (value < Number.MAX_SAFE_INTEGER) {
11+
return Number(value);
12+
}
13+
14+
return value;
15+
}
16+
17+
return (dv.getUint32(0) * MAX_UINT32) + dv.getUint32(4);
18+
};
19+
20+
module.exports = {
21+
getUint64: getUint64,
22+
MAX_UINT32: MAX_UINT32
23+
};

0 commit comments

Comments
 (0)