mediabunny 1.6.2 → 1.7.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 +296 -202
- package/dist/bundles/mediabunny.min.cjs +5 -5
- package/dist/bundles/mediabunny.min.mjs +5 -5
- package/dist/bundles/mediabunny.mjs +296 -202
- package/dist/mediabunny.d.ts +88 -55
- package/dist/modules/src/conversion.d.ts +77 -57
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/conversion.js +139 -100
- package/dist/modules/src/index.d.ts +2 -2
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/input-format.js +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.js +4 -7
- package/dist/modules/src/matroska/ebml.d.ts +2 -2
- package/dist/modules/src/matroska/ebml.d.ts.map +1 -1
- package/dist/modules/src/matroska/ebml.js +3 -4
- package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.js +7 -10
- package/dist/modules/src/media-sink.d.ts.map +1 -1
- package/dist/modules/src/media-sink.js +42 -21
- package/dist/modules/src/media-source.d.ts.map +1 -1
- package/dist/modules/src/media-source.js +2 -0
- package/dist/modules/src/misc.d.ts +8 -0
- package/dist/modules/src/misc.d.ts.map +1 -1
- package/dist/modules/src/misc.js +18 -0
- package/dist/modules/src/mp3/mp3-demuxer.d.ts +9 -1
- package/dist/modules/src/mp3/mp3-demuxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-demuxer.js +106 -49
- package/dist/modules/src/mp3/mp3-muxer.d.ts.map +1 -1
- package/dist/modules/src/mp3/mp3-muxer.js +5 -2
- package/dist/modules/src/output-format.d.ts +5 -0
- package/dist/modules/src/output-format.d.ts.map +1 -1
- package/dist/modules/src/output-format.js +3 -0
- package/dist/modules/src/source.d.ts.map +1 -1
- package/dist/modules/src/source.js +17 -1
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/conversion.ts +243 -180
- package/src/index.ts +2 -2
- package/src/input-format.ts +1 -1
- package/src/isobmff/isobmff-demuxer.ts +5 -15
- package/src/matroska/ebml.ts +3 -4
- package/src/matroska/matroska-demuxer.ts +7 -22
- package/src/media-sink.ts +50 -28
- package/src/media-source.ts +4 -0
- package/src/misc.ts +30 -0
- package/src/mp3/mp3-demuxer.ts +123 -54
- package/src/mp3/mp3-muxer.ts +7 -2
- package/src/output-format.ts +9 -0
- package/src/source.ts +22 -2
|
@@ -344,6 +344,10 @@ var Mediabunny = (() => {
|
|
|
344
344
|
}
|
|
345
345
|
return ans;
|
|
346
346
|
};
|
|
347
|
+
var insertSorted = (arr, item, valueGetter) => {
|
|
348
|
+
const insertionIndex = binarySearchLessOrEqual(arr, valueGetter(item), valueGetter);
|
|
349
|
+
arr.splice(insertionIndex + 1, 0, item);
|
|
350
|
+
};
|
|
347
351
|
var promiseWithResolvers = () => {
|
|
348
352
|
let resolve;
|
|
349
353
|
let reject;
|
|
@@ -550,6 +554,15 @@ var Mediabunny = (() => {
|
|
|
550
554
|
return this.currentPromise = this.currentPromise.then(fn);
|
|
551
555
|
}
|
|
552
556
|
};
|
|
557
|
+
var isSafariCache = null;
|
|
558
|
+
var isSafari = () => {
|
|
559
|
+
if (isSafariCache !== null) {
|
|
560
|
+
return isSafariCache;
|
|
561
|
+
}
|
|
562
|
+
const result = !!(typeof navigator !== "undefined" && navigator.vendor?.match(/apple/i) && !navigator.userAgent?.match(/crios/i) && !navigator.userAgent?.match(/fxios/i) && !navigator.userAgent?.match(/Opera|OPT\//));
|
|
563
|
+
isSafariCache = result;
|
|
564
|
+
return result;
|
|
565
|
+
};
|
|
553
566
|
|
|
554
567
|
// src/custom-coder.ts
|
|
555
568
|
var CustomVideoDecoder = class {
|
|
@@ -5536,8 +5549,7 @@ var Mediabunny = (() => {
|
|
|
5536
5549
|
}
|
|
5537
5550
|
this.writer.write(this.helper.subarray(0, pos));
|
|
5538
5551
|
}
|
|
5539
|
-
|
|
5540
|
-
writeString(str) {
|
|
5552
|
+
writeAsciiString(str) {
|
|
5541
5553
|
this.writer.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0))));
|
|
5542
5554
|
}
|
|
5543
5555
|
writeEBML(data) {
|
|
@@ -5575,7 +5587,7 @@ var Mediabunny = (() => {
|
|
|
5575
5587
|
this.writeUnsignedInt(data.data, size);
|
|
5576
5588
|
} else if (typeof data.data === "string") {
|
|
5577
5589
|
this.writeVarInt(data.data.length);
|
|
5578
|
-
this.
|
|
5590
|
+
this.writeAsciiString(data.data);
|
|
5579
5591
|
} else if (data.data instanceof Uint8Array) {
|
|
5580
5592
|
this.writeVarInt(data.data.byteLength, data.size);
|
|
5581
5593
|
this.writer.write(data.data);
|
|
@@ -5677,7 +5689,7 @@ var Mediabunny = (() => {
|
|
|
5677
5689
|
this.pos += width;
|
|
5678
5690
|
return value;
|
|
5679
5691
|
}
|
|
5680
|
-
|
|
5692
|
+
readAsciiString(length) {
|
|
5681
5693
|
const { view: view2, offset } = this.reader.getViewAndOffset(this.pos, this.pos + length);
|
|
5682
5694
|
this.pos += length;
|
|
5683
5695
|
let strLength = 0;
|
|
@@ -6637,7 +6649,8 @@ ${cue.notes ?? ""}`;
|
|
|
6637
6649
|
async addEncodedAudioPacket(track, packet) {
|
|
6638
6650
|
const release = await this.mutex.acquire();
|
|
6639
6651
|
try {
|
|
6640
|
-
|
|
6652
|
+
const writeXingHeader = this.format._options.xingHeader !== false;
|
|
6653
|
+
if (!this.xingFrameData && writeXingHeader) {
|
|
6641
6654
|
const view2 = toDataView(packet.data);
|
|
6642
6655
|
if (view2.byteLength < 4) {
|
|
6643
6656
|
throw new Error("Invalid MP3 header in sample.");
|
|
@@ -6672,10 +6685,12 @@ ${cue.notes ?? ""}`;
|
|
|
6672
6685
|
this.frameCount++;
|
|
6673
6686
|
}
|
|
6674
6687
|
this.validateAndNormalizeTimestamp(track, packet.timestamp, packet.type === "key");
|
|
6675
|
-
this.framePositions.push(this.writer.getPos());
|
|
6676
6688
|
this.writer.write(packet.data);
|
|
6677
6689
|
this.frameCount++;
|
|
6678
6690
|
await this.writer.flush();
|
|
6691
|
+
if (writeXingHeader) {
|
|
6692
|
+
this.framePositions.push(this.writer.getPos());
|
|
6693
|
+
}
|
|
6679
6694
|
} finally {
|
|
6680
6695
|
release();
|
|
6681
6696
|
}
|
|
@@ -8745,6 +8760,7 @@ ${cue.notes ?? ""}`;
|
|
|
8745
8760
|
return decodedSampleQueueSize === 0 ? 40 : 8;
|
|
8746
8761
|
};
|
|
8747
8762
|
var VideoDecoderWrapper = class extends DecoderWrapper {
|
|
8763
|
+
// Safari-specific thing, check usage.
|
|
8748
8764
|
constructor(onSample, onError, codec, decoderConfig, rotation, timeResolution) {
|
|
8749
8765
|
super(onSample, onError);
|
|
8750
8766
|
this.rotation = rotation;
|
|
@@ -8753,21 +8769,9 @@ ${cue.notes ?? ""}`;
|
|
|
8753
8769
|
this.customDecoder = null;
|
|
8754
8770
|
this.customDecoderCallSerializer = new CallSerializer();
|
|
8755
8771
|
this.customDecoderQueueSize = 0;
|
|
8772
|
+
this.inputTimestamps = [];
|
|
8773
|
+
// Timestamps input into the decoder, sorted.
|
|
8756
8774
|
this.sampleQueue = [];
|
|
8757
|
-
const sampleHandler = (sample) => {
|
|
8758
|
-
if (this.sampleQueue.length > 0 && sample.timestamp >= last(this.sampleQueue).timestamp) {
|
|
8759
|
-
for (const sample2 of this.sampleQueue) {
|
|
8760
|
-
this.finalizeAndEmitSample(sample2);
|
|
8761
|
-
}
|
|
8762
|
-
this.sampleQueue.length = 0;
|
|
8763
|
-
}
|
|
8764
|
-
const insertionIndex = binarySearchLessOrEqual(
|
|
8765
|
-
this.sampleQueue,
|
|
8766
|
-
sample.timestamp,
|
|
8767
|
-
(x) => x.timestamp
|
|
8768
|
-
);
|
|
8769
|
-
this.sampleQueue.splice(insertionIndex + 1, 0, sample);
|
|
8770
|
-
};
|
|
8771
8775
|
const MatchingCustomDecoder = customVideoDecoders.find((x) => x.supports(codec, decoderConfig));
|
|
8772
8776
|
if (MatchingCustomDecoder) {
|
|
8773
8777
|
this.customDecoder = new MatchingCustomDecoder();
|
|
@@ -8777,10 +8781,26 @@ ${cue.notes ?? ""}`;
|
|
|
8777
8781
|
if (!(sample instanceof VideoSample)) {
|
|
8778
8782
|
throw new TypeError("The argument passed to onSample must be a VideoSample.");
|
|
8779
8783
|
}
|
|
8780
|
-
|
|
8784
|
+
this.finalizeAndEmitSample(sample);
|
|
8781
8785
|
};
|
|
8782
8786
|
void this.customDecoderCallSerializer.call(() => this.customDecoder.init());
|
|
8783
8787
|
} else {
|
|
8788
|
+
const sampleHandler = (sample) => {
|
|
8789
|
+
if (isSafari()) {
|
|
8790
|
+
if (this.sampleQueue.length > 0 && sample.timestamp >= last(this.sampleQueue).timestamp) {
|
|
8791
|
+
for (const sample2 of this.sampleQueue) {
|
|
8792
|
+
this.finalizeAndEmitSample(sample2);
|
|
8793
|
+
}
|
|
8794
|
+
this.sampleQueue.length = 0;
|
|
8795
|
+
}
|
|
8796
|
+
insertSorted(this.sampleQueue, sample, (x) => x.timestamp);
|
|
8797
|
+
} else {
|
|
8798
|
+
const timestamp = this.inputTimestamps.shift();
|
|
8799
|
+
assert(timestamp !== void 0);
|
|
8800
|
+
sample.setTimestamp(timestamp);
|
|
8801
|
+
this.finalizeAndEmitSample(sample);
|
|
8802
|
+
}
|
|
8803
|
+
};
|
|
8784
8804
|
this.decoder = new VideoDecoder({
|
|
8785
8805
|
output: (frame) => sampleHandler(new VideoSample(frame)),
|
|
8786
8806
|
error: onError
|
|
@@ -8808,6 +8828,9 @@ ${cue.notes ?? ""}`;
|
|
|
8808
8828
|
void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--);
|
|
8809
8829
|
} else {
|
|
8810
8830
|
assert(this.decoder);
|
|
8831
|
+
if (!isSafari()) {
|
|
8832
|
+
insertSorted(this.inputTimestamps, packet.timestamp, (x) => x);
|
|
8833
|
+
}
|
|
8811
8834
|
this.decoder.decode(packet.toEncodedVideoChunk());
|
|
8812
8835
|
}
|
|
8813
8836
|
}
|
|
@@ -8818,10 +8841,12 @@ ${cue.notes ?? ""}`;
|
|
|
8818
8841
|
assert(this.decoder);
|
|
8819
8842
|
await this.decoder.flush();
|
|
8820
8843
|
}
|
|
8821
|
-
|
|
8822
|
-
this.
|
|
8844
|
+
if (isSafari()) {
|
|
8845
|
+
for (const sample of this.sampleQueue) {
|
|
8846
|
+
this.finalizeAndEmitSample(sample);
|
|
8847
|
+
}
|
|
8848
|
+
this.sampleQueue.length = 0;
|
|
8823
8849
|
}
|
|
8824
|
-
this.sampleQueue.length = 0;
|
|
8825
8850
|
}
|
|
8826
8851
|
close() {
|
|
8827
8852
|
if (this.customDecoder) {
|
|
@@ -10420,6 +10445,9 @@ ${cue.notes ?? ""}`;
|
|
|
10420
10445
|
if (!options || typeof options !== "object") {
|
|
10421
10446
|
throw new TypeError("options must be an object.");
|
|
10422
10447
|
}
|
|
10448
|
+
if (options.xingHeader !== void 0 && typeof options.xingHeader !== "boolean") {
|
|
10449
|
+
throw new TypeError("options.xingHeader, when provided, must be a boolean.");
|
|
10450
|
+
}
|
|
10423
10451
|
if (options.onXingFrame !== void 0 && typeof options.onXingFrame !== "function") {
|
|
10424
10452
|
throw new TypeError("options.onXingFrame, when provided, must be a function.");
|
|
10425
10453
|
}
|
|
@@ -11567,6 +11595,7 @@ ${cue.notes ?? ""}`;
|
|
|
11567
11595
|
this._promiseWithResolvers.reject(error);
|
|
11568
11596
|
});
|
|
11569
11597
|
} else {
|
|
11598
|
+
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
11570
11599
|
this._audioContext = new AudioContext({ sampleRate: this._track.getSettings().sampleRate });
|
|
11571
11600
|
const sourceNode = this._audioContext.createMediaStreamSource(new MediaStream([this._track]));
|
|
11572
11601
|
this._scriptProcessorNode = this._audioContext.createScriptProcessor(4096);
|
|
@@ -12100,7 +12129,9 @@ ${cue.notes ?? ""}`;
|
|
|
12100
12129
|
super();
|
|
12101
12130
|
/** @internal */
|
|
12102
12131
|
this._fullData = null;
|
|
12103
|
-
|
|
12132
|
+
/** @internal */
|
|
12133
|
+
this._nextUrlVersion = null;
|
|
12134
|
+
this._url = url2 instanceof URL ? url2 : new URL(url2);
|
|
12104
12135
|
this._options = options;
|
|
12105
12136
|
}
|
|
12106
12137
|
/** @internal */
|
|
@@ -12109,6 +12140,10 @@ ${cue.notes ?? ""}`;
|
|
|
12109
12140
|
if (range) {
|
|
12110
12141
|
headers["Range"] = `bytes=${range.start}-${range.end - 1}`;
|
|
12111
12142
|
}
|
|
12143
|
+
if (this._nextUrlVersion !== null) {
|
|
12144
|
+
this._url.searchParams.set("mediabunny_version", this._nextUrlVersion.toString());
|
|
12145
|
+
this._nextUrlVersion++;
|
|
12146
|
+
}
|
|
12112
12147
|
const response = await retriedFetch(
|
|
12113
12148
|
this._url,
|
|
12114
12149
|
mergeObjectsDeeply(this._options.requestInit ?? {}, {
|
|
@@ -12121,6 +12156,10 @@ ${cue.notes ?? ""}`;
|
|
|
12121
12156
|
throw new Error(`Error fetching ${this._url}: ${response.status} ${response.statusText}`);
|
|
12122
12157
|
}
|
|
12123
12158
|
const buffer = await response.arrayBuffer();
|
|
12159
|
+
if (response.status === 206 && range && buffer.byteLength !== range.end - range.start && this._nextUrlVersion === null) {
|
|
12160
|
+
this._nextUrlVersion = 1;
|
|
12161
|
+
return this._makeRequest(range);
|
|
12162
|
+
}
|
|
12124
12163
|
if (response.status === 200) {
|
|
12125
12164
|
this._fullData = buffer;
|
|
12126
12165
|
}
|
|
@@ -13365,12 +13404,7 @@ ${cue.notes ?? ""}`;
|
|
|
13365
13404
|
isKnownToBeFirstFragment: false
|
|
13366
13405
|
};
|
|
13367
13406
|
this.readContiguousBoxes(boxInfo.contentSize);
|
|
13368
|
-
|
|
13369
|
-
this.fragments,
|
|
13370
|
-
this.currentFragment.moofOffset,
|
|
13371
|
-
(x) => x.moofOffset
|
|
13372
|
-
);
|
|
13373
|
-
this.fragments.splice(insertionIndex + 1, 0, this.currentFragment);
|
|
13407
|
+
insertSorted(this.fragments, this.currentFragment, (x) => x.moofOffset);
|
|
13374
13408
|
for (const [, trackData] of this.currentFragment.trackData) {
|
|
13375
13409
|
const firstSample = trackData.samples[0];
|
|
13376
13410
|
const lastSample = last(trackData.samples);
|
|
@@ -13394,20 +13428,14 @@ ${cue.notes ?? ""}`;
|
|
|
13394
13428
|
if (this.currentTrack) {
|
|
13395
13429
|
const trackData = this.currentFragment.trackData.get(this.currentTrack.id);
|
|
13396
13430
|
if (trackData) {
|
|
13397
|
-
|
|
13398
|
-
this.currentTrack.fragments,
|
|
13399
|
-
this.currentFragment.moofOffset,
|
|
13400
|
-
(x) => x.moofOffset
|
|
13401
|
-
);
|
|
13402
|
-
this.currentTrack.fragments.splice(insertionIndex + 1, 0, this.currentFragment);
|
|
13431
|
+
insertSorted(this.currentTrack.fragments, this.currentFragment, (x) => x.moofOffset);
|
|
13403
13432
|
const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null;
|
|
13404
13433
|
if (hasKeyFrame) {
|
|
13405
|
-
|
|
13434
|
+
insertSorted(
|
|
13406
13435
|
this.currentTrack.fragmentsWithKeyFrame,
|
|
13407
|
-
this.currentFragment
|
|
13436
|
+
this.currentFragment,
|
|
13408
13437
|
(x) => x.moofOffset
|
|
13409
13438
|
);
|
|
13410
|
-
this.currentTrack.fragmentsWithKeyFrame.splice(insertionIndex2 + 1, 0, this.currentFragment);
|
|
13411
13439
|
}
|
|
13412
13440
|
const { currentFragmentState } = this.currentTrack;
|
|
13413
13441
|
assert(currentFragmentState);
|
|
@@ -14496,29 +14524,14 @@ ${cue.notes ?? ""}`;
|
|
|
14496
14524
|
trackData.startTimestamp = firstBlock.timestamp;
|
|
14497
14525
|
trackData.endTimestamp = lastBlock.timestamp + lastBlock.duration;
|
|
14498
14526
|
if (track) {
|
|
14499
|
-
|
|
14500
|
-
track.clusters,
|
|
14501
|
-
cluster.elementStartPos,
|
|
14502
|
-
(x) => x.elementStartPos
|
|
14503
|
-
);
|
|
14504
|
-
track.clusters.splice(insertionIndex2 + 1, 0, cluster);
|
|
14527
|
+
insertSorted(track.clusters, cluster, (x) => x.elementStartPos);
|
|
14505
14528
|
const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null;
|
|
14506
14529
|
if (hasKeyFrame) {
|
|
14507
|
-
|
|
14508
|
-
track.clustersWithKeyFrame,
|
|
14509
|
-
cluster.elementStartPos,
|
|
14510
|
-
(x) => x.elementStartPos
|
|
14511
|
-
);
|
|
14512
|
-
track.clustersWithKeyFrame.splice(insertionIndex3 + 1, 0, cluster);
|
|
14530
|
+
insertSorted(track.clustersWithKeyFrame, cluster, (x) => x.elementStartPos);
|
|
14513
14531
|
}
|
|
14514
14532
|
}
|
|
14515
14533
|
}
|
|
14516
|
-
|
|
14517
|
-
segment.clusters,
|
|
14518
|
-
elementStartPos,
|
|
14519
|
-
(x) => x.elementStartPos
|
|
14520
|
-
);
|
|
14521
|
-
segment.clusters.splice(insertionIndex + 1, 0, cluster);
|
|
14534
|
+
insertSorted(segment.clusters, cluster, (x) => x.elementStartPos);
|
|
14522
14535
|
this.currentCluster = null;
|
|
14523
14536
|
return cluster;
|
|
14524
14537
|
}
|
|
@@ -14638,7 +14651,7 @@ ${cue.notes ?? ""}`;
|
|
|
14638
14651
|
switch (id) {
|
|
14639
14652
|
case 17026 /* DocType */:
|
|
14640
14653
|
{
|
|
14641
|
-
this.isWebM = reader.
|
|
14654
|
+
this.isWebM = reader.readAsciiString(size) === "webm";
|
|
14642
14655
|
}
|
|
14643
14656
|
;
|
|
14644
14657
|
break;
|
|
@@ -14836,7 +14849,7 @@ ${cue.notes ?? ""}`;
|
|
|
14836
14849
|
case 134 /* CodecID */:
|
|
14837
14850
|
{
|
|
14838
14851
|
if (!this.currentTrack) break;
|
|
14839
|
-
this.currentTrack.codecId = reader.
|
|
14852
|
+
this.currentTrack.codecId = reader.readAsciiString(size);
|
|
14840
14853
|
}
|
|
14841
14854
|
;
|
|
14842
14855
|
break;
|
|
@@ -14857,7 +14870,7 @@ ${cue.notes ?? ""}`;
|
|
|
14857
14870
|
case 2274716 /* Language */:
|
|
14858
14871
|
{
|
|
14859
14872
|
if (!this.currentTrack) break;
|
|
14860
|
-
this.currentTrack.languageCode = reader.
|
|
14873
|
+
this.currentTrack.languageCode = reader.readAsciiString(size);
|
|
14861
14874
|
if (!isIso639Dash2LanguageCode(this.currentTrack.languageCode)) {
|
|
14862
14875
|
this.currentTrack.languageCode = UNDETERMINED_LANGUAGE;
|
|
14863
14876
|
}
|
|
@@ -15672,45 +15685,21 @@ ${cue.notes ?? ""}`;
|
|
|
15672
15685
|
super(input);
|
|
15673
15686
|
this.metadataPromise = null;
|
|
15674
15687
|
this.firstFrameHeader = null;
|
|
15675
|
-
this.
|
|
15688
|
+
this.loadedSamples = [];
|
|
15689
|
+
// All samples from the start of the file to lastLoadedPos
|
|
15676
15690
|
this.tracks = [];
|
|
15691
|
+
this.loadingMutex = new AsyncMutex();
|
|
15692
|
+
this.lastLoadedPos = 0;
|
|
15693
|
+
this.fileSize = 0;
|
|
15694
|
+
this.nextTimestampInSamples = 0;
|
|
15677
15695
|
this.reader = new Mp3Reader(input._mainReader);
|
|
15678
15696
|
}
|
|
15679
15697
|
async readMetadata() {
|
|
15680
15698
|
return this.metadataPromise ??= (async () => {
|
|
15681
|
-
|
|
15682
|
-
this.reader.fileSize = fileSize;
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
if (id3Tag) {
|
|
15686
|
-
this.reader.pos += id3Tag.size;
|
|
15687
|
-
}
|
|
15688
|
-
let nextTimestampInSamples = 0;
|
|
15689
|
-
while (true) {
|
|
15690
|
-
const header = this.reader.readNextFrameHeader();
|
|
15691
|
-
if (!header) {
|
|
15692
|
-
break;
|
|
15693
|
-
}
|
|
15694
|
-
const xingOffset = getXingOffset(header.mpegVersionId, header.channel);
|
|
15695
|
-
this.reader.pos = header.startPos + xingOffset;
|
|
15696
|
-
const word = this.reader.readU32();
|
|
15697
|
-
const isXing = word === XING || word === INFO;
|
|
15698
|
-
this.reader.pos = header.startPos + header.totalSize - 1;
|
|
15699
|
-
if (isXing) {
|
|
15700
|
-
continue;
|
|
15701
|
-
}
|
|
15702
|
-
if (!this.firstFrameHeader) {
|
|
15703
|
-
this.firstFrameHeader = header;
|
|
15704
|
-
}
|
|
15705
|
-
const sampleDuration = header.audioSamplesInFrame / header.sampleRate;
|
|
15706
|
-
const sample = {
|
|
15707
|
-
timestamp: nextTimestampInSamples / header.sampleRate,
|
|
15708
|
-
duration: sampleDuration,
|
|
15709
|
-
dataStart: header.startPos,
|
|
15710
|
-
dataSize: header.totalSize
|
|
15711
|
-
};
|
|
15712
|
-
this.allSamples.push(sample);
|
|
15713
|
-
nextTimestampInSamples += header.audioSamplesInFrame;
|
|
15699
|
+
this.fileSize = await this.input.source.getSize();
|
|
15700
|
+
this.reader.fileSize = this.fileSize;
|
|
15701
|
+
while (!this.firstFrameHeader && this.lastLoadedPos < this.fileSize) {
|
|
15702
|
+
await this.loadNextChunk();
|
|
15714
15703
|
}
|
|
15715
15704
|
if (!this.firstFrameHeader) {
|
|
15716
15705
|
throw new Error("No MP3 frames found.");
|
|
@@ -15718,6 +15707,61 @@ ${cue.notes ?? ""}`;
|
|
|
15718
15707
|
this.tracks = [new InputAudioTrack(new Mp3AudioTrackBacking(this))];
|
|
15719
15708
|
})();
|
|
15720
15709
|
}
|
|
15710
|
+
/** Loads the next 0.5 MiB of frames. */
|
|
15711
|
+
async loadNextChunk() {
|
|
15712
|
+
const release = await this.loadingMutex.acquire();
|
|
15713
|
+
try {
|
|
15714
|
+
assert(this.lastLoadedPos < this.fileSize);
|
|
15715
|
+
const chunkSize = 0.5 * 1024 * 1024;
|
|
15716
|
+
const endPos = Math.min(this.lastLoadedPos + chunkSize, this.fileSize);
|
|
15717
|
+
await this.reader.reader.loadRange(this.lastLoadedPos, endPos);
|
|
15718
|
+
this.lastLoadedPos = endPos;
|
|
15719
|
+
assert(this.lastLoadedPos <= this.fileSize);
|
|
15720
|
+
if (this.reader.pos === 0) {
|
|
15721
|
+
const id3Tag = this.reader.readId3();
|
|
15722
|
+
if (id3Tag) {
|
|
15723
|
+
this.reader.pos += id3Tag.size;
|
|
15724
|
+
}
|
|
15725
|
+
}
|
|
15726
|
+
this.parseFramesFromLoadedData();
|
|
15727
|
+
} finally {
|
|
15728
|
+
release();
|
|
15729
|
+
}
|
|
15730
|
+
}
|
|
15731
|
+
parseFramesFromLoadedData() {
|
|
15732
|
+
while (true) {
|
|
15733
|
+
const startPos = this.reader.pos;
|
|
15734
|
+
const header = this.reader.readNextFrameHeader();
|
|
15735
|
+
if (!header) {
|
|
15736
|
+
break;
|
|
15737
|
+
}
|
|
15738
|
+
if (header.startPos + header.totalSize > this.lastLoadedPos) {
|
|
15739
|
+
this.reader.pos = startPos;
|
|
15740
|
+
this.lastLoadedPos = startPos;
|
|
15741
|
+
break;
|
|
15742
|
+
}
|
|
15743
|
+
const xingOffset = getXingOffset(header.mpegVersionId, header.channel);
|
|
15744
|
+
this.reader.pos = header.startPos + xingOffset;
|
|
15745
|
+
const word = this.reader.readU32();
|
|
15746
|
+
const isXing = word === XING || word === INFO;
|
|
15747
|
+
this.reader.pos = header.startPos + header.totalSize - 1;
|
|
15748
|
+
if (isXing) {
|
|
15749
|
+
continue;
|
|
15750
|
+
}
|
|
15751
|
+
if (!this.firstFrameHeader) {
|
|
15752
|
+
this.firstFrameHeader = header;
|
|
15753
|
+
}
|
|
15754
|
+
const sampleDuration = header.audioSamplesInFrame / header.sampleRate;
|
|
15755
|
+
const sample = {
|
|
15756
|
+
timestamp: this.nextTimestampInSamples / header.sampleRate,
|
|
15757
|
+
duration: sampleDuration,
|
|
15758
|
+
dataStart: header.startPos,
|
|
15759
|
+
dataSize: header.totalSize
|
|
15760
|
+
};
|
|
15761
|
+
this.loadedSamples.push(sample);
|
|
15762
|
+
this.nextTimestampInSamples += header.audioSamplesInFrame;
|
|
15763
|
+
}
|
|
15764
|
+
}
|
|
15721
15765
|
async getMimeType() {
|
|
15722
15766
|
return "audio/mpeg";
|
|
15723
15767
|
}
|
|
@@ -15727,9 +15771,9 @@ ${cue.notes ?? ""}`;
|
|
|
15727
15771
|
}
|
|
15728
15772
|
async computeDuration() {
|
|
15729
15773
|
await this.readMetadata();
|
|
15730
|
-
const
|
|
15731
|
-
assert(
|
|
15732
|
-
return
|
|
15774
|
+
const track = this.tracks[0];
|
|
15775
|
+
assert(track);
|
|
15776
|
+
return track.computeDuration();
|
|
15733
15777
|
}
|
|
15734
15778
|
};
|
|
15735
15779
|
var Mp3AudioTrackBacking = class {
|
|
@@ -15746,8 +15790,9 @@ ${cue.notes ?? ""}`;
|
|
|
15746
15790
|
assert(this.demuxer.firstFrameHeader);
|
|
15747
15791
|
return this.demuxer.firstFrameHeader.sampleRate / this.demuxer.firstFrameHeader.audioSamplesInFrame;
|
|
15748
15792
|
}
|
|
15749
|
-
computeDuration() {
|
|
15750
|
-
|
|
15793
|
+
async computeDuration() {
|
|
15794
|
+
const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
|
|
15795
|
+
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
|
15751
15796
|
}
|
|
15752
15797
|
getLanguageCode() {
|
|
15753
15798
|
return UNDETERMINED_LANGUAGE;
|
|
@@ -15775,7 +15820,7 @@ ${cue.notes ?? ""}`;
|
|
|
15775
15820
|
if (sampleIndex === -1) {
|
|
15776
15821
|
return null;
|
|
15777
15822
|
}
|
|
15778
|
-
const rawSample = this.demuxer.
|
|
15823
|
+
const rawSample = this.demuxer.loadedSamples[sampleIndex];
|
|
15779
15824
|
if (!rawSample) {
|
|
15780
15825
|
return null;
|
|
15781
15826
|
}
|
|
@@ -15796,26 +15841,44 @@ ${cue.notes ?? ""}`;
|
|
|
15796
15841
|
);
|
|
15797
15842
|
}
|
|
15798
15843
|
async getFirstPacket(options) {
|
|
15844
|
+
while (this.demuxer.loadedSamples.length === 0 && this.demuxer.lastLoadedPos < this.demuxer.fileSize) {
|
|
15845
|
+
await this.demuxer.loadNextChunk();
|
|
15846
|
+
}
|
|
15799
15847
|
return this.getPacketAtIndex(0, options);
|
|
15800
15848
|
}
|
|
15801
15849
|
async getNextPacket(packet, options) {
|
|
15802
15850
|
const sampleIndex = binarySearchExact(
|
|
15803
|
-
this.demuxer.
|
|
15851
|
+
this.demuxer.loadedSamples,
|
|
15804
15852
|
packet.timestamp,
|
|
15805
15853
|
(x) => x.timestamp
|
|
15806
15854
|
);
|
|
15807
15855
|
if (sampleIndex === -1) {
|
|
15808
15856
|
throw new Error("Packet was not created from this track.");
|
|
15809
15857
|
}
|
|
15810
|
-
|
|
15858
|
+
const nextIndex = sampleIndex + 1;
|
|
15859
|
+
while (nextIndex >= this.demuxer.loadedSamples.length && this.demuxer.lastLoadedPos < this.demuxer.fileSize) {
|
|
15860
|
+
await this.demuxer.loadNextChunk();
|
|
15861
|
+
}
|
|
15862
|
+
return this.getPacketAtIndex(nextIndex, options);
|
|
15811
15863
|
}
|
|
15812
15864
|
async getPacket(timestamp, options) {
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15865
|
+
while (true) {
|
|
15866
|
+
const index = binarySearchLessOrEqual(
|
|
15867
|
+
this.demuxer.loadedSamples,
|
|
15868
|
+
timestamp,
|
|
15869
|
+
(x) => x.timestamp
|
|
15870
|
+
);
|
|
15871
|
+
if (index === -1 && this.demuxer.loadedSamples.length > 0) {
|
|
15872
|
+
return null;
|
|
15873
|
+
}
|
|
15874
|
+
if (this.demuxer.lastLoadedPos === this.demuxer.fileSize) {
|
|
15875
|
+
return this.getPacketAtIndex(index, options);
|
|
15876
|
+
}
|
|
15877
|
+
if (index >= 0 && index + 1 < this.demuxer.loadedSamples.length) {
|
|
15878
|
+
return this.getPacketAtIndex(index, options);
|
|
15879
|
+
}
|
|
15880
|
+
await this.demuxer.loadNextChunk();
|
|
15881
|
+
}
|
|
15819
15882
|
}
|
|
15820
15883
|
getKeyPacket(timestamp, options) {
|
|
15821
15884
|
return this.getPacket(timestamp, options);
|
|
@@ -16615,7 +16678,7 @@ ${cue.notes ?? ""}`;
|
|
|
16615
16678
|
break;
|
|
16616
16679
|
case 17026 /* DocType */:
|
|
16617
16680
|
{
|
|
16618
|
-
const docType = ebmlReader.
|
|
16681
|
+
const docType = ebmlReader.readAsciiString(size);
|
|
16619
16682
|
if (docType !== desiredDocType) {
|
|
16620
16683
|
return false;
|
|
16621
16684
|
}
|
|
@@ -16856,6 +16919,70 @@ ${cue.notes ?? ""}`;
|
|
|
16856
16919
|
};
|
|
16857
16920
|
|
|
16858
16921
|
// src/conversion.ts
|
|
16922
|
+
var validateVideoOptions = (videoOptions) => {
|
|
16923
|
+
if (videoOptions !== void 0 && (!videoOptions || typeof videoOptions !== "object")) {
|
|
16924
|
+
throw new TypeError("options.video, when provided, must be an object.");
|
|
16925
|
+
}
|
|
16926
|
+
if (videoOptions?.discard !== void 0 && typeof videoOptions.discard !== "boolean") {
|
|
16927
|
+
throw new TypeError("options.video.discard, when provided, must be a boolean.");
|
|
16928
|
+
}
|
|
16929
|
+
if (videoOptions?.forceTranscode !== void 0 && typeof videoOptions.forceTranscode !== "boolean") {
|
|
16930
|
+
throw new TypeError("options.video.forceTranscode, when provided, must be a boolean.");
|
|
16931
|
+
}
|
|
16932
|
+
if (videoOptions?.codec !== void 0 && !VIDEO_CODECS.includes(videoOptions.codec)) {
|
|
16933
|
+
throw new TypeError(
|
|
16934
|
+
`options.video.codec, when provided, must be one of: ${VIDEO_CODECS.join(", ")}.`
|
|
16935
|
+
);
|
|
16936
|
+
}
|
|
16937
|
+
if (videoOptions?.bitrate !== void 0 && !(videoOptions.bitrate instanceof Quality) && (!Number.isInteger(videoOptions.bitrate) || videoOptions.bitrate <= 0)) {
|
|
16938
|
+
throw new TypeError("options.video.bitrate, when provided, must be a positive integer or a quality.");
|
|
16939
|
+
}
|
|
16940
|
+
if (videoOptions?.width !== void 0 && (!Number.isInteger(videoOptions.width) || videoOptions.width <= 0)) {
|
|
16941
|
+
throw new TypeError("options.video.width, when provided, must be a positive integer.");
|
|
16942
|
+
}
|
|
16943
|
+
if (videoOptions?.height !== void 0 && (!Number.isInteger(videoOptions.height) || videoOptions.height <= 0)) {
|
|
16944
|
+
throw new TypeError("options.video.height, when provided, must be a positive integer.");
|
|
16945
|
+
}
|
|
16946
|
+
if (videoOptions?.fit !== void 0 && !["fill", "contain", "cover"].includes(videoOptions.fit)) {
|
|
16947
|
+
throw new TypeError('options.video.fit, when provided, must be one of "fill", "contain", or "cover".');
|
|
16948
|
+
}
|
|
16949
|
+
if (videoOptions?.width !== void 0 && videoOptions.height !== void 0 && videoOptions.fit === void 0) {
|
|
16950
|
+
throw new TypeError(
|
|
16951
|
+
"When both options.video.width and options.video.height are provided, options.video.fit must also be provided."
|
|
16952
|
+
);
|
|
16953
|
+
}
|
|
16954
|
+
if (videoOptions?.rotate !== void 0 && ![0, 90, 180, 270].includes(videoOptions.rotate)) {
|
|
16955
|
+
throw new TypeError("options.video.rotate, when provided, must be 0, 90, 180 or 270.");
|
|
16956
|
+
}
|
|
16957
|
+
if (videoOptions?.frameRate !== void 0 && (!Number.isFinite(videoOptions.frameRate) || videoOptions.frameRate <= 0)) {
|
|
16958
|
+
throw new TypeError("options.video.frameRate, when provided, must be a finite positive number.");
|
|
16959
|
+
}
|
|
16960
|
+
};
|
|
16961
|
+
var validateAudioOptions = (audioOptions) => {
|
|
16962
|
+
if (audioOptions !== void 0 && (!audioOptions || typeof audioOptions !== "object")) {
|
|
16963
|
+
throw new TypeError("options.audio, when provided, must be an object.");
|
|
16964
|
+
}
|
|
16965
|
+
if (audioOptions?.discard !== void 0 && typeof audioOptions.discard !== "boolean") {
|
|
16966
|
+
throw new TypeError("options.audio.discard, when provided, must be a boolean.");
|
|
16967
|
+
}
|
|
16968
|
+
if (audioOptions?.forceTranscode !== void 0 && typeof audioOptions.forceTranscode !== "boolean") {
|
|
16969
|
+
throw new TypeError("options.audio.forceTranscode, when provided, must be a boolean.");
|
|
16970
|
+
}
|
|
16971
|
+
if (audioOptions?.codec !== void 0 && !AUDIO_CODECS.includes(audioOptions.codec)) {
|
|
16972
|
+
throw new TypeError(
|
|
16973
|
+
`options.audio.codec, when provided, must be one of: ${AUDIO_CODECS.join(", ")}.`
|
|
16974
|
+
);
|
|
16975
|
+
}
|
|
16976
|
+
if (audioOptions?.bitrate !== void 0 && !(audioOptions.bitrate instanceof Quality) && (!Number.isInteger(audioOptions.bitrate) || audioOptions.bitrate <= 0)) {
|
|
16977
|
+
throw new TypeError("options.audio.bitrate, when provided, must be a positive integer or a quality.");
|
|
16978
|
+
}
|
|
16979
|
+
if (audioOptions?.numberOfChannels !== void 0 && (!Number.isInteger(audioOptions.numberOfChannels) || audioOptions.numberOfChannels <= 0)) {
|
|
16980
|
+
throw new TypeError("options.audio.numberOfChannels, when provided, must be a positive integer.");
|
|
16981
|
+
}
|
|
16982
|
+
if (audioOptions?.sampleRate !== void 0 && (!Number.isInteger(audioOptions.sampleRate) || audioOptions.sampleRate <= 0)) {
|
|
16983
|
+
throw new TypeError("options.audio.sampleRate, when provided, must be a positive integer.");
|
|
16984
|
+
}
|
|
16985
|
+
};
|
|
16859
16986
|
var FALLBACK_NUMBER_OF_CHANNELS = 2;
|
|
16860
16987
|
var FALLBACK_SAMPLE_RATE = 48e3;
|
|
16861
16988
|
var Conversion = class _Conversion {
|
|
@@ -16909,65 +17036,13 @@ ${cue.notes ?? ""}`;
|
|
|
16909
17036
|
if (options.output._tracks.length > 0 || options.output.state !== "pending") {
|
|
16910
17037
|
throw new TypeError("options.output must be fresh: no tracks added and not started.");
|
|
16911
17038
|
}
|
|
16912
|
-
if (
|
|
16913
|
-
|
|
16914
|
-
}
|
|
16915
|
-
if (options.video?.discard !== void 0 && typeof options.video.discard !== "boolean") {
|
|
16916
|
-
throw new TypeError("options.video.discard, when provided, must be a boolean.");
|
|
16917
|
-
}
|
|
16918
|
-
if (options.video?.forceTranscode !== void 0 && typeof options.video.forceTranscode !== "boolean") {
|
|
16919
|
-
throw new TypeError("options.video.forceTranscode, when provided, must be a boolean.");
|
|
16920
|
-
}
|
|
16921
|
-
if (options.video?.codec !== void 0 && !VIDEO_CODECS.includes(options.video.codec)) {
|
|
16922
|
-
throw new TypeError(
|
|
16923
|
-
`options.video.codec, when provided, must be one of: ${VIDEO_CODECS.join(", ")}.`
|
|
16924
|
-
);
|
|
16925
|
-
}
|
|
16926
|
-
if (options.video?.bitrate !== void 0 && !(options.video.bitrate instanceof Quality) && (!Number.isInteger(options.video.bitrate) || options.video.bitrate <= 0)) {
|
|
16927
|
-
throw new TypeError("options.video.bitrate, when provided, must be a positive integer or a quality.");
|
|
16928
|
-
}
|
|
16929
|
-
if (options.video?.width !== void 0 && (!Number.isInteger(options.video.width) || options.video.width <= 0)) {
|
|
16930
|
-
throw new TypeError("options.video.width, when provided, must be a positive integer.");
|
|
16931
|
-
}
|
|
16932
|
-
if (options.video?.height !== void 0 && (!Number.isInteger(options.video.height) || options.video.height <= 0)) {
|
|
16933
|
-
throw new TypeError("options.video.height, when provided, must be a positive integer.");
|
|
16934
|
-
}
|
|
16935
|
-
if (options.video?.fit !== void 0 && !["fill", "contain", "cover"].includes(options.video.fit)) {
|
|
16936
|
-
throw new TypeError('options.video.fit, when provided, must be one of "fill", "contain", or "cover".');
|
|
16937
|
-
}
|
|
16938
|
-
if (options.video?.width !== void 0 && options.video.height !== void 0 && options.video.fit === void 0) {
|
|
16939
|
-
throw new TypeError(
|
|
16940
|
-
"When both options.video.width and options.video.height are provided, options.video.fit must also be provided."
|
|
16941
|
-
);
|
|
16942
|
-
}
|
|
16943
|
-
if (options.video?.rotate !== void 0 && ![0, 90, 180, 270].includes(options.video.rotate)) {
|
|
16944
|
-
throw new TypeError("options.video.rotate, when provided, must be 0, 90, 180 or 270.");
|
|
16945
|
-
}
|
|
16946
|
-
if (options.video?.frameRate !== void 0 && (!Number.isFinite(options.video.frameRate) || options.video.frameRate <= 0)) {
|
|
16947
|
-
throw new TypeError("options.video.frameRate, when provided, must be a finite positive number.");
|
|
16948
|
-
}
|
|
16949
|
-
if (options.audio !== void 0 && (!options.audio || typeof options.audio !== "object")) {
|
|
16950
|
-
throw new TypeError("options.audio, when provided, must be an object.");
|
|
16951
|
-
}
|
|
16952
|
-
if (options.audio?.discard !== void 0 && typeof options.audio.discard !== "boolean") {
|
|
16953
|
-
throw new TypeError("options.audio.discard, when provided, must be a boolean.");
|
|
16954
|
-
}
|
|
16955
|
-
if (options.audio?.forceTranscode !== void 0 && typeof options.audio.forceTranscode !== "boolean") {
|
|
16956
|
-
throw new TypeError("options.audio.forceTranscode, when provided, must be a boolean.");
|
|
16957
|
-
}
|
|
16958
|
-
if (options.audio?.codec !== void 0 && !AUDIO_CODECS.includes(options.audio.codec)) {
|
|
16959
|
-
throw new TypeError(
|
|
16960
|
-
`options.audio.codec, when provided, must be one of: ${AUDIO_CODECS.join(", ")}.`
|
|
16961
|
-
);
|
|
16962
|
-
}
|
|
16963
|
-
if (options.audio?.bitrate !== void 0 && !(options.audio.bitrate instanceof Quality) && (!Number.isInteger(options.audio.bitrate) || options.audio.bitrate <= 0)) {
|
|
16964
|
-
throw new TypeError("options.audio.bitrate, when provided, must be a positive integer or a quality.");
|
|
16965
|
-
}
|
|
16966
|
-
if (options.audio?.numberOfChannels !== void 0 && (!Number.isInteger(options.audio.numberOfChannels) || options.audio.numberOfChannels <= 0)) {
|
|
16967
|
-
throw new TypeError("options.audio.numberOfChannels, when provided, must be a positive integer.");
|
|
17039
|
+
if (typeof options.video !== "function") {
|
|
17040
|
+
validateVideoOptions(options.video);
|
|
17041
|
+
} else {
|
|
16968
17042
|
}
|
|
16969
|
-
if (options.audio
|
|
16970
|
-
|
|
17043
|
+
if (typeof options.audio !== "function") {
|
|
17044
|
+
validateAudioOptions(options.audio);
|
|
17045
|
+
} else {
|
|
16971
17046
|
}
|
|
16972
17047
|
if (options.trim !== void 0 && (!options.trim || typeof options.trim !== "object")) {
|
|
16973
17048
|
throw new TypeError("options.trim, when provided, must be an object.");
|
|
@@ -17000,15 +17075,34 @@ ${cue.notes ?? ""}`;
|
|
|
17000
17075
|
async _init() {
|
|
17001
17076
|
const inputTracks = await this.input.getTracks();
|
|
17002
17077
|
const outputTrackCounts = this.output.format.getSupportedTrackCounts();
|
|
17078
|
+
let nVideo = 1;
|
|
17079
|
+
let nAudio = 1;
|
|
17003
17080
|
for (const track of inputTracks) {
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17081
|
+
let trackOptions = void 0;
|
|
17082
|
+
if (track.isVideoTrack()) {
|
|
17083
|
+
if (this._options.video) {
|
|
17084
|
+
if (typeof this._options.video === "function") {
|
|
17085
|
+
trackOptions = await this._options.video(track, nVideo);
|
|
17086
|
+
validateVideoOptions(trackOptions);
|
|
17087
|
+
nVideo++;
|
|
17088
|
+
} else {
|
|
17089
|
+
trackOptions = this._options.video;
|
|
17090
|
+
}
|
|
17091
|
+
}
|
|
17092
|
+
} else if (track.isAudioTrack()) {
|
|
17093
|
+
if (this._options.audio) {
|
|
17094
|
+
if (typeof this._options.audio === "function") {
|
|
17095
|
+
trackOptions = await this._options.audio(track, nAudio);
|
|
17096
|
+
validateAudioOptions(trackOptions);
|
|
17097
|
+
nAudio++;
|
|
17098
|
+
} else {
|
|
17099
|
+
trackOptions = this._options.audio;
|
|
17100
|
+
}
|
|
17101
|
+
}
|
|
17102
|
+
} else {
|
|
17103
|
+
assert(false);
|
|
17010
17104
|
}
|
|
17011
|
-
if (
|
|
17105
|
+
if (trackOptions?.discard) {
|
|
17012
17106
|
this.discardedTracks.push({
|
|
17013
17107
|
track,
|
|
17014
17108
|
reason: "discarded_by_user"
|
|
@@ -17030,9 +17124,9 @@ ${cue.notes ?? ""}`;
|
|
|
17030
17124
|
continue;
|
|
17031
17125
|
}
|
|
17032
17126
|
if (track.isVideoTrack()) {
|
|
17033
|
-
await this._processVideoTrack(track);
|
|
17127
|
+
await this._processVideoTrack(track, trackOptions ?? {});
|
|
17034
17128
|
} else if (track.isAudioTrack()) {
|
|
17035
|
-
await this._processAudioTrack(track);
|
|
17129
|
+
await this._processAudioTrack(track, trackOptions ?? {});
|
|
17036
17130
|
}
|
|
17037
17131
|
}
|
|
17038
17132
|
const unintentionallyDiscardedTracks = this.discardedTracks.filter((x) => x.reason !== "discarded_by_user");
|
|
@@ -17086,7 +17180,7 @@ ${cue.notes ?? ""}`;
|
|
|
17086
17180
|
await this.output.cancel();
|
|
17087
17181
|
}
|
|
17088
17182
|
/** @internal */
|
|
17089
|
-
async _processVideoTrack(track) {
|
|
17183
|
+
async _processVideoTrack(track, trackOptions) {
|
|
17090
17184
|
const sourceCodec = track.codec;
|
|
17091
17185
|
if (!sourceCodec) {
|
|
17092
17186
|
this.discardedTracks.push({
|
|
@@ -17096,28 +17190,28 @@ ${cue.notes ?? ""}`;
|
|
|
17096
17190
|
return;
|
|
17097
17191
|
}
|
|
17098
17192
|
let videoSource;
|
|
17099
|
-
const totalRotation = normalizeRotation(track.rotation + (
|
|
17193
|
+
const totalRotation = normalizeRotation(track.rotation + (trackOptions.rotate ?? 0));
|
|
17100
17194
|
const outputSupportsRotation = this.output.format.supportsVideoRotationMetadata;
|
|
17101
17195
|
const [originalWidth, originalHeight] = totalRotation % 180 === 0 ? [track.codedWidth, track.codedHeight] : [track.codedHeight, track.codedWidth];
|
|
17102
17196
|
let width = originalWidth;
|
|
17103
17197
|
let height = originalHeight;
|
|
17104
17198
|
const aspectRatio = width / height;
|
|
17105
17199
|
const ceilToMultipleOfTwo = (value) => Math.ceil(value / 2) * 2;
|
|
17106
|
-
if (
|
|
17107
|
-
width = ceilToMultipleOfTwo(
|
|
17200
|
+
if (trackOptions.width !== void 0 && trackOptions.height === void 0) {
|
|
17201
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
17108
17202
|
height = ceilToMultipleOfTwo(Math.round(width / aspectRatio));
|
|
17109
|
-
} else if (
|
|
17110
|
-
height = ceilToMultipleOfTwo(
|
|
17203
|
+
} else if (trackOptions.width === void 0 && trackOptions.height !== void 0) {
|
|
17204
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
17111
17205
|
width = ceilToMultipleOfTwo(Math.round(height * aspectRatio));
|
|
17112
|
-
} else if (
|
|
17113
|
-
width = ceilToMultipleOfTwo(
|
|
17114
|
-
height = ceilToMultipleOfTwo(
|
|
17206
|
+
} else if (trackOptions.width !== void 0 && trackOptions.height !== void 0) {
|
|
17207
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
17208
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
17115
17209
|
}
|
|
17116
17210
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
17117
|
-
const needsTranscode = !!
|
|
17211
|
+
const needsTranscode = !!trackOptions.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0 || !!trackOptions.frameRate;
|
|
17118
17212
|
const needsRerender = width !== originalWidth || height !== originalHeight || totalRotation !== 0 && !outputSupportsRotation;
|
|
17119
17213
|
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
|
17120
|
-
if (!needsTranscode && !
|
|
17214
|
+
if (!needsTranscode && !trackOptions.bitrate && !needsRerender && videoCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec)) {
|
|
17121
17215
|
const source = new EncodedVideoPacketSource(sourceCodec);
|
|
17122
17216
|
videoSource = source;
|
|
17123
17217
|
this._trackPromises.push((async () => {
|
|
@@ -17148,10 +17242,10 @@ ${cue.notes ?? ""}`;
|
|
|
17148
17242
|
});
|
|
17149
17243
|
return;
|
|
17150
17244
|
}
|
|
17151
|
-
if (
|
|
17152
|
-
videoCodecs = videoCodecs.filter((codec) => codec ===
|
|
17245
|
+
if (trackOptions.codec) {
|
|
17246
|
+
videoCodecs = videoCodecs.filter((codec) => codec === trackOptions.codec);
|
|
17153
17247
|
}
|
|
17154
|
-
const bitrate =
|
|
17248
|
+
const bitrate = trackOptions.bitrate ?? QUALITY_HIGH;
|
|
17155
17249
|
const encodableCodec = await getFirstEncodableVideoCodec(videoCodecs, { width, height, bitrate });
|
|
17156
17250
|
if (!encodableCodec) {
|
|
17157
17251
|
this.discardedTracks.push({
|
|
@@ -17173,13 +17267,13 @@ ${cue.notes ?? ""}`;
|
|
|
17173
17267
|
const sink = new CanvasSink(track, {
|
|
17174
17268
|
width,
|
|
17175
17269
|
height,
|
|
17176
|
-
fit:
|
|
17270
|
+
fit: trackOptions.fit ?? "fill",
|
|
17177
17271
|
rotation: totalRotation,
|
|
17178
17272
|
// Bake the rotation into the output
|
|
17179
17273
|
poolSize: 1
|
|
17180
17274
|
});
|
|
17181
17275
|
const iterator = sink.canvases(this._startTimestamp, this._endTimestamp);
|
|
17182
|
-
const frameRate =
|
|
17276
|
+
const frameRate = trackOptions.frameRate;
|
|
17183
17277
|
let lastCanvas = null;
|
|
17184
17278
|
let lastCanvasTimestamp = null;
|
|
17185
17279
|
let lastCanvasEndTimestamp = null;
|
|
@@ -17241,7 +17335,7 @@ ${cue.notes ?? ""}`;
|
|
|
17241
17335
|
this._trackPromises.push((async () => {
|
|
17242
17336
|
await this._started;
|
|
17243
17337
|
const sink = new VideoSampleSink(track);
|
|
17244
|
-
const frameRate =
|
|
17338
|
+
const frameRate = trackOptions.frameRate;
|
|
17245
17339
|
let lastSample = null;
|
|
17246
17340
|
let lastSampleTimestamp = null;
|
|
17247
17341
|
let lastSampleEndTimestamp = null;
|
|
@@ -17301,7 +17395,7 @@ ${cue.notes ?? ""}`;
|
|
|
17301
17395
|
}
|
|
17302
17396
|
}
|
|
17303
17397
|
this.output.addVideoTrack(videoSource, {
|
|
17304
|
-
frameRate:
|
|
17398
|
+
frameRate: trackOptions.frameRate,
|
|
17305
17399
|
languageCode: track.languageCode,
|
|
17306
17400
|
rotation: needsRerender ? 0 : totalRotation
|
|
17307
17401
|
// Rerendering will bake the rotation into the output
|
|
@@ -17311,7 +17405,7 @@ ${cue.notes ?? ""}`;
|
|
|
17311
17405
|
this.utilizedTracks.push(track);
|
|
17312
17406
|
}
|
|
17313
17407
|
/** @internal */
|
|
17314
|
-
async _processAudioTrack(track) {
|
|
17408
|
+
async _processAudioTrack(track, trackOptions) {
|
|
17315
17409
|
const sourceCodec = track.codec;
|
|
17316
17410
|
if (!sourceCodec) {
|
|
17317
17411
|
this.discardedTracks.push({
|
|
@@ -17324,11 +17418,11 @@ ${cue.notes ?? ""}`;
|
|
|
17324
17418
|
const originalNumberOfChannels = track.numberOfChannels;
|
|
17325
17419
|
const originalSampleRate = track.sampleRate;
|
|
17326
17420
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
17327
|
-
let numberOfChannels =
|
|
17328
|
-
let sampleRate =
|
|
17421
|
+
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
|
17422
|
+
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
|
17329
17423
|
let needsResample = numberOfChannels !== originalNumberOfChannels || sampleRate !== originalSampleRate || this._startTimestamp > 0 || firstTimestamp < 0;
|
|
17330
17424
|
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
|
17331
|
-
if (!
|
|
17425
|
+
if (!trackOptions.forceTranscode && !trackOptions.bitrate && !needsResample && audioCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec)) {
|
|
17332
17426
|
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
17333
17427
|
audioSource = source;
|
|
17334
17428
|
this._trackPromises.push((async () => {
|
|
@@ -17360,10 +17454,10 @@ ${cue.notes ?? ""}`;
|
|
|
17360
17454
|
return;
|
|
17361
17455
|
}
|
|
17362
17456
|
let codecOfChoice = null;
|
|
17363
|
-
if (
|
|
17364
|
-
audioCodecs = audioCodecs.filter((codec) => codec ===
|
|
17457
|
+
if (trackOptions.codec) {
|
|
17458
|
+
audioCodecs = audioCodecs.filter((codec) => codec === trackOptions.codec);
|
|
17365
17459
|
}
|
|
17366
|
-
const bitrate =
|
|
17460
|
+
const bitrate = trackOptions.bitrate ?? QUALITY_HIGH;
|
|
17367
17461
|
const encodableCodecs = await getEncodableAudioCodecs(audioCodecs, {
|
|
17368
17462
|
numberOfChannels,
|
|
17369
17463
|
sampleRate,
|