mediabunny 1.45.3 → 1.45.4
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 +84 -50
- package/dist/bundles/mediabunny.min.cjs +11 -11
- package/dist/bundles/mediabunny.min.mjs +11 -11
- package/dist/bundles/mediabunny.mjs +84 -50
- package/dist/bundles/mediabunny.node.cjs +84 -50
- package/dist/mediabunny.d.ts +59 -36
- 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.d.ts.map +1 -1
- package/dist/modules/src/input.js +0 -12
- 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/source.d.ts +27 -6
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/source.js +64 -19
- 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.ts +0 -14
- 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/source.ts +93 -24
|
@@ -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];
|
|
@@ -10880,20 +10884,22 @@ var Mediabunny = (() => {
|
|
|
10880
10884
|
};
|
|
10881
10885
|
|
|
10882
10886
|
// src/mp3/mp3-reader.ts
|
|
10883
|
-
var readNextMp3FrameHeader = async (reader, startPos, until) => {
|
|
10887
|
+
var readNextMp3FrameHeader = async (reader, startPos, until, ref = null) => {
|
|
10884
10888
|
const CHUNK_SIZE = 2 ** 16;
|
|
10885
10889
|
let currentPos = startPos;
|
|
10886
10890
|
while (until === null || currentPos < until) {
|
|
10887
10891
|
const maxLength = until !== null ? Math.min(CHUNK_SIZE, until - currentPos) : CHUNK_SIZE;
|
|
10888
|
-
let slice = reader.requestSliceRange(currentPos,
|
|
10892
|
+
let slice = reader.requestSliceRange(currentPos, MP3_FRAME_HEADER_SIZE, maxLength);
|
|
10889
10893
|
if (slice instanceof Promise) slice = await slice;
|
|
10890
|
-
if (!slice || slice.length <
|
|
10891
|
-
while (slice.remainingLength >=
|
|
10894
|
+
if (!slice || slice.length < MP3_FRAME_HEADER_SIZE) break;
|
|
10895
|
+
while (slice.remainingLength >= MP3_FRAME_HEADER_SIZE) {
|
|
10892
10896
|
const posBeforeRead = slice.filePos;
|
|
10893
10897
|
const word = readU32Be(slice);
|
|
10894
10898
|
const remainingBytes = reader.fileSize !== null ? reader.fileSize - currentPos : null;
|
|
10895
10899
|
const result = readMp3FrameHeader(word, remainingBytes);
|
|
10896
|
-
if (result.header
|
|
10900
|
+
if (result.header && (!ref || // This condition helps us recover malformed streams
|
|
10901
|
+
// https://stackoverflow.com/a/20884944
|
|
10902
|
+
result.header.sampleRate === ref.sampleRate && result.header.mpegVersionId === ref.mpegVersionId && result.header.layer === ref.layer && getMp3ChannelCount(result.header.channel) === getMp3ChannelCount(ref.channel))) {
|
|
10897
10903
|
return { header: result.header, startPos: currentPos };
|
|
10898
10904
|
}
|
|
10899
10905
|
slice.filePos = posBeforeRead + result.bytesAdvanced;
|
|
@@ -10948,7 +10954,12 @@ var Mediabunny = (() => {
|
|
|
10948
10954
|
this.lastLoadedPos = slice2.filePos + id3V2Header.size;
|
|
10949
10955
|
}
|
|
10950
10956
|
}
|
|
10951
|
-
const result = await readNextMp3FrameHeader(
|
|
10957
|
+
const result = await readNextMp3FrameHeader(
|
|
10958
|
+
this.reader,
|
|
10959
|
+
this.lastLoadedPos,
|
|
10960
|
+
this.reader.fileSize,
|
|
10961
|
+
this.firstFrameHeader
|
|
10962
|
+
);
|
|
10952
10963
|
if (!result) {
|
|
10953
10964
|
this.lastSampleLoaded = true;
|
|
10954
10965
|
return;
|
|
@@ -10982,11 +10993,6 @@ var Mediabunny = (() => {
|
|
|
10982
10993
|
this.firstFrameHeader = header;
|
|
10983
10994
|
this.firstFrameHeaderPos = result.startPos;
|
|
10984
10995
|
}
|
|
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
10996
|
const sampleDuration = header.audioSamplesInFrame / this.firstFrameHeader.sampleRate;
|
|
10991
10997
|
const sample = {
|
|
10992
10998
|
timestamp: this.nextTimestampInSamples / this.firstFrameHeader.sampleRate,
|
|
@@ -11113,7 +11119,7 @@ var Mediabunny = (() => {
|
|
|
11113
11119
|
}
|
|
11114
11120
|
getNumberOfChannels() {
|
|
11115
11121
|
assert(this.demuxer.firstFrameHeader);
|
|
11116
|
-
return this.demuxer.firstFrameHeader.channel
|
|
11122
|
+
return getMp3ChannelCount(this.demuxer.firstFrameHeader.channel);
|
|
11117
11123
|
}
|
|
11118
11124
|
getSampleRate() {
|
|
11119
11125
|
assert(this.demuxer.firstFrameHeader);
|
|
@@ -11128,7 +11134,7 @@ var Mediabunny = (() => {
|
|
|
11128
11134
|
assert(this.demuxer.firstFrameHeader);
|
|
11129
11135
|
return {
|
|
11130
11136
|
codec: "mp3",
|
|
11131
|
-
numberOfChannels: this.demuxer.firstFrameHeader.channel
|
|
11137
|
+
numberOfChannels: getMp3ChannelCount(this.demuxer.firstFrameHeader.channel),
|
|
11132
11138
|
sampleRate: this.demuxer.firstFrameHeader.sampleRate
|
|
11133
11139
|
};
|
|
11134
11140
|
}
|
|
@@ -13850,7 +13856,7 @@ var Mediabunny = (() => {
|
|
|
13850
13856
|
"Invalid MP3 audio stream; could not read frame header from first packet."
|
|
13851
13857
|
);
|
|
13852
13858
|
}
|
|
13853
|
-
elementaryStream.info.numberOfChannels = result.header.channel
|
|
13859
|
+
elementaryStream.info.numberOfChannels = getMp3ChannelCount(result.header.channel);
|
|
13854
13860
|
elementaryStream.info.sampleRate = result.header.sampleRate;
|
|
13855
13861
|
} else if (elementaryStream.info.codec === "ac3") {
|
|
13856
13862
|
const frameInfo = parseAc3SyncFrame(context.suppliedPacket.data);
|
|
@@ -14914,12 +14920,12 @@ var Mediabunny = (() => {
|
|
|
14914
14920
|
}
|
|
14915
14921
|
this.skip(-1);
|
|
14916
14922
|
const possibleHeaderStartPos = this.currentPos;
|
|
14917
|
-
let remaining2 = this.ensureBuffered(
|
|
14923
|
+
let remaining2 = this.ensureBuffered(MP3_FRAME_HEADER_SIZE);
|
|
14918
14924
|
if (remaining2 instanceof Promise) remaining2 = await remaining2;
|
|
14919
|
-
if (remaining2 <
|
|
14925
|
+
if (remaining2 < MP3_FRAME_HEADER_SIZE) {
|
|
14920
14926
|
return;
|
|
14921
14927
|
}
|
|
14922
|
-
const headerBytes = this.readBytes(
|
|
14928
|
+
const headerBytes = this.readBytes(MP3_FRAME_HEADER_SIZE);
|
|
14923
14929
|
const word = toDataView(headerBytes).getUint32(0);
|
|
14924
14930
|
const result = readMp3FrameHeader(word, null);
|
|
14925
14931
|
if (result.header) {
|
|
@@ -15562,9 +15568,15 @@ var Mediabunny = (() => {
|
|
|
15562
15568
|
var node = typeof nodeAlias !== "undefined" ? nodeAlias : void 0;
|
|
15563
15569
|
var DEFAULT_MIN_READ_POSITION = 0;
|
|
15564
15570
|
var DEFAULT_MAX_READ_POSITION = Infinity;
|
|
15571
|
+
var sourceFinalizationRegistry = null;
|
|
15572
|
+
if (typeof FinalizationRegistry !== "undefined") {
|
|
15573
|
+
sourceFinalizationRegistry = new FinalizationRegistry((cleanup) => {
|
|
15574
|
+
cleanup();
|
|
15575
|
+
});
|
|
15576
|
+
}
|
|
15565
15577
|
var Source = class extends EventEmitter {
|
|
15566
15578
|
constructor() {
|
|
15567
|
-
super(
|
|
15579
|
+
super();
|
|
15568
15580
|
/** @internal */
|
|
15569
15581
|
this._disposed = false;
|
|
15570
15582
|
/** @internal */
|
|
@@ -15574,6 +15586,13 @@ var Mediabunny = (() => {
|
|
|
15574
15586
|
* @internal
|
|
15575
15587
|
*/
|
|
15576
15588
|
this._usedForHls = false;
|
|
15589
|
+
/**
|
|
15590
|
+
* FinalizationRegistry for rogue refs to this source that didn't get freed. It lives on the Source itself so that
|
|
15591
|
+
* in case the Source transitively points back to itself and forms a cycle (for example through a custom
|
|
15592
|
+
* CustomSource callback) that we're not leaking memory.
|
|
15593
|
+
* @internal
|
|
15594
|
+
*/
|
|
15595
|
+
this._refFinalizationRegistry = null;
|
|
15577
15596
|
/** @internal */
|
|
15578
15597
|
this._sizePromise = null;
|
|
15579
15598
|
/**
|
|
@@ -15582,6 +15601,11 @@ var Mediabunny = (() => {
|
|
|
15582
15601
|
* @deprecated Use `source.on('read', ({ start, end }) => ...)` instead.
|
|
15583
15602
|
*/
|
|
15584
15603
|
this.onread = null;
|
|
15604
|
+
if (typeof FinalizationRegistry !== "undefined") {
|
|
15605
|
+
this._refFinalizationRegistry = new FinalizationRegistry((source) => {
|
|
15606
|
+
source._decrementRefCount();
|
|
15607
|
+
});
|
|
15608
|
+
}
|
|
15585
15609
|
}
|
|
15586
15610
|
/**
|
|
15587
15611
|
* Resolves with the total size of the file in bytes. This function is memoized, meaning only the first call
|
|
@@ -15647,6 +15671,18 @@ var Mediabunny = (() => {
|
|
|
15647
15671
|
ref() {
|
|
15648
15672
|
return new SourceRef(this);
|
|
15649
15673
|
}
|
|
15674
|
+
/** @internal */
|
|
15675
|
+
_incrementRefCount() {
|
|
15676
|
+
this._refCount++;
|
|
15677
|
+
}
|
|
15678
|
+
/** @internal */
|
|
15679
|
+
_decrementRefCount() {
|
|
15680
|
+
this._refCount--;
|
|
15681
|
+
if (this._refCount === 0) {
|
|
15682
|
+
this._dispose();
|
|
15683
|
+
this._disposed = true;
|
|
15684
|
+
}
|
|
15685
|
+
}
|
|
15650
15686
|
};
|
|
15651
15687
|
var SourceRef = class {
|
|
15652
15688
|
/** @internal */
|
|
@@ -15656,7 +15692,8 @@ var Mediabunny = (() => {
|
|
|
15656
15692
|
if (source._disposed) {
|
|
15657
15693
|
throw new Error("Cannot ref a disposed source.");
|
|
15658
15694
|
}
|
|
15659
|
-
source.
|
|
15695
|
+
source._incrementRefCount();
|
|
15696
|
+
source._refFinalizationRegistry?.register(this, source, this);
|
|
15660
15697
|
this._source = source;
|
|
15661
15698
|
}
|
|
15662
15699
|
/** The {@link Source} this ref references. Accessing this field throws an error after having freed the ref. */
|
|
@@ -15680,11 +15717,8 @@ var Mediabunny = (() => {
|
|
|
15680
15717
|
}
|
|
15681
15718
|
const source = this.source;
|
|
15682
15719
|
assert(source._refCount > 0);
|
|
15683
|
-
source.
|
|
15684
|
-
|
|
15685
|
-
source._dispose();
|
|
15686
|
-
source._disposed = true;
|
|
15687
|
-
}
|
|
15720
|
+
source._decrementRefCount();
|
|
15721
|
+
source._refFinalizationRegistry?.unregister(this);
|
|
15688
15722
|
this._freed = true;
|
|
15689
15723
|
this._source = null;
|
|
15690
15724
|
}
|
|
@@ -16110,10 +16144,14 @@ var Mediabunny = (() => {
|
|
|
16110
16144
|
super(filePath, (request) => new _FilePathSource(request.path, options));
|
|
16111
16145
|
/** @internal */
|
|
16112
16146
|
this._fileHandle = null;
|
|
16113
|
-
this.
|
|
16147
|
+
this._customSource = new CustomSource({
|
|
16114
16148
|
getSize: async () => {
|
|
16115
|
-
|
|
16116
|
-
|
|
16149
|
+
const fileHandle = await node.fs.open(filePath, "r");
|
|
16150
|
+
this._fileHandle = fileHandle;
|
|
16151
|
+
sourceFinalizationRegistry?.register(this, () => {
|
|
16152
|
+
void fileHandle.close();
|
|
16153
|
+
}, this);
|
|
16154
|
+
const stats = await fileHandle.stat();
|
|
16117
16155
|
return stats.size;
|
|
16118
16156
|
},
|
|
16119
16157
|
read: async (start, end) => {
|
|
@@ -16128,21 +16166,24 @@ var Mediabunny = (() => {
|
|
|
16128
16166
|
}
|
|
16129
16167
|
/** @internal */
|
|
16130
16168
|
_read(start, end, minReadPosition, maxReadPosition) {
|
|
16131
|
-
return this.
|
|
16169
|
+
return this._customSource._read(start, end, minReadPosition, maxReadPosition);
|
|
16132
16170
|
}
|
|
16133
16171
|
/** @internal */
|
|
16134
16172
|
_getFileSize() {
|
|
16135
|
-
return this.
|
|
16173
|
+
return this._customSource._getFileSize();
|
|
16136
16174
|
}
|
|
16137
16175
|
/** @internal */
|
|
16138
16176
|
_dispose() {
|
|
16139
|
-
this.
|
|
16140
|
-
|
|
16141
|
-
|
|
16177
|
+
this._customSource._dispose();
|
|
16178
|
+
if (this._fileHandle) {
|
|
16179
|
+
void this._fileHandle.close();
|
|
16180
|
+
this._fileHandle = null;
|
|
16181
|
+
sourceFinalizationRegistry?.unregister(this);
|
|
16182
|
+
}
|
|
16142
16183
|
}
|
|
16143
16184
|
};
|
|
16144
|
-
var
|
|
16145
|
-
/** Creates a new {@link
|
|
16185
|
+
var CustomSource = class extends Source {
|
|
16186
|
+
/** Creates a new {@link CustomSource} whose behavior is specified by `options`. */
|
|
16146
16187
|
constructor(options) {
|
|
16147
16188
|
if (!options || typeof options !== "object") {
|
|
16148
16189
|
throw new TypeError("options must be an object.");
|
|
@@ -16253,6 +16294,7 @@ var Mediabunny = (() => {
|
|
|
16253
16294
|
this._options.dispose?.();
|
|
16254
16295
|
}
|
|
16255
16296
|
};
|
|
16297
|
+
var StreamSource = CustomSource;
|
|
16256
16298
|
var ReadableStreamSource = class extends Source {
|
|
16257
16299
|
/** Creates a new {@link ReadableStreamSource} backed by the specified `ReadableStream<Uint8Array>`. */
|
|
16258
16300
|
constructor(stream, options = {}) {
|
|
@@ -18449,7 +18491,11 @@ var Mediabunny = (() => {
|
|
|
18449
18491
|
return true;
|
|
18450
18492
|
}
|
|
18451
18493
|
currentPos = firstResult.startPos + firstResult.header.totalSize;
|
|
18452
|
-
const secondResult = await readNextMp3FrameHeader(
|
|
18494
|
+
const secondResult = await readNextMp3FrameHeader(
|
|
18495
|
+
input._reader,
|
|
18496
|
+
currentPos,
|
|
18497
|
+
currentPos + MP3_FRAME_HEADER_SIZE
|
|
18498
|
+
);
|
|
18453
18499
|
if (!secondResult) {
|
|
18454
18500
|
return false;
|
|
18455
18501
|
}
|
|
@@ -24050,16 +24096,6 @@ var Mediabunny = (() => {
|
|
|
24050
24096
|
polyfillSymbolDispose();
|
|
24051
24097
|
var DEFAULT_SOURCE_CACHE_GROUP = 1;
|
|
24052
24098
|
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
24099
|
var Input = class _Input extends EventEmitter {
|
|
24064
24100
|
/**
|
|
24065
24101
|
* Creates a new input file from the specified options. No reading operations will be performed until methods are
|
|
@@ -24114,7 +24150,6 @@ var Mediabunny = (() => {
|
|
|
24114
24150
|
this._rootRef = options.source;
|
|
24115
24151
|
}
|
|
24116
24152
|
this._sourceRefs.push(this._rootRef);
|
|
24117
|
-
inputFinalizationRegistry?.register(this, this._sourceRefs, this);
|
|
24118
24153
|
}
|
|
24119
24154
|
/** True if the input has been disposed. */
|
|
24120
24155
|
get disposed() {
|
|
@@ -24402,7 +24437,6 @@ var Mediabunny = (() => {
|
|
|
24402
24437
|
ref.free();
|
|
24403
24438
|
}
|
|
24404
24439
|
this._sourceRefs.length = 0;
|
|
24405
|
-
inputFinalizationRegistry?.unregister(this);
|
|
24406
24440
|
void this._demuxerPromise?.then((demuxer) => demuxer.dispose());
|
|
24407
24441
|
}
|
|
24408
24442
|
/**
|
|
@@ -24918,7 +24952,7 @@ var Mediabunny = (() => {
|
|
|
24918
24952
|
const yearText = readId3V1String(slice, 4);
|
|
24919
24953
|
const year = Number.parseInt(yearText, 10);
|
|
24920
24954
|
if (Number.isInteger(year) && year > 0) {
|
|
24921
|
-
tags.date ??= new Date(year
|
|
24955
|
+
tags.date ??= new Date(String(year));
|
|
24922
24956
|
}
|
|
24923
24957
|
const commentBytes = readBytes(slice, 30);
|
|
24924
24958
|
let comment;
|
|
@@ -25138,7 +25172,7 @@ var Mediabunny = (() => {
|
|
|
25138
25172
|
const yearText = reader.readId3V2EncodingAndText(frameEndPos);
|
|
25139
25173
|
const year = Number.parseInt(yearText, 10);
|
|
25140
25174
|
if (Number.isInteger(year)) {
|
|
25141
|
-
tags.date ??= new Date(year
|
|
25175
|
+
tags.date ??= new Date(String(year));
|
|
25142
25176
|
}
|
|
25143
25177
|
}
|
|
25144
25178
|
;
|