mediabunny 1.45.3 → 1.45.5
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 +216 -83
- package/dist/bundles/mediabunny.min.cjs +11 -11
- package/dist/bundles/mediabunny.min.mjs +11 -11
- package/dist/bundles/mediabunny.mjs +216 -83
- package/dist/bundles/mediabunny.node.cjs +216 -83
- package/dist/mediabunny.d.ts +64 -41
- package/dist/modules/shared/mp3-misc.d.ts +2 -1
- package/dist/modules/shared/mp3-misc.d.ts.map +1 -1
- package/dist/modules/shared/mp3-misc.js +5 -2
- package/dist/modules/src/id3.js +2 -2
- package/dist/modules/src/index.d.ts +1 -1
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/index.js +3 -1
- package/dist/modules/src/input-format.d.ts.map +1 -1
- package/dist/modules/src/input-format.js +2 -2
- package/dist/modules/src/input-track.js +1 -1
- package/dist/modules/src/input.d.ts.map +1 -1
- package/dist/modules/src/input.js +0 -12
- package/dist/modules/src/isobmff/isobmff-boxes.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-boxes.js +12 -8
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.js +6 -3
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts +1 -0
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.js +22 -0
- package/dist/modules/src/media-sink.d.ts.map +1 -1
- package/dist/modules/src/media-sink.js +7 -1
- package/dist/modules/src/misc.d.ts +2 -0
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/misc.js +1 -1
- package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-demuxer.js +4 -8
- package/dist/modules/src/mp3/mp3-reader.d.ts +1 -1
- package/dist/modules/src/mp3/mp3-reader.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-reader.js +13 -6
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mpeg-ts/mpeg-ts-demuxer.js +2 -2
- package/dist/modules/src/reader.d.ts.map +1 -1
- package/dist/modules/src/reader.js +4 -4
- package/dist/modules/src/source.d.ts +32 -11
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/source.js +151 -37
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/id3.ts +2 -2
- package/src/index.ts +4 -0
- package/src/input-format.ts +6 -2
- package/src/input-track.ts +1 -1
- package/src/input.ts +0 -14
- package/src/isobmff/isobmff-boxes.ts +12 -8
- package/src/isobmff/isobmff-demuxer.ts +7 -3
- package/src/isobmff/isobmff-muxer.ts +28 -1
- package/src/media-sink.ts +9 -0
- package/src/misc.ts +1 -1
- package/src/mp3/mp3-demuxer.ts +9 -10
- package/src/mp3/mp3-reader.ts +21 -6
- package/src/mpeg-ts/mpeg-ts-demuxer.ts +6 -2
- package/src/reader.ts +5 -4
- package/src/source.ts +205 -44
|
@@ -120,6 +120,7 @@ var Mediabunny = (() => {
|
|
|
120
120
|
CustomAudioDecoder: () => CustomAudioDecoder,
|
|
121
121
|
CustomAudioEncoder: () => CustomAudioEncoder,
|
|
122
122
|
CustomPathedSource: () => CustomPathedSource,
|
|
123
|
+
CustomSource: () => CustomSource,
|
|
123
124
|
CustomVideoDecoder: () => CustomVideoDecoder,
|
|
124
125
|
CustomVideoEncoder: () => CustomVideoEncoder,
|
|
125
126
|
EncodedAudioPacketSource: () => EncodedAudioPacketSource,
|
|
@@ -2283,7 +2284,7 @@ var Mediabunny = (() => {
|
|
|
2283
2284
|
};
|
|
2284
2285
|
|
|
2285
2286
|
// shared/mp3-misc.ts
|
|
2286
|
-
var
|
|
2287
|
+
var MP3_FRAME_HEADER_SIZE = 4;
|
|
2287
2288
|
var SAMPLING_RATES = [44100, 48e3, 32e3];
|
|
2288
2289
|
var KILOBIT_RATES = [
|
|
2289
2290
|
// lowSamplingFrequency === 0
|
|
@@ -2551,6 +2552,9 @@ var Mediabunny = (() => {
|
|
|
2551
2552
|
}
|
|
2552
2553
|
return unsynchsafed;
|
|
2553
2554
|
};
|
|
2555
|
+
var getMp3ChannelCount = (channel) => {
|
|
2556
|
+
return channel === 3 ? 1 : 2;
|
|
2557
|
+
};
|
|
2554
2558
|
|
|
2555
2559
|
// shared/ac3-misc.ts
|
|
2556
2560
|
var AC3_SAMPLE_RATES = [48e3, 44100, 32e3];
|
|
@@ -6349,18 +6353,21 @@ var Mediabunny = (() => {
|
|
|
6349
6353
|
}
|
|
6350
6354
|
assert(track.info?.type === "video");
|
|
6351
6355
|
const colourType = readAscii(slice, 4);
|
|
6352
|
-
if (colourType !== "nclx") {
|
|
6356
|
+
if (colourType !== "nclx" && colourType !== "nclc") {
|
|
6353
6357
|
break;
|
|
6354
6358
|
}
|
|
6355
6359
|
const colourPrimaries = readU16Be(slice);
|
|
6356
6360
|
const transferCharacteristics = readU16Be(slice);
|
|
6357
6361
|
const matrixCoefficients = readU16Be(slice);
|
|
6358
|
-
|
|
6362
|
+
let fullRange = void 0;
|
|
6363
|
+
if (colourType === "nclx") {
|
|
6364
|
+
fullRange = Boolean(readU8(slice) & 128);
|
|
6365
|
+
}
|
|
6359
6366
|
track.info.colorSpace = {
|
|
6360
6367
|
primaries: COLOR_PRIMARIES_MAP_INVERSE[colourPrimaries],
|
|
6361
6368
|
transfer: TRANSFER_CHARACTERISTICS_MAP_INVERSE[transferCharacteristics],
|
|
6362
6369
|
matrix: MATRIX_COEFFICIENTS_MAP_INVERSE[matrixCoefficients],
|
|
6363
|
-
fullRange
|
|
6370
|
+
fullRange
|
|
6364
6371
|
};
|
|
6365
6372
|
}
|
|
6366
6373
|
;
|
|
@@ -10880,20 +10887,22 @@ var Mediabunny = (() => {
|
|
|
10880
10887
|
};
|
|
10881
10888
|
|
|
10882
10889
|
// src/mp3/mp3-reader.ts
|
|
10883
|
-
var readNextMp3FrameHeader = async (reader, startPos, until) => {
|
|
10890
|
+
var readNextMp3FrameHeader = async (reader, startPos, until, ref = null) => {
|
|
10884
10891
|
const CHUNK_SIZE = 2 ** 16;
|
|
10885
10892
|
let currentPos = startPos;
|
|
10886
10893
|
while (until === null || currentPos < until) {
|
|
10887
10894
|
const maxLength = until !== null ? Math.min(CHUNK_SIZE, until - currentPos) : CHUNK_SIZE;
|
|
10888
|
-
let slice = reader.requestSliceRange(currentPos,
|
|
10895
|
+
let slice = reader.requestSliceRange(currentPos, MP3_FRAME_HEADER_SIZE, maxLength);
|
|
10889
10896
|
if (slice instanceof Promise) slice = await slice;
|
|
10890
|
-
if (!slice || slice.length <
|
|
10891
|
-
while (slice.remainingLength >=
|
|
10897
|
+
if (!slice || slice.length < MP3_FRAME_HEADER_SIZE) break;
|
|
10898
|
+
while (slice.remainingLength >= MP3_FRAME_HEADER_SIZE) {
|
|
10892
10899
|
const posBeforeRead = slice.filePos;
|
|
10893
10900
|
const word = readU32Be(slice);
|
|
10894
10901
|
const remainingBytes = reader.fileSize !== null ? reader.fileSize - currentPos : null;
|
|
10895
10902
|
const result = readMp3FrameHeader(word, remainingBytes);
|
|
10896
|
-
if (result.header
|
|
10903
|
+
if (result.header && (!ref || // This condition helps us recover malformed streams
|
|
10904
|
+
// https://stackoverflow.com/a/20884944
|
|
10905
|
+
result.header.sampleRate === ref.sampleRate && result.header.mpegVersionId === ref.mpegVersionId && result.header.layer === ref.layer && getMp3ChannelCount(result.header.channel) === getMp3ChannelCount(ref.channel))) {
|
|
10897
10906
|
return { header: result.header, startPos: currentPos };
|
|
10898
10907
|
}
|
|
10899
10908
|
slice.filePos = posBeforeRead + result.bytesAdvanced;
|
|
@@ -10948,7 +10957,12 @@ var Mediabunny = (() => {
|
|
|
10948
10957
|
this.lastLoadedPos = slice2.filePos + id3V2Header.size;
|
|
10949
10958
|
}
|
|
10950
10959
|
}
|
|
10951
|
-
const result = await readNextMp3FrameHeader(
|
|
10960
|
+
const result = await readNextMp3FrameHeader(
|
|
10961
|
+
this.reader,
|
|
10962
|
+
this.lastLoadedPos,
|
|
10963
|
+
this.reader.fileSize,
|
|
10964
|
+
this.firstFrameHeader
|
|
10965
|
+
);
|
|
10952
10966
|
if (!result) {
|
|
10953
10967
|
this.lastSampleLoaded = true;
|
|
10954
10968
|
return;
|
|
@@ -10982,11 +10996,6 @@ var Mediabunny = (() => {
|
|
|
10982
10996
|
this.firstFrameHeader = header;
|
|
10983
10997
|
this.firstFrameHeaderPos = result.startPos;
|
|
10984
10998
|
}
|
|
10985
|
-
if (header.sampleRate !== this.firstFrameHeader.sampleRate) {
|
|
10986
|
-
console.warn(
|
|
10987
|
-
`MP3 changed sample rate mid-file: ${this.firstFrameHeader.sampleRate} Hz to ${header.sampleRate} Hz. Might be a bug, so please report this file.`
|
|
10988
|
-
);
|
|
10989
|
-
}
|
|
10990
10999
|
const sampleDuration = header.audioSamplesInFrame / this.firstFrameHeader.sampleRate;
|
|
10991
11000
|
const sample = {
|
|
10992
11001
|
timestamp: this.nextTimestampInSamples / this.firstFrameHeader.sampleRate,
|
|
@@ -11113,7 +11122,7 @@ var Mediabunny = (() => {
|
|
|
11113
11122
|
}
|
|
11114
11123
|
getNumberOfChannels() {
|
|
11115
11124
|
assert(this.demuxer.firstFrameHeader);
|
|
11116
|
-
return this.demuxer.firstFrameHeader.channel
|
|
11125
|
+
return getMp3ChannelCount(this.demuxer.firstFrameHeader.channel);
|
|
11117
11126
|
}
|
|
11118
11127
|
getSampleRate() {
|
|
11119
11128
|
assert(this.demuxer.firstFrameHeader);
|
|
@@ -11128,7 +11137,7 @@ var Mediabunny = (() => {
|
|
|
11128
11137
|
assert(this.demuxer.firstFrameHeader);
|
|
11129
11138
|
return {
|
|
11130
11139
|
codec: "mp3",
|
|
11131
|
-
numberOfChannels: this.demuxer.firstFrameHeader.channel
|
|
11140
|
+
numberOfChannels: getMp3ChannelCount(this.demuxer.firstFrameHeader.channel),
|
|
11132
11141
|
sampleRate: this.demuxer.firstFrameHeader.sampleRate
|
|
11133
11142
|
};
|
|
11134
11143
|
}
|
|
@@ -13850,7 +13859,7 @@ var Mediabunny = (() => {
|
|
|
13850
13859
|
"Invalid MP3 audio stream; could not read frame header from first packet."
|
|
13851
13860
|
);
|
|
13852
13861
|
}
|
|
13853
|
-
elementaryStream.info.numberOfChannels = result.header.channel
|
|
13862
|
+
elementaryStream.info.numberOfChannels = getMp3ChannelCount(result.header.channel);
|
|
13854
13863
|
elementaryStream.info.sampleRate = result.header.sampleRate;
|
|
13855
13864
|
} else if (elementaryStream.info.codec === "ac3") {
|
|
13856
13865
|
const frameInfo = parseAc3SyncFrame(context.suppliedPacket.data);
|
|
@@ -14914,12 +14923,12 @@ var Mediabunny = (() => {
|
|
|
14914
14923
|
}
|
|
14915
14924
|
this.skip(-1);
|
|
14916
14925
|
const possibleHeaderStartPos = this.currentPos;
|
|
14917
|
-
let remaining2 = this.ensureBuffered(
|
|
14926
|
+
let remaining2 = this.ensureBuffered(MP3_FRAME_HEADER_SIZE);
|
|
14918
14927
|
if (remaining2 instanceof Promise) remaining2 = await remaining2;
|
|
14919
|
-
if (remaining2 <
|
|
14928
|
+
if (remaining2 < MP3_FRAME_HEADER_SIZE) {
|
|
14920
14929
|
return;
|
|
14921
14930
|
}
|
|
14922
|
-
const headerBytes = this.readBytes(
|
|
14931
|
+
const headerBytes = this.readBytes(MP3_FRAME_HEADER_SIZE);
|
|
14923
14932
|
const word = toDataView(headerBytes).getUint32(0);
|
|
14924
14933
|
const result = readMp3FrameHeader(word, null);
|
|
14925
14934
|
if (result.header) {
|
|
@@ -15562,9 +15571,15 @@ var Mediabunny = (() => {
|
|
|
15562
15571
|
var node = typeof nodeAlias !== "undefined" ? nodeAlias : void 0;
|
|
15563
15572
|
var DEFAULT_MIN_READ_POSITION = 0;
|
|
15564
15573
|
var DEFAULT_MAX_READ_POSITION = Infinity;
|
|
15574
|
+
var sourceFinalizationRegistry = null;
|
|
15575
|
+
if (typeof FinalizationRegistry !== "undefined") {
|
|
15576
|
+
sourceFinalizationRegistry = new FinalizationRegistry((cleanup) => {
|
|
15577
|
+
cleanup();
|
|
15578
|
+
});
|
|
15579
|
+
}
|
|
15565
15580
|
var Source = class extends EventEmitter {
|
|
15566
15581
|
constructor() {
|
|
15567
|
-
super(
|
|
15582
|
+
super();
|
|
15568
15583
|
/** @internal */
|
|
15569
15584
|
this._disposed = false;
|
|
15570
15585
|
/** @internal */
|
|
@@ -15574,6 +15589,13 @@ var Mediabunny = (() => {
|
|
|
15574
15589
|
* @internal
|
|
15575
15590
|
*/
|
|
15576
15591
|
this._usedForHls = false;
|
|
15592
|
+
/**
|
|
15593
|
+
* FinalizationRegistry for rogue refs to this source that didn't get freed. It lives on the Source itself so that
|
|
15594
|
+
* in case the Source transitively points back to itself and forms a cycle (for example through a custom
|
|
15595
|
+
* CustomSource callback) that we're not leaking memory.
|
|
15596
|
+
* @internal
|
|
15597
|
+
*/
|
|
15598
|
+
this._refFinalizationRegistry = null;
|
|
15577
15599
|
/** @internal */
|
|
15578
15600
|
this._sizePromise = null;
|
|
15579
15601
|
/**
|
|
@@ -15582,6 +15604,11 @@ var Mediabunny = (() => {
|
|
|
15582
15604
|
* @deprecated Use `source.on('read', ({ start, end }) => ...)` instead.
|
|
15583
15605
|
*/
|
|
15584
15606
|
this.onread = null;
|
|
15607
|
+
if (typeof FinalizationRegistry !== "undefined") {
|
|
15608
|
+
this._refFinalizationRegistry = new FinalizationRegistry((source) => {
|
|
15609
|
+
source._decrementRefCount();
|
|
15610
|
+
});
|
|
15611
|
+
}
|
|
15585
15612
|
}
|
|
15586
15613
|
/**
|
|
15587
15614
|
* Resolves with the total size of the file in bytes. This function is memoized, meaning only the first call
|
|
@@ -15647,6 +15674,18 @@ var Mediabunny = (() => {
|
|
|
15647
15674
|
ref() {
|
|
15648
15675
|
return new SourceRef(this);
|
|
15649
15676
|
}
|
|
15677
|
+
/** @internal */
|
|
15678
|
+
_incrementRefCount() {
|
|
15679
|
+
this._refCount++;
|
|
15680
|
+
}
|
|
15681
|
+
/** @internal */
|
|
15682
|
+
_decrementRefCount() {
|
|
15683
|
+
this._refCount--;
|
|
15684
|
+
if (this._refCount === 0) {
|
|
15685
|
+
this._dispose();
|
|
15686
|
+
this._disposed = true;
|
|
15687
|
+
}
|
|
15688
|
+
}
|
|
15650
15689
|
};
|
|
15651
15690
|
var SourceRef = class {
|
|
15652
15691
|
/** @internal */
|
|
@@ -15656,7 +15695,8 @@ var Mediabunny = (() => {
|
|
|
15656
15695
|
if (source._disposed) {
|
|
15657
15696
|
throw new Error("Cannot ref a disposed source.");
|
|
15658
15697
|
}
|
|
15659
|
-
source.
|
|
15698
|
+
source._incrementRefCount();
|
|
15699
|
+
source._refFinalizationRegistry?.register(this, source, this);
|
|
15660
15700
|
this._source = source;
|
|
15661
15701
|
}
|
|
15662
15702
|
/** The {@link Source} this ref references. Accessing this field throws an error after having freed the ref. */
|
|
@@ -15680,11 +15720,8 @@ var Mediabunny = (() => {
|
|
|
15680
15720
|
}
|
|
15681
15721
|
const source = this.source;
|
|
15682
15722
|
assert(source._refCount > 0);
|
|
15683
|
-
source.
|
|
15684
|
-
|
|
15685
|
-
source._dispose();
|
|
15686
|
-
source._disposed = true;
|
|
15687
|
-
}
|
|
15723
|
+
source._decrementRefCount();
|
|
15724
|
+
source._refFinalizationRegistry?.unregister(this);
|
|
15688
15725
|
this._freed = true;
|
|
15689
15726
|
this._source = null;
|
|
15690
15727
|
}
|
|
@@ -15922,8 +15959,8 @@ var Mediabunny = (() => {
|
|
|
15922
15959
|
/**
|
|
15923
15960
|
* Creates a new {@link UrlSource} backed by the resource at the specified URL.
|
|
15924
15961
|
*
|
|
15925
|
-
* When passing a `Request` instance, note that
|
|
15926
|
-
*
|
|
15962
|
+
* When passing a `Request` instance, note that its `signal` will be overridden by Mediabunny; if you want to cancel
|
|
15963
|
+
* ongoing requests, use {@link Input.dispose}.
|
|
15927
15964
|
*/
|
|
15928
15965
|
constructor(url2, options = {}) {
|
|
15929
15966
|
if (typeof url2 !== "string" && !(url2 instanceof URL) && !(typeof Request !== "undefined" && url2 instanceof Request)) {
|
|
@@ -15949,6 +15986,10 @@ var Mediabunny = (() => {
|
|
|
15949
15986
|
}
|
|
15950
15987
|
const urlString = url2 instanceof Request ? url2.url : url2 instanceof URL ? url2.href : url2;
|
|
15951
15988
|
super(urlString, (request) => new _UrlSource(request.path, this._options));
|
|
15989
|
+
/** @internal */
|
|
15990
|
+
this._offset = 0;
|
|
15991
|
+
/** @internal */
|
|
15992
|
+
this._length = null;
|
|
15952
15993
|
/**
|
|
15953
15994
|
* Note that this value being true does NOT mean the file size can't change anymore; it just signals that we have at
|
|
15954
15995
|
* least checked if we know the file size or not.
|
|
@@ -15958,6 +15999,33 @@ var Mediabunny = (() => {
|
|
|
15958
15999
|
this._url = url2;
|
|
15959
16000
|
this._options = options;
|
|
15960
16001
|
this._getRetryDelay = options.getRetryDelay ?? DEFAULT_RETRY_DELAY;
|
|
16002
|
+
this._requestInit = { ...options.requestInit };
|
|
16003
|
+
let rangeHeaderValue = null;
|
|
16004
|
+
if (options.requestInit?.headers) {
|
|
16005
|
+
const headers = { ...normalizeHeaders(options.requestInit.headers) };
|
|
16006
|
+
const rangeKey = Object.keys(headers).find((key) => key.toLowerCase() === "range");
|
|
16007
|
+
if (rangeKey !== void 0) {
|
|
16008
|
+
rangeHeaderValue = headers[rangeKey];
|
|
16009
|
+
delete headers[rangeKey];
|
|
16010
|
+
this._requestInit.headers = headers;
|
|
16011
|
+
}
|
|
16012
|
+
}
|
|
16013
|
+
if (url2 instanceof Request) {
|
|
16014
|
+
const requestRange = url2.headers.get("Range");
|
|
16015
|
+
if (requestRange !== null) {
|
|
16016
|
+
rangeHeaderValue ??= requestRange;
|
|
16017
|
+
const strippedRequest = new Request(url2);
|
|
16018
|
+
strippedRequest.headers.delete("Range");
|
|
16019
|
+
this._url = strippedRequest;
|
|
16020
|
+
}
|
|
16021
|
+
}
|
|
16022
|
+
if (rangeHeaderValue !== null) {
|
|
16023
|
+
const parsed = parseByteRangeHeader(rangeHeaderValue);
|
|
16024
|
+
if (parsed) {
|
|
16025
|
+
this._offset = parsed.offset;
|
|
16026
|
+
this._length = parsed.length;
|
|
16027
|
+
}
|
|
16028
|
+
}
|
|
15961
16029
|
const DEFAULT_PARALLELISM = 2;
|
|
15962
16030
|
this._orchestrator = new ReadOrchestrator({
|
|
15963
16031
|
maxCacheSize: options.maxCacheSize ?? 64 * 2 ** 20,
|
|
@@ -15968,11 +16036,39 @@ var Mediabunny = (() => {
|
|
|
15968
16036
|
}
|
|
15969
16037
|
/** @internal */
|
|
15970
16038
|
_getFileSize() {
|
|
15971
|
-
|
|
16039
|
+
if (!this._fileSizeDetermined) {
|
|
16040
|
+
return this._length !== null ? this._length : void 0;
|
|
16041
|
+
}
|
|
16042
|
+
const baseSize = this._orchestrator.fileSize;
|
|
16043
|
+
if (baseSize === null) {
|
|
16044
|
+
return this._length !== null ? this._length : null;
|
|
16045
|
+
}
|
|
16046
|
+
return clamp(baseSize - this._offset, 0, this._length ?? Infinity);
|
|
15972
16047
|
}
|
|
15973
16048
|
/** @internal */
|
|
15974
16049
|
_read(start, end, minReadPosition, maxReadPosition) {
|
|
15975
|
-
|
|
16050
|
+
if (this._length !== null && end > this._length) {
|
|
16051
|
+
return null;
|
|
16052
|
+
}
|
|
16053
|
+
const offset = this._offset;
|
|
16054
|
+
const result = this._orchestrator.read(
|
|
16055
|
+
offset + start,
|
|
16056
|
+
offset + end,
|
|
16057
|
+
Math.max(offset + minReadPosition, offset),
|
|
16058
|
+
offset + Math.min(maxReadPosition, this._length ?? Infinity)
|
|
16059
|
+
);
|
|
16060
|
+
const processResult = (result2) => {
|
|
16061
|
+
if (!result2) {
|
|
16062
|
+
return null;
|
|
16063
|
+
}
|
|
16064
|
+
result2.offset -= this._offset;
|
|
16065
|
+
return result2;
|
|
16066
|
+
};
|
|
16067
|
+
if (result instanceof Promise) {
|
|
16068
|
+
return result.then(processResult);
|
|
16069
|
+
} else {
|
|
16070
|
+
return processResult(result);
|
|
16071
|
+
}
|
|
15976
16072
|
}
|
|
15977
16073
|
/** @internal */
|
|
15978
16074
|
async _runWorker(worker) {
|
|
@@ -15981,7 +16077,7 @@ var Mediabunny = (() => {
|
|
|
15981
16077
|
const response = await retriedFetch(
|
|
15982
16078
|
this._options.fetchFn ?? fetch,
|
|
15983
16079
|
this._url,
|
|
15984
|
-
mergeRequestInit(this.
|
|
16080
|
+
mergeRequestInit(this._requestInit, {
|
|
15985
16081
|
headers: {
|
|
15986
16082
|
// Always sending a range request is a good way to probe if the server supports them
|
|
15987
16083
|
Range: `bytes=${worker.currentPos}-`
|
|
@@ -16090,6 +16186,22 @@ var Mediabunny = (() => {
|
|
|
16090
16186
|
this._orchestrator.dispose();
|
|
16091
16187
|
}
|
|
16092
16188
|
};
|
|
16189
|
+
var BYTE_RANGE_REGEX = /^bytes=(\d+)-(\d*)$/;
|
|
16190
|
+
var parseByteRangeHeader = (value) => {
|
|
16191
|
+
const match = BYTE_RANGE_REGEX.exec(value.trim());
|
|
16192
|
+
if (!match) {
|
|
16193
|
+
return null;
|
|
16194
|
+
}
|
|
16195
|
+
const offset = Number(match[1]);
|
|
16196
|
+
const end = match[2] === "" ? null : Number(match[2]);
|
|
16197
|
+
if (end !== null && end < offset) {
|
|
16198
|
+
return null;
|
|
16199
|
+
}
|
|
16200
|
+
return {
|
|
16201
|
+
offset,
|
|
16202
|
+
length: end !== null ? end - offset + 1 : null
|
|
16203
|
+
};
|
|
16204
|
+
};
|
|
16093
16205
|
var FilePathSource = class _FilePathSource extends PathedSource {
|
|
16094
16206
|
/** Creates a new {@link FilePathSource} backed by the file at the specified file path. */
|
|
16095
16207
|
constructor(filePath, options = {}) {
|
|
@@ -16110,10 +16222,14 @@ var Mediabunny = (() => {
|
|
|
16110
16222
|
super(filePath, (request) => new _FilePathSource(request.path, options));
|
|
16111
16223
|
/** @internal */
|
|
16112
16224
|
this._fileHandle = null;
|
|
16113
|
-
this.
|
|
16225
|
+
this._customSource = new CustomSource({
|
|
16114
16226
|
getSize: async () => {
|
|
16115
|
-
|
|
16116
|
-
|
|
16227
|
+
const fileHandle = await node.fs.open(filePath, "r");
|
|
16228
|
+
this._fileHandle = fileHandle;
|
|
16229
|
+
sourceFinalizationRegistry?.register(this, () => {
|
|
16230
|
+
void fileHandle.close();
|
|
16231
|
+
}, this);
|
|
16232
|
+
const stats = await fileHandle.stat();
|
|
16117
16233
|
return stats.size;
|
|
16118
16234
|
},
|
|
16119
16235
|
read: async (start, end) => {
|
|
@@ -16128,21 +16244,24 @@ var Mediabunny = (() => {
|
|
|
16128
16244
|
}
|
|
16129
16245
|
/** @internal */
|
|
16130
16246
|
_read(start, end, minReadPosition, maxReadPosition) {
|
|
16131
|
-
return this.
|
|
16247
|
+
return this._customSource._read(start, end, minReadPosition, maxReadPosition);
|
|
16132
16248
|
}
|
|
16133
16249
|
/** @internal */
|
|
16134
16250
|
_getFileSize() {
|
|
16135
|
-
return this.
|
|
16251
|
+
return this._customSource._getFileSize();
|
|
16136
16252
|
}
|
|
16137
16253
|
/** @internal */
|
|
16138
16254
|
_dispose() {
|
|
16139
|
-
this.
|
|
16140
|
-
|
|
16141
|
-
|
|
16255
|
+
this._customSource._dispose();
|
|
16256
|
+
if (this._fileHandle) {
|
|
16257
|
+
void this._fileHandle.close();
|
|
16258
|
+
this._fileHandle = null;
|
|
16259
|
+
sourceFinalizationRegistry?.unregister(this);
|
|
16260
|
+
}
|
|
16142
16261
|
}
|
|
16143
16262
|
};
|
|
16144
|
-
var
|
|
16145
|
-
/** Creates a new {@link
|
|
16263
|
+
var CustomSource = class extends Source {
|
|
16264
|
+
/** Creates a new {@link CustomSource} whose behavior is specified by `options`. */
|
|
16146
16265
|
constructor(options) {
|
|
16147
16266
|
if (!options || typeof options !== "object") {
|
|
16148
16267
|
throw new TypeError("options must be an object.");
|
|
@@ -16253,6 +16372,7 @@ var Mediabunny = (() => {
|
|
|
16253
16372
|
this._options.dispose?.();
|
|
16254
16373
|
}
|
|
16255
16374
|
};
|
|
16375
|
+
var StreamSource = CustomSource;
|
|
16256
16376
|
var ReadableStreamSource = class extends Source {
|
|
16257
16377
|
/** Creates a new {@link ReadableStreamSource} backed by the specified `ReadableStream<Uint8Array>`. */
|
|
16258
16378
|
constructor(stream, options = {}) {
|
|
@@ -16995,20 +17115,17 @@ var Mediabunny = (() => {
|
|
|
16995
17115
|
this._offset + minReadPosition,
|
|
16996
17116
|
this._offset + maxReadPosition
|
|
16997
17117
|
);
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
if (!result2) {
|
|
17001
|
-
return null;
|
|
17002
|
-
}
|
|
17003
|
-
result2.offset -= this._offset;
|
|
17004
|
-
return result2;
|
|
17005
|
-
});
|
|
17006
|
-
} else {
|
|
17007
|
-
if (!result) {
|
|
17118
|
+
const processResult = (result2) => {
|
|
17119
|
+
if (!result2) {
|
|
17008
17120
|
return null;
|
|
17009
17121
|
}
|
|
17010
|
-
|
|
17011
|
-
return
|
|
17122
|
+
result2.offset -= this._offset;
|
|
17123
|
+
return result2;
|
|
17124
|
+
};
|
|
17125
|
+
if (result instanceof Promise) {
|
|
17126
|
+
return result.then(processResult);
|
|
17127
|
+
} else {
|
|
17128
|
+
return processResult(result);
|
|
17012
17129
|
}
|
|
17013
17130
|
}
|
|
17014
17131
|
/** @internal */
|
|
@@ -18449,7 +18566,11 @@ var Mediabunny = (() => {
|
|
|
18449
18566
|
return true;
|
|
18450
18567
|
}
|
|
18451
18568
|
currentPos = firstResult.startPos + firstResult.header.totalSize;
|
|
18452
|
-
const secondResult = await readNextMp3FrameHeader(
|
|
18569
|
+
const secondResult = await readNextMp3FrameHeader(
|
|
18570
|
+
input._reader,
|
|
18571
|
+
currentPos,
|
|
18572
|
+
currentPos + MP3_FRAME_HEADER_SIZE
|
|
18573
|
+
);
|
|
18453
18574
|
if (!secondResult) {
|
|
18454
18575
|
return false;
|
|
18455
18576
|
}
|
|
@@ -22084,6 +22205,9 @@ var Mediabunny = (() => {
|
|
|
22084
22205
|
const filteredNalUnits = [];
|
|
22085
22206
|
for (const loc of iterateAvcNalUnits(packet.data, this.decoderConfig)) {
|
|
22086
22207
|
const type = extractNalUnitTypeForAvc(packet.data[loc.offset]);
|
|
22208
|
+
if (type === 9 /* AUD */) {
|
|
22209
|
+
filteredNalUnits.length = 0;
|
|
22210
|
+
}
|
|
22087
22211
|
if (!(type >= 20 && type <= 31)) {
|
|
22088
22212
|
filteredNalUnits.push(packet.data.subarray(loc.offset, loc.offset + loc.length));
|
|
22089
22213
|
}
|
|
@@ -23770,7 +23894,7 @@ var Mediabunny = (() => {
|
|
|
23770
23894
|
/** If this method returns true, the track's samples use a high dynamic range (HDR). */
|
|
23771
23895
|
async hasHighDynamicRange() {
|
|
23772
23896
|
const colorSpace = await this._backing.getColorSpace();
|
|
23773
|
-
return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "
|
|
23897
|
+
return colorSpace.primaries === "bt2020" || colorSpace.primaries === "smpte432" || colorSpace.transfer === "pq" || colorSpace.transfer === "hlg" || colorSpace.matrix === "bt2020-ncl";
|
|
23774
23898
|
}
|
|
23775
23899
|
/** Checks if this track may contain transparent samples with alpha data. */
|
|
23776
23900
|
async canBeTransparent() {
|
|
@@ -24050,16 +24174,6 @@ var Mediabunny = (() => {
|
|
|
24050
24174
|
polyfillSymbolDispose();
|
|
24051
24175
|
var DEFAULT_SOURCE_CACHE_GROUP = 1;
|
|
24052
24176
|
var ENCRYPTION_KEY_CACHE_GROUP = 2;
|
|
24053
|
-
var inputFinalizationRegistry = null;
|
|
24054
|
-
if (typeof FinalizationRegistry !== "undefined") {
|
|
24055
|
-
inputFinalizationRegistry = new FinalizationRegistry((refs) => {
|
|
24056
|
-
for (const ref of refs) {
|
|
24057
|
-
if (!ref.freed) {
|
|
24058
|
-
ref.free();
|
|
24059
|
-
}
|
|
24060
|
-
}
|
|
24061
|
-
});
|
|
24062
|
-
}
|
|
24063
24177
|
var Input = class _Input extends EventEmitter {
|
|
24064
24178
|
/**
|
|
24065
24179
|
* Creates a new input file from the specified options. No reading operations will be performed until methods are
|
|
@@ -24114,7 +24228,6 @@ var Mediabunny = (() => {
|
|
|
24114
24228
|
this._rootRef = options.source;
|
|
24115
24229
|
}
|
|
24116
24230
|
this._sourceRefs.push(this._rootRef);
|
|
24117
|
-
inputFinalizationRegistry?.register(this, this._sourceRefs, this);
|
|
24118
24231
|
}
|
|
24119
24232
|
/** True if the input has been disposed. */
|
|
24120
24233
|
get disposed() {
|
|
@@ -24402,7 +24515,6 @@ var Mediabunny = (() => {
|
|
|
24402
24515
|
ref.free();
|
|
24403
24516
|
}
|
|
24404
24517
|
this._sourceRefs.length = 0;
|
|
24405
|
-
inputFinalizationRegistry?.unregister(this);
|
|
24406
24518
|
void this._demuxerPromise?.then((demuxer) => demuxer.dispose());
|
|
24407
24519
|
}
|
|
24408
24520
|
/**
|
|
@@ -24516,13 +24628,13 @@ var Mediabunny = (() => {
|
|
|
24516
24628
|
if (chunks.length === 1 && this.fileSizeNonStrict !== null) {
|
|
24517
24629
|
return this.requestSlice(0, this.fileSizeNonStrict);
|
|
24518
24630
|
}
|
|
24519
|
-
|
|
24520
|
-
let slice = this.requestSliceRange(startOffset, 0, CHUNK_SIZE);
|
|
24631
|
+
let slice = this.requestSliceRange(currentSize, 0, CHUNK_SIZE);
|
|
24521
24632
|
if (slice instanceof Promise) slice = await slice;
|
|
24522
|
-
if (!slice) {
|
|
24633
|
+
if (!slice || slice.length === 0) {
|
|
24523
24634
|
break;
|
|
24524
24635
|
}
|
|
24525
|
-
|
|
24636
|
+
const chunk = readBytes(slice, slice.length);
|
|
24637
|
+
chunks.push(chunk);
|
|
24526
24638
|
currentSize += slice.length;
|
|
24527
24639
|
}
|
|
24528
24640
|
const joined = new Uint8Array(currentSize);
|
|
@@ -24918,7 +25030,7 @@ var Mediabunny = (() => {
|
|
|
24918
25030
|
const yearText = readId3V1String(slice, 4);
|
|
24919
25031
|
const year = Number.parseInt(yearText, 10);
|
|
24920
25032
|
if (Number.isInteger(year) && year > 0) {
|
|
24921
|
-
tags.date ??= new Date(year
|
|
25033
|
+
tags.date ??= new Date(String(year));
|
|
24922
25034
|
}
|
|
24923
25035
|
const commentBytes = readBytes(slice, 30);
|
|
24924
25036
|
let comment;
|
|
@@ -25138,7 +25250,7 @@ var Mediabunny = (() => {
|
|
|
25138
25250
|
const yearText = reader.readId3V2EncodingAndText(frameEndPos);
|
|
25139
25251
|
const year = Number.parseInt(yearText, 10);
|
|
25140
25252
|
if (Number.isInteger(year)) {
|
|
25141
|
-
tags.date ??= new Date(year
|
|
25253
|
+
tags.date ??= new Date(String(year));
|
|
25142
25254
|
}
|
|
25143
25255
|
}
|
|
25144
25256
|
;
|
|
@@ -26379,12 +26491,14 @@ var Mediabunny = (() => {
|
|
|
26379
26491
|
};
|
|
26380
26492
|
var mdat = (reserveLargeSize) => ({ type: "mdat", largeSize: reserveLargeSize });
|
|
26381
26493
|
var free = (size) => ({ type: "free", size });
|
|
26382
|
-
var moov = (muxer) =>
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26494
|
+
var moov = (muxer) => {
|
|
26495
|
+
return box("moov", void 0, [
|
|
26496
|
+
mvhd(muxer.creationTime, muxer.trackDatas),
|
|
26497
|
+
...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)),
|
|
26498
|
+
muxer.isFragmented ? mvex(muxer.trackDatas) : null,
|
|
26499
|
+
udta(muxer)
|
|
26500
|
+
]);
|
|
26501
|
+
};
|
|
26388
26502
|
var mvhd = (creationTime, trackDatas) => {
|
|
26389
26503
|
const duration = Math.max(
|
|
26390
26504
|
0,
|
|
@@ -26695,7 +26809,7 @@ var Mediabunny = (() => {
|
|
|
26695
26809
|
]);
|
|
26696
26810
|
};
|
|
26697
26811
|
var colr = (trackData) => box("colr", [
|
|
26698
|
-
ascii("nclx"),
|
|
26812
|
+
ascii(trackData.muxer.isQuickTime ? "nclc" : "nclx"),
|
|
26699
26813
|
// Colour type
|
|
26700
26814
|
u16(COLOR_PRIMARIES_MAP[trackData.info.decoderConfig.colorSpace.primaries]),
|
|
26701
26815
|
// Colour primaries
|
|
@@ -26703,7 +26817,7 @@ var Mediabunny = (() => {
|
|
|
26703
26817
|
// Transfer characteristics
|
|
26704
26818
|
u16(MATRIX_COEFFICIENTS_MAP[trackData.info.decoderConfig.colorSpace.matrix]),
|
|
26705
26819
|
// Matrix coefficients
|
|
26706
|
-
u8((trackData.info.decoderConfig.colorSpace.fullRange ? 1 : 0) << 7)
|
|
26820
|
+
trackData.muxer.isQuickTime ? [] : u8((trackData.info.decoderConfig.colorSpace.fullRange ? 1 : 0) << 7)
|
|
26707
26821
|
// Full range flag
|
|
26708
26822
|
]);
|
|
26709
26823
|
var avcC = (trackData) => trackData.info.decoderConfig && box("avcC", [
|
|
@@ -29203,6 +29317,7 @@ var Mediabunny = (() => {
|
|
|
29203
29317
|
if (this.format._options.onMoov) {
|
|
29204
29318
|
boxWriter.writer.startTrackingWrites();
|
|
29205
29319
|
}
|
|
29320
|
+
this.ensureOneEnabledTrack();
|
|
29206
29321
|
const movieBox = moov(this);
|
|
29207
29322
|
boxWriter.writeBox(movieBox);
|
|
29208
29323
|
if (this.format._options.onMoov) {
|
|
@@ -29285,6 +29400,7 @@ var Mediabunny = (() => {
|
|
|
29285
29400
|
assert(this.boxWriter);
|
|
29286
29401
|
if (this.allTracksAreKnown()) {
|
|
29287
29402
|
if (!this.mdat) {
|
|
29403
|
+
this.ensureOneEnabledTrack();
|
|
29288
29404
|
const moovBox = moov(this);
|
|
29289
29405
|
const moovSize = this.boxWriter.measureBox(moovBox);
|
|
29290
29406
|
const reservedSize = moovSize + this.computeSampleTableSizeUpperBound() + 4096;
|
|
@@ -29341,10 +29457,27 @@ var Mediabunny = (() => {
|
|
|
29341
29457
|
}
|
|
29342
29458
|
release();
|
|
29343
29459
|
}
|
|
29460
|
+
ensureOneEnabledTrack() {
|
|
29461
|
+
for (const type of ["video", "audio", "subtitle"]) {
|
|
29462
|
+
const tracks = this.trackDatas.filter((t) => t.type === type);
|
|
29463
|
+
if (tracks.length === 0) {
|
|
29464
|
+
continue;
|
|
29465
|
+
}
|
|
29466
|
+
const hasEnabled = tracks.some((t) => t.track.metadata.disposition?.default !== false);
|
|
29467
|
+
if (!hasEnabled) {
|
|
29468
|
+
const firstTrack = tracks[0];
|
|
29469
|
+
firstTrack.track.metadata.disposition = {
|
|
29470
|
+
...firstTrack.track.metadata.disposition,
|
|
29471
|
+
default: true
|
|
29472
|
+
};
|
|
29473
|
+
}
|
|
29474
|
+
}
|
|
29475
|
+
}
|
|
29344
29476
|
/** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */
|
|
29345
29477
|
async finalize() {
|
|
29346
29478
|
const release = await this.mutex.acquire();
|
|
29347
29479
|
this.allTracksKnown.resolve();
|
|
29480
|
+
this.ensureOneEnabledTrack();
|
|
29348
29481
|
for (const trackData of this.trackDatas) {
|
|
29349
29482
|
trackData.closed = true;
|
|
29350
29483
|
if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") {
|