mediabunny 1.6.1 → 1.7.0
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 +278 -193
- package/dist/bundles/mediabunny.min.cjs +5 -5
- package/dist/bundles/mediabunny.min.mjs +5 -5
- package/dist/bundles/mediabunny.mjs +278 -193
- 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/isobmff/isobmff-demuxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-demuxer.js +4 -7
- package/dist/modules/src/matroska/matroska-demuxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-demuxer.js +4 -7
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/conversion.ts +243 -180
- package/src/index.ts +2 -2
- package/src/isobmff/isobmff-demuxer.ts +5 -15
- package/src/matroska/matroska-demuxer.ts +4 -19
- 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
|
@@ -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 {
|
|
@@ -6637,7 +6650,8 @@ ${cue.notes ?? ""}`;
|
|
|
6637
6650
|
async addEncodedAudioPacket(track, packet) {
|
|
6638
6651
|
const release = await this.mutex.acquire();
|
|
6639
6652
|
try {
|
|
6640
|
-
|
|
6653
|
+
const writeXingHeader = this.format._options.xingHeader !== false;
|
|
6654
|
+
if (!this.xingFrameData && writeXingHeader) {
|
|
6641
6655
|
const view2 = toDataView(packet.data);
|
|
6642
6656
|
if (view2.byteLength < 4) {
|
|
6643
6657
|
throw new Error("Invalid MP3 header in sample.");
|
|
@@ -6672,10 +6686,12 @@ ${cue.notes ?? ""}`;
|
|
|
6672
6686
|
this.frameCount++;
|
|
6673
6687
|
}
|
|
6674
6688
|
this.validateAndNormalizeTimestamp(track, packet.timestamp, packet.type === "key");
|
|
6675
|
-
this.framePositions.push(this.writer.getPos());
|
|
6676
6689
|
this.writer.write(packet.data);
|
|
6677
6690
|
this.frameCount++;
|
|
6678
6691
|
await this.writer.flush();
|
|
6692
|
+
if (writeXingHeader) {
|
|
6693
|
+
this.framePositions.push(this.writer.getPos());
|
|
6694
|
+
}
|
|
6679
6695
|
} finally {
|
|
6680
6696
|
release();
|
|
6681
6697
|
}
|
|
@@ -8745,6 +8761,7 @@ ${cue.notes ?? ""}`;
|
|
|
8745
8761
|
return decodedSampleQueueSize === 0 ? 40 : 8;
|
|
8746
8762
|
};
|
|
8747
8763
|
var VideoDecoderWrapper = class extends DecoderWrapper {
|
|
8764
|
+
// Safari-specific thing, check usage.
|
|
8748
8765
|
constructor(onSample, onError, codec, decoderConfig, rotation, timeResolution) {
|
|
8749
8766
|
super(onSample, onError);
|
|
8750
8767
|
this.rotation = rotation;
|
|
@@ -8753,21 +8770,9 @@ ${cue.notes ?? ""}`;
|
|
|
8753
8770
|
this.customDecoder = null;
|
|
8754
8771
|
this.customDecoderCallSerializer = new CallSerializer();
|
|
8755
8772
|
this.customDecoderQueueSize = 0;
|
|
8773
|
+
this.inputTimestamps = [];
|
|
8774
|
+
// Timestamps input into the decoder, sorted.
|
|
8756
8775
|
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
8776
|
const MatchingCustomDecoder = customVideoDecoders.find((x) => x.supports(codec, decoderConfig));
|
|
8772
8777
|
if (MatchingCustomDecoder) {
|
|
8773
8778
|
this.customDecoder = new MatchingCustomDecoder();
|
|
@@ -8777,10 +8782,26 @@ ${cue.notes ?? ""}`;
|
|
|
8777
8782
|
if (!(sample instanceof VideoSample)) {
|
|
8778
8783
|
throw new TypeError("The argument passed to onSample must be a VideoSample.");
|
|
8779
8784
|
}
|
|
8780
|
-
|
|
8785
|
+
this.finalizeAndEmitSample(sample);
|
|
8781
8786
|
};
|
|
8782
8787
|
void this.customDecoderCallSerializer.call(() => this.customDecoder.init());
|
|
8783
8788
|
} else {
|
|
8789
|
+
const sampleHandler = (sample) => {
|
|
8790
|
+
if (isSafari()) {
|
|
8791
|
+
if (this.sampleQueue.length > 0 && sample.timestamp >= last(this.sampleQueue).timestamp) {
|
|
8792
|
+
for (const sample2 of this.sampleQueue) {
|
|
8793
|
+
this.finalizeAndEmitSample(sample2);
|
|
8794
|
+
}
|
|
8795
|
+
this.sampleQueue.length = 0;
|
|
8796
|
+
}
|
|
8797
|
+
insertSorted(this.sampleQueue, sample, (x) => x.timestamp);
|
|
8798
|
+
} else {
|
|
8799
|
+
const timestamp = this.inputTimestamps.shift();
|
|
8800
|
+
assert(timestamp !== void 0);
|
|
8801
|
+
sample.setTimestamp(timestamp);
|
|
8802
|
+
this.finalizeAndEmitSample(sample);
|
|
8803
|
+
}
|
|
8804
|
+
};
|
|
8784
8805
|
this.decoder = new VideoDecoder({
|
|
8785
8806
|
output: (frame) => sampleHandler(new VideoSample(frame)),
|
|
8786
8807
|
error: onError
|
|
@@ -8808,6 +8829,9 @@ ${cue.notes ?? ""}`;
|
|
|
8808
8829
|
void this.customDecoderCallSerializer.call(() => this.customDecoder.decode(packet)).then(() => this.customDecoderQueueSize--);
|
|
8809
8830
|
} else {
|
|
8810
8831
|
assert(this.decoder);
|
|
8832
|
+
if (!isSafari()) {
|
|
8833
|
+
insertSorted(this.inputTimestamps, packet.timestamp, (x) => x);
|
|
8834
|
+
}
|
|
8811
8835
|
this.decoder.decode(packet.toEncodedVideoChunk());
|
|
8812
8836
|
}
|
|
8813
8837
|
}
|
|
@@ -8818,10 +8842,12 @@ ${cue.notes ?? ""}`;
|
|
|
8818
8842
|
assert(this.decoder);
|
|
8819
8843
|
await this.decoder.flush();
|
|
8820
8844
|
}
|
|
8821
|
-
|
|
8822
|
-
this.
|
|
8845
|
+
if (isSafari()) {
|
|
8846
|
+
for (const sample of this.sampleQueue) {
|
|
8847
|
+
this.finalizeAndEmitSample(sample);
|
|
8848
|
+
}
|
|
8849
|
+
this.sampleQueue.length = 0;
|
|
8823
8850
|
}
|
|
8824
|
-
this.sampleQueue.length = 0;
|
|
8825
8851
|
}
|
|
8826
8852
|
close() {
|
|
8827
8853
|
if (this.customDecoder) {
|
|
@@ -10420,6 +10446,9 @@ ${cue.notes ?? ""}`;
|
|
|
10420
10446
|
if (!options || typeof options !== "object") {
|
|
10421
10447
|
throw new TypeError("options must be an object.");
|
|
10422
10448
|
}
|
|
10449
|
+
if (options.xingHeader !== void 0 && typeof options.xingHeader !== "boolean") {
|
|
10450
|
+
throw new TypeError("options.xingHeader, when provided, must be a boolean.");
|
|
10451
|
+
}
|
|
10423
10452
|
if (options.onXingFrame !== void 0 && typeof options.onXingFrame !== "function") {
|
|
10424
10453
|
throw new TypeError("options.onXingFrame, when provided, must be a function.");
|
|
10425
10454
|
}
|
|
@@ -11567,6 +11596,7 @@ ${cue.notes ?? ""}`;
|
|
|
11567
11596
|
this._promiseWithResolvers.reject(error);
|
|
11568
11597
|
});
|
|
11569
11598
|
} else {
|
|
11599
|
+
const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|
11570
11600
|
this._audioContext = new AudioContext({ sampleRate: this._track.getSettings().sampleRate });
|
|
11571
11601
|
const sourceNode = this._audioContext.createMediaStreamSource(new MediaStream([this._track]));
|
|
11572
11602
|
this._scriptProcessorNode = this._audioContext.createScriptProcessor(4096);
|
|
@@ -13365,12 +13395,7 @@ ${cue.notes ?? ""}`;
|
|
|
13365
13395
|
isKnownToBeFirstFragment: false
|
|
13366
13396
|
};
|
|
13367
13397
|
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);
|
|
13398
|
+
insertSorted(this.fragments, this.currentFragment, (x) => x.moofOffset);
|
|
13374
13399
|
for (const [, trackData] of this.currentFragment.trackData) {
|
|
13375
13400
|
const firstSample = trackData.samples[0];
|
|
13376
13401
|
const lastSample = last(trackData.samples);
|
|
@@ -13394,20 +13419,14 @@ ${cue.notes ?? ""}`;
|
|
|
13394
13419
|
if (this.currentTrack) {
|
|
13395
13420
|
const trackData = this.currentFragment.trackData.get(this.currentTrack.id);
|
|
13396
13421
|
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);
|
|
13422
|
+
insertSorted(this.currentTrack.fragments, this.currentFragment, (x) => x.moofOffset);
|
|
13403
13423
|
const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null;
|
|
13404
13424
|
if (hasKeyFrame) {
|
|
13405
|
-
|
|
13425
|
+
insertSorted(
|
|
13406
13426
|
this.currentTrack.fragmentsWithKeyFrame,
|
|
13407
|
-
this.currentFragment
|
|
13427
|
+
this.currentFragment,
|
|
13408
13428
|
(x) => x.moofOffset
|
|
13409
13429
|
);
|
|
13410
|
-
this.currentTrack.fragmentsWithKeyFrame.splice(insertionIndex2 + 1, 0, this.currentFragment);
|
|
13411
13430
|
}
|
|
13412
13431
|
const { currentFragmentState } = this.currentTrack;
|
|
13413
13432
|
assert(currentFragmentState);
|
|
@@ -14496,29 +14515,14 @@ ${cue.notes ?? ""}`;
|
|
|
14496
14515
|
trackData.startTimestamp = firstBlock.timestamp;
|
|
14497
14516
|
trackData.endTimestamp = lastBlock.timestamp + lastBlock.duration;
|
|
14498
14517
|
if (track) {
|
|
14499
|
-
|
|
14500
|
-
track.clusters,
|
|
14501
|
-
cluster.elementStartPos,
|
|
14502
|
-
(x) => x.elementStartPos
|
|
14503
|
-
);
|
|
14504
|
-
track.clusters.splice(insertionIndex2 + 1, 0, cluster);
|
|
14518
|
+
insertSorted(track.clusters, cluster, (x) => x.elementStartPos);
|
|
14505
14519
|
const hasKeyFrame = trackData.firstKeyFrameTimestamp !== null;
|
|
14506
14520
|
if (hasKeyFrame) {
|
|
14507
|
-
|
|
14508
|
-
track.clustersWithKeyFrame,
|
|
14509
|
-
cluster.elementStartPos,
|
|
14510
|
-
(x) => x.elementStartPos
|
|
14511
|
-
);
|
|
14512
|
-
track.clustersWithKeyFrame.splice(insertionIndex3 + 1, 0, cluster);
|
|
14521
|
+
insertSorted(track.clustersWithKeyFrame, cluster, (x) => x.elementStartPos);
|
|
14513
14522
|
}
|
|
14514
14523
|
}
|
|
14515
14524
|
}
|
|
14516
|
-
|
|
14517
|
-
segment.clusters,
|
|
14518
|
-
elementStartPos,
|
|
14519
|
-
(x) => x.elementStartPos
|
|
14520
|
-
);
|
|
14521
|
-
segment.clusters.splice(insertionIndex + 1, 0, cluster);
|
|
14525
|
+
insertSorted(segment.clusters, cluster, (x) => x.elementStartPos);
|
|
14522
14526
|
this.currentCluster = null;
|
|
14523
14527
|
return cluster;
|
|
14524
14528
|
}
|
|
@@ -15672,45 +15676,21 @@ ${cue.notes ?? ""}`;
|
|
|
15672
15676
|
super(input);
|
|
15673
15677
|
this.metadataPromise = null;
|
|
15674
15678
|
this.firstFrameHeader = null;
|
|
15675
|
-
this.
|
|
15679
|
+
this.loadedSamples = [];
|
|
15680
|
+
// All samples from the start of the file to lastLoadedPos
|
|
15676
15681
|
this.tracks = [];
|
|
15682
|
+
this.loadingMutex = new AsyncMutex();
|
|
15683
|
+
this.lastLoadedPos = 0;
|
|
15684
|
+
this.fileSize = 0;
|
|
15685
|
+
this.nextTimestampInSamples = 0;
|
|
15677
15686
|
this.reader = new Mp3Reader(input._mainReader);
|
|
15678
15687
|
}
|
|
15679
15688
|
async readMetadata() {
|
|
15680
15689
|
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;
|
|
15690
|
+
this.fileSize = await this.input.source.getSize();
|
|
15691
|
+
this.reader.fileSize = this.fileSize;
|
|
15692
|
+
while (!this.firstFrameHeader && this.lastLoadedPos < this.fileSize) {
|
|
15693
|
+
await this.loadNextChunk();
|
|
15714
15694
|
}
|
|
15715
15695
|
if (!this.firstFrameHeader) {
|
|
15716
15696
|
throw new Error("No MP3 frames found.");
|
|
@@ -15718,6 +15698,61 @@ ${cue.notes ?? ""}`;
|
|
|
15718
15698
|
this.tracks = [new InputAudioTrack(new Mp3AudioTrackBacking(this))];
|
|
15719
15699
|
})();
|
|
15720
15700
|
}
|
|
15701
|
+
/** Loads the next 0.5 MiB of frames. */
|
|
15702
|
+
async loadNextChunk() {
|
|
15703
|
+
const release = await this.loadingMutex.acquire();
|
|
15704
|
+
try {
|
|
15705
|
+
assert(this.lastLoadedPos < this.fileSize);
|
|
15706
|
+
const chunkSize = 0.5 * 1024 * 1024;
|
|
15707
|
+
const endPos = Math.min(this.lastLoadedPos + chunkSize, this.fileSize);
|
|
15708
|
+
await this.reader.reader.loadRange(this.lastLoadedPos, endPos);
|
|
15709
|
+
this.lastLoadedPos = endPos;
|
|
15710
|
+
assert(this.lastLoadedPos <= this.fileSize);
|
|
15711
|
+
if (this.reader.pos === 0) {
|
|
15712
|
+
const id3Tag = this.reader.readId3();
|
|
15713
|
+
if (id3Tag) {
|
|
15714
|
+
this.reader.pos += id3Tag.size;
|
|
15715
|
+
}
|
|
15716
|
+
}
|
|
15717
|
+
this.parseFramesFromLoadedData();
|
|
15718
|
+
} finally {
|
|
15719
|
+
release();
|
|
15720
|
+
}
|
|
15721
|
+
}
|
|
15722
|
+
parseFramesFromLoadedData() {
|
|
15723
|
+
while (true) {
|
|
15724
|
+
const startPos = this.reader.pos;
|
|
15725
|
+
const header = this.reader.readNextFrameHeader();
|
|
15726
|
+
if (!header) {
|
|
15727
|
+
break;
|
|
15728
|
+
}
|
|
15729
|
+
if (header.startPos + header.totalSize > this.lastLoadedPos) {
|
|
15730
|
+
this.reader.pos = startPos;
|
|
15731
|
+
this.lastLoadedPos = startPos;
|
|
15732
|
+
break;
|
|
15733
|
+
}
|
|
15734
|
+
const xingOffset = getXingOffset(header.mpegVersionId, header.channel);
|
|
15735
|
+
this.reader.pos = header.startPos + xingOffset;
|
|
15736
|
+
const word = this.reader.readU32();
|
|
15737
|
+
const isXing = word === XING || word === INFO;
|
|
15738
|
+
this.reader.pos = header.startPos + header.totalSize - 1;
|
|
15739
|
+
if (isXing) {
|
|
15740
|
+
continue;
|
|
15741
|
+
}
|
|
15742
|
+
if (!this.firstFrameHeader) {
|
|
15743
|
+
this.firstFrameHeader = header;
|
|
15744
|
+
}
|
|
15745
|
+
const sampleDuration = header.audioSamplesInFrame / header.sampleRate;
|
|
15746
|
+
const sample = {
|
|
15747
|
+
timestamp: this.nextTimestampInSamples / header.sampleRate,
|
|
15748
|
+
duration: sampleDuration,
|
|
15749
|
+
dataStart: header.startPos,
|
|
15750
|
+
dataSize: header.totalSize
|
|
15751
|
+
};
|
|
15752
|
+
this.loadedSamples.push(sample);
|
|
15753
|
+
this.nextTimestampInSamples += header.audioSamplesInFrame;
|
|
15754
|
+
}
|
|
15755
|
+
}
|
|
15721
15756
|
async getMimeType() {
|
|
15722
15757
|
return "audio/mpeg";
|
|
15723
15758
|
}
|
|
@@ -15727,9 +15762,9 @@ ${cue.notes ?? ""}`;
|
|
|
15727
15762
|
}
|
|
15728
15763
|
async computeDuration() {
|
|
15729
15764
|
await this.readMetadata();
|
|
15730
|
-
const
|
|
15731
|
-
assert(
|
|
15732
|
-
return
|
|
15765
|
+
const track = this.tracks[0];
|
|
15766
|
+
assert(track);
|
|
15767
|
+
return track.computeDuration();
|
|
15733
15768
|
}
|
|
15734
15769
|
};
|
|
15735
15770
|
var Mp3AudioTrackBacking = class {
|
|
@@ -15746,8 +15781,9 @@ ${cue.notes ?? ""}`;
|
|
|
15746
15781
|
assert(this.demuxer.firstFrameHeader);
|
|
15747
15782
|
return this.demuxer.firstFrameHeader.sampleRate / this.demuxer.firstFrameHeader.audioSamplesInFrame;
|
|
15748
15783
|
}
|
|
15749
|
-
computeDuration() {
|
|
15750
|
-
|
|
15784
|
+
async computeDuration() {
|
|
15785
|
+
const lastPacket = await this.getPacket(Infinity, { metadataOnly: true });
|
|
15786
|
+
return (lastPacket?.timestamp ?? 0) + (lastPacket?.duration ?? 0);
|
|
15751
15787
|
}
|
|
15752
15788
|
getLanguageCode() {
|
|
15753
15789
|
return UNDETERMINED_LANGUAGE;
|
|
@@ -15775,7 +15811,7 @@ ${cue.notes ?? ""}`;
|
|
|
15775
15811
|
if (sampleIndex === -1) {
|
|
15776
15812
|
return null;
|
|
15777
15813
|
}
|
|
15778
|
-
const rawSample = this.demuxer.
|
|
15814
|
+
const rawSample = this.demuxer.loadedSamples[sampleIndex];
|
|
15779
15815
|
if (!rawSample) {
|
|
15780
15816
|
return null;
|
|
15781
15817
|
}
|
|
@@ -15796,26 +15832,44 @@ ${cue.notes ?? ""}`;
|
|
|
15796
15832
|
);
|
|
15797
15833
|
}
|
|
15798
15834
|
async getFirstPacket(options) {
|
|
15835
|
+
while (this.demuxer.loadedSamples.length === 0 && this.demuxer.lastLoadedPos < this.demuxer.fileSize) {
|
|
15836
|
+
await this.demuxer.loadNextChunk();
|
|
15837
|
+
}
|
|
15799
15838
|
return this.getPacketAtIndex(0, options);
|
|
15800
15839
|
}
|
|
15801
15840
|
async getNextPacket(packet, options) {
|
|
15802
15841
|
const sampleIndex = binarySearchExact(
|
|
15803
|
-
this.demuxer.
|
|
15842
|
+
this.demuxer.loadedSamples,
|
|
15804
15843
|
packet.timestamp,
|
|
15805
15844
|
(x) => x.timestamp
|
|
15806
15845
|
);
|
|
15807
15846
|
if (sampleIndex === -1) {
|
|
15808
15847
|
throw new Error("Packet was not created from this track.");
|
|
15809
15848
|
}
|
|
15810
|
-
|
|
15849
|
+
const nextIndex = sampleIndex + 1;
|
|
15850
|
+
while (nextIndex >= this.demuxer.loadedSamples.length && this.demuxer.lastLoadedPos < this.demuxer.fileSize) {
|
|
15851
|
+
await this.demuxer.loadNextChunk();
|
|
15852
|
+
}
|
|
15853
|
+
return this.getPacketAtIndex(nextIndex, options);
|
|
15811
15854
|
}
|
|
15812
15855
|
async getPacket(timestamp, options) {
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15856
|
+
while (true) {
|
|
15857
|
+
const index = binarySearchLessOrEqual(
|
|
15858
|
+
this.demuxer.loadedSamples,
|
|
15859
|
+
timestamp,
|
|
15860
|
+
(x) => x.timestamp
|
|
15861
|
+
);
|
|
15862
|
+
if (index === -1 && this.demuxer.loadedSamples.length > 0) {
|
|
15863
|
+
return null;
|
|
15864
|
+
}
|
|
15865
|
+
if (this.demuxer.lastLoadedPos === this.demuxer.fileSize) {
|
|
15866
|
+
return this.getPacketAtIndex(index, options);
|
|
15867
|
+
}
|
|
15868
|
+
if (index >= 0 && index + 1 < this.demuxer.loadedSamples.length) {
|
|
15869
|
+
return this.getPacketAtIndex(index, options);
|
|
15870
|
+
}
|
|
15871
|
+
await this.demuxer.loadNextChunk();
|
|
15872
|
+
}
|
|
15819
15873
|
}
|
|
15820
15874
|
getKeyPacket(timestamp, options) {
|
|
15821
15875
|
return this.getPacket(timestamp, options);
|
|
@@ -16856,6 +16910,70 @@ ${cue.notes ?? ""}`;
|
|
|
16856
16910
|
};
|
|
16857
16911
|
|
|
16858
16912
|
// src/conversion.ts
|
|
16913
|
+
var validateVideoOptions = (videoOptions) => {
|
|
16914
|
+
if (videoOptions !== void 0 && (!videoOptions || typeof videoOptions !== "object")) {
|
|
16915
|
+
throw new TypeError("options.video, when provided, must be an object.");
|
|
16916
|
+
}
|
|
16917
|
+
if (videoOptions?.discard !== void 0 && typeof videoOptions.discard !== "boolean") {
|
|
16918
|
+
throw new TypeError("options.video.discard, when provided, must be a boolean.");
|
|
16919
|
+
}
|
|
16920
|
+
if (videoOptions?.forceTranscode !== void 0 && typeof videoOptions.forceTranscode !== "boolean") {
|
|
16921
|
+
throw new TypeError("options.video.forceTranscode, when provided, must be a boolean.");
|
|
16922
|
+
}
|
|
16923
|
+
if (videoOptions?.codec !== void 0 && !VIDEO_CODECS.includes(videoOptions.codec)) {
|
|
16924
|
+
throw new TypeError(
|
|
16925
|
+
`options.video.codec, when provided, must be one of: ${VIDEO_CODECS.join(", ")}.`
|
|
16926
|
+
);
|
|
16927
|
+
}
|
|
16928
|
+
if (videoOptions?.bitrate !== void 0 && !(videoOptions.bitrate instanceof Quality) && (!Number.isInteger(videoOptions.bitrate) || videoOptions.bitrate <= 0)) {
|
|
16929
|
+
throw new TypeError("options.video.bitrate, when provided, must be a positive integer or a quality.");
|
|
16930
|
+
}
|
|
16931
|
+
if (videoOptions?.width !== void 0 && (!Number.isInteger(videoOptions.width) || videoOptions.width <= 0)) {
|
|
16932
|
+
throw new TypeError("options.video.width, when provided, must be a positive integer.");
|
|
16933
|
+
}
|
|
16934
|
+
if (videoOptions?.height !== void 0 && (!Number.isInteger(videoOptions.height) || videoOptions.height <= 0)) {
|
|
16935
|
+
throw new TypeError("options.video.height, when provided, must be a positive integer.");
|
|
16936
|
+
}
|
|
16937
|
+
if (videoOptions?.fit !== void 0 && !["fill", "contain", "cover"].includes(videoOptions.fit)) {
|
|
16938
|
+
throw new TypeError('options.video.fit, when provided, must be one of "fill", "contain", or "cover".');
|
|
16939
|
+
}
|
|
16940
|
+
if (videoOptions?.width !== void 0 && videoOptions.height !== void 0 && videoOptions.fit === void 0) {
|
|
16941
|
+
throw new TypeError(
|
|
16942
|
+
"When both options.video.width and options.video.height are provided, options.video.fit must also be provided."
|
|
16943
|
+
);
|
|
16944
|
+
}
|
|
16945
|
+
if (videoOptions?.rotate !== void 0 && ![0, 90, 180, 270].includes(videoOptions.rotate)) {
|
|
16946
|
+
throw new TypeError("options.video.rotate, when provided, must be 0, 90, 180 or 270.");
|
|
16947
|
+
}
|
|
16948
|
+
if (videoOptions?.frameRate !== void 0 && (!Number.isFinite(videoOptions.frameRate) || videoOptions.frameRate <= 0)) {
|
|
16949
|
+
throw new TypeError("options.video.frameRate, when provided, must be a finite positive number.");
|
|
16950
|
+
}
|
|
16951
|
+
};
|
|
16952
|
+
var validateAudioOptions = (audioOptions) => {
|
|
16953
|
+
if (audioOptions !== void 0 && (!audioOptions || typeof audioOptions !== "object")) {
|
|
16954
|
+
throw new TypeError("options.audio, when provided, must be an object.");
|
|
16955
|
+
}
|
|
16956
|
+
if (audioOptions?.discard !== void 0 && typeof audioOptions.discard !== "boolean") {
|
|
16957
|
+
throw new TypeError("options.audio.discard, when provided, must be a boolean.");
|
|
16958
|
+
}
|
|
16959
|
+
if (audioOptions?.forceTranscode !== void 0 && typeof audioOptions.forceTranscode !== "boolean") {
|
|
16960
|
+
throw new TypeError("options.audio.forceTranscode, when provided, must be a boolean.");
|
|
16961
|
+
}
|
|
16962
|
+
if (audioOptions?.codec !== void 0 && !AUDIO_CODECS.includes(audioOptions.codec)) {
|
|
16963
|
+
throw new TypeError(
|
|
16964
|
+
`options.audio.codec, when provided, must be one of: ${AUDIO_CODECS.join(", ")}.`
|
|
16965
|
+
);
|
|
16966
|
+
}
|
|
16967
|
+
if (audioOptions?.bitrate !== void 0 && !(audioOptions.bitrate instanceof Quality) && (!Number.isInteger(audioOptions.bitrate) || audioOptions.bitrate <= 0)) {
|
|
16968
|
+
throw new TypeError("options.audio.bitrate, when provided, must be a positive integer or a quality.");
|
|
16969
|
+
}
|
|
16970
|
+
if (audioOptions?.numberOfChannels !== void 0 && (!Number.isInteger(audioOptions.numberOfChannels) || audioOptions.numberOfChannels <= 0)) {
|
|
16971
|
+
throw new TypeError("options.audio.numberOfChannels, when provided, must be a positive integer.");
|
|
16972
|
+
}
|
|
16973
|
+
if (audioOptions?.sampleRate !== void 0 && (!Number.isInteger(audioOptions.sampleRate) || audioOptions.sampleRate <= 0)) {
|
|
16974
|
+
throw new TypeError("options.audio.sampleRate, when provided, must be a positive integer.");
|
|
16975
|
+
}
|
|
16976
|
+
};
|
|
16859
16977
|
var FALLBACK_NUMBER_OF_CHANNELS = 2;
|
|
16860
16978
|
var FALLBACK_SAMPLE_RATE = 48e3;
|
|
16861
16979
|
var Conversion = class _Conversion {
|
|
@@ -16909,65 +17027,13 @@ ${cue.notes ?? ""}`;
|
|
|
16909
17027
|
if (options.output._tracks.length > 0 || options.output.state !== "pending") {
|
|
16910
17028
|
throw new TypeError("options.output must be fresh: no tracks added and not started.");
|
|
16911
17029
|
}
|
|
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.");
|
|
17030
|
+
if (typeof options.video !== "function") {
|
|
17031
|
+
validateVideoOptions(options.video);
|
|
17032
|
+
} else {
|
|
16968
17033
|
}
|
|
16969
|
-
if (options.audio
|
|
16970
|
-
|
|
17034
|
+
if (typeof options.audio !== "function") {
|
|
17035
|
+
validateAudioOptions(options.audio);
|
|
17036
|
+
} else {
|
|
16971
17037
|
}
|
|
16972
17038
|
if (options.trim !== void 0 && (!options.trim || typeof options.trim !== "object")) {
|
|
16973
17039
|
throw new TypeError("options.trim, when provided, must be an object.");
|
|
@@ -17000,15 +17066,34 @@ ${cue.notes ?? ""}`;
|
|
|
17000
17066
|
async _init() {
|
|
17001
17067
|
const inputTracks = await this.input.getTracks();
|
|
17002
17068
|
const outputTrackCounts = this.output.format.getSupportedTrackCounts();
|
|
17069
|
+
let nVideo = 1;
|
|
17070
|
+
let nAudio = 1;
|
|
17003
17071
|
for (const track of inputTracks) {
|
|
17004
|
-
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17072
|
+
let trackOptions = void 0;
|
|
17073
|
+
if (track.isVideoTrack()) {
|
|
17074
|
+
if (this._options.video) {
|
|
17075
|
+
if (typeof this._options.video === "function") {
|
|
17076
|
+
trackOptions = await this._options.video(track, nVideo);
|
|
17077
|
+
validateVideoOptions(trackOptions);
|
|
17078
|
+
nVideo++;
|
|
17079
|
+
} else {
|
|
17080
|
+
trackOptions = this._options.video;
|
|
17081
|
+
}
|
|
17082
|
+
}
|
|
17083
|
+
} else if (track.isAudioTrack()) {
|
|
17084
|
+
if (this._options.audio) {
|
|
17085
|
+
if (typeof this._options.audio === "function") {
|
|
17086
|
+
trackOptions = await this._options.audio(track, nAudio);
|
|
17087
|
+
validateAudioOptions(trackOptions);
|
|
17088
|
+
nAudio++;
|
|
17089
|
+
} else {
|
|
17090
|
+
trackOptions = this._options.audio;
|
|
17091
|
+
}
|
|
17092
|
+
}
|
|
17093
|
+
} else {
|
|
17094
|
+
assert(false);
|
|
17010
17095
|
}
|
|
17011
|
-
if (
|
|
17096
|
+
if (trackOptions?.discard) {
|
|
17012
17097
|
this.discardedTracks.push({
|
|
17013
17098
|
track,
|
|
17014
17099
|
reason: "discarded_by_user"
|
|
@@ -17030,9 +17115,9 @@ ${cue.notes ?? ""}`;
|
|
|
17030
17115
|
continue;
|
|
17031
17116
|
}
|
|
17032
17117
|
if (track.isVideoTrack()) {
|
|
17033
|
-
await this._processVideoTrack(track);
|
|
17118
|
+
await this._processVideoTrack(track, trackOptions ?? {});
|
|
17034
17119
|
} else if (track.isAudioTrack()) {
|
|
17035
|
-
await this._processAudioTrack(track);
|
|
17120
|
+
await this._processAudioTrack(track, trackOptions ?? {});
|
|
17036
17121
|
}
|
|
17037
17122
|
}
|
|
17038
17123
|
const unintentionallyDiscardedTracks = this.discardedTracks.filter((x) => x.reason !== "discarded_by_user");
|
|
@@ -17086,7 +17171,7 @@ ${cue.notes ?? ""}`;
|
|
|
17086
17171
|
await this.output.cancel();
|
|
17087
17172
|
}
|
|
17088
17173
|
/** @internal */
|
|
17089
|
-
async _processVideoTrack(track) {
|
|
17174
|
+
async _processVideoTrack(track, trackOptions) {
|
|
17090
17175
|
const sourceCodec = track.codec;
|
|
17091
17176
|
if (!sourceCodec) {
|
|
17092
17177
|
this.discardedTracks.push({
|
|
@@ -17096,28 +17181,28 @@ ${cue.notes ?? ""}`;
|
|
|
17096
17181
|
return;
|
|
17097
17182
|
}
|
|
17098
17183
|
let videoSource;
|
|
17099
|
-
const totalRotation = normalizeRotation(track.rotation + (
|
|
17184
|
+
const totalRotation = normalizeRotation(track.rotation + (trackOptions.rotate ?? 0));
|
|
17100
17185
|
const outputSupportsRotation = this.output.format.supportsVideoRotationMetadata;
|
|
17101
17186
|
const [originalWidth, originalHeight] = totalRotation % 180 === 0 ? [track.codedWidth, track.codedHeight] : [track.codedHeight, track.codedWidth];
|
|
17102
17187
|
let width = originalWidth;
|
|
17103
17188
|
let height = originalHeight;
|
|
17104
17189
|
const aspectRatio = width / height;
|
|
17105
17190
|
const ceilToMultipleOfTwo = (value) => Math.ceil(value / 2) * 2;
|
|
17106
|
-
if (
|
|
17107
|
-
width = ceilToMultipleOfTwo(
|
|
17191
|
+
if (trackOptions.width !== void 0 && trackOptions.height === void 0) {
|
|
17192
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
17108
17193
|
height = ceilToMultipleOfTwo(Math.round(width / aspectRatio));
|
|
17109
|
-
} else if (
|
|
17110
|
-
height = ceilToMultipleOfTwo(
|
|
17194
|
+
} else if (trackOptions.width === void 0 && trackOptions.height !== void 0) {
|
|
17195
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
17111
17196
|
width = ceilToMultipleOfTwo(Math.round(height * aspectRatio));
|
|
17112
|
-
} else if (
|
|
17113
|
-
width = ceilToMultipleOfTwo(
|
|
17114
|
-
height = ceilToMultipleOfTwo(
|
|
17197
|
+
} else if (trackOptions.width !== void 0 && trackOptions.height !== void 0) {
|
|
17198
|
+
width = ceilToMultipleOfTwo(trackOptions.width);
|
|
17199
|
+
height = ceilToMultipleOfTwo(trackOptions.height);
|
|
17115
17200
|
}
|
|
17116
17201
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
17117
|
-
const needsTranscode = !!
|
|
17202
|
+
const needsTranscode = !!trackOptions.forceTranscode || this._startTimestamp > 0 || firstTimestamp < 0 || !!trackOptions.frameRate;
|
|
17118
17203
|
const needsRerender = width !== originalWidth || height !== originalHeight || totalRotation !== 0 && !outputSupportsRotation;
|
|
17119
17204
|
let videoCodecs = this.output.format.getSupportedVideoCodecs();
|
|
17120
|
-
if (!needsTranscode && !
|
|
17205
|
+
if (!needsTranscode && !trackOptions.bitrate && !needsRerender && videoCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec)) {
|
|
17121
17206
|
const source = new EncodedVideoPacketSource(sourceCodec);
|
|
17122
17207
|
videoSource = source;
|
|
17123
17208
|
this._trackPromises.push((async () => {
|
|
@@ -17148,10 +17233,10 @@ ${cue.notes ?? ""}`;
|
|
|
17148
17233
|
});
|
|
17149
17234
|
return;
|
|
17150
17235
|
}
|
|
17151
|
-
if (
|
|
17152
|
-
videoCodecs = videoCodecs.filter((codec) => codec ===
|
|
17236
|
+
if (trackOptions.codec) {
|
|
17237
|
+
videoCodecs = videoCodecs.filter((codec) => codec === trackOptions.codec);
|
|
17153
17238
|
}
|
|
17154
|
-
const bitrate =
|
|
17239
|
+
const bitrate = trackOptions.bitrate ?? QUALITY_HIGH;
|
|
17155
17240
|
const encodableCodec = await getFirstEncodableVideoCodec(videoCodecs, { width, height, bitrate });
|
|
17156
17241
|
if (!encodableCodec) {
|
|
17157
17242
|
this.discardedTracks.push({
|
|
@@ -17173,13 +17258,13 @@ ${cue.notes ?? ""}`;
|
|
|
17173
17258
|
const sink = new CanvasSink(track, {
|
|
17174
17259
|
width,
|
|
17175
17260
|
height,
|
|
17176
|
-
fit:
|
|
17261
|
+
fit: trackOptions.fit ?? "fill",
|
|
17177
17262
|
rotation: totalRotation,
|
|
17178
17263
|
// Bake the rotation into the output
|
|
17179
17264
|
poolSize: 1
|
|
17180
17265
|
});
|
|
17181
17266
|
const iterator = sink.canvases(this._startTimestamp, this._endTimestamp);
|
|
17182
|
-
const frameRate =
|
|
17267
|
+
const frameRate = trackOptions.frameRate;
|
|
17183
17268
|
let lastCanvas = null;
|
|
17184
17269
|
let lastCanvasTimestamp = null;
|
|
17185
17270
|
let lastCanvasEndTimestamp = null;
|
|
@@ -17241,7 +17326,7 @@ ${cue.notes ?? ""}`;
|
|
|
17241
17326
|
this._trackPromises.push((async () => {
|
|
17242
17327
|
await this._started;
|
|
17243
17328
|
const sink = new VideoSampleSink(track);
|
|
17244
|
-
const frameRate =
|
|
17329
|
+
const frameRate = trackOptions.frameRate;
|
|
17245
17330
|
let lastSample = null;
|
|
17246
17331
|
let lastSampleTimestamp = null;
|
|
17247
17332
|
let lastSampleEndTimestamp = null;
|
|
@@ -17301,7 +17386,7 @@ ${cue.notes ?? ""}`;
|
|
|
17301
17386
|
}
|
|
17302
17387
|
}
|
|
17303
17388
|
this.output.addVideoTrack(videoSource, {
|
|
17304
|
-
frameRate:
|
|
17389
|
+
frameRate: trackOptions.frameRate,
|
|
17305
17390
|
languageCode: track.languageCode,
|
|
17306
17391
|
rotation: needsRerender ? 0 : totalRotation
|
|
17307
17392
|
// Rerendering will bake the rotation into the output
|
|
@@ -17311,7 +17396,7 @@ ${cue.notes ?? ""}`;
|
|
|
17311
17396
|
this.utilizedTracks.push(track);
|
|
17312
17397
|
}
|
|
17313
17398
|
/** @internal */
|
|
17314
|
-
async _processAudioTrack(track) {
|
|
17399
|
+
async _processAudioTrack(track, trackOptions) {
|
|
17315
17400
|
const sourceCodec = track.codec;
|
|
17316
17401
|
if (!sourceCodec) {
|
|
17317
17402
|
this.discardedTracks.push({
|
|
@@ -17324,11 +17409,11 @@ ${cue.notes ?? ""}`;
|
|
|
17324
17409
|
const originalNumberOfChannels = track.numberOfChannels;
|
|
17325
17410
|
const originalSampleRate = track.sampleRate;
|
|
17326
17411
|
const firstTimestamp = await track.getFirstTimestamp();
|
|
17327
|
-
let numberOfChannels =
|
|
17328
|
-
let sampleRate =
|
|
17412
|
+
let numberOfChannels = trackOptions.numberOfChannels ?? originalNumberOfChannels;
|
|
17413
|
+
let sampleRate = trackOptions.sampleRate ?? originalSampleRate;
|
|
17329
17414
|
let needsResample = numberOfChannels !== originalNumberOfChannels || sampleRate !== originalSampleRate || this._startTimestamp > 0 || firstTimestamp < 0;
|
|
17330
17415
|
let audioCodecs = this.output.format.getSupportedAudioCodecs();
|
|
17331
|
-
if (!
|
|
17416
|
+
if (!trackOptions.forceTranscode && !trackOptions.bitrate && !needsResample && audioCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec)) {
|
|
17332
17417
|
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
17333
17418
|
audioSource = source;
|
|
17334
17419
|
this._trackPromises.push((async () => {
|
|
@@ -17360,10 +17445,10 @@ ${cue.notes ?? ""}`;
|
|
|
17360
17445
|
return;
|
|
17361
17446
|
}
|
|
17362
17447
|
let codecOfChoice = null;
|
|
17363
|
-
if (
|
|
17364
|
-
audioCodecs = audioCodecs.filter((codec) => codec ===
|
|
17448
|
+
if (trackOptions.codec) {
|
|
17449
|
+
audioCodecs = audioCodecs.filter((codec) => codec === trackOptions.codec);
|
|
17365
17450
|
}
|
|
17366
|
-
const bitrate =
|
|
17451
|
+
const bitrate = trackOptions.bitrate ?? QUALITY_HIGH;
|
|
17367
17452
|
const encodableCodecs = await getEncodableAudioCodecs(audioCodecs, {
|
|
17368
17453
|
numberOfChannels,
|
|
17369
17454
|
sampleRate,
|