mediabunny 1.55.7 → 1.56.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/bundles/mediabunny.cjs +655 -204
- package/dist/bundles/mediabunny.min.cjs +13 -13
- package/dist/bundles/mediabunny.min.mjs +13 -13
- package/dist/bundles/mediabunny.mjs +655 -204
- package/dist/bundles/mediabunny.node.cjs +655 -204
- package/dist/mediabunny.d.ts +112 -23
- package/dist/modules/shared/bitstream.d.ts +1 -0
- package/dist/modules/shared/bitstream.d.ts.map +1 -1
- package/dist/modules/shared/bitstream.js +10 -1
- package/dist/modules/src/codec-data.d.ts +12 -0
- package/dist/modules/src/codec-data.d.ts.map +1 -1
- package/dist/modules/src/codec-data.js +98 -20
- package/dist/modules/src/conversion.d.ts +57 -12
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/conversion.js +354 -120
- package/dist/modules/src/index.d.ts +2 -2
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-boxes.d.ts +1 -1
- package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-boxes.js +59 -29
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.js +0 -4
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.js +14 -6
- package/dist/modules/src/matroska/matroska-demuxer.d.ts +1 -0
- package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.js +20 -8
- package/dist/modules/src/matroska/matroska-muxer.d.ts +1 -0
- package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-muxer.js +10 -1
- package/dist/modules/src/media-sink.d.ts.map +1 -1
- package/dist/modules/src/media-sink.js +34 -9
- package/dist/modules/src/media-source.d.ts +17 -5
- package/dist/modules/src/media-source.d.ts.map +1 -1
- package/dist/modules/src/media-source.js +14 -9
- package/dist/modules/src/metadata.d.ts +6 -4
- package/dist/modules/src/metadata.d.ts.map +1 -1
- package/dist/modules/src/metadata.js +2 -1
- package/dist/modules/src/misc.d.ts +3 -0
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/misc.js +13 -0
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.d.ts +5 -0
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js +37 -16
- package/dist/modules/src/mpeg-ts/mpeg-ts-misc.d.ts +1 -0
- package/dist/modules/src/mpeg-ts/mpeg-ts-misc.d.ts.map +1 -1
- package/dist/modules/src/mpeg-ts/mpeg-ts-misc.js +1 -0
- package/dist/modules/src/mpeg-ts/mpeg-ts-muxer.d.ts.map +1 -1
- package/dist/modules/src/mpeg-ts/mpeg-ts-muxer.js +14 -10
- package/dist/modules/src/muxer.d.ts.map +1 -1
- package/dist/modules/src/muxer.js +0 -3
- package/dist/modules/src/output-format.d.ts +17 -0
- package/dist/modules/src/output-format.d.ts.map +1 -1
- package/dist/modules/src/output-format.js +39 -1
- package/dist/modules/src/sample.d.ts +2 -2
- package/dist/modules/src/sample.d.ts.map +1 -1
- package/dist/modules/src/sample.js +16 -16
- package/dist/modules/src/source.d.ts +10 -0
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/source.js +38 -3
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/codec-data.ts +112 -20
- package/src/conversion.ts +461 -100
- package/src/index.ts +2 -0
- package/src/isobmff/isobmff-boxes.ts +76 -32
- package/src/isobmff/isobmff-demuxer.ts +0 -5
- package/src/isobmff/isobmff-muxer.ts +22 -12
- package/src/matroska/matroska-demuxer.ts +24 -11
- package/src/matroska/matroska-muxer.ts +14 -1
- package/src/media-sink.ts +39 -8
- package/src/media-source.ts +28 -8
- package/src/metadata.ts +8 -5
- package/src/misc.ts +16 -0
- package/src/mpeg-ts/mpeg-ts-demuxer.ts +52 -14
- package/src/mpeg-ts/mpeg-ts-misc.ts +1 -0
- package/src/mpeg-ts/mpeg-ts-muxer.ts +20 -10
- package/src/muxer.ts +0 -4
- package/src/output-format.ts +58 -1
- package/src/sample.ts +16 -16
- package/src/source.ts +55 -3
|
@@ -259,6 +259,9 @@ var Mediabunny = (() => {
|
|
|
259
259
|
var isU32 = (value) => {
|
|
260
260
|
return value >= 0 && value < 2 ** 32;
|
|
261
261
|
};
|
|
262
|
+
var isI32 = (value) => {
|
|
263
|
+
return value >= -(2 ** 31) && value < 2 ** 31;
|
|
264
|
+
};
|
|
262
265
|
var readExpGolomb = (bitstream) => {
|
|
263
266
|
let leadingZeroBits = 0;
|
|
264
267
|
while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) {
|
|
@@ -270,6 +273,13 @@ var Mediabunny = (() => {
|
|
|
270
273
|
const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits);
|
|
271
274
|
return result;
|
|
272
275
|
};
|
|
276
|
+
var writeExpGolomb = (bitstream, value) => {
|
|
277
|
+
const codeNum = value + 1;
|
|
278
|
+
const leadingZeroBits = Math.floor(Math.log2(codeNum));
|
|
279
|
+
bitstream.writeBits(leadingZeroBits, 0);
|
|
280
|
+
bitstream.writeBits(1, 1);
|
|
281
|
+
bitstream.writeBits(leadingZeroBits, codeNum - 2 ** leadingZeroBits);
|
|
282
|
+
};
|
|
273
283
|
var readSignedExpGolomb = (bitstream) => {
|
|
274
284
|
const codeNum = readExpGolomb(bitstream);
|
|
275
285
|
return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1;
|
|
@@ -570,6 +580,9 @@ var Mediabunny = (() => {
|
|
|
570
580
|
var lerp = (from, to, t) => {
|
|
571
581
|
return from + (to - from) * t;
|
|
572
582
|
};
|
|
583
|
+
var modEuclid = (value, modulus) => {
|
|
584
|
+
return value - Math.floor(value / modulus) * modulus;
|
|
585
|
+
};
|
|
573
586
|
var UNDETERMINED_LANGUAGE = "und";
|
|
574
587
|
var roundIfAlmostInteger = (value) => {
|
|
575
588
|
const rounded = Math.round(value);
|
|
@@ -1293,9 +1306,9 @@ var Mediabunny = (() => {
|
|
|
1293
1306
|
throw new TypeError("tags.raw, when provided, must be an object.");
|
|
1294
1307
|
}
|
|
1295
1308
|
for (const value of Object.values(tags.raw)) {
|
|
1296
|
-
if (value !== null && typeof value !== "string" && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {
|
|
1309
|
+
if (value !== null && typeof value !== "string" && !(Array.isArray(value) && value.every((x) => typeof x === "string")) && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) {
|
|
1297
1310
|
throw new TypeError(
|
|
1298
|
-
"Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, Record<string, string>, or null."
|
|
1311
|
+
"Each value in tags.raw must be a string, string array, Uint8Array, RichImageData, AttachedFile, Record<string, string>, or null."
|
|
1299
1312
|
);
|
|
1300
1313
|
}
|
|
1301
1314
|
}
|
|
@@ -1381,6 +1394,16 @@ var Mediabunny = (() => {
|
|
|
1381
1394
|
}
|
|
1382
1395
|
this.pos = end;
|
|
1383
1396
|
}
|
|
1397
|
+
copyBits(n, other) {
|
|
1398
|
+
let i = 0;
|
|
1399
|
+
for (i; i < n - 7; i += 8) {
|
|
1400
|
+
this.writeBits(8, other.readBits(8));
|
|
1401
|
+
}
|
|
1402
|
+
const leftover = n - i;
|
|
1403
|
+
if (leftover > 0) {
|
|
1404
|
+
this.writeBits(leftover, other.readBits(leftover));
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1384
1407
|
readAlignedByte() {
|
|
1385
1408
|
if (this.pos % 8 !== 0) {
|
|
1386
1409
|
throw new Error("Bitstream is not byte-aligned.");
|
|
@@ -1657,6 +1680,19 @@ var Mediabunny = (() => {
|
|
|
1657
1680
|
}
|
|
1658
1681
|
return new Uint8Array(result);
|
|
1659
1682
|
};
|
|
1683
|
+
var addEmulationPreventionBytes = (data) => {
|
|
1684
|
+
const result = [];
|
|
1685
|
+
let zeroCount = 0;
|
|
1686
|
+
for (const byte of data) {
|
|
1687
|
+
if (zeroCount === 2 && byte <= 3) {
|
|
1688
|
+
result.push(3);
|
|
1689
|
+
zeroCount = 0;
|
|
1690
|
+
}
|
|
1691
|
+
result.push(byte);
|
|
1692
|
+
zeroCount = byte === 0 ? zeroCount + 1 : 0;
|
|
1693
|
+
}
|
|
1694
|
+
return new Uint8Array(result);
|
|
1695
|
+
};
|
|
1660
1696
|
var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]);
|
|
1661
1697
|
var concatNalUnitsInAnnexB = (nalUnits) => {
|
|
1662
1698
|
const totalLength = nalUnits.reduce((a, b) => a + ANNEX_B_START_CODE.byteLength + b.byteLength, 0);
|
|
@@ -1776,8 +1812,7 @@ var Mediabunny = (() => {
|
|
|
1776
1812
|
bytes2.push(pps[i]);
|
|
1777
1813
|
}
|
|
1778
1814
|
}
|
|
1779
|
-
if (record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) {
|
|
1780
|
-
assert(record.chromaFormat !== null);
|
|
1815
|
+
if ((record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) && record.chromaFormat !== null) {
|
|
1781
1816
|
assert(record.bitDepthLumaMinus8 !== null);
|
|
1782
1817
|
assert(record.bitDepthChromaMinus8 !== null);
|
|
1783
1818
|
assert(record.sequenceParameterSetExt !== null);
|
|
@@ -1877,7 +1912,8 @@ var Mediabunny = (() => {
|
|
|
1877
1912
|
};
|
|
1878
1913
|
var parseAvcSps = (sps) => {
|
|
1879
1914
|
try {
|
|
1880
|
-
const
|
|
1915
|
+
const emulationUnpreventedBytes = removeEmulationPreventionBytes(sps);
|
|
1916
|
+
const bitstream = new Bitstream(emulationUnpreventedBytes);
|
|
1881
1917
|
bitstream.skipBits(1);
|
|
1882
1918
|
bitstream.skipBits(2);
|
|
1883
1919
|
const nalUnitType = bitstream.readBits(5);
|
|
@@ -1973,6 +2009,9 @@ var Mediabunny = (() => {
|
|
|
1973
2009
|
let pixelAspectRatio = { num: 1, den: 1 };
|
|
1974
2010
|
let numReorderFrames = null;
|
|
1975
2011
|
let maxDecFrameBuffering = null;
|
|
2012
|
+
let bitstreamRestrictionFlagBitOffset = null;
|
|
2013
|
+
let bitstreamRestrictionFlag = null;
|
|
2014
|
+
const vuiParametersFlagBitOffset = bitstream.pos;
|
|
1976
2015
|
const vuiParametersPresentFlag = bitstream.readBits(1);
|
|
1977
2016
|
if (vuiParametersPresentFlag) {
|
|
1978
2017
|
const aspectRatioInfoPresentFlag = bitstream.readBits(1);
|
|
@@ -2028,7 +2067,8 @@ var Mediabunny = (() => {
|
|
|
2028
2067
|
bitstream.skipBits(1);
|
|
2029
2068
|
}
|
|
2030
2069
|
bitstream.skipBits(1);
|
|
2031
|
-
|
|
2070
|
+
bitstreamRestrictionFlagBitOffset = bitstream.pos;
|
|
2071
|
+
bitstreamRestrictionFlag = bitstream.readBits(1);
|
|
2032
2072
|
if (bitstreamRestrictionFlag) {
|
|
2033
2073
|
bitstream.skipBits(1);
|
|
2034
2074
|
readExpGolomb(bitstream);
|
|
@@ -2062,6 +2102,7 @@ var Mediabunny = (() => {
|
|
|
2062
2102
|
}
|
|
2063
2103
|
assert(maxDecFrameBuffering !== null);
|
|
2064
2104
|
return {
|
|
2105
|
+
emulationUnpreventedBytes,
|
|
2065
2106
|
profileIdc,
|
|
2066
2107
|
constraintFlags,
|
|
2067
2108
|
levelIdc,
|
|
@@ -2079,7 +2120,10 @@ var Mediabunny = (() => {
|
|
|
2079
2120
|
transferCharacteristics,
|
|
2080
2121
|
fullRangeFlag,
|
|
2081
2122
|
numReorderFrames,
|
|
2082
|
-
maxDecFrameBuffering
|
|
2123
|
+
maxDecFrameBuffering,
|
|
2124
|
+
vuiParametersFlagBitOffset,
|
|
2125
|
+
bitstreamRestrictionFlagBitOffset,
|
|
2126
|
+
bitstreamRestrictionFlag
|
|
2083
2127
|
};
|
|
2084
2128
|
} catch (error) {
|
|
2085
2129
|
Logging._error("Error parsing AVC SPS:", error);
|
|
@@ -2100,6 +2144,40 @@ var Mediabunny = (() => {
|
|
|
2100
2144
|
bitstream.skipBits(5);
|
|
2101
2145
|
bitstream.skipBits(5);
|
|
2102
2146
|
};
|
|
2147
|
+
var addAvcBitstreamRestriction = (sps) => {
|
|
2148
|
+
assert(sps.bitstreamRestrictionFlag !== 1);
|
|
2149
|
+
const modifiedBytes = new Uint8Array(sps.emulationUnpreventedBytes.byteLength + 64);
|
|
2150
|
+
const oldBitstream = new Bitstream(sps.emulationUnpreventedBytes);
|
|
2151
|
+
const newBitstream = new Bitstream(modifiedBytes);
|
|
2152
|
+
if (sps.bitstreamRestrictionFlag === null) {
|
|
2153
|
+
newBitstream.copyBits(sps.vuiParametersFlagBitOffset, oldBitstream);
|
|
2154
|
+
newBitstream.writeBits(1, 1);
|
|
2155
|
+
newBitstream.writeBits(1, 0);
|
|
2156
|
+
newBitstream.writeBits(1, 0);
|
|
2157
|
+
newBitstream.writeBits(1, 0);
|
|
2158
|
+
newBitstream.writeBits(1, 0);
|
|
2159
|
+
newBitstream.writeBits(1, 0);
|
|
2160
|
+
newBitstream.writeBits(1, 0);
|
|
2161
|
+
newBitstream.writeBits(1, 0);
|
|
2162
|
+
newBitstream.writeBits(1, 0);
|
|
2163
|
+
} else {
|
|
2164
|
+
assert(sps.bitstreamRestrictionFlagBitOffset !== null);
|
|
2165
|
+
newBitstream.copyBits(sps.bitstreamRestrictionFlagBitOffset, oldBitstream);
|
|
2166
|
+
}
|
|
2167
|
+
newBitstream.writeBits(1, 1);
|
|
2168
|
+
newBitstream.writeBits(1, 1);
|
|
2169
|
+
writeExpGolomb(newBitstream, 2);
|
|
2170
|
+
writeExpGolomb(newBitstream, 1);
|
|
2171
|
+
writeExpGolomb(newBitstream, 16);
|
|
2172
|
+
writeExpGolomb(newBitstream, 16);
|
|
2173
|
+
writeExpGolomb(newBitstream, sps.numReorderFrames);
|
|
2174
|
+
writeExpGolomb(newBitstream, sps.maxDecFrameBuffering);
|
|
2175
|
+
newBitstream.writeBits(1, 1);
|
|
2176
|
+
newBitstream.writeBits((8 - newBitstream.pos % 8) % 8, 0);
|
|
2177
|
+
const byteLength = newBitstream.pos / 8;
|
|
2178
|
+
assert(Number.isInteger(byteLength));
|
|
2179
|
+
return addEmulationPreventionBytes(modifiedBytes.subarray(0, byteLength));
|
|
2180
|
+
};
|
|
2103
2181
|
var concatHevcNalUnits = (nalUnits, decoderConfig) => {
|
|
2104
2182
|
if (decoderConfig.description) {
|
|
2105
2183
|
const bytes2 = toUint8Array(decoderConfig.description);
|
|
@@ -3416,7 +3494,13 @@ var Mediabunny = (() => {
|
|
|
3416
3494
|
const key = string.slice(0, separatorIndex).toUpperCase();
|
|
3417
3495
|
const value = string.slice(separatorIndex + 1);
|
|
3418
3496
|
metadataTags.raw ??= {};
|
|
3419
|
-
metadataTags.raw[key]
|
|
3497
|
+
if (Array.isArray(metadataTags.raw[key])) {
|
|
3498
|
+
metadataTags.raw[key] = [...metadataTags.raw[key], value];
|
|
3499
|
+
} else if (typeof metadataTags.raw[key] === "string") {
|
|
3500
|
+
metadataTags.raw[key] = [metadataTags.raw[key], value];
|
|
3501
|
+
} else {
|
|
3502
|
+
metadataTags.raw[key] ??= value;
|
|
3503
|
+
}
|
|
3420
3504
|
switch (key) {
|
|
3421
3505
|
case "TITLE":
|
|
3422
3506
|
{
|
|
@@ -3563,7 +3647,7 @@ var Mediabunny = (() => {
|
|
|
3563
3647
|
currentView.setUint32(0, encodedVendorString.length, true);
|
|
3564
3648
|
currentBuffer.set(encodedVendorString, 4);
|
|
3565
3649
|
commentHeaderParts.push(currentBuffer);
|
|
3566
|
-
const writtenTags =
|
|
3650
|
+
const writtenTags = [];
|
|
3567
3651
|
const addCommentTag = (key, value) => {
|
|
3568
3652
|
const joined = `${key}=${value}`;
|
|
3569
3653
|
const encoded = textEncoder.encode(joined);
|
|
@@ -3572,7 +3656,7 @@ var Mediabunny = (() => {
|
|
|
3572
3656
|
currentView.setUint32(0, encoded.length, true);
|
|
3573
3657
|
currentBuffer.set(encoded, 4);
|
|
3574
3658
|
commentHeaderParts.push(currentBuffer);
|
|
3575
|
-
writtenTags.
|
|
3659
|
+
writtenTags.push(key);
|
|
3576
3660
|
};
|
|
3577
3661
|
for (const { key, value } of keyValueIterator(tags)) {
|
|
3578
3662
|
switch (key) {
|
|
@@ -3614,12 +3698,7 @@ var Mediabunny = (() => {
|
|
|
3614
3698
|
break;
|
|
3615
3699
|
case "date":
|
|
3616
3700
|
{
|
|
3617
|
-
|
|
3618
|
-
if (rawVersion && typeof rawVersion === "string") {
|
|
3619
|
-
addCommentTag("DATE", rawVersion);
|
|
3620
|
-
} else {
|
|
3621
|
-
addCommentTag("DATE", value.toISOString().slice(0, 10));
|
|
3622
|
-
}
|
|
3701
|
+
addCommentTag("DATE", value.toISOString().slice(0, 10));
|
|
3623
3702
|
}
|
|
3624
3703
|
;
|
|
3625
3704
|
break;
|
|
@@ -3708,16 +3787,23 @@ var Mediabunny = (() => {
|
|
|
3708
3787
|
if (tags.raw) {
|
|
3709
3788
|
for (const key in tags.raw) {
|
|
3710
3789
|
const value = tags.raw[key] ?? tags.raw[key.toLowerCase()];
|
|
3711
|
-
if (key === "vendor" || value == null || writtenTags.
|
|
3790
|
+
if (key === "vendor" || value == null || writtenTags.includes(key)) {
|
|
3712
3791
|
continue;
|
|
3713
3792
|
}
|
|
3714
3793
|
if (typeof value === "string") {
|
|
3715
3794
|
addCommentTag(key, value);
|
|
3795
|
+
} else if (Array.isArray(value)) {
|
|
3796
|
+
const isOnlyStrings = value.every((x) => typeof x === "string");
|
|
3797
|
+
if (isOnlyStrings) {
|
|
3798
|
+
for (const elem of value) {
|
|
3799
|
+
addCommentTag(key, elem);
|
|
3800
|
+
}
|
|
3801
|
+
}
|
|
3716
3802
|
}
|
|
3717
3803
|
}
|
|
3718
3804
|
}
|
|
3719
3805
|
const listLengthBuffer = new Uint8Array(4);
|
|
3720
|
-
toDataView(listLengthBuffer).setUint32(0, writtenTags.
|
|
3806
|
+
toDataView(listLengthBuffer).setUint32(0, writtenTags.length, true);
|
|
3721
3807
|
commentHeaderParts.splice(2, 0, listLengthBuffer);
|
|
3722
3808
|
const commentHeaderLength = commentHeaderParts.reduce((a, b) => a + b.length, 0);
|
|
3723
3809
|
const commentHeader = new Uint8Array(commentHeaderLength);
|
|
@@ -6657,9 +6743,6 @@ var Mediabunny = (() => {
|
|
|
6657
6743
|
const segmentDuration = version === 1 ? readU64Be(slice) : readU32Be(slice);
|
|
6658
6744
|
const mediaTime = version === 1 ? readI64Be(slice) : readI32Be(slice);
|
|
6659
6745
|
const mediaRate = readFixed_16_16(slice);
|
|
6660
|
-
if (segmentDuration === 0) {
|
|
6661
|
-
continue;
|
|
6662
|
-
}
|
|
6663
6746
|
if (relevantEntryFound) {
|
|
6664
6747
|
Logging._warn(
|
|
6665
6748
|
"Unsupported edit list: multiple edits are not currently supported. Only using first edit."
|
|
@@ -11321,6 +11404,22 @@ var Mediabunny = (() => {
|
|
|
11321
11404
|
}
|
|
11322
11405
|
}
|
|
11323
11406
|
}
|
|
11407
|
+
async getDurationFromMetadata(segment) {
|
|
11408
|
+
if (segment.duration <= 0) {
|
|
11409
|
+
return null;
|
|
11410
|
+
}
|
|
11411
|
+
let minTimestamp = null;
|
|
11412
|
+
for (const track of segment.tracks) {
|
|
11413
|
+
assert(track.trackBacking);
|
|
11414
|
+
const firstPacket = await track.trackBacking.getFirstPacket({ metadataOnly: true });
|
|
11415
|
+
if (firstPacket) {
|
|
11416
|
+
minTimestamp = Math.min(minTimestamp ?? Infinity, firstPacket.timestamp);
|
|
11417
|
+
}
|
|
11418
|
+
}
|
|
11419
|
+
let endTimestamp = segment.duration / segment.timestampFactor;
|
|
11420
|
+
endTimestamp += minTimestamp ?? 0;
|
|
11421
|
+
return endTimestamp;
|
|
11422
|
+
}
|
|
11324
11423
|
};
|
|
11325
11424
|
var MatroskaTrackBacking = class {
|
|
11326
11425
|
constructor(internalTrack) {
|
|
@@ -11380,14 +11479,7 @@ var Mediabunny = (() => {
|
|
|
11380
11479
|
return null;
|
|
11381
11480
|
}
|
|
11382
11481
|
async getDurationFromMetadata() {
|
|
11383
|
-
|
|
11384
|
-
if (segment.duration <= 0) {
|
|
11385
|
-
return null;
|
|
11386
|
-
}
|
|
11387
|
-
let endTimestamp = segment.duration / segment.timestampFactor;
|
|
11388
|
-
const firstPacket = await this.getFirstPacket({ metadataOnly: true });
|
|
11389
|
-
endTimestamp += firstPacket?.timestamp ?? 0;
|
|
11390
|
-
return endTimestamp;
|
|
11482
|
+
return this.internalTrack.demuxer.getDurationFromMetadata(this.internalTrack.segment);
|
|
11391
11483
|
}
|
|
11392
11484
|
async getLiveRefreshInterval() {
|
|
11393
11485
|
return null;
|
|
@@ -14474,6 +14566,7 @@ var Mediabunny = (() => {
|
|
|
14474
14566
|
|
|
14475
14567
|
// src/mpeg-ts/mpeg-ts-misc.ts
|
|
14476
14568
|
var TIMESCALE = 9e4;
|
|
14569
|
+
var TIMESTAMP_MODULUS = 2 ** 33;
|
|
14477
14570
|
var TS_PACKET_SIZE = 188;
|
|
14478
14571
|
var buildMpegTsMimeType = (codecStrings) => {
|
|
14479
14572
|
let string = "video/MP2T";
|
|
@@ -14508,6 +14601,7 @@ var Mediabunny = (() => {
|
|
|
14508
14601
|
this.seekChunkSize = 5 * 1024 * 1024;
|
|
14509
14602
|
// 5 MiB, picked because most HLS segments are below this size
|
|
14510
14603
|
this.minReferencePointByteDistance = -1;
|
|
14604
|
+
this.timestampWrapInfo = null;
|
|
14511
14605
|
this.reader = input._reader;
|
|
14512
14606
|
}
|
|
14513
14607
|
async readMetadata() {
|
|
@@ -14765,7 +14859,7 @@ var Mediabunny = (() => {
|
|
|
14765
14859
|
const elementaryStream = this.elementaryStreams.find((x) => x.pid === section.pid);
|
|
14766
14860
|
outer:
|
|
14767
14861
|
if (elementaryStream && !elementaryStream.initialized) {
|
|
14768
|
-
const pesPacket = readPesPacket(section, true);
|
|
14862
|
+
const pesPacket = readPesPacket(this, section, true);
|
|
14769
14863
|
if (!pesPacket) {
|
|
14770
14864
|
throw new Error(
|
|
14771
14865
|
`Couldn't read first PES packet for Elementary Stream with PID ${elementaryStream.pid}`
|
|
@@ -15160,8 +15254,22 @@ var Mediabunny = (() => {
|
|
|
15160
15254
|
body: bytes2.subarray(4)
|
|
15161
15255
|
};
|
|
15162
15256
|
}
|
|
15257
|
+
normalizeTimestamp(timestamp) {
|
|
15258
|
+
if (!this.timestampWrapInfo) {
|
|
15259
|
+
const tolerance = 60 * TIMESCALE;
|
|
15260
|
+
this.timestampWrapInfo = {
|
|
15261
|
+
reference: timestamp - tolerance,
|
|
15262
|
+
offset: timestamp >= TIMESTAMP_MODULUS - tolerance ? -TIMESTAMP_MODULUS : TIMESTAMP_MODULUS
|
|
15263
|
+
};
|
|
15264
|
+
}
|
|
15265
|
+
const { reference, offset } = this.timestampWrapInfo;
|
|
15266
|
+
if (offset < 0 && timestamp >= reference || offset > 0 && timestamp < reference) {
|
|
15267
|
+
return timestamp + offset;
|
|
15268
|
+
}
|
|
15269
|
+
return timestamp;
|
|
15270
|
+
}
|
|
15163
15271
|
};
|
|
15164
|
-
var readPesPacketHeader = (section, expectPts) => {
|
|
15272
|
+
var readPesPacketHeader = (demuxer, section, expectPts) => {
|
|
15165
15273
|
if (section.payload.byteLength < 3) {
|
|
15166
15274
|
return null;
|
|
15167
15275
|
}
|
|
@@ -15187,6 +15295,7 @@ var Mediabunny = (() => {
|
|
|
15187
15295
|
pts += bitstream.readBits(15) * (1 << 15);
|
|
15188
15296
|
bitstream.skipBits(1);
|
|
15189
15297
|
pts += bitstream.readBits(15);
|
|
15298
|
+
pts = demuxer.normalizeTimestamp(pts);
|
|
15190
15299
|
} else {
|
|
15191
15300
|
if (expectPts) {
|
|
15192
15301
|
throw new Error(MISSING_PTS_ERROR_MESSAGE);
|
|
@@ -15199,9 +15308,9 @@ var Mediabunny = (() => {
|
|
|
15199
15308
|
randomAccessIndicator: section.randomAccessIndicator
|
|
15200
15309
|
};
|
|
15201
15310
|
};
|
|
15202
|
-
var readPesPacket = (section, expectPts) => {
|
|
15311
|
+
var readPesPacket = (demuxer, section, expectPts) => {
|
|
15203
15312
|
assert(section.endPos !== null);
|
|
15204
|
-
const header = readPesPacketHeader(section, expectPts);
|
|
15313
|
+
const header = readPesPacketHeader(demuxer, section, expectPts);
|
|
15205
15314
|
if (!header) {
|
|
15206
15315
|
return null;
|
|
15207
15316
|
}
|
|
@@ -15313,7 +15422,7 @@ var Mediabunny = (() => {
|
|
|
15313
15422
|
async getFirstPacket(options) {
|
|
15314
15423
|
const section = this.elementaryStream.firstSection;
|
|
15315
15424
|
assert(section);
|
|
15316
|
-
const pesPacket = readPesPacket(section, true);
|
|
15425
|
+
const pesPacket = readPesPacket(this.elementaryStream.demuxer, section, true);
|
|
15317
15426
|
assert(pesPacket);
|
|
15318
15427
|
const context = new PacketReadingContext(this.elementaryStream, pesPacket);
|
|
15319
15428
|
const buffer = new PacketBuffer(this, context);
|
|
@@ -15346,7 +15455,7 @@ var Mediabunny = (() => {
|
|
|
15346
15455
|
const demuxer = this.elementaryStream.demuxer;
|
|
15347
15456
|
const section = await demuxer.readSection(sectionStartPos, true);
|
|
15348
15457
|
assert(section);
|
|
15349
|
-
const pesPacket = readPesPacket(section, true);
|
|
15458
|
+
const pesPacket = readPesPacket(demuxer, section, true);
|
|
15350
15459
|
assert(pesPacket);
|
|
15351
15460
|
const context = new PacketReadingContext(this.elementaryStream, pesPacket);
|
|
15352
15461
|
buffer = new PacketBuffer(this, context);
|
|
@@ -15404,7 +15513,7 @@ var Mediabunny = (() => {
|
|
|
15404
15513
|
if (!section) {
|
|
15405
15514
|
return null;
|
|
15406
15515
|
}
|
|
15407
|
-
const pesPacketHeader = readPesPacketHeader(section, false);
|
|
15516
|
+
const pesPacketHeader = readPesPacketHeader(demuxer, section, false);
|
|
15408
15517
|
if (pesPacketHeader && pesPacketHeader.pts !== null) {
|
|
15409
15518
|
return {
|
|
15410
15519
|
pesPacketHeader,
|
|
@@ -15418,7 +15527,7 @@ var Mediabunny = (() => {
|
|
|
15418
15527
|
};
|
|
15419
15528
|
const firstSection = this.elementaryStream.firstSection;
|
|
15420
15529
|
assert(firstSection);
|
|
15421
|
-
const firstPesPacketHeader = readPesPacketHeader(firstSection, true);
|
|
15530
|
+
const firstPesPacketHeader = readPesPacketHeader(demuxer, firstSection, true);
|
|
15422
15531
|
assert(firstPesPacketHeader);
|
|
15423
15532
|
if (searchPts < firstPesPacketHeader.pts) {
|
|
15424
15533
|
return null;
|
|
@@ -15473,7 +15582,7 @@ var Mediabunny = (() => {
|
|
|
15473
15582
|
const retrieveEncodedPacket = async (sectionStartPos, predicate) => {
|
|
15474
15583
|
const section = await demuxer.readSection(sectionStartPos, true);
|
|
15475
15584
|
assert(section);
|
|
15476
|
-
const pesPacket = readPesPacket(section, true);
|
|
15585
|
+
const pesPacket = readPesPacket(demuxer, section, true);
|
|
15477
15586
|
assert(pesPacket);
|
|
15478
15587
|
const context = new PacketReadingContext(this.elementaryStream, pesPacket);
|
|
15479
15588
|
const buffer = new PacketBuffer(this, context);
|
|
@@ -15516,7 +15625,7 @@ var Mediabunny = (() => {
|
|
|
15516
15625
|
if (packetHeader.pid === pid && packetHeader.payloadUnitStartIndicator === 1) {
|
|
15517
15626
|
const section = await demuxer.readSection(currentPos, false);
|
|
15518
15627
|
if (section) {
|
|
15519
|
-
const nextPesHeader = readPesPacketHeader(section, false);
|
|
15628
|
+
const nextPesHeader = readPesPacketHeader(demuxer, section, false);
|
|
15520
15629
|
if (nextPesHeader && nextPesHeader.pts !== null) {
|
|
15521
15630
|
if (nextPesHeader.pts > searchPts) {
|
|
15522
15631
|
break outer;
|
|
@@ -15541,7 +15650,7 @@ var Mediabunny = (() => {
|
|
|
15541
15650
|
if (packetHeader.pid === pid && packetHeader.payloadUnitStartIndicator === 1) {
|
|
15542
15651
|
const section = await demuxer.readSection(pos, false);
|
|
15543
15652
|
if (section) {
|
|
15544
|
-
const header = readPesPacketHeader(section, false);
|
|
15653
|
+
const header = readPesPacketHeader(demuxer, section, false);
|
|
15545
15654
|
if (header && header.pts !== null) {
|
|
15546
15655
|
currentPesHeader = header;
|
|
15547
15656
|
break;
|
|
@@ -15586,7 +15695,7 @@ var Mediabunny = (() => {
|
|
|
15586
15695
|
isKeyPacket = pesHeader.randomAccessIndicator === 1;
|
|
15587
15696
|
} else {
|
|
15588
15697
|
assert(pesHeaderSection);
|
|
15589
|
-
const pesPacket = readPesPacket(pesHeaderSection, true);
|
|
15698
|
+
const pesPacket = readPesPacket(demuxer, pesHeaderSection, true);
|
|
15590
15699
|
assert(pesPacket);
|
|
15591
15700
|
const context = new PacketReadingContext(this.elementaryStream, pesPacket);
|
|
15592
15701
|
await context.markNextPacket();
|
|
@@ -15614,7 +15723,7 @@ var Mediabunny = (() => {
|
|
|
15614
15723
|
if (packetHeader.pid === pid && packetHeader.payloadUnitStartIndicator === 1) {
|
|
15615
15724
|
const section = await demuxer.readSection(currentPos, readSectionsInFull);
|
|
15616
15725
|
if (section) {
|
|
15617
|
-
const nextPesHeader = readPesPacketHeader(section, false);
|
|
15726
|
+
const nextPesHeader = readPesPacketHeader(demuxer, section, false);
|
|
15618
15727
|
if (nextPesHeader && nextPesHeader.pts !== null) {
|
|
15619
15728
|
pesHeader = nextPesHeader;
|
|
15620
15729
|
pesHeaderSection = section;
|
|
@@ -15640,7 +15749,7 @@ var Mediabunny = (() => {
|
|
|
15640
15749
|
if (packetHeader.pid === pid && packetHeader.payloadUnitStartIndicator === 1) {
|
|
15641
15750
|
const section = await demuxer.readSection(pos, readSectionsInFull);
|
|
15642
15751
|
if (section) {
|
|
15643
|
-
const header = readPesPacketHeader(section, false);
|
|
15752
|
+
const header = readPesPacketHeader(demuxer, section, false);
|
|
15644
15753
|
if (header && header.pts !== null) {
|
|
15645
15754
|
startPesHeader = header;
|
|
15646
15755
|
break;
|
|
@@ -15812,7 +15921,7 @@ var Mediabunny = (() => {
|
|
|
15812
15921
|
if (!nextSection) {
|
|
15813
15922
|
return;
|
|
15814
15923
|
}
|
|
15815
|
-
const nextPesPacket = readPesPacket(nextSection, false);
|
|
15924
|
+
const nextPesPacket = readPesPacket(this.demuxer, nextSection, false);
|
|
15816
15925
|
if (nextPesPacket) {
|
|
15817
15926
|
pesPacket = nextPesPacket;
|
|
15818
15927
|
break;
|
|
@@ -17084,6 +17193,9 @@ var Mediabunny = (() => {
|
|
|
17084
17193
|
if (options.useStreamReader !== void 0 && typeof options.useStreamReader !== "boolean") {
|
|
17085
17194
|
throw new TypeError("options.useStreamReader, when provided, must be a boolean.");
|
|
17086
17195
|
}
|
|
17196
|
+
if (options.handleUnhandledError !== void 0 && typeof options.handleUnhandledError !== "function") {
|
|
17197
|
+
throw new TypeError("options.handleUnhandledError, when provided, must be a function.");
|
|
17198
|
+
}
|
|
17087
17199
|
super();
|
|
17088
17200
|
/** @internal */
|
|
17089
17201
|
this._readers = /* @__PURE__ */ new WeakMap();
|
|
@@ -17102,7 +17214,8 @@ var Mediabunny = (() => {
|
|
|
17102
17214
|
});
|
|
17103
17215
|
}
|
|
17104
17216
|
},
|
|
17105
|
-
prefetchProfile: PREFETCH_PROFILES.fileSystem
|
|
17217
|
+
prefetchProfile: PREFETCH_PROFILES.fileSystem,
|
|
17218
|
+
handleUnhandledError: options.handleUnhandledError
|
|
17106
17219
|
});
|
|
17107
17220
|
this._orchestrator.fileSize = blob.size;
|
|
17108
17221
|
}
|
|
@@ -17210,6 +17323,9 @@ var Mediabunny = (() => {
|
|
|
17210
17323
|
if (options.fetchFn !== void 0 && typeof options.fetchFn !== "function") {
|
|
17211
17324
|
throw new TypeError("options.fetchFn, when provided, must be a function.");
|
|
17212
17325
|
}
|
|
17326
|
+
if (options.handleUnhandledError !== void 0 && typeof options.handleUnhandledError !== "function") {
|
|
17327
|
+
throw new TypeError("options.handleUnhandledError, when provided, must be a function.");
|
|
17328
|
+
}
|
|
17213
17329
|
const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
|
|
17214
17330
|
super(
|
|
17215
17331
|
urlString,
|
|
@@ -17266,7 +17382,8 @@ var Mediabunny = (() => {
|
|
|
17266
17382
|
maxCacheSize: options.maxCacheSize ?? 64 * 2 ** 20,
|
|
17267
17383
|
maxWorkerCount: options.parallelism ?? DEFAULT_PARALLELISM,
|
|
17268
17384
|
runWorker: this._runWorker.bind(this),
|
|
17269
|
-
prefetchProfile: PREFETCH_PROFILES.network
|
|
17385
|
+
prefetchProfile: PREFETCH_PROFILES.network,
|
|
17386
|
+
handleUnhandledError: options.handleUnhandledError
|
|
17270
17387
|
});
|
|
17271
17388
|
}
|
|
17272
17389
|
/** @internal */
|
|
@@ -17329,7 +17446,7 @@ var Mediabunny = (() => {
|
|
|
17329
17446
|
this.rootPath = response.url;
|
|
17330
17447
|
}
|
|
17331
17448
|
outer:
|
|
17332
|
-
if (this._orchestrator.fileSize === null) {
|
|
17449
|
+
if (this._orchestrator.fileSize === null && (response.status === 206 || response.type === "basic" && !response.headers.has("Content-Encoding"))) {
|
|
17333
17450
|
const contentRange = response.headers.get("Content-Range");
|
|
17334
17451
|
if (contentRange) {
|
|
17335
17452
|
const match = /\/(\d+)/.exec(contentRange);
|
|
@@ -17485,7 +17602,8 @@ var Mediabunny = (() => {
|
|
|
17485
17602
|
cancel: () => currentReader.cancel()
|
|
17486
17603
|
});
|
|
17487
17604
|
const backing = new ReadableStreamSource(wrappedStream, {
|
|
17488
|
-
maxCacheSize: this._orchestrator.options.maxCacheSize
|
|
17605
|
+
maxCacheSize: this._orchestrator.options.maxCacheSize,
|
|
17606
|
+
handleUnhandledError: this._options.handleUnhandledError
|
|
17489
17607
|
});
|
|
17490
17608
|
backing._endIndex = this._orchestrator.fileSize;
|
|
17491
17609
|
backing._cacheMissErrorMessage = "Attempted to read data from an already-evicted part of the cache. Because the HTTP server did not honor the range request, data can only be read sequentially, with old data being evicted from the cache. To fix this issue, either ensure your server responds to range requests with 206 Partial Content, or set maxCacheSize to Infinity in the UrlSource options. Note that the latter will store the entire file in the cache if needed, no matter how large.";
|
|
@@ -17585,7 +17703,8 @@ var Mediabunny = (() => {
|
|
|
17585
17703
|
return buffer;
|
|
17586
17704
|
},
|
|
17587
17705
|
maxCacheSize: options.maxCacheSize,
|
|
17588
|
-
prefetchProfile: "fileSystem"
|
|
17706
|
+
prefetchProfile: "fileSystem",
|
|
17707
|
+
handleUnhandledError: options.handleUnhandledError
|
|
17589
17708
|
});
|
|
17590
17709
|
}
|
|
17591
17710
|
/** @internal */
|
|
@@ -17621,6 +17740,9 @@ var Mediabunny = (() => {
|
|
|
17621
17740
|
if (options.dispose !== void 0 && typeof options.dispose !== "function") {
|
|
17622
17741
|
throw new TypeError("options.dispose, when provided, must be a function.");
|
|
17623
17742
|
}
|
|
17743
|
+
if (options.handleUnhandledError !== void 0 && typeof options.handleUnhandledError !== "function") {
|
|
17744
|
+
throw new TypeError("options.handleUnhandledError, when provided, must be a function.");
|
|
17745
|
+
}
|
|
17624
17746
|
if (options.maxCacheSize !== void 0 && (!isNumber(options.maxCacheSize) || options.maxCacheSize < 0)) {
|
|
17625
17747
|
throw new TypeError("options.maxCacheSize, when provided, must be a non-negative number.");
|
|
17626
17748
|
}
|
|
@@ -17636,7 +17758,8 @@ var Mediabunny = (() => {
|
|
|
17636
17758
|
maxWorkerCount: 2,
|
|
17637
17759
|
// Fixed for now, *should* be fine
|
|
17638
17760
|
prefetchProfile: PREFETCH_PROFILES[options.prefetchProfile ?? "none"],
|
|
17639
|
-
runWorker: this._runWorker.bind(this)
|
|
17761
|
+
runWorker: this._runWorker.bind(this),
|
|
17762
|
+
handleUnhandledError: options.handleUnhandledError
|
|
17640
17763
|
});
|
|
17641
17764
|
}
|
|
17642
17765
|
/** @internal */
|
|
@@ -17728,6 +17851,9 @@ var Mediabunny = (() => {
|
|
|
17728
17851
|
if (!options || typeof options !== "object") {
|
|
17729
17852
|
throw new TypeError("options must be an object.");
|
|
17730
17853
|
}
|
|
17854
|
+
if (options.handleUnhandledError !== void 0 && typeof options.handleUnhandledError !== "function") {
|
|
17855
|
+
throw new TypeError("options.handleUnhandledError, when provided, must be a function.");
|
|
17856
|
+
}
|
|
17731
17857
|
if (options.maxCacheSize !== void 0 && (!isNumber(options.maxCacheSize) || options.maxCacheSize < 0)) {
|
|
17732
17858
|
throw new TypeError("options.maxCacheSize, when provided, must be a non-negative number.");
|
|
17733
17859
|
}
|
|
@@ -17755,6 +17881,7 @@ var Mediabunny = (() => {
|
|
|
17755
17881
|
this._cacheMissErrorMessage = "Attempted to read data from an already-evicted part of the cache. With ReadableStreamSource, you must access the data more sequentially or increase the size of its cache.";
|
|
17756
17882
|
this._stream = stream;
|
|
17757
17883
|
this._maxCacheSize = options.maxCacheSize ?? 32 * 2 ** 20;
|
|
17884
|
+
this._handleUnhandledError = options.handleUnhandledError;
|
|
17758
17885
|
}
|
|
17759
17886
|
/** @internal */
|
|
17760
17887
|
_getFileSize() {
|
|
@@ -17823,6 +17950,8 @@ var Mediabunny = (() => {
|
|
|
17823
17950
|
if (this._pendingSlices.length > 0) {
|
|
17824
17951
|
this._pendingSlices.forEach((x) => x.reject(error));
|
|
17825
17952
|
this._pendingSlices.length = 0;
|
|
17953
|
+
} else if (this._handleUnhandledError) {
|
|
17954
|
+
this._handleUnhandledError(error);
|
|
17826
17955
|
} else {
|
|
17827
17956
|
throw error;
|
|
17828
17957
|
}
|
|
@@ -18106,7 +18235,11 @@ var Mediabunny = (() => {
|
|
|
18106
18235
|
if (this.disposed) {
|
|
18107
18236
|
return;
|
|
18108
18237
|
}
|
|
18109
|
-
|
|
18238
|
+
if (this.options.handleUnhandledError) {
|
|
18239
|
+
this.options.handleUnhandledError(error);
|
|
18240
|
+
} else {
|
|
18241
|
+
throw error;
|
|
18242
|
+
}
|
|
18110
18243
|
});
|
|
18111
18244
|
}
|
|
18112
18245
|
return result;
|
|
@@ -18194,7 +18327,11 @@ var Mediabunny = (() => {
|
|
|
18194
18327
|
worker.pendingSlices.forEach((x) => x.reject(error));
|
|
18195
18328
|
worker.pendingSlices.length = 0;
|
|
18196
18329
|
} else if (!worker.aborted && !this.disposed) {
|
|
18197
|
-
|
|
18330
|
+
if (this.options.handleUnhandledError) {
|
|
18331
|
+
this.options.handleUnhandledError(error);
|
|
18332
|
+
} else {
|
|
18333
|
+
throw error;
|
|
18334
|
+
}
|
|
18198
18335
|
}
|
|
18199
18336
|
}).finally(() => {
|
|
18200
18337
|
if (worker.running) {
|
|
@@ -22098,30 +22235,30 @@ var Mediabunny = (() => {
|
|
|
22098
22235
|
}
|
|
22099
22236
|
}
|
|
22100
22237
|
/**
|
|
22101
|
-
* Returns a new {@link AudioSample} containing only the frames in the range [
|
|
22238
|
+
* Returns a new {@link AudioSample} containing only the frames in the range [startFrame, endFrame). Both bounds
|
|
22102
22239
|
* must lie within this sample's range of frames. The returned sample's timestamp is shifted to match the start of
|
|
22103
22240
|
* the trimmed section.
|
|
22104
22241
|
*/
|
|
22105
|
-
trim(
|
|
22106
|
-
if (!Number.isInteger(
|
|
22107
|
-
throw new TypeError("
|
|
22242
|
+
trim(startFrame, endFrame = this.numberOfFrames) {
|
|
22243
|
+
if (!Number.isInteger(startFrame) || startFrame < 0) {
|
|
22244
|
+
throw new TypeError("startFrame must be a non-negative integer.");
|
|
22108
22245
|
}
|
|
22109
|
-
if (!Number.isInteger(
|
|
22110
|
-
throw new TypeError("
|
|
22246
|
+
if (!Number.isInteger(endFrame) || endFrame < 0) {
|
|
22247
|
+
throw new TypeError("endFrame must be a non-negative integer.");
|
|
22111
22248
|
}
|
|
22112
|
-
if (
|
|
22113
|
-
throw new RangeError("
|
|
22249
|
+
if (startFrame > this.numberOfFrames) {
|
|
22250
|
+
throw new RangeError("startFrame out of range.");
|
|
22114
22251
|
}
|
|
22115
|
-
if (
|
|
22116
|
-
throw new RangeError("
|
|
22252
|
+
if (endFrame > this.numberOfFrames) {
|
|
22253
|
+
throw new RangeError("endFrame out of range.");
|
|
22117
22254
|
}
|
|
22118
|
-
if (
|
|
22119
|
-
throw new RangeError("
|
|
22255
|
+
if (endFrame < startFrame) {
|
|
22256
|
+
throw new RangeError("endFrame must not be less than startFrame.");
|
|
22120
22257
|
}
|
|
22121
22258
|
if (this._closed) {
|
|
22122
22259
|
throw new Error("AudioSample is closed.");
|
|
22123
22260
|
}
|
|
22124
|
-
const frameCount =
|
|
22261
|
+
const frameCount = endFrame - startFrame;
|
|
22125
22262
|
const bytesPerSample = getBytesPerSample(this.format);
|
|
22126
22263
|
let data;
|
|
22127
22264
|
if (formatIsPlanar(this.format)) {
|
|
@@ -22132,7 +22269,7 @@ var Mediabunny = (() => {
|
|
|
22132
22269
|
this.copyTo(data.subarray(i * planeSize, (i + 1) * planeSize), {
|
|
22133
22270
|
planeIndex: i,
|
|
22134
22271
|
format: this.format,
|
|
22135
|
-
frameOffset:
|
|
22272
|
+
frameOffset: startFrame,
|
|
22136
22273
|
frameCount
|
|
22137
22274
|
});
|
|
22138
22275
|
}
|
|
@@ -22143,7 +22280,7 @@ var Mediabunny = (() => {
|
|
|
22143
22280
|
this.copyTo(data, {
|
|
22144
22281
|
planeIndex: 0,
|
|
22145
22282
|
format: this.format,
|
|
22146
|
-
frameOffset:
|
|
22283
|
+
frameOffset: startFrame,
|
|
22147
22284
|
frameCount
|
|
22148
22285
|
});
|
|
22149
22286
|
}
|
|
@@ -22153,7 +22290,7 @@ var Mediabunny = (() => {
|
|
|
22153
22290
|
format: this.format,
|
|
22154
22291
|
sampleRate: this.sampleRate,
|
|
22155
22292
|
numberOfChannels: this.numberOfChannels,
|
|
22156
|
-
timestamp: this.timestamp +
|
|
22293
|
+
timestamp: this.timestamp + startFrame / this.sampleRate
|
|
22157
22294
|
});
|
|
22158
22295
|
}
|
|
22159
22296
|
/**
|
|
@@ -24013,11 +24150,20 @@ var Mediabunny = (() => {
|
|
|
24013
24150
|
);
|
|
24014
24151
|
if (record && record.sequenceParameterSets.length > 0) {
|
|
24015
24152
|
const sps = parseAvcSps(record.sequenceParameterSets[0]);
|
|
24016
|
-
if (sps
|
|
24017
|
-
|
|
24018
|
-
|
|
24019
|
-
|
|
24020
|
-
|
|
24153
|
+
if (sps) {
|
|
24154
|
+
if (sps.frameMbsOnlyFlag === 0) {
|
|
24155
|
+
this.decoderConfig = {
|
|
24156
|
+
...this.decoderConfig,
|
|
24157
|
+
hardwareAcceleration: "prefer-software"
|
|
24158
|
+
};
|
|
24159
|
+
}
|
|
24160
|
+
if (sps.maxDecFrameBuffering !== 0 && sps.bitstreamRestrictionFlag !== 1) {
|
|
24161
|
+
record.sequenceParameterSets[0] = addAvcBitstreamRestriction(sps);
|
|
24162
|
+
this.decoderConfig = {
|
|
24163
|
+
...this.decoderConfig,
|
|
24164
|
+
description: serializeAvcDecoderConfigurationRecord(record)
|
|
24165
|
+
};
|
|
24166
|
+
}
|
|
24021
24167
|
}
|
|
24022
24168
|
}
|
|
24023
24169
|
}
|
|
@@ -24094,6 +24240,19 @@ var Mediabunny = (() => {
|
|
|
24094
24240
|
filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length));
|
|
24095
24241
|
}
|
|
24096
24242
|
}
|
|
24243
|
+
if (!this.decoderConfig.description) {
|
|
24244
|
+
for (let i = 0; i < filteredNalUnits.length; i++) {
|
|
24245
|
+
const nalUnit = filteredNalUnits[i];
|
|
24246
|
+
if (extractNalUnitTypeForAvc(nalUnit[0]) !== 7 /* SPS */) {
|
|
24247
|
+
continue;
|
|
24248
|
+
}
|
|
24249
|
+
const sps = parseAvcSps(nalUnit);
|
|
24250
|
+
if (sps && sps.maxDecFrameBuffering !== 0 && sps.bitstreamRestrictionFlag !== 1) {
|
|
24251
|
+
filteredNalUnits[i] = addAvcBitstreamRestriction(sps);
|
|
24252
|
+
}
|
|
24253
|
+
break;
|
|
24254
|
+
}
|
|
24255
|
+
}
|
|
24097
24256
|
const newData = concatAvcNalUnits(filteredNalUnits, this.decoderConfig);
|
|
24098
24257
|
packet = new EncodedPacket(newData, packet.type, packet.timestamp, packet.duration);
|
|
24099
24258
|
} else if (this.codec === "hevc") {
|
|
@@ -27886,9 +28045,6 @@ var Mediabunny = (() => {
|
|
|
27886
28045
|
onTrackClose(track) {
|
|
27887
28046
|
}
|
|
27888
28047
|
validateTimestamp(track, timestampInSeconds, isKeyPacket) {
|
|
27889
|
-
if (timestampInSeconds < 0) {
|
|
27890
|
-
throw new Error(`Timestamps must be non-negative (got ${timestampInSeconds}s).`);
|
|
27891
|
-
}
|
|
27892
28048
|
let timestampInfo = this.trackTimestampInfo.get(track);
|
|
27893
28049
|
if (!timestampInfo) {
|
|
27894
28050
|
if (!isKeyPacket) {
|
|
@@ -28564,7 +28720,8 @@ var Mediabunny = (() => {
|
|
|
28564
28720
|
ascii("dash")
|
|
28565
28721
|
]);
|
|
28566
28722
|
var sidx = (muxer, referencedSize) => {
|
|
28567
|
-
|
|
28723
|
+
const earliestPresentationTime = Math.max(0, muxer.minWrittenTimestamp);
|
|
28724
|
+
let duration = Math.max(0, muxer.maxWrittenEndTimestamp - earliestPresentationTime);
|
|
28568
28725
|
if (!Number.isFinite(duration)) {
|
|
28569
28726
|
duration = 0;
|
|
28570
28727
|
}
|
|
@@ -28573,7 +28730,7 @@ var Mediabunny = (() => {
|
|
|
28573
28730
|
// Reference ID
|
|
28574
28731
|
u32(GLOBAL_TIMESCALE),
|
|
28575
28732
|
// Timescale
|
|
28576
|
-
u64(intoTimescale(
|
|
28733
|
+
u64(intoTimescale(earliestPresentationTime, GLOBAL_TIMESCALE)),
|
|
28577
28734
|
// Earliest presentation time
|
|
28578
28735
|
u64(0),
|
|
28579
28736
|
// First offset
|
|
@@ -28602,7 +28759,13 @@ var Mediabunny = (() => {
|
|
|
28602
28759
|
var mvhd = (creationTime, trackDatas) => {
|
|
28603
28760
|
const duration = Math.max(
|
|
28604
28761
|
0,
|
|
28605
|
-
...trackDatas.map((trackData) =>
|
|
28762
|
+
...trackDatas.map((trackData) => (
|
|
28763
|
+
// Round separately to match the edit list
|
|
28764
|
+
Math.max(
|
|
28765
|
+
0,
|
|
28766
|
+
intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE)
|
|
28767
|
+
)
|
|
28768
|
+
))
|
|
28606
28769
|
);
|
|
28607
28770
|
const nextTrackId = Math.max(0, ...trackDatas.map((x) => x.track.id)) + 1;
|
|
28608
28771
|
const needsU64 = !isU32(creationTime) || !isU32(duration);
|
|
@@ -28652,10 +28815,10 @@ var Mediabunny = (() => {
|
|
|
28652
28815
|
};
|
|
28653
28816
|
var trak = (trackData, creationTime) => {
|
|
28654
28817
|
const trackMetadata = getTrackMetadata(trackData);
|
|
28655
|
-
const needsEditList = trackData.startTimestampOffset !== null && trackData.startTimestampOffset
|
|
28818
|
+
const needsEditList = trackData.startTimestampOffset !== null && trackData.startTimestampOffset !== 0;
|
|
28656
28819
|
return box("trak", void 0, [
|
|
28657
28820
|
tkhd(trackData, creationTime),
|
|
28658
|
-
needsEditList ? edts(trackData
|
|
28821
|
+
needsEditList ? edts(trackData) : null,
|
|
28659
28822
|
mdia(trackData, creationTime),
|
|
28660
28823
|
trackMetadata.name !== void 0 ? box("udta", void 0, [
|
|
28661
28824
|
box("name", [
|
|
@@ -28666,7 +28829,10 @@ var Mediabunny = (() => {
|
|
|
28666
28829
|
]);
|
|
28667
28830
|
};
|
|
28668
28831
|
var tkhd = (trackData, creationTime) => {
|
|
28669
|
-
const durationInGlobalTimescale =
|
|
28832
|
+
const durationInGlobalTimescale = Math.max(
|
|
28833
|
+
0,
|
|
28834
|
+
intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE)
|
|
28835
|
+
);
|
|
28670
28836
|
const needsU64 = !isU32(creationTime) || !isU32(durationInGlobalTimescale);
|
|
28671
28837
|
const u32OrU64 = needsU64 ? u64 : u32;
|
|
28672
28838
|
let matrix;
|
|
@@ -28710,32 +28876,58 @@ var Mediabunny = (() => {
|
|
|
28710
28876
|
// Track height
|
|
28711
28877
|
]);
|
|
28712
28878
|
};
|
|
28713
|
-
var edts = (trackData
|
|
28714
|
-
const
|
|
28715
|
-
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
|
|
28719
|
-
|
|
28720
|
-
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
28724
|
-
|
|
28725
|
-
|
|
28726
|
-
|
|
28727
|
-
|
|
28728
|
-
|
|
28729
|
-
|
|
28730
|
-
|
|
28731
|
-
|
|
28732
|
-
|
|
28733
|
-
|
|
28734
|
-
|
|
28735
|
-
|
|
28736
|
-
|
|
28737
|
-
|
|
28738
|
-
|
|
28879
|
+
var edts = (trackData) => {
|
|
28880
|
+
const offset = trackData.startTimestampOffset;
|
|
28881
|
+
assert(offset !== null);
|
|
28882
|
+
if (offset > 0) {
|
|
28883
|
+
const startOffset = intoTimescale(offset, GLOBAL_TIMESCALE);
|
|
28884
|
+
const mediaDuration = intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE);
|
|
28885
|
+
const needs64Bits = !isU32(startOffset) || !isU32(mediaDuration);
|
|
28886
|
+
const u32OrU64 = needs64Bits ? u64 : u32;
|
|
28887
|
+
const i32OrI64 = needs64Bits ? i64 : i32;
|
|
28888
|
+
return box("edts", void 0, [
|
|
28889
|
+
fullBox("elst", needs64Bits ? 1 : 0, 0, [
|
|
28890
|
+
u32(2),
|
|
28891
|
+
// Entry count
|
|
28892
|
+
// #1
|
|
28893
|
+
u32OrU64(startOffset),
|
|
28894
|
+
// Segment duration
|
|
28895
|
+
i32OrI64(-1),
|
|
28896
|
+
// Media time
|
|
28897
|
+
fixed_16_16(1),
|
|
28898
|
+
// Media rate
|
|
28899
|
+
// #2
|
|
28900
|
+
u32OrU64(mediaDuration),
|
|
28901
|
+
// Segment duration
|
|
28902
|
+
i32OrI64(0),
|
|
28903
|
+
// Media time
|
|
28904
|
+
fixed_16_16(1)
|
|
28905
|
+
// Media rate
|
|
28906
|
+
])
|
|
28907
|
+
]);
|
|
28908
|
+
} else {
|
|
28909
|
+
const mediaTime = intoTimescale(-offset, trackData.timescale);
|
|
28910
|
+
const mediaDuration = Math.max(
|
|
28911
|
+
0,
|
|
28912
|
+
intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(offset, GLOBAL_TIMESCALE)
|
|
28913
|
+
);
|
|
28914
|
+
const needs64Bits = !isI32(mediaTime) || !isU32(mediaDuration);
|
|
28915
|
+
const u32OrU64 = needs64Bits ? u64 : u32;
|
|
28916
|
+
const i32OrI64 = needs64Bits ? i64 : i32;
|
|
28917
|
+
return box("edts", void 0, [
|
|
28918
|
+
fullBox("elst", needs64Bits ? 1 : 0, 0, [
|
|
28919
|
+
u32(1),
|
|
28920
|
+
// Entry count
|
|
28921
|
+
// #1
|
|
28922
|
+
u32OrU64(mediaDuration),
|
|
28923
|
+
// Segment duration
|
|
28924
|
+
i32OrI64(mediaTime),
|
|
28925
|
+
// Media time
|
|
28926
|
+
fixed_16_16(1)
|
|
28927
|
+
// Media rate
|
|
28928
|
+
])
|
|
28929
|
+
]);
|
|
28930
|
+
}
|
|
28739
28931
|
};
|
|
28740
28932
|
var mdia = (trackData, creationTime) => box("mdia", void 0, [
|
|
28741
28933
|
mdhd(trackData, creationTime),
|
|
@@ -31044,7 +31236,7 @@ var Mediabunny = (() => {
|
|
|
31044
31236
|
currentChunk: null,
|
|
31045
31237
|
compactlyCodedChunkTable: [],
|
|
31046
31238
|
closed: false,
|
|
31047
|
-
lastCueEndTimestamp:
|
|
31239
|
+
lastCueEndTimestamp: null,
|
|
31048
31240
|
cueQueue: [],
|
|
31049
31241
|
nextSourceId: 0,
|
|
31050
31242
|
cueToSourceId: /* @__PURE__ */ new WeakMap()
|
|
@@ -31176,6 +31368,7 @@ var Mediabunny = (() => {
|
|
|
31176
31368
|
}
|
|
31177
31369
|
async processWebVTTCues(trackData, until) {
|
|
31178
31370
|
while (trackData.cueQueue.length > 0) {
|
|
31371
|
+
trackData.lastCueEndTimestamp ??= Math.min(0, trackData.cueQueue[0].timestamp);
|
|
31179
31372
|
const timestamps = /* @__PURE__ */ new Set([]);
|
|
31180
31373
|
for (const cue of trackData.cueQueue) {
|
|
31181
31374
|
assert(cue.timestamp <= until);
|
|
@@ -31259,9 +31452,8 @@ var Mediabunny = (() => {
|
|
|
31259
31452
|
return;
|
|
31260
31453
|
}
|
|
31261
31454
|
if (trackData.type === "audio" && trackData.info.requiresPcmTransformation) {
|
|
31262
|
-
|
|
31263
|
-
|
|
31264
|
-
}
|
|
31455
|
+
assert(!this.isFragmented);
|
|
31456
|
+
trackData.startTimestampOffset ??= trackData.timestampProcessingQueue[0].timestamp;
|
|
31265
31457
|
let totalDuration = 0;
|
|
31266
31458
|
for (let i = 0; i < trackData.timestampProcessingQueue.length; i++) {
|
|
31267
31459
|
const sample = trackData.timestampProcessingQueue[i];
|
|
@@ -31281,7 +31473,9 @@ var Mediabunny = (() => {
|
|
|
31281
31473
|
return;
|
|
31282
31474
|
}
|
|
31283
31475
|
const sortedTimestamps = trackData.timestampProcessingQueue.map((x) => x.timestamp).sort((a, b) => a - b);
|
|
31284
|
-
if (
|
|
31476
|
+
if (this.isFragmented) {
|
|
31477
|
+
trackData.startTimestampOffset ??= Math.min(sortedTimestamps[0], 0);
|
|
31478
|
+
} else {
|
|
31285
31479
|
trackData.startTimestampOffset ??= sortedTimestamps[0];
|
|
31286
31480
|
}
|
|
31287
31481
|
for (let i = 0; i < trackData.timestampProcessingQueue.length; i++) {
|
|
@@ -31532,11 +31726,16 @@ var Mediabunny = (() => {
|
|
|
31532
31726
|
let fragmentStartTimestamp = Infinity;
|
|
31533
31727
|
for (let i = 0; i < tracksInFragment.length; i++) {
|
|
31534
31728
|
const trackData = tracksInFragment[i];
|
|
31729
|
+
assert(trackData.currentChunk);
|
|
31730
|
+
assert(trackData.startTimestampOffset !== null);
|
|
31535
31731
|
trackData.currentChunk.offset = currentPos;
|
|
31536
31732
|
trackData.currentChunk.moofOffset = moofOffset;
|
|
31537
31733
|
trackData.currentChunk.trafIndex = i;
|
|
31734
|
+
trackData.currentChunk.startTimestamp -= trackData.startTimestampOffset;
|
|
31538
31735
|
for (const sample of trackData.currentChunk.samples) {
|
|
31539
31736
|
currentPos += sample.size;
|
|
31737
|
+
sample.timestamp -= trackData.startTimestampOffset;
|
|
31738
|
+
sample.decodeTimestamp -= trackData.startTimestampOffset;
|
|
31540
31739
|
}
|
|
31541
31740
|
fragmentStartTimestamp = Math.min(fragmentStartTimestamp, trackData.currentChunk.startTimestamp);
|
|
31542
31741
|
}
|
|
@@ -31838,6 +32037,7 @@ var Mediabunny = (() => {
|
|
|
31838
32037
|
this.trackDatasInCurrentCluster = /* @__PURE__ */ new Map();
|
|
31839
32038
|
this.startTimestamp = Infinity;
|
|
31840
32039
|
this.endTimestamp = -Infinity;
|
|
32040
|
+
this.warnedAboutTooNegativeTimestamp = false;
|
|
31841
32041
|
this.format = format;
|
|
31842
32042
|
}
|
|
31843
32043
|
async start() {
|
|
@@ -32635,6 +32835,13 @@ ${cue.notes ?? ""}`;
|
|
|
32635
32835
|
}
|
|
32636
32836
|
const relativeTimestamp = msTimestamp - this.currentClusterStartMsTimestamp;
|
|
32637
32837
|
if (relativeTimestamp < MIN_CLUSTER_TIMESTAMP_MS) {
|
|
32838
|
+
if (!this.warnedAboutTooNegativeTimestamp) {
|
|
32839
|
+
const formatName = this.format instanceof WebMOutputFormat ? "WebM" : "Matroska";
|
|
32840
|
+
Logging._warn(
|
|
32841
|
+
`Packets had to be discarded because their timestamp is too negative to represent in ${formatName}.`
|
|
32842
|
+
);
|
|
32843
|
+
this.warnedAboutTooNegativeTimestamp = true;
|
|
32844
|
+
}
|
|
32638
32845
|
return;
|
|
32639
32846
|
}
|
|
32640
32847
|
const prelude = new Uint8Array(4);
|
|
@@ -32683,6 +32890,7 @@ ${cue.notes ?? ""}`;
|
|
|
32683
32890
|
}
|
|
32684
32891
|
/** Creates a new Cluster element to contain media chunks. */
|
|
32685
32892
|
createNewCluster(msTimestamp) {
|
|
32893
|
+
msTimestamp = Math.max(0, msTimestamp);
|
|
32686
32894
|
if (this.currentCluster) {
|
|
32687
32895
|
this.finalizeCurrentCluster();
|
|
32688
32896
|
}
|
|
@@ -32727,7 +32935,8 @@ ${cue.notes ?? ""}`;
|
|
|
32727
32935
|
for (const [msTimestamp, trackDatas] of groupedAndSortedByTimestamp) {
|
|
32728
32936
|
assert(this.cues);
|
|
32729
32937
|
this.cues.data.push({ id: 187 /* CuePoint */, data: [
|
|
32730
|
-
{ id: 179 /* CueTime */, data: msTimestamp },
|
|
32938
|
+
{ id: 179 /* CueTime */, data: Math.max(0, msTimestamp) },
|
|
32939
|
+
// CueTime is unsigned
|
|
32731
32940
|
// Create CueTrackPositions for each track that starts at this timestamp
|
|
32732
32941
|
...trackDatas.map((trackData) => {
|
|
32733
32942
|
return { id: 183 /* CueTrackPositions */, data: [
|
|
@@ -33786,24 +33995,26 @@ ${cue.notes ?? ""}`;
|
|
|
33786
33995
|
pesView.setUint8(6, 132);
|
|
33787
33996
|
pesView.setUint8(7, includeDts ? 192 : 128);
|
|
33788
33997
|
pesView.setUint8(8, headerDataLength);
|
|
33789
|
-
const
|
|
33998
|
+
const unwrappedPts = Math.round(queuedPacket.presentationTimestamp * TIMESCALE);
|
|
33999
|
+
const pts = modEuclid(unwrappedPts, TIMESTAMP_MODULUS);
|
|
33790
34000
|
ptsDtsBitstream.pos = 0;
|
|
33791
34001
|
ptsDtsBitstream.writeBits(4, includeDts ? 3 : 2);
|
|
33792
|
-
ptsDtsBitstream.writeBits(3, pts
|
|
34002
|
+
ptsDtsBitstream.writeBits(3, Math.floor(pts / 2 ** 30));
|
|
33793
34003
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33794
|
-
ptsDtsBitstream.writeBits(15, pts
|
|
34004
|
+
ptsDtsBitstream.writeBits(15, Math.floor(pts / 2 ** 15) % 2 ** 15);
|
|
33795
34005
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33796
|
-
ptsDtsBitstream.writeBits(15, pts
|
|
34006
|
+
ptsDtsBitstream.writeBits(15, pts % 2 ** 15);
|
|
33797
34007
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33798
34008
|
if (includeDts) {
|
|
33799
34009
|
assert(queuedPacket.decodeTimestamp !== null);
|
|
33800
|
-
const
|
|
34010
|
+
const unwrappedDts = Math.round(queuedPacket.decodeTimestamp * TIMESCALE);
|
|
34011
|
+
const dts = modEuclid(unwrappedDts, TIMESTAMP_MODULUS);
|
|
33801
34012
|
ptsDtsBitstream.writeBits(4, 1);
|
|
33802
|
-
ptsDtsBitstream.writeBits(3, dts
|
|
34013
|
+
ptsDtsBitstream.writeBits(3, Math.floor(dts / 2 ** 30));
|
|
33803
34014
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33804
|
-
ptsDtsBitstream.writeBits(15, dts
|
|
34015
|
+
ptsDtsBitstream.writeBits(15, Math.floor(dts / 2 ** 15) % 2 ** 15);
|
|
33805
34016
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33806
|
-
ptsDtsBitstream.writeBits(15, dts
|
|
34017
|
+
ptsDtsBitstream.writeBits(15, dts % 2 ** 15);
|
|
33807
34018
|
ptsDtsBitstream.writeBits(1, 1);
|
|
33808
34019
|
}
|
|
33809
34020
|
const totalLength = pesHeaderBuffer.length + queuedPacket.data.length;
|
|
@@ -35390,8 +35601,8 @@ ${cue.notes ?? ""}`;
|
|
|
35390
35601
|
* to respect writer and encoder backpressure.
|
|
35391
35602
|
*/
|
|
35392
35603
|
add(timestamp, duration = 0, encodeOptions) {
|
|
35393
|
-
if (!Number.isFinite(timestamp)
|
|
35394
|
-
throw new TypeError("timestamp must be a
|
|
35604
|
+
if (!Number.isFinite(timestamp)) {
|
|
35605
|
+
throw new TypeError("timestamp must be a finite number.");
|
|
35395
35606
|
}
|
|
35396
35607
|
if (!Number.isFinite(duration) || duration < 0) {
|
|
35397
35608
|
throw new TypeError("duration must be a non-negative number.");
|
|
@@ -36236,19 +36447,24 @@ ${cue.notes ?? ""}`;
|
|
|
36236
36447
|
var AudioBufferSource = class extends AudioSource {
|
|
36237
36448
|
/**
|
|
36238
36449
|
* Creates a new {@link AudioBufferSource} whose `AudioBuffer` instances are encoded according to the specified
|
|
36239
|
-
* {@link AudioEncodingConfig}.
|
|
36450
|
+
* {@link AudioEncodingConfig} and {@link AudioBufferSourceOptions}.
|
|
36240
36451
|
*/
|
|
36241
|
-
constructor(encodingConfig) {
|
|
36452
|
+
constructor(encodingConfig, options = {}) {
|
|
36242
36453
|
validateAudioEncodingConfig(encodingConfig);
|
|
36454
|
+
if (typeof options !== "object" || !options) {
|
|
36455
|
+
throw new TypeError("options must be an object.");
|
|
36456
|
+
}
|
|
36457
|
+
if (options.startTimestamp !== void 0 && !Number.isFinite(options.startTimestamp)) {
|
|
36458
|
+
throw new TypeError("options.startTimestamp, when provided, must be a finite number.");
|
|
36459
|
+
}
|
|
36243
36460
|
super(encodingConfig.codec);
|
|
36244
|
-
/** @internal */
|
|
36245
|
-
this._accumulatedTime = 0;
|
|
36246
36461
|
this._encoder = new AudioEncoderWrapper(this, encodingConfig);
|
|
36462
|
+
this._accumulatedTime = options.startTimestamp ?? 0;
|
|
36247
36463
|
}
|
|
36248
36464
|
/**
|
|
36249
|
-
* Converts an AudioBuffer to audio samples, encodes them and adds them to the output. The first AudioBuffer will
|
|
36250
|
-
* be played at timestamp 0, and
|
|
36251
|
-
*
|
|
36465
|
+
* Converts an AudioBuffer to audio samples, encodes them and adds them to the output. The first `AudioBuffer` will
|
|
36466
|
+
* be played at the configured start timestamp (the default is 0), and each subsequent `AudioBuffer` will be placed
|
|
36467
|
+
* directly after the previous one.
|
|
36252
36468
|
*
|
|
36253
36469
|
* @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise
|
|
36254
36470
|
* to respect writer and encoder backpressure.
|
|
@@ -37830,6 +38046,9 @@ ${cue.notes ?? ""}`;
|
|
|
37830
38046
|
get supportsTimestampedMediaData() {
|
|
37831
38047
|
return true;
|
|
37832
38048
|
}
|
|
38049
|
+
get negativeTimestampSupport() {
|
|
38050
|
+
return "full";
|
|
38051
|
+
}
|
|
37833
38052
|
/** @internal */
|
|
37834
38053
|
_createMuxer(output) {
|
|
37835
38054
|
return new IsobmffMuxer2(output, this);
|
|
@@ -38004,6 +38223,9 @@ ${cue.notes ?? ""}`;
|
|
|
38004
38223
|
get supportsTimestampedMediaData() {
|
|
38005
38224
|
return true;
|
|
38006
38225
|
}
|
|
38226
|
+
get negativeTimestampSupport() {
|
|
38227
|
+
return "prefer-non-negative";
|
|
38228
|
+
}
|
|
38007
38229
|
};
|
|
38008
38230
|
var WebMOutputFormat = class extends MkvOutputFormat2 {
|
|
38009
38231
|
/** Creates a new {@link WebMOutputFormat} configured with the specified `options`. */
|
|
@@ -38081,6 +38303,9 @@ ${cue.notes ?? ""}`;
|
|
|
38081
38303
|
get supportsTimestampedMediaData() {
|
|
38082
38304
|
return false;
|
|
38083
38305
|
}
|
|
38306
|
+
get negativeTimestampSupport() {
|
|
38307
|
+
return null;
|
|
38308
|
+
}
|
|
38084
38309
|
};
|
|
38085
38310
|
var WavOutputFormat = class extends OutputFormat {
|
|
38086
38311
|
/** Creates a new {@link WavOutputFormat} configured with the specified `options`. */
|
|
@@ -38135,6 +38360,9 @@ ${cue.notes ?? ""}`;
|
|
|
38135
38360
|
get supportsTimestampedMediaData() {
|
|
38136
38361
|
return false;
|
|
38137
38362
|
}
|
|
38363
|
+
get negativeTimestampSupport() {
|
|
38364
|
+
return null;
|
|
38365
|
+
}
|
|
38138
38366
|
};
|
|
38139
38367
|
var OggOutputFormat = class extends OutputFormat {
|
|
38140
38368
|
/** Creates a new {@link OggOutputFormat} configured with the specified `options`. */
|
|
@@ -38185,6 +38413,9 @@ ${cue.notes ?? ""}`;
|
|
|
38185
38413
|
get supportsTimestampedMediaData() {
|
|
38186
38414
|
return false;
|
|
38187
38415
|
}
|
|
38416
|
+
get negativeTimestampSupport() {
|
|
38417
|
+
return null;
|
|
38418
|
+
}
|
|
38188
38419
|
};
|
|
38189
38420
|
var AdtsOutputFormat = class extends OutputFormat {
|
|
38190
38421
|
/** Creates a new {@link AdtsOutputFormat} configured with the specified `options`. */
|
|
@@ -38229,6 +38460,9 @@ ${cue.notes ?? ""}`;
|
|
|
38229
38460
|
get supportsTimestampedMediaData() {
|
|
38230
38461
|
return false;
|
|
38231
38462
|
}
|
|
38463
|
+
get negativeTimestampSupport() {
|
|
38464
|
+
return null;
|
|
38465
|
+
}
|
|
38232
38466
|
};
|
|
38233
38467
|
var FlacOutputFormat = class extends OutputFormat {
|
|
38234
38468
|
/** Creates a new {@link FlacOutputFormat} configured with the specified `options`. */
|
|
@@ -38273,6 +38507,9 @@ ${cue.notes ?? ""}`;
|
|
|
38273
38507
|
get supportsTimestampedMediaData() {
|
|
38274
38508
|
return false;
|
|
38275
38509
|
}
|
|
38510
|
+
get negativeTimestampSupport() {
|
|
38511
|
+
return null;
|
|
38512
|
+
}
|
|
38276
38513
|
};
|
|
38277
38514
|
var MpegTsOutputFormat = class extends OutputFormat {
|
|
38278
38515
|
/** Creates a new {@link MpegTsOutputFormat} configured with the specified `options`. */
|
|
@@ -38323,6 +38560,9 @@ ${cue.notes ?? ""}`;
|
|
|
38323
38560
|
get supportsTimestampedMediaData() {
|
|
38324
38561
|
return true;
|
|
38325
38562
|
}
|
|
38563
|
+
get negativeTimestampSupport() {
|
|
38564
|
+
return "prefer-non-negative";
|
|
38565
|
+
}
|
|
38326
38566
|
};
|
|
38327
38567
|
var HlsOutputFormat = class extends OutputFormat {
|
|
38328
38568
|
/** Creates a new {@link HlsOutputFormat} configured with the specified `options`. */
|
|
@@ -38416,6 +38656,19 @@ ${cue.notes ?? ""}`;
|
|
|
38416
38656
|
get supportsTimestampedMediaData() {
|
|
38417
38657
|
return true;
|
|
38418
38658
|
}
|
|
38659
|
+
get negativeTimestampSupport() {
|
|
38660
|
+
const formats = toArray(this._options.segmentFormat);
|
|
38661
|
+
if (formats.some((format) => format.negativeTimestampSupport === "none")) {
|
|
38662
|
+
return "none";
|
|
38663
|
+
}
|
|
38664
|
+
if (formats.some((format) => format.negativeTimestampSupport === "prefer-non-negative")) {
|
|
38665
|
+
return "prefer-non-negative";
|
|
38666
|
+
}
|
|
38667
|
+
if (formats.some((format) => format.negativeTimestampSupport === "full")) {
|
|
38668
|
+
return "full";
|
|
38669
|
+
}
|
|
38670
|
+
return null;
|
|
38671
|
+
}
|
|
38419
38672
|
/** @internal */
|
|
38420
38673
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
38421
38674
|
_codecUnsupportedHint(codec) {
|
|
@@ -39154,15 +39407,16 @@ ${cue.notes ?? ""}`;
|
|
|
39154
39407
|
var Conversion = class _Conversion {
|
|
39155
39408
|
/** Creates a new Conversion instance (duh). */
|
|
39156
39409
|
constructor(options) {
|
|
39157
|
-
/**
|
|
39158
|
-
|
|
39159
|
-
|
|
39160
|
-
|
|
39161
|
-
|
|
39162
|
-
|
|
39163
|
-
|
|
39164
|
-
|
|
39165
|
-
|
|
39410
|
+
/** @internal */
|
|
39411
|
+
this._state = "idle";
|
|
39412
|
+
/** @internal */
|
|
39413
|
+
this._timestampOffset = 0;
|
|
39414
|
+
/** @internal */
|
|
39415
|
+
this._timestampOffsetAdjusted = false;
|
|
39416
|
+
/** @internal */
|
|
39417
|
+
this._copyTimestampPossible = /* @__PURE__ */ new Map();
|
|
39418
|
+
/** @internal */
|
|
39419
|
+
this._copyStartPackets = /* @__PURE__ */ new Map();
|
|
39166
39420
|
/** @internal */
|
|
39167
39421
|
this._nextOutputTrackId = 0;
|
|
39168
39422
|
/** @internal */
|
|
@@ -39233,6 +39487,22 @@ ${cue.notes ?? ""}`;
|
|
|
39233
39487
|
if (options.composable !== void 0 && typeof options.composable !== "boolean") {
|
|
39234
39488
|
throw new TypeError("options.composable, when provided, must be a boolean.");
|
|
39235
39489
|
}
|
|
39490
|
+
if (options.copy !== void 0 && options.copy !== false) {
|
|
39491
|
+
if (!options.copy || typeof options.copy !== "object") {
|
|
39492
|
+
throw new TypeError("options.copy, when provided, must be an object or false.");
|
|
39493
|
+
}
|
|
39494
|
+
if (options.copy.mode !== void 0 && !["forced", "preferred"].includes(options.copy.mode)) {
|
|
39495
|
+
throw new TypeError("options.copy.mode, when provided, must be 'forced' or 'preferred'.");
|
|
39496
|
+
}
|
|
39497
|
+
if (options.copy.shiftTolerance !== void 0 && (!isNumber(options.copy.shiftTolerance) || options.copy.shiftTolerance < 0)) {
|
|
39498
|
+
throw new TypeError("options.copy.shiftTolerance, when provided, must be a non-negative number.");
|
|
39499
|
+
}
|
|
39500
|
+
if (options.copy.boundaryPolicy !== void 0 && !["expand", "shrink"].includes(options.copy.boundaryPolicy)) {
|
|
39501
|
+
throw new TypeError(
|
|
39502
|
+
"options.copy.boundaryPolicy, when provided, must be 'expand' or 'shrink'."
|
|
39503
|
+
);
|
|
39504
|
+
}
|
|
39505
|
+
}
|
|
39236
39506
|
const composable = options.composable ?? false;
|
|
39237
39507
|
if (!composable) {
|
|
39238
39508
|
if (options.output.tracks.length > 0 || Object.keys(options.output._metadataTags).length > 0 || options.output.state !== "pending") {
|
|
@@ -39274,8 +39544,8 @@ ${cue.notes ?? ""}`;
|
|
|
39274
39544
|
if (options.trim?.start !== void 0 && !Number.isFinite(options.trim.start)) {
|
|
39275
39545
|
throw new TypeError("options.trim.start, when provided, must be a finite number.");
|
|
39276
39546
|
}
|
|
39277
|
-
if (options.trim?.end !== void 0 && !
|
|
39278
|
-
throw new TypeError("options.trim.end, when provided, must be a
|
|
39547
|
+
if (options.trim?.end !== void 0 && !isNumber(options.trim.end)) {
|
|
39548
|
+
throw new TypeError("options.trim.end, when provided, must be a number.");
|
|
39279
39549
|
}
|
|
39280
39550
|
if (options.trim?.start !== void 0 && options.trim.end !== void 0 && options.trim.start >= options.trim.end) {
|
|
39281
39551
|
throw new TypeError("options.trim.start must be less than options.trim.end.");
|
|
@@ -39290,10 +39560,24 @@ ${cue.notes ?? ""}`;
|
|
|
39290
39560
|
throw new TypeError("options.showWarnings, when provided, must be a boolean.");
|
|
39291
39561
|
}
|
|
39292
39562
|
this._options = options;
|
|
39563
|
+
this._copyMode = options.copy === false ? false : options.copy?.mode ?? "preferred";
|
|
39564
|
+
this._copyTimestampShiftTolerance = options.copy === false ? 0 : options.copy?.shiftTolerance ?? 0;
|
|
39565
|
+
this._copyBoundaryPolicy = options.copy === false ? "expand" : options.copy?.boundaryPolicy ?? "expand";
|
|
39293
39566
|
this._composable = composable;
|
|
39294
39567
|
this.input = options.input;
|
|
39295
39568
|
this.output = options.output;
|
|
39296
39569
|
}
|
|
39570
|
+
/**
|
|
39571
|
+
* The current state of the conversion.
|
|
39572
|
+
*
|
|
39573
|
+
* - `'idle'`: The conversion is not currently executing and isn't done; `execute` can be called.
|
|
39574
|
+
* - `'executing'`: A call to `execute` is currently running.
|
|
39575
|
+
* - `'canceled'`: The conversion has been canceled and can no longer be executed.
|
|
39576
|
+
* - `'done'`: The conversion has run to completion. Subsequent calls to `execute` do nothing.
|
|
39577
|
+
*/
|
|
39578
|
+
get state() {
|
|
39579
|
+
return this._state;
|
|
39580
|
+
}
|
|
39297
39581
|
/** Initializes a new conversion process without starting the conversion. */
|
|
39298
39582
|
static async init(options) {
|
|
39299
39583
|
const conversion = new _Conversion(options);
|
|
@@ -39400,6 +39684,7 @@ ${cue.notes ?? ""}`;
|
|
|
39400
39684
|
);
|
|
39401
39685
|
}
|
|
39402
39686
|
this._endTimestamp = Math.max(this._options.trim?.end ?? Infinity, this._startTimestamp);
|
|
39687
|
+
this._timestampOffset = -this._startTimestamp;
|
|
39403
39688
|
for (let i = 0; i < filteredTracks.length; i++) {
|
|
39404
39689
|
const track = filteredTracks[i];
|
|
39405
39690
|
const options = filteredTrackOptions[i];
|
|
@@ -39459,7 +39744,7 @@ ${cue.notes ?? ""}`;
|
|
|
39459
39744
|
}
|
|
39460
39745
|
const inputAndOutputFormatMatch = inputFormat.mimeType === this.output.format.mimeType;
|
|
39461
39746
|
const rawTagsAreUnchanged = inputTags.raw === outputTags.raw;
|
|
39462
|
-
if (
|
|
39747
|
+
if (rawTagsAreUnchanged && !inputAndOutputFormatMatch) {
|
|
39463
39748
|
delete outputTags.raw;
|
|
39464
39749
|
}
|
|
39465
39750
|
this.output.setMetadataTags(outputTags);
|
|
@@ -39578,13 +39863,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39578
39863
|
"Cannot execute this conversion because its output configuration is invalid. Make sure to always check the isValid field before executing a conversion.\n" + this._getInvalidityExplanation().join("")
|
|
39579
39864
|
);
|
|
39580
39865
|
}
|
|
39581
|
-
if (this.
|
|
39866
|
+
if (this._state === "executing") {
|
|
39582
39867
|
throw new Error("Cannot call execute() while a previous call to execute() is still running.");
|
|
39583
39868
|
}
|
|
39584
|
-
if (this.
|
|
39869
|
+
if (this._state === "canceled") {
|
|
39585
39870
|
throw new ConversionCanceledError();
|
|
39586
39871
|
}
|
|
39587
|
-
if (this.
|
|
39872
|
+
if (this._state === "done") {
|
|
39588
39873
|
return;
|
|
39589
39874
|
}
|
|
39590
39875
|
if (this._composable && this.output.state === "pending") {
|
|
@@ -39592,11 +39877,11 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39592
39877
|
"A composable conversion requires the output to be started. Call start() on the output before executing the conversion."
|
|
39593
39878
|
);
|
|
39594
39879
|
}
|
|
39595
|
-
this.
|
|
39880
|
+
this._state = "executing";
|
|
39596
39881
|
this._executionUntil = options.until ?? Infinity;
|
|
39597
39882
|
this._pauseRequested = options.pauseSignal?.aborted ?? false;
|
|
39598
39883
|
const onPause = () => {
|
|
39599
|
-
if (this.
|
|
39884
|
+
if (this._state !== "executing") {
|
|
39600
39885
|
return;
|
|
39601
39886
|
}
|
|
39602
39887
|
this._pauseRequested = true;
|
|
@@ -39646,18 +39931,18 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39646
39931
|
try {
|
|
39647
39932
|
await Promise.all(this._trackPumps.map((x) => x.resolvers.promise));
|
|
39648
39933
|
} catch (error) {
|
|
39649
|
-
if (this.
|
|
39934
|
+
if (this._state !== "canceled") {
|
|
39650
39935
|
void this.cancel();
|
|
39651
39936
|
}
|
|
39652
39937
|
throw error;
|
|
39653
39938
|
} finally {
|
|
39654
39939
|
options.pauseSignal?.removeEventListener("abort", onPause);
|
|
39655
39940
|
}
|
|
39656
|
-
if (this.
|
|
39941
|
+
if (this._state === "canceled") {
|
|
39657
39942
|
throw new ConversionCanceledError();
|
|
39658
39943
|
}
|
|
39659
39944
|
const isDone = this._trackPumps.every((x) => x.done);
|
|
39660
|
-
this.
|
|
39945
|
+
this._state = isDone ? "done" : "idle";
|
|
39661
39946
|
if (isDone) {
|
|
39662
39947
|
if (!this._composable) {
|
|
39663
39948
|
await this.output.finalize();
|
|
@@ -39673,14 +39958,14 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39673
39958
|
* Does nothing if the conversion is already complete.
|
|
39674
39959
|
*/
|
|
39675
39960
|
async cancel() {
|
|
39676
|
-
if (this.
|
|
39961
|
+
if (this._state === "done") {
|
|
39677
39962
|
return;
|
|
39678
39963
|
}
|
|
39679
|
-
if (this.
|
|
39964
|
+
if (this._state === "canceled") {
|
|
39680
39965
|
Logging._warn("Conversion already canceled.");
|
|
39681
39966
|
return;
|
|
39682
39967
|
}
|
|
39683
|
-
this.
|
|
39968
|
+
this._state = "canceled";
|
|
39684
39969
|
for (const pump of this._trackPumps) {
|
|
39685
39970
|
pump.wake?.();
|
|
39686
39971
|
}
|
|
@@ -39726,10 +40011,63 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39726
40011
|
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
39727
40012
|
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
39728
40013
|
}
|
|
39729
|
-
const firstTimestamp = await track.getFirstTimestamp();
|
|
39730
40014
|
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
|
39731
|
-
const needsTranscode = !!trackOptions.forceTranscode || firstTimestamp < this._startTimestamp || !!trackOptions.frameRate || trackOptions.keyFrameInterval !== void 0 || trackOptions.process !== void 0 || trackOptions.quality !== void 0 || trackOptions.bitrate !== void 0 || !videoCodecs.includes(sourceCodec) || trackOptions.codec && trackOptions.codec !== sourceCodec || width !== originalWidth || height !== originalHeight || totalRotation !== 0 && !canUseRotationMetadata || !!crop;
|
|
39732
40015
|
const alpha = trackOptions.alpha ?? "discard";
|
|
40016
|
+
let needsTranscode = !this._copyMode || !!trackOptions.forceTranscode || !!trackOptions.frameRate || trackOptions.keyFrameInterval !== void 0 || trackOptions.process !== void 0 || trackOptions.quality !== void 0 || trackOptions.bitrate !== void 0 || !videoCodecs.includes(sourceCodec) || trackOptions.codec && trackOptions.codec !== sourceCodec || width !== originalWidth || height !== originalHeight || totalRotation !== 0 && !canUseRotationMetadata || !!crop;
|
|
40017
|
+
let copyStartPacket = null;
|
|
40018
|
+
if (!needsTranscode) {
|
|
40019
|
+
const sink = new EncodedPacketSink(track);
|
|
40020
|
+
let startPacket = await sink.getKeyPacket(this._startTimestamp, { verifyKeyPackets: true }) ?? await sink.getFirstKeyPacket({ verifyKeyPackets: true });
|
|
40021
|
+
if (startPacket && startPacket.timestamp < this._startTimestamp && startPacket.timestamp + startPacket.duration <= this._startTimestamp && this._copyBoundaryPolicy === "shrink") {
|
|
40022
|
+
startPacket = await sink.getNextKeyPacket(startPacket, { verifyKeyPackets: true });
|
|
40023
|
+
}
|
|
40024
|
+
copyStartPacket = startPacket;
|
|
40025
|
+
if (startPacket) {
|
|
40026
|
+
const effectiveStartTimestamp = this._copyBoundaryPolicy === "shrink" ? Math.max(startPacket.timestamp, this._startTimestamp) : startPacket.timestamp;
|
|
40027
|
+
if (!this.output.format.supportsTimestampedMediaData) {
|
|
40028
|
+
if (this._timestampOffsetAdjusted) {
|
|
40029
|
+
const isValid = effectiveStartTimestamp + this._timestampOffset === 0;
|
|
40030
|
+
if (!isValid) {
|
|
40031
|
+
needsTranscode = true;
|
|
40032
|
+
}
|
|
40033
|
+
} else {
|
|
40034
|
+
const correction = clamp(
|
|
40035
|
+
this._startTimestamp - effectiveStartTimestamp,
|
|
40036
|
+
-this._copyTimestampShiftTolerance,
|
|
40037
|
+
this._copyTimestampShiftTolerance
|
|
40038
|
+
);
|
|
40039
|
+
const shiftedStartTimestamp = effectiveStartTimestamp + correction;
|
|
40040
|
+
const isValid = shiftedStartTimestamp === this._startTimestamp;
|
|
40041
|
+
if (isValid) {
|
|
40042
|
+
this._timestampOffset = -this._startTimestamp + correction;
|
|
40043
|
+
this._timestampOffsetAdjusted = true;
|
|
40044
|
+
} else {
|
|
40045
|
+
needsTranscode = true;
|
|
40046
|
+
}
|
|
40047
|
+
}
|
|
40048
|
+
} else if (this.output.format.negativeTimestampSupport !== "full" && effectiveStartTimestamp < this._startTimestamp) {
|
|
40049
|
+
const correction = Math.min(
|
|
40050
|
+
this._startTimestamp - effectiveStartTimestamp,
|
|
40051
|
+
this._copyTimestampShiftTolerance
|
|
40052
|
+
);
|
|
40053
|
+
const shiftedStartTimestamp = effectiveStartTimestamp + correction;
|
|
40054
|
+
const isValid = shiftedStartTimestamp >= this._startTimestamp || this.output.format.negativeTimestampSupport === "prefer-non-negative" && this._copyMode === "forced";
|
|
40055
|
+
if (isValid) {
|
|
40056
|
+
this._timestampOffset = Math.max(this._timestampOffset, -this._startTimestamp + correction);
|
|
40057
|
+
} else {
|
|
40058
|
+
needsTranscode = true;
|
|
40059
|
+
}
|
|
40060
|
+
}
|
|
40061
|
+
}
|
|
40062
|
+
}
|
|
40063
|
+
if (needsTranscode && this._copyMode === "forced") {
|
|
40064
|
+
this.discardedTracks.push({
|
|
40065
|
+
track,
|
|
40066
|
+
reason: "cannot_copy",
|
|
40067
|
+
trackOptions
|
|
40068
|
+
});
|
|
40069
|
+
return;
|
|
40070
|
+
}
|
|
39733
40071
|
if (!needsTranscode) {
|
|
39734
40072
|
const source = new EncodedVideoPacketSource(sourceCodec);
|
|
39735
40073
|
videoSource = source;
|
|
@@ -39737,18 +40075,51 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39737
40075
|
const sink = new EncodedPacketSink(track);
|
|
39738
40076
|
const decoderConfig = await track.getDecoderConfig();
|
|
39739
40077
|
const meta = { decoderConfig: decoderConfig ?? void 0 };
|
|
39740
|
-
for await (const packet of sink.packets(
|
|
39741
|
-
|
|
40078
|
+
if (copyStartPacket) for await (const packet of sink.packets(
|
|
40079
|
+
copyStartPacket,
|
|
40080
|
+
void 0,
|
|
40081
|
+
{ verifyKeyPackets: true }
|
|
40082
|
+
)) {
|
|
40083
|
+
if (this._state === "canceled") {
|
|
39742
40084
|
break;
|
|
39743
40085
|
}
|
|
39744
40086
|
if (packet.timestamp >= this._endTimestamp) {
|
|
39745
|
-
|
|
40087
|
+
if (this._copyBoundaryPolicy === "shrink") {
|
|
40088
|
+
break;
|
|
40089
|
+
} else {
|
|
40090
|
+
let current = packet;
|
|
40091
|
+
let found = false;
|
|
40092
|
+
const lookahead = 6;
|
|
40093
|
+
for (let i = 0; i < lookahead; i++) {
|
|
40094
|
+
const next = await sink.getNextPacket(current, { metadataOnly: true });
|
|
40095
|
+
if (!next) {
|
|
40096
|
+
break;
|
|
40097
|
+
}
|
|
40098
|
+
if (next.timestamp < this._endTimestamp) {
|
|
40099
|
+
found = true;
|
|
40100
|
+
break;
|
|
40101
|
+
}
|
|
40102
|
+
current = next;
|
|
40103
|
+
}
|
|
40104
|
+
if (!found) {
|
|
40105
|
+
break;
|
|
40106
|
+
}
|
|
40107
|
+
}
|
|
39746
40108
|
}
|
|
40109
|
+
let packetStartTimestamp = packet.timestamp;
|
|
40110
|
+
let packetEndTimestamp = packet.timestamp + packet.duration;
|
|
40111
|
+
if (this._copyBoundaryPolicy === "shrink") {
|
|
40112
|
+
packetStartTimestamp = Math.max(packetStartTimestamp, this._startTimestamp);
|
|
40113
|
+
packetEndTimestamp = Math.min(packetEndTimestamp, this._endTimestamp);
|
|
40114
|
+
packetEndTimestamp = Math.max(packetEndTimestamp, packetStartTimestamp);
|
|
40115
|
+
}
|
|
40116
|
+
packetStartTimestamp += this._timestampOffset;
|
|
40117
|
+
packetEndTimestamp += this._timestampOffset;
|
|
39747
40118
|
const modifiedPacket = packet.clone({
|
|
39748
|
-
timestamp:
|
|
40119
|
+
timestamp: packetStartTimestamp,
|
|
40120
|
+
duration: packetEndTimestamp - packetStartTimestamp,
|
|
39749
40121
|
sideData: alpha === "discard" ? {} : packet.sideData
|
|
39750
40122
|
});
|
|
39751
|
-
assert(modifiedPacket.timestamp >= 0);
|
|
39752
40123
|
this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
|
|
39753
40124
|
await source.add(modifiedPacket, meta);
|
|
39754
40125
|
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
@@ -39809,7 +40180,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39809
40180
|
tempOutput.addVideoTrack(tempSource);
|
|
39810
40181
|
await tempOutput.start();
|
|
39811
40182
|
const sink = new VideoSampleSink(track);
|
|
39812
|
-
const firstSample = __using(_stack, await sink.getSample(
|
|
40183
|
+
const firstSample = __using(_stack, await sink.getSample(await track.getFirstTimestamp()));
|
|
39813
40184
|
if (firstSample) {
|
|
39814
40185
|
try {
|
|
39815
40186
|
await tempSource.add(firstSample);
|
|
@@ -39860,11 +40231,16 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39860
40231
|
var _stack2 = [];
|
|
39861
40232
|
try {
|
|
39862
40233
|
const sample = __using(_stack2, _sample);
|
|
39863
|
-
if (this.
|
|
40234
|
+
if (this._state === "canceled") {
|
|
39864
40235
|
break;
|
|
39865
40236
|
}
|
|
39866
|
-
const
|
|
39867
|
-
sample.
|
|
40237
|
+
const clampedStartTimestamp = Math.max(this._startTimestamp, sample.timestamp);
|
|
40238
|
+
const clampedEndTimestamp = Math.min(this._endTimestamp, sample.timestamp + sample.duration);
|
|
40239
|
+
if (clampedStartTimestamp >= clampedEndTimestamp) {
|
|
40240
|
+
continue;
|
|
40241
|
+
}
|
|
40242
|
+
sample.setTimestamp(clampedStartTimestamp + this._timestampOffset);
|
|
40243
|
+
sample.setDuration(clampedEndTimestamp - clampedStartTimestamp);
|
|
39868
40244
|
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
39869
40245
|
await source.add(sample);
|
|
39870
40246
|
sample.close();
|
|
@@ -39889,12 +40265,14 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39889
40265
|
ownGroup = new OutputTrackGroup();
|
|
39890
40266
|
}
|
|
39891
40267
|
const videoTrackLanguageCode = await track.getLanguageCode();
|
|
40268
|
+
const trackName = await track.getName();
|
|
40269
|
+
const trackDisposition = await track.getDisposition();
|
|
39892
40270
|
this.output.addVideoTrack(videoSource, {
|
|
39893
40271
|
frameRate: trackOptions.frameRate,
|
|
39894
40272
|
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
|
|
39895
40273
|
languageCode: isIso639Dash2LanguageCode(videoTrackLanguageCode) ? videoTrackLanguageCode : void 0,
|
|
39896
|
-
name:
|
|
39897
|
-
disposition:
|
|
40274
|
+
name: trackName ?? void 0,
|
|
40275
|
+
disposition: trackDisposition,
|
|
39898
40276
|
rotation: outputTrackRotation,
|
|
39899
40277
|
group: ownGroup ?? trackOptions.group
|
|
39900
40278
|
});
|
|
@@ -39916,30 +40294,93 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
39916
40294
|
let audioSource;
|
|
39917
40295
|
const originalNumberOfChannels = await track.getNumberOfChannels();
|
|
39918
40296
|
const originalSampleRate = await track.getSampleRate();
|
|
39919
|
-
const firstTimestamp = await track.getFirstTimestamp();
|
|
39920
40297
|
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
|
39921
40298
|
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
|
39922
|
-
const needsTrimming = firstTimestamp < this._startTimestamp;
|
|
39923
|
-
let needsPadding = firstTimestamp > this._startTimestamp && !this.output.format.supportsTimestampedMediaData;
|
|
39924
40299
|
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
|
39925
|
-
|
|
40300
|
+
let needsTranscode = !this._copyMode || !!trackOptions.forceTranscode || !!trackOptions.quality || !!trackOptions.bitrate || numberOfChannels !== originalNumberOfChannels || sampleRate !== originalSampleRate || !audioCodecs.includes(sourceCodec) || !!trackOptions.codec && trackOptions.codec !== sourceCodec || trackOptions.process !== void 0 || trackOptions.sampleFormat !== void 0;
|
|
40301
|
+
let copyStartPacket = null;
|
|
40302
|
+
if (!needsTranscode) {
|
|
40303
|
+
const sink = new EncodedPacketSink(track);
|
|
40304
|
+
let startPacket = await sink.getKeyPacket(this._startTimestamp) ?? await sink.getFirstKeyPacket();
|
|
40305
|
+
if (startPacket && (this._copyBoundaryPolicy === "shrink" && startPacket.timestamp < this._startTimestamp || this._copyBoundaryPolicy === "expand" && startPacket.timestamp + startPacket.duration <= this._startTimestamp)) {
|
|
40306
|
+
startPacket = await sink.getNextKeyPacket(startPacket);
|
|
40307
|
+
}
|
|
40308
|
+
const hasDecoderWarmup = NON_PCM_AUDIO_CODECS.includes(sourceCodec) && sourceCodec !== "flac";
|
|
40309
|
+
if (startPacket && this._copyBoundaryPolicy === "expand" && hasDecoderWarmup) {
|
|
40310
|
+
const previousPacket = await sink.getKeyPacket(
|
|
40311
|
+
startPacket.timestamp - 1 / await track.getTimeResolution()
|
|
40312
|
+
);
|
|
40313
|
+
if (previousPacket) {
|
|
40314
|
+
startPacket = previousPacket;
|
|
40315
|
+
}
|
|
40316
|
+
}
|
|
40317
|
+
copyStartPacket = startPacket;
|
|
40318
|
+
if (startPacket) {
|
|
40319
|
+
if (!this.output.format.supportsTimestampedMediaData) {
|
|
40320
|
+
if (this._timestampOffsetAdjusted) {
|
|
40321
|
+
const isValid = startPacket.timestamp + this._timestampOffset === 0;
|
|
40322
|
+
if (!isValid) {
|
|
40323
|
+
needsTranscode = true;
|
|
40324
|
+
}
|
|
40325
|
+
} else {
|
|
40326
|
+
const correction = clamp(
|
|
40327
|
+
this._startTimestamp - startPacket.timestamp,
|
|
40328
|
+
-this._copyTimestampShiftTolerance,
|
|
40329
|
+
this._copyTimestampShiftTolerance
|
|
40330
|
+
);
|
|
40331
|
+
const shiftedStartTimestamp = startPacket.timestamp + correction;
|
|
40332
|
+
const isValid = shiftedStartTimestamp === this._startTimestamp;
|
|
40333
|
+
if (isValid) {
|
|
40334
|
+
this._timestampOffset = -this._startTimestamp + correction;
|
|
40335
|
+
this._timestampOffsetAdjusted = true;
|
|
40336
|
+
} else {
|
|
40337
|
+
needsTranscode = true;
|
|
40338
|
+
}
|
|
40339
|
+
}
|
|
40340
|
+
} else if (this.output.format.negativeTimestampSupport !== "full" && startPacket.timestamp < this._startTimestamp) {
|
|
40341
|
+
const correction = Math.min(
|
|
40342
|
+
this._startTimestamp - startPacket.timestamp,
|
|
40343
|
+
this._copyTimestampShiftTolerance
|
|
40344
|
+
);
|
|
40345
|
+
const shiftedStartTimestamp = startPacket.timestamp + correction;
|
|
40346
|
+
const isValid = shiftedStartTimestamp >= this._startTimestamp || this.output.format.negativeTimestampSupport === "prefer-non-negative" && this._copyMode === "forced";
|
|
40347
|
+
if (isValid) {
|
|
40348
|
+
this._timestampOffset = Math.max(this._timestampOffset, -this._startTimestamp + correction);
|
|
40349
|
+
} else {
|
|
40350
|
+
needsTranscode = true;
|
|
40351
|
+
}
|
|
40352
|
+
}
|
|
40353
|
+
}
|
|
40354
|
+
}
|
|
40355
|
+
if (needsTranscode && this._copyMode === "forced") {
|
|
40356
|
+
this.discardedTracks.push({
|
|
40357
|
+
track,
|
|
40358
|
+
reason: "cannot_copy",
|
|
40359
|
+
trackOptions
|
|
40360
|
+
});
|
|
40361
|
+
return;
|
|
40362
|
+
}
|
|
40363
|
+
if (!needsTranscode) {
|
|
39926
40364
|
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
39927
40365
|
audioSource = source;
|
|
39928
40366
|
this._registerTrackPump(async (pump) => {
|
|
39929
40367
|
const sink = new EncodedPacketSink(track);
|
|
39930
40368
|
const decoderConfig = await track.getDecoderConfig();
|
|
39931
40369
|
const meta = { decoderConfig: decoderConfig ?? void 0 };
|
|
39932
|
-
for await (const packet of sink.packets()) {
|
|
39933
|
-
if (this.
|
|
40370
|
+
if (copyStartPacket) for await (const packet of sink.packets(copyStartPacket)) {
|
|
40371
|
+
if (this._state === "canceled") {
|
|
39934
40372
|
break;
|
|
39935
40373
|
}
|
|
39936
40374
|
if (packet.timestamp >= this._endTimestamp) {
|
|
39937
40375
|
break;
|
|
39938
40376
|
}
|
|
40377
|
+
if (this._copyBoundaryPolicy === "shrink" && packet.timestamp + packet.duration > this._endTimestamp) {
|
|
40378
|
+
break;
|
|
40379
|
+
}
|
|
39939
40380
|
const modifiedPacket = packet.clone({
|
|
39940
|
-
timestamp: packet.timestamp
|
|
40381
|
+
timestamp: packet.timestamp + this._timestampOffset,
|
|
40382
|
+
duration: packet.duration
|
|
39941
40383
|
});
|
|
39942
|
-
assert(modifiedPacket.timestamp >= 0);
|
|
39943
40384
|
this._reportProgress(outputTrackId, modifiedPacket.timestamp + modifiedPacket.duration);
|
|
39944
40385
|
await source.add(modifiedPacket, meta);
|
|
39945
40386
|
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
@@ -40015,6 +40456,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40015
40456
|
const source = new AudioSampleSource(encodingConfig);
|
|
40016
40457
|
audioSource = source;
|
|
40017
40458
|
this._registerTrackPump(async (pump) => {
|
|
40459
|
+
let needsPadding = null;
|
|
40018
40460
|
const sink = new AudioSampleSink(track);
|
|
40019
40461
|
for await (var _sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
40020
40462
|
var _stack3 = [];
|
|
@@ -40022,13 +40464,42 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40022
40464
|
const sample = __using(_stack3, _sample);
|
|
40023
40465
|
var _stack2 = [];
|
|
40024
40466
|
try {
|
|
40025
|
-
if (this.
|
|
40467
|
+
if (this._state === "canceled") {
|
|
40026
40468
|
break;
|
|
40027
40469
|
}
|
|
40470
|
+
let startFrame = 0;
|
|
40471
|
+
let endFrame = sample.numberOfFrames;
|
|
40472
|
+
if (sample.timestamp < this._startTimestamp) {
|
|
40473
|
+
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
|
40474
|
+
}
|
|
40475
|
+
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
|
40476
|
+
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
|
40477
|
+
}
|
|
40478
|
+
if (startFrame >= endFrame) {
|
|
40479
|
+
sample.close();
|
|
40480
|
+
continue;
|
|
40481
|
+
}
|
|
40482
|
+
let finalSampleLet;
|
|
40483
|
+
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
|
40484
|
+
const trimmedSample = sample.trim(startFrame, endFrame);
|
|
40485
|
+
sample.close();
|
|
40486
|
+
finalSampleLet = trimmedSample;
|
|
40487
|
+
if (trimmedSample.numberOfFrames === 0) {
|
|
40488
|
+
trimmedSample.close();
|
|
40489
|
+
continue;
|
|
40490
|
+
}
|
|
40491
|
+
} else {
|
|
40492
|
+
finalSampleLet = sample;
|
|
40493
|
+
}
|
|
40494
|
+
const finalSample = __using(_stack2, finalSampleLet);
|
|
40495
|
+
finalSample.setTimestamp(finalSample.timestamp + this._timestampOffset);
|
|
40496
|
+
if (needsPadding === null) {
|
|
40497
|
+
needsPadding = finalSample.timestamp > 0 && !this.output.format.supportsTimestampedMediaData;
|
|
40498
|
+
}
|
|
40028
40499
|
if (needsPadding) {
|
|
40029
40500
|
var _stack = [];
|
|
40030
40501
|
try {
|
|
40031
|
-
const paddingLength =
|
|
40502
|
+
const paddingLength = finalSample.timestamp;
|
|
40032
40503
|
const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
|
|
40033
40504
|
const bytesPerSample = getBytesPerSample(sample.format);
|
|
40034
40505
|
const data = new Uint8Array(bytesPerSample * paddingLengthSamples * originalNumberOfChannels);
|
|
@@ -40058,28 +40529,6 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40058
40529
|
__callDispose(_stack, _error, _hasError);
|
|
40059
40530
|
}
|
|
40060
40531
|
}
|
|
40061
|
-
let startFrame = 0;
|
|
40062
|
-
let endFrame = sample.numberOfFrames;
|
|
40063
|
-
if (sample.timestamp < this._startTimestamp) {
|
|
40064
|
-
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
|
40065
|
-
}
|
|
40066
|
-
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
|
40067
|
-
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
|
40068
|
-
}
|
|
40069
|
-
let finalSampleLet;
|
|
40070
|
-
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
|
40071
|
-
const trimmedSample = sample.trim(startFrame, endFrame);
|
|
40072
|
-
sample.close();
|
|
40073
|
-
finalSampleLet = trimmedSample;
|
|
40074
|
-
if (trimmedSample.numberOfFrames === 0) {
|
|
40075
|
-
trimmedSample.close();
|
|
40076
|
-
continue;
|
|
40077
|
-
}
|
|
40078
|
-
} else {
|
|
40079
|
-
finalSampleLet = sample;
|
|
40080
|
-
}
|
|
40081
|
-
const finalSample = __using(_stack2, finalSampleLet);
|
|
40082
|
-
finalSample.setTimestamp(finalSample.timestamp - this._startTimestamp);
|
|
40083
40532
|
await this._registerAudioSample(
|
|
40084
40533
|
pump,
|
|
40085
40534
|
finalSample,
|
|
@@ -40107,11 +40556,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40107
40556
|
ownGroup = new OutputTrackGroup();
|
|
40108
40557
|
}
|
|
40109
40558
|
const audioTrackLanguageCode = await track.getLanguageCode();
|
|
40559
|
+
const trackName = await track.getName();
|
|
40560
|
+
const trackDisposition = await track.getDisposition();
|
|
40110
40561
|
this.output.addAudioTrack(audioSource, {
|
|
40111
40562
|
// TODO: This condition can be removed when all demuxers properly homogenize to BCP47 in v2
|
|
40112
40563
|
languageCode: isIso639Dash2LanguageCode(audioTrackLanguageCode) ? audioTrackLanguageCode : void 0,
|
|
40113
|
-
name:
|
|
40114
|
-
disposition:
|
|
40564
|
+
name: trackName ?? void 0,
|
|
40565
|
+
disposition: trackDisposition,
|
|
40115
40566
|
group: ownGroup ?? trackOptions.group
|
|
40116
40567
|
});
|
|
40117
40568
|
this.utilizedTracks.push(track);
|
|
@@ -40150,7 +40601,7 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40150
40601
|
}
|
|
40151
40602
|
/** @internal */
|
|
40152
40603
|
async _checkpoint(pump, timestamp) {
|
|
40153
|
-
while (this.
|
|
40604
|
+
while (this._state !== "canceled" && (timestamp >= this._executionUntil || this._pauseRequested)) {
|
|
40154
40605
|
pump.resolvers.resolve();
|
|
40155
40606
|
const { promise, resolve } = promiseWithResolvers();
|
|
40156
40607
|
pump.wake = resolve;
|
|
@@ -40191,14 +40642,14 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
40191
40642
|
this.conversion = conversion;
|
|
40192
40643
|
}
|
|
40193
40644
|
declareTrack(trackId) {
|
|
40194
|
-
this.maxTimestamps.set(trackId,
|
|
40645
|
+
this.maxTimestamps.set(trackId, -Infinity);
|
|
40195
40646
|
}
|
|
40196
40647
|
shouldWait(trackId, timestamp) {
|
|
40197
40648
|
const currentValue = this.maxTimestamps.get(trackId);
|
|
40198
40649
|
assert(currentValue !== void 0);
|
|
40199
40650
|
this.maxTimestamps.set(trackId, Math.max(timestamp, currentValue));
|
|
40200
40651
|
const newMin = this.computeMinAndMaybeResolve();
|
|
40201
|
-
if (this.conversion.
|
|
40652
|
+
if (this.conversion._state === "canceled" || this.conversion._pauseRequested || timestamp >= this.conversion._executionUntil) {
|
|
40202
40653
|
return false;
|
|
40203
40654
|
}
|
|
40204
40655
|
return timestamp - newMin > MAX_TIMESTAMP_GAP;
|