mediabunny 1.50.8 → 1.51.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 +428 -201
- package/dist/bundles/mediabunny.min.cjs +12 -12
- package/dist/bundles/mediabunny.min.mjs +12 -12
- package/dist/bundles/mediabunny.mjs +428 -201
- package/dist/bundles/mediabunny.node.cjs +428 -201
- package/dist/mediabunny.d.ts +57 -4
- package/dist/modules/src/codec-data.d.ts.map +1 -1
- package/dist/modules/src/codec-data.js +15 -1
- package/dist/modules/src/conversion.d.ts +47 -4
- package/dist/modules/src/conversion.d.ts.map +1 -1
- package/dist/modules/src/conversion.js +420 -188
- package/dist/modules/src/hls/hls-muxer.js +5 -5
- package/dist/modules/src/index.d.ts +1 -1
- package/dist/modules/src/index.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.d.ts.map +1 -1
- package/dist/modules/src/isobmff/isobmff-muxer.js +4 -3
- package/dist/modules/src/matroska/matroska-muxer.d.ts.map +1 -1
- package/dist/modules/src/matroska/matroska-muxer.js +4 -2
- package/dist/modules/src/mpeg-ts/mpeg-ts-muxer.js +1 -1
- package/dist/modules/src/ogg/ogg-muxer.js +1 -1
- package/dist/modules/src/output-format.js +1 -1
- package/dist/modules/src/output.d.ts +9 -0
- package/dist/modules/src/output.d.ts.map +1 -1
- package/dist/modules/src/output.js +34 -13
- package/dist/modules/src/tsconfig.tsbuildinfo +1 -1
- package/dist/modules/src/wave/wave-demuxer.d.ts.map +1 -1
- package/dist/modules/src/wave/wave-demuxer.js +17 -1
- package/package.json +5 -5
- package/src/codec-data.ts +14 -1
- package/src/conversion.ts +347 -139
- package/src/hls/hls-muxer.ts +5 -5
- package/src/index.ts +1 -0
- package/src/isobmff/isobmff-muxer.ts +5 -3
- package/src/matroska/matroska-muxer.ts +5 -2
- package/src/mpeg-ts/mpeg-ts-muxer.ts +1 -1
- package/src/ogg/ogg-muxer.ts +1 -1
- package/src/output-format.ts +1 -1
- package/src/output.ts +41 -13
- package/src/wave/wave-demuxer.ts +25 -1
|
@@ -4215,8 +4215,17 @@ var Mediabunny = (() => {
|
|
|
4215
4215
|
];
|
|
4216
4216
|
var parseOpusTocByte = (packet) => {
|
|
4217
4217
|
const config = packet[0] >> 3;
|
|
4218
|
+
const code = packet[0] & 3;
|
|
4219
|
+
let frameCount;
|
|
4220
|
+
if (code === 0) {
|
|
4221
|
+
frameCount = 1;
|
|
4222
|
+
} else if (code === 1 || code === 2) {
|
|
4223
|
+
frameCount = 2;
|
|
4224
|
+
} else {
|
|
4225
|
+
frameCount = packet[1] & 63;
|
|
4226
|
+
}
|
|
4218
4227
|
return {
|
|
4219
|
-
durationInSamples: OPUS_FRAME_DURATION_TABLE[config]
|
|
4228
|
+
durationInSamples: OPUS_FRAME_DURATION_TABLE[config] * frameCount
|
|
4220
4229
|
};
|
|
4221
4230
|
};
|
|
4222
4231
|
var parseModesFromVorbisSetupPacket = (setupHeader) => {
|
|
@@ -12379,6 +12388,21 @@ var Mediabunny = (() => {
|
|
|
12379
12388
|
if (formatTag === 7 /* MULAW */ || formatTag === 6 /* ALAW */) {
|
|
12380
12389
|
bitsPerSample = 8;
|
|
12381
12390
|
}
|
|
12391
|
+
if (formatTag !== 1 /* PCM */ && formatTag !== 3 /* IEEE_FLOAT */ && formatTag !== 6 /* ALAW */ && formatTag !== 7 /* MULAW */) {
|
|
12392
|
+
throw new Error(
|
|
12393
|
+
`Unsupported WAVE codec (format tag ${formatTag}). Only integer/float PCM, A-law, and \u03BC-law are supported.`
|
|
12394
|
+
);
|
|
12395
|
+
}
|
|
12396
|
+
if (formatTag === 1 /* PCM */ && ![8, 16, 24, 32].includes(bitsPerSample)) {
|
|
12397
|
+
throw new Error(
|
|
12398
|
+
`Unsupported WAVE PCM bit depth (${bitsPerSample}). Only 8, 16, 24, and 32 bits are supported.`
|
|
12399
|
+
);
|
|
12400
|
+
}
|
|
12401
|
+
if (formatTag === 3 /* IEEE_FLOAT */ && ![32, 64].includes(bitsPerSample)) {
|
|
12402
|
+
throw new Error(
|
|
12403
|
+
`Unsupported WAVE float bit depth (${bitsPerSample}). Only 32 and 64 bits are supported.`
|
|
12404
|
+
);
|
|
12405
|
+
}
|
|
12382
12406
|
this.audioInfo = {
|
|
12383
12407
|
format: formatTag,
|
|
12384
12408
|
numberOfChannels: numChannels,
|
|
@@ -12527,9 +12551,11 @@ var Mediabunny = (() => {
|
|
|
12527
12551
|
if (this.audioInfo.format === 3 /* IEEE_FLOAT */) {
|
|
12528
12552
|
if (this.audioInfo.sampleSizeInBytes === 4) {
|
|
12529
12553
|
return "pcm-f32";
|
|
12554
|
+
} else if (this.audioInfo.sampleSizeInBytes === 8) {
|
|
12555
|
+
return "pcm-f64";
|
|
12530
12556
|
}
|
|
12531
12557
|
}
|
|
12532
|
-
|
|
12558
|
+
assert(false);
|
|
12533
12559
|
}
|
|
12534
12560
|
async getMimeType() {
|
|
12535
12561
|
return "audio/wav";
|
|
@@ -28949,6 +28975,7 @@ var Mediabunny = (() => {
|
|
|
28949
28975
|
this.isQuickTime = format instanceof MovOutputFormat;
|
|
28950
28976
|
this.isCmaf = format instanceof CmafOutputFormat;
|
|
28951
28977
|
this.minimumFragmentDuration = format._options.minimumFragmentDuration ?? (format instanceof CmafOutputFormat ? Infinity : 1);
|
|
28978
|
+
this.auxWriter.start();
|
|
28952
28979
|
}
|
|
28953
28980
|
async start() {
|
|
28954
28981
|
const release = await this.mutex.acquire();
|
|
@@ -28973,7 +29000,7 @@ var Mediabunny = (() => {
|
|
|
28973
29000
|
this.initWriter = initWriter;
|
|
28974
29001
|
this.initBoxWriter = new IsobmffBoxWriter(initWriter);
|
|
28975
29002
|
}
|
|
28976
|
-
const holdsAvc = this.output.
|
|
29003
|
+
const holdsAvc = this.output.tracks.some((x) => x.isVideoTrack() && x.source._codec === "avc");
|
|
28977
29004
|
{
|
|
28978
29005
|
const boxWriter = this.initBoxWriter ?? this.boxWriter;
|
|
28979
29006
|
assert(boxWriter);
|
|
@@ -28997,7 +29024,7 @@ var Mediabunny = (() => {
|
|
|
28997
29024
|
}
|
|
28998
29025
|
if (this.fastStart === "in-memory") {
|
|
28999
29026
|
} else if (this.fastStart === "reserve") {
|
|
29000
|
-
for (const track of this.output.
|
|
29027
|
+
for (const track of this.output.tracks) {
|
|
29001
29028
|
if (track.metadata.maximumPacketCount === void 0) {
|
|
29002
29029
|
throw new Error(
|
|
29003
29030
|
"All tracks must specify maximumPacketCount in their metadata when using fastStart: 'reserve'."
|
|
@@ -29018,7 +29045,7 @@ var Mediabunny = (() => {
|
|
|
29018
29045
|
release();
|
|
29019
29046
|
}
|
|
29020
29047
|
allTracksAreKnown() {
|
|
29021
|
-
for (const track of this.output.
|
|
29048
|
+
for (const track of this.output.tracks) {
|
|
29022
29049
|
if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {
|
|
29023
29050
|
return false;
|
|
29024
29051
|
}
|
|
@@ -30412,7 +30439,7 @@ var Mediabunny = (() => {
|
|
|
30412
30439
|
return this.ebmlWriter.dataOffsets.get(this.segment);
|
|
30413
30440
|
}
|
|
30414
30441
|
allTracksAreKnown() {
|
|
30415
|
-
for (const track of this.output.
|
|
30442
|
+
for (const track of this.output.tracks) {
|
|
30416
30443
|
if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {
|
|
30417
30444
|
return false;
|
|
30418
30445
|
}
|
|
@@ -30750,7 +30777,8 @@ ${cue.notes ?? ""}`;
|
|
|
30750
30777
|
view2.setUint8(0, 128 | trackData.track.id);
|
|
30751
30778
|
view2.setInt16(1, relativeTimestamp, false);
|
|
30752
30779
|
const msDuration = Math.round(1e3 * chunk.duration);
|
|
30753
|
-
|
|
30780
|
+
const needsBlockGroup = !!chunk.additions || trackData.type === "subtitle";
|
|
30781
|
+
if (!needsBlockGroup) {
|
|
30754
30782
|
view2.setUint8(3, Number(chunk.type === "key") << 7);
|
|
30755
30783
|
const simpleBlock = { id: 163 /* SimpleBlock */, data: [
|
|
30756
30784
|
prelude,
|
|
@@ -31260,7 +31288,7 @@ ${cue.notes ?? ""}`;
|
|
|
31260
31288
|
throw new Error("Subtitle tracks are not supported.");
|
|
31261
31289
|
}
|
|
31262
31290
|
allTracksAreKnown() {
|
|
31263
|
-
for (const track of this.output.
|
|
31291
|
+
for (const track of this.output.tracks) {
|
|
31264
31292
|
if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {
|
|
31265
31293
|
return false;
|
|
31266
31294
|
}
|
|
@@ -31715,7 +31743,7 @@ ${cue.notes ?? ""}`;
|
|
|
31715
31743
|
return result;
|
|
31716
31744
|
}
|
|
31717
31745
|
allTracksAreKnown() {
|
|
31718
|
-
for (const track of this.output.
|
|
31746
|
+
for (const track of this.output.tracks) {
|
|
31719
31747
|
if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) {
|
|
31720
31748
|
return false;
|
|
31721
31749
|
}
|
|
@@ -34539,8 +34567,8 @@ ${cue.notes ?? ""}`;
|
|
|
34539
34567
|
}
|
|
34540
34568
|
async start() {
|
|
34541
34569
|
const release = await this.mutex.acquire();
|
|
34542
|
-
const someRelative = this.output.
|
|
34543
|
-
const someNotRelative = this.output.
|
|
34570
|
+
const someRelative = this.output.tracks.some((t) => t.metadata.isRelativeToUnixEpoch);
|
|
34571
|
+
const someNotRelative = this.output.tracks.some((t) => !t.metadata.isRelativeToUnixEpoch);
|
|
34544
34572
|
if (someRelative && someNotRelative) {
|
|
34545
34573
|
throw new Error(
|
|
34546
34574
|
"All tracks must agree on `relativeToUnixEpoch`: some tracks are relative to the Unix epoch and some are not."
|
|
@@ -34552,12 +34580,12 @@ ${cue.notes ?? ""}`;
|
|
|
34552
34580
|
let hasVideo = false;
|
|
34553
34581
|
let illegalPairingDetected = false;
|
|
34554
34582
|
let keyPacketsOnlyPairingWarned = false;
|
|
34555
|
-
for (const track of this.output.
|
|
34583
|
+
for (const track of this.output.tracks) {
|
|
34556
34584
|
if (track.type === "video") {
|
|
34557
34585
|
hasVideo = true;
|
|
34558
34586
|
}
|
|
34559
34587
|
const pairableGroups = /* @__PURE__ */ new Map();
|
|
34560
|
-
for (const otherTrack of this.output.
|
|
34588
|
+
for (const otherTrack of this.output.tracks) {
|
|
34561
34589
|
if (track === otherTrack) {
|
|
34562
34590
|
continue;
|
|
34563
34591
|
}
|
|
@@ -34611,7 +34639,7 @@ ${cue.notes ?? ""}`;
|
|
|
34611
34639
|
const variantStreams = [];
|
|
34612
34640
|
const unpairedVideoTracks = [];
|
|
34613
34641
|
const unpairedAudioTracks = [];
|
|
34614
|
-
for (const track of this.output.
|
|
34642
|
+
for (const track of this.output.tracks) {
|
|
34615
34643
|
const assignedGroupKeys = groupAssignment.get(track);
|
|
34616
34644
|
if (assignedGroupKeys) {
|
|
34617
34645
|
assert(assignedGroupKeys.length > 0);
|
|
@@ -35907,7 +35935,7 @@ ${cue.notes ?? ""}`;
|
|
|
35907
35935
|
getSupportedCodecs() {
|
|
35908
35936
|
return [
|
|
35909
35937
|
...PCM_AUDIO_CODECS.filter(
|
|
35910
|
-
(codec) => ["pcm-s16", "pcm-s24", "pcm-s32", "pcm-f32", "pcm-u8", "ulaw", "alaw"].includes(codec)
|
|
35938
|
+
(codec) => ["pcm-s16", "pcm-s24", "pcm-s32", "pcm-f32", "pcm-f64", "pcm-u8", "ulaw", "alaw"].includes(codec)
|
|
35911
35939
|
)
|
|
35912
35940
|
];
|
|
35913
35941
|
}
|
|
@@ -36333,6 +36361,10 @@ ${cue.notes ?? ""}`;
|
|
|
36333
36361
|
* {@link BaseTrackMetadata.group}.
|
|
36334
36362
|
*/
|
|
36335
36363
|
this.defaultTrackGroup = new OutputTrackGroup();
|
|
36364
|
+
/**
|
|
36365
|
+
* The tracks that have been added to this output. Treat it as a readonly field; to add tracks, use the methods.
|
|
36366
|
+
*/
|
|
36367
|
+
this.tracks = [];
|
|
36336
36368
|
/** @internal */
|
|
36337
36369
|
this._onFinalize = null;
|
|
36338
36370
|
/** @internal */
|
|
@@ -36340,8 +36372,6 @@ ${cue.notes ?? ""}`;
|
|
|
36340
36372
|
/** @internal */
|
|
36341
36373
|
this._rootWriterPromise = null;
|
|
36342
36374
|
/** @internal */
|
|
36343
|
-
this._tracks = [];
|
|
36344
|
-
/** @internal */
|
|
36345
36375
|
this._startPromise = null;
|
|
36346
36376
|
/** @internal */
|
|
36347
36377
|
this._cancelPromise = null;
|
|
@@ -36521,7 +36551,7 @@ ${cue.notes ?? ""}`;
|
|
|
36521
36551
|
const metadataCopy = { ...metadata };
|
|
36522
36552
|
metadataCopy.group ??= this.defaultTrackGroup;
|
|
36523
36553
|
return this._addTrack(new OutputVideoTrack2(
|
|
36524
|
-
this.
|
|
36554
|
+
this.tracks.length + 1,
|
|
36525
36555
|
this,
|
|
36526
36556
|
source,
|
|
36527
36557
|
metadataCopy
|
|
@@ -36536,7 +36566,7 @@ ${cue.notes ?? ""}`;
|
|
|
36536
36566
|
const metadataCopy = { ...metadata };
|
|
36537
36567
|
metadataCopy.group ??= this.defaultTrackGroup;
|
|
36538
36568
|
return this._addTrack(new OutputAudioTrack2(
|
|
36539
|
-
this.
|
|
36569
|
+
this.tracks.length + 1,
|
|
36540
36570
|
this,
|
|
36541
36571
|
source,
|
|
36542
36572
|
metadataCopy
|
|
@@ -36551,7 +36581,7 @@ ${cue.notes ?? ""}`;
|
|
|
36551
36581
|
const metadataCopy = { ...metadata };
|
|
36552
36582
|
metadataCopy.group ??= this.defaultTrackGroup;
|
|
36553
36583
|
return this._addTrack(new OutputSubtitleTrack2(
|
|
36554
|
-
this.
|
|
36584
|
+
this.tracks.length + 1,
|
|
36555
36585
|
this,
|
|
36556
36586
|
source,
|
|
36557
36587
|
metadataCopy
|
|
@@ -36579,7 +36609,7 @@ ${cue.notes ?? ""}`;
|
|
|
36579
36609
|
throw new Error("Source is already used for a track.");
|
|
36580
36610
|
}
|
|
36581
36611
|
const supportedTrackCounts = this.format.getSupportedTrackCounts();
|
|
36582
|
-
const presentTracksOfThisType = this.
|
|
36612
|
+
const presentTracksOfThisType = this.tracks.reduce(
|
|
36583
36613
|
(count, t) => count + (t.type === track.type ? 1 : 0),
|
|
36584
36614
|
0
|
|
36585
36615
|
);
|
|
@@ -36590,7 +36620,7 @@ ${cue.notes ?? ""}`;
|
|
|
36590
36620
|
);
|
|
36591
36621
|
}
|
|
36592
36622
|
const maxTotalCount = supportedTrackCounts.total.max;
|
|
36593
|
-
if (this.
|
|
36623
|
+
if (this.tracks.length === maxTotalCount) {
|
|
36594
36624
|
throw new Error(
|
|
36595
36625
|
`${this.format._name} does not support more than ${maxTotalCount} tracks${maxTotalCount === 1 ? "" : "s"} in total.`
|
|
36596
36626
|
);
|
|
@@ -36629,10 +36659,32 @@ ${cue.notes ?? ""}`;
|
|
|
36629
36659
|
);
|
|
36630
36660
|
}
|
|
36631
36661
|
}
|
|
36632
|
-
this.
|
|
36662
|
+
this.tracks.push(track);
|
|
36633
36663
|
track.source._connectedTrack = track;
|
|
36634
36664
|
return track;
|
|
36635
36665
|
}
|
|
36666
|
+
/**
|
|
36667
|
+
* Whether the output has enough tracks (of the correct type) to be started, based on the requirements of the output
|
|
36668
|
+
* format.
|
|
36669
|
+
*/
|
|
36670
|
+
hasEnoughTracks() {
|
|
36671
|
+
const supportedTrackCounts = this.format.getSupportedTrackCounts();
|
|
36672
|
+
for (const trackType of ALL_TRACK_TYPES) {
|
|
36673
|
+
const presentTracksOfThisType = this.tracks.reduce(
|
|
36674
|
+
(count, track) => count + (track.type === trackType ? 1 : 0),
|
|
36675
|
+
0
|
|
36676
|
+
);
|
|
36677
|
+
const minCount = supportedTrackCounts[trackType].min;
|
|
36678
|
+
if (presentTracksOfThisType < minCount) {
|
|
36679
|
+
return false;
|
|
36680
|
+
}
|
|
36681
|
+
}
|
|
36682
|
+
const totalMinCount = supportedTrackCounts.total.min;
|
|
36683
|
+
if (this.tracks.length < totalMinCount) {
|
|
36684
|
+
return false;
|
|
36685
|
+
}
|
|
36686
|
+
return true;
|
|
36687
|
+
}
|
|
36636
36688
|
/**
|
|
36637
36689
|
* Starts the creation of the output file. This method should be called after all tracks have been added. Only after
|
|
36638
36690
|
* the output has started can media samples be added to the tracks.
|
|
@@ -36642,7 +36694,7 @@ ${cue.notes ?? ""}`;
|
|
|
36642
36694
|
async start() {
|
|
36643
36695
|
const supportedTrackCounts = this.format.getSupportedTrackCounts();
|
|
36644
36696
|
for (const trackType of ALL_TRACK_TYPES) {
|
|
36645
|
-
const presentTracksOfThisType = this.
|
|
36697
|
+
const presentTracksOfThisType = this.tracks.reduce(
|
|
36646
36698
|
(count, track) => count + (track.type === trackType ? 1 : 0),
|
|
36647
36699
|
0
|
|
36648
36700
|
);
|
|
@@ -36654,7 +36706,7 @@ ${cue.notes ?? ""}`;
|
|
|
36654
36706
|
}
|
|
36655
36707
|
}
|
|
36656
36708
|
const totalMinCount = supportedTrackCounts.total.min;
|
|
36657
|
-
if (this.
|
|
36709
|
+
if (this.tracks.length < totalMinCount) {
|
|
36658
36710
|
throw new Error(
|
|
36659
36711
|
totalMinCount === supportedTrackCounts.total.max ? `${this.format._name} requires exactly ${totalMinCount} track${totalMinCount === 1 ? "" : "s"}.` : `${this.format._name} requires at least ${totalMinCount} track${totalMinCount === 1 ? "" : "s"}.`
|
|
36660
36712
|
);
|
|
@@ -36671,7 +36723,7 @@ ${cue.notes ?? ""}`;
|
|
|
36671
36723
|
const release = await this._mutex.acquire();
|
|
36672
36724
|
try {
|
|
36673
36725
|
await this._muxer.start();
|
|
36674
|
-
const promises = this.
|
|
36726
|
+
const promises = this.tracks.map((track) => track.source._start());
|
|
36675
36727
|
await Promise.all(promises);
|
|
36676
36728
|
} finally {
|
|
36677
36729
|
release();
|
|
@@ -36706,7 +36758,7 @@ ${cue.notes ?? ""}`;
|
|
|
36706
36758
|
this.state = "canceled";
|
|
36707
36759
|
const release = await this._mutex.acquire();
|
|
36708
36760
|
try {
|
|
36709
|
-
const promises = this.
|
|
36761
|
+
const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(true));
|
|
36710
36762
|
await Promise.all(promises);
|
|
36711
36763
|
await Promise.all([...this._unfinalizedTargets].map((target) => target._close()));
|
|
36712
36764
|
this._unfinalizedTargets.clear();
|
|
@@ -36734,7 +36786,7 @@ ${cue.notes ?? ""}`;
|
|
|
36734
36786
|
this.state = "finalizing";
|
|
36735
36787
|
const release = await this._mutex.acquire();
|
|
36736
36788
|
try {
|
|
36737
|
-
const promises = this.
|
|
36789
|
+
const promises = this.tracks.map((x) => x.source._flushOrWaitForOngoingClose(false));
|
|
36738
36790
|
await Promise.all(promises);
|
|
36739
36791
|
await this._muxer.finalize();
|
|
36740
36792
|
if (this._rootWriterPromise) {
|
|
@@ -36873,14 +36925,15 @@ ${cue.notes ?? ""}`;
|
|
|
36873
36925
|
var Conversion = class _Conversion {
|
|
36874
36926
|
/** Creates a new Conversion instance (duh). */
|
|
36875
36927
|
constructor(options) {
|
|
36876
|
-
/**
|
|
36877
|
-
|
|
36878
|
-
|
|
36879
|
-
|
|
36880
|
-
|
|
36881
|
-
|
|
36882
|
-
|
|
36883
|
-
|
|
36928
|
+
/**
|
|
36929
|
+
* The current state of the conversion.
|
|
36930
|
+
*
|
|
36931
|
+
* - `'idle'`: The conversion is not currently executing and isn't done; `execute` can be called.
|
|
36932
|
+
* - `'executing'`: A call to `execute` is currently running.
|
|
36933
|
+
* - `'canceled'`: The conversion has been canceled and can no longer be executed.
|
|
36934
|
+
* - `'done'`: The conversion has run to completion. Subsequent calls to `execute` do nothing.
|
|
36935
|
+
*/
|
|
36936
|
+
this.state = "idle";
|
|
36884
36937
|
/** @internal */
|
|
36885
36938
|
this._nextOutputTrackId = 0;
|
|
36886
36939
|
/** @internal */
|
|
@@ -36888,18 +36941,22 @@ ${cue.notes ?? ""}`;
|
|
|
36888
36941
|
/** @internal */
|
|
36889
36942
|
this._outputOwnTrackGroups = [];
|
|
36890
36943
|
/** @internal */
|
|
36891
|
-
this.
|
|
36944
|
+
this._trackPumps = [];
|
|
36945
|
+
/** @internal */
|
|
36946
|
+
this._composable = false;
|
|
36892
36947
|
/** @internal */
|
|
36893
36948
|
this._executed = false;
|
|
36894
36949
|
/** @internal */
|
|
36895
|
-
this.
|
|
36950
|
+
this._executionUntil = Infinity;
|
|
36951
|
+
/** @internal */
|
|
36952
|
+
this._pauseRequested = false;
|
|
36953
|
+
/** @internal */
|
|
36954
|
+
this._synchronizer = new TrackSynchronizer(this);
|
|
36896
36955
|
/** @internal */
|
|
36897
36956
|
this._totalDuration = null;
|
|
36898
36957
|
/** @internal */
|
|
36899
36958
|
this._maxTimestamps = /* @__PURE__ */ new Map();
|
|
36900
36959
|
// Track ID -> timestamp
|
|
36901
|
-
/** @internal */
|
|
36902
|
-
this._canceled = false;
|
|
36903
36960
|
/**
|
|
36904
36961
|
* A callback that is fired whenever the conversion progresses. Gets passed as first argument a number between
|
|
36905
36962
|
* 0 and 1, indicating the completion of the conversion. Note that a progress of 1 doesn't necessarily mean the
|
|
@@ -36916,7 +36973,8 @@ ${cue.notes ?? ""}`;
|
|
|
36916
36973
|
this._lastProgress = 0;
|
|
36917
36974
|
/**
|
|
36918
36975
|
* Whether this conversion, as it has been configured, is valid and can be executed. If this field is `false`, check
|
|
36919
|
-
* the `discardedTracks` field for reasons.
|
|
36976
|
+
* the `discardedTracks` field for reasons. Composable conversions are always valid, even if they utilize
|
|
36977
|
+
* zero tracks.
|
|
36920
36978
|
*
|
|
36921
36979
|
* Note: a conversion having discarded tracks does not automatically mean it is invalid; if the remaining, utilized
|
|
36922
36980
|
* tracks make for a valid output file, the conversion is still allowed.
|
|
@@ -36943,8 +37001,23 @@ ${cue.notes ?? ""}`;
|
|
|
36943
37001
|
"options.tracks, when provided, must be either 'all' or 'primary'."
|
|
36944
37002
|
);
|
|
36945
37003
|
}
|
|
36946
|
-
if (options.
|
|
36947
|
-
throw new TypeError("options.
|
|
37004
|
+
if (options.composable !== void 0 && typeof options.composable !== "boolean") {
|
|
37005
|
+
throw new TypeError("options.composable, when provided, must be a boolean.");
|
|
37006
|
+
}
|
|
37007
|
+
const composable = options.composable ?? false;
|
|
37008
|
+
if (!composable) {
|
|
37009
|
+
if (options.output.tracks.length > 0 || Object.keys(options.output._metadataTags).length > 0 || options.output.state !== "pending") {
|
|
37010
|
+
throw new TypeError("options.output must be fresh: no tracks or metadata tags added and not started.");
|
|
37011
|
+
}
|
|
37012
|
+
} else {
|
|
37013
|
+
if (options.tags !== void 0) {
|
|
37014
|
+
throw new TypeError(
|
|
37015
|
+
"options.tags cannot be set by a composable conversion; set metadata directly on the output instead."
|
|
37016
|
+
);
|
|
37017
|
+
}
|
|
37018
|
+
if (options.output.state !== "pending") {
|
|
37019
|
+
throw new TypeError("options.output must not have been started yet.");
|
|
37020
|
+
}
|
|
36948
37021
|
}
|
|
36949
37022
|
if (options.video !== void 0 && typeof options.video !== "function") {
|
|
36950
37023
|
if (Array.isArray(options.video)) {
|
|
@@ -36988,11 +37061,9 @@ ${cue.notes ?? ""}`;
|
|
|
36988
37061
|
throw new TypeError("options.showWarnings, when provided, must be a boolean.");
|
|
36989
37062
|
}
|
|
36990
37063
|
this._options = options;
|
|
37064
|
+
this._composable = composable;
|
|
36991
37065
|
this.input = options.input;
|
|
36992
37066
|
this.output = options.output;
|
|
36993
|
-
const { promise: started, resolve: start } = promiseWithResolvers();
|
|
36994
|
-
this._started = started;
|
|
36995
|
-
this._start = start;
|
|
36996
37067
|
}
|
|
36997
37068
|
/** Initializes a new conversion process without starting the conversion. */
|
|
36998
37069
|
static async init(options) {
|
|
@@ -37104,7 +37175,7 @@ ${cue.notes ?? ""}`;
|
|
|
37104
37175
|
const track = filteredTracks[i];
|
|
37105
37176
|
const options = filteredTrackOptions[i];
|
|
37106
37177
|
for (const option of options) {
|
|
37107
|
-
if (this.
|
|
37178
|
+
if (this.output.tracks.length === outputTrackCounts.total.max) {
|
|
37108
37179
|
this.discardedTracks.push({
|
|
37109
37180
|
track,
|
|
37110
37181
|
reason: "max_track_count_reached",
|
|
@@ -37112,7 +37183,11 @@ ${cue.notes ?? ""}`;
|
|
|
37112
37183
|
});
|
|
37113
37184
|
continue;
|
|
37114
37185
|
}
|
|
37115
|
-
|
|
37186
|
+
const addedCountOfType = this.output.tracks.reduce(
|
|
37187
|
+
(count, t) => count + (t.type === track.type ? 1 : 0),
|
|
37188
|
+
0
|
|
37189
|
+
);
|
|
37190
|
+
if (addedCountOfType === outputTrackCounts[track.type].max) {
|
|
37116
37191
|
this.discardedTracks.push({
|
|
37117
37192
|
track,
|
|
37118
37193
|
reason: "max_track_count_of_type_reached",
|
|
@@ -37143,22 +37218,28 @@ ${cue.notes ?? ""}`;
|
|
|
37143
37218
|
}
|
|
37144
37219
|
}
|
|
37145
37220
|
}
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37151
|
-
|
|
37152
|
-
|
|
37153
|
-
|
|
37221
|
+
if (!this._composable) {
|
|
37222
|
+
const inputTags = await this.input.getMetadataTags();
|
|
37223
|
+
let outputTags;
|
|
37224
|
+
if (this._options.tags) {
|
|
37225
|
+
const result = typeof this._options.tags === "function" ? await this._options.tags(inputTags) : this._options.tags;
|
|
37226
|
+
validateMetadataTags(result);
|
|
37227
|
+
outputTags = result;
|
|
37228
|
+
} else {
|
|
37229
|
+
outputTags = inputTags;
|
|
37230
|
+
}
|
|
37231
|
+
const inputAndOutputFormatMatch = inputFormat.mimeType === this.output.format.mimeType;
|
|
37232
|
+
const rawTagsAreUnchanged = inputTags.raw === outputTags.raw;
|
|
37233
|
+
if (inputTags.raw && rawTagsAreUnchanged && !inputAndOutputFormatMatch) {
|
|
37234
|
+
delete outputTags.raw;
|
|
37235
|
+
}
|
|
37236
|
+
this.output.setMetadataTags(outputTags);
|
|
37154
37237
|
}
|
|
37155
|
-
|
|
37156
|
-
|
|
37157
|
-
|
|
37158
|
-
|
|
37238
|
+
if (!this._composable) {
|
|
37239
|
+
this.isValid = this.output.hasEnoughTracks();
|
|
37240
|
+
} else {
|
|
37241
|
+
this.isValid = true;
|
|
37159
37242
|
}
|
|
37160
|
-
this.output.setMetadataTags(outputTags);
|
|
37161
|
-
this.isValid = this._totalTrackCount >= outputTrackCounts.total.min && this._addedCounts.video >= outputTrackCounts.video.min && this._addedCounts.audio >= outputTrackCounts.audio.min && this._addedCounts.subtitle >= outputTrackCounts.subtitle.min;
|
|
37162
37243
|
if (this._options.showWarnings ?? true) {
|
|
37163
37244
|
const warnElements = [];
|
|
37164
37245
|
const unintentionallyDiscardedTracks = this.discardedTracks.filter((x) => x.reason !== "discarded_by_user");
|
|
@@ -37244,59 +37325,116 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37244
37325
|
return elements;
|
|
37245
37326
|
}
|
|
37246
37327
|
/**
|
|
37247
|
-
* Executes the conversion process
|
|
37328
|
+
* Executes the conversion process and resolves when the conversion is complete. When
|
|
37329
|
+
* {@link ConversionExecuteOptions.until} is provided, the conversion will be suspended once that output timestamp
|
|
37330
|
+
* is reached and can be resumed with another call to `execute`. An ongoing execution may also be suspended via
|
|
37331
|
+
* {@link ConversionExecuteOptions.pauseSignal}.
|
|
37248
37332
|
*
|
|
37249
|
-
*
|
|
37333
|
+
* Execution will throw if `isValid` is `false`.
|
|
37250
37334
|
*/
|
|
37251
|
-
async execute() {
|
|
37335
|
+
async execute(options = {}) {
|
|
37336
|
+
if (!options || typeof options !== "object") {
|
|
37337
|
+
throw new TypeError("options must be an object.");
|
|
37338
|
+
}
|
|
37339
|
+
if (options.until !== void 0 && (typeof options.until !== "number" || Number.isNaN(options.until))) {
|
|
37340
|
+
throw new TypeError("options.until, when provided, must be a number.");
|
|
37341
|
+
}
|
|
37342
|
+
if (options.pauseSignal !== void 0 && !(options.pauseSignal instanceof AbortSignal)) {
|
|
37343
|
+
throw new TypeError("options.pauseSignal, when provided, must be an AbortSignal.");
|
|
37344
|
+
}
|
|
37252
37345
|
if (!this.isValid) {
|
|
37253
37346
|
throw new Error(
|
|
37254
37347
|
"Cannot execute this conversion because its output configuration is invalid. Make sure to always check the isValid field before executing a conversion.\n" + this._getInvalidityExplanation().join("")
|
|
37255
37348
|
);
|
|
37256
37349
|
}
|
|
37257
|
-
if (this.
|
|
37258
|
-
throw new Error("
|
|
37350
|
+
if (this.state === "executing") {
|
|
37351
|
+
throw new Error("Cannot call execute() while a previous call to execute() is still running.");
|
|
37259
37352
|
}
|
|
37260
|
-
this.
|
|
37261
|
-
|
|
37262
|
-
this._synchronizer.declareTrack(id);
|
|
37353
|
+
if (this.state === "canceled") {
|
|
37354
|
+
throw new ConversionCanceledError();
|
|
37263
37355
|
}
|
|
37264
|
-
if (this.
|
|
37265
|
-
|
|
37266
|
-
|
|
37267
|
-
|
|
37268
|
-
|
|
37269
|
-
|
|
37270
|
-
return await track.getDurationFromMetadata() ?? await track.computeDuration();
|
|
37271
|
-
});
|
|
37272
|
-
const duration = Math.max(0, ...await Promise.all(durationPromises));
|
|
37273
|
-
this._computeProgress = true;
|
|
37274
|
-
this._totalDuration = Math.min(
|
|
37275
|
-
duration - this._startTimestamp,
|
|
37276
|
-
this._endTimestamp - this._startTimestamp
|
|
37356
|
+
if (this.state === "done") {
|
|
37357
|
+
return;
|
|
37358
|
+
}
|
|
37359
|
+
if (this._composable && this.output.state === "pending") {
|
|
37360
|
+
throw new Error(
|
|
37361
|
+
"A composable conversion requires the output to be started. Call start() on the output before executing the conversion."
|
|
37277
37362
|
);
|
|
37363
|
+
}
|
|
37364
|
+
this.state = "executing";
|
|
37365
|
+
this._executionUntil = options.until ?? Infinity;
|
|
37366
|
+
this._pauseRequested = options.pauseSignal?.aborted ?? false;
|
|
37367
|
+
const onPause = () => {
|
|
37368
|
+
if (this.state !== "executing") {
|
|
37369
|
+
return;
|
|
37370
|
+
}
|
|
37371
|
+
this._pauseRequested = true;
|
|
37372
|
+
this._synchronizer.resolveAll();
|
|
37373
|
+
};
|
|
37374
|
+
options.pauseSignal?.addEventListener("abort", onPause);
|
|
37375
|
+
for (const pump of this._trackPumps) {
|
|
37376
|
+
if (!pump.done) {
|
|
37377
|
+
pump.resolvers = promiseWithResolvers();
|
|
37378
|
+
}
|
|
37379
|
+
}
|
|
37380
|
+
if (!this._executed) {
|
|
37381
|
+
this._executed = true;
|
|
37278
37382
|
for (const id of this._outputTrackIds) {
|
|
37279
|
-
this.
|
|
37383
|
+
this._synchronizer.declareTrack(id);
|
|
37384
|
+
}
|
|
37385
|
+
if (this.onProgress) {
|
|
37386
|
+
const uniqueUtilizedTracks = new Set(this.utilizedTracks);
|
|
37387
|
+
const durationPromises = [...uniqueUtilizedTracks].map(async (track) => {
|
|
37388
|
+
if (await track.isLive()) {
|
|
37389
|
+
return Infinity;
|
|
37390
|
+
}
|
|
37391
|
+
return await track.getDurationFromMetadata() ?? await track.computeDuration();
|
|
37392
|
+
});
|
|
37393
|
+
const duration = Math.max(0, ...await Promise.all(durationPromises));
|
|
37394
|
+
this._computeProgress = true;
|
|
37395
|
+
this._totalDuration = Math.min(
|
|
37396
|
+
duration - this._startTimestamp,
|
|
37397
|
+
this._endTimestamp - this._startTimestamp
|
|
37398
|
+
);
|
|
37399
|
+
for (const id of this._outputTrackIds) {
|
|
37400
|
+
this._maxTimestamps.set(id, 0);
|
|
37401
|
+
}
|
|
37402
|
+
this.onProgress?.(0, 0);
|
|
37403
|
+
}
|
|
37404
|
+
if (!this._composable) {
|
|
37405
|
+
await this.output.start();
|
|
37406
|
+
}
|
|
37407
|
+
for (const pump of this._trackPumps) {
|
|
37408
|
+
pump.start();
|
|
37409
|
+
}
|
|
37410
|
+
} else {
|
|
37411
|
+
for (const pump of this._trackPumps) {
|
|
37412
|
+
pump.wake?.();
|
|
37280
37413
|
}
|
|
37281
|
-
this.onProgress?.(0, 0);
|
|
37282
37414
|
}
|
|
37283
|
-
await this.output.start();
|
|
37284
|
-
this._start();
|
|
37285
37415
|
try {
|
|
37286
|
-
await Promise.all(this.
|
|
37416
|
+
await Promise.all(this._trackPumps.map((x) => x.resolvers.promise));
|
|
37287
37417
|
} catch (error) {
|
|
37288
|
-
if (
|
|
37418
|
+
if (this.state !== "canceled") {
|
|
37289
37419
|
void this.cancel();
|
|
37290
37420
|
}
|
|
37291
37421
|
throw error;
|
|
37422
|
+
} finally {
|
|
37423
|
+
options.pauseSignal?.removeEventListener("abort", onPause);
|
|
37292
37424
|
}
|
|
37293
|
-
if (this.
|
|
37425
|
+
if (this.state === "canceled") {
|
|
37294
37426
|
throw new ConversionCanceledError();
|
|
37295
37427
|
}
|
|
37296
|
-
|
|
37297
|
-
|
|
37298
|
-
|
|
37299
|
-
this.
|
|
37428
|
+
const isDone = this._trackPumps.every((x) => x.done);
|
|
37429
|
+
this.state = isDone ? "done" : "idle";
|
|
37430
|
+
if (isDone) {
|
|
37431
|
+
if (!this._composable) {
|
|
37432
|
+
await this.output.finalize();
|
|
37433
|
+
}
|
|
37434
|
+
if (this._computeProgress) {
|
|
37435
|
+
const minTimestamp = Math.min(...this._maxTimestamps.values());
|
|
37436
|
+
this.onProgress?.(1, minTimestamp);
|
|
37437
|
+
}
|
|
37300
37438
|
}
|
|
37301
37439
|
}
|
|
37302
37440
|
/**
|
|
@@ -37304,15 +37442,21 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37304
37442
|
* Does nothing if the conversion is already complete.
|
|
37305
37443
|
*/
|
|
37306
37444
|
async cancel() {
|
|
37307
|
-
if (this.
|
|
37445
|
+
if (this.state === "done") {
|
|
37308
37446
|
return;
|
|
37309
37447
|
}
|
|
37310
|
-
if (this.
|
|
37448
|
+
if (this.state === "canceled") {
|
|
37311
37449
|
Logging._warn("Conversion already canceled.");
|
|
37312
37450
|
return;
|
|
37313
37451
|
}
|
|
37314
|
-
this.
|
|
37315
|
-
|
|
37452
|
+
this.state = "canceled";
|
|
37453
|
+
for (const pump of this._trackPumps) {
|
|
37454
|
+
pump.wake?.();
|
|
37455
|
+
}
|
|
37456
|
+
this._synchronizer.resolveAll();
|
|
37457
|
+
if (!this._composable) {
|
|
37458
|
+
await this.output.cancel();
|
|
37459
|
+
}
|
|
37316
37460
|
}
|
|
37317
37461
|
/** @internal */
|
|
37318
37462
|
async _processVideoTrack(track, trackOptions, outputTrackId) {
|
|
@@ -37358,14 +37502,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37358
37502
|
if (!needsTranscode) {
|
|
37359
37503
|
const source = new EncodedVideoPacketSource(sourceCodec);
|
|
37360
37504
|
videoSource = source;
|
|
37361
|
-
this.
|
|
37362
|
-
await this._started;
|
|
37505
|
+
this._registerTrackPump(async (pump) => {
|
|
37363
37506
|
const sink = new EncodedPacketSink(track);
|
|
37364
37507
|
const decoderConfig = await track.getDecoderConfig();
|
|
37365
37508
|
const meta = { decoderConfig: decoderConfig ?? void 0 };
|
|
37366
37509
|
for await (const packet of sink.packets(void 0, void 0, { verifyKeyPackets: true })) {
|
|
37367
|
-
if (this.
|
|
37368
|
-
|
|
37510
|
+
if (this.state === "canceled") {
|
|
37511
|
+
break;
|
|
37369
37512
|
}
|
|
37370
37513
|
if (packet.timestamp >= this._endTimestamp) {
|
|
37371
37514
|
break;
|
|
@@ -37380,10 +37523,11 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37380
37523
|
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
37381
37524
|
await this._synchronizer.wait(modifiedPacket.timestamp);
|
|
37382
37525
|
}
|
|
37526
|
+
await this._checkpoint(pump, modifiedPacket.timestamp);
|
|
37383
37527
|
}
|
|
37384
37528
|
source.close();
|
|
37385
37529
|
this._synchronizer.closeTrack(outputTrackId);
|
|
37386
|
-
})
|
|
37530
|
+
});
|
|
37387
37531
|
} else {
|
|
37388
37532
|
const canDecode2 = await track.canDecode();
|
|
37389
37533
|
if (!canDecode2) {
|
|
@@ -37423,32 +37567,39 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37423
37567
|
assert(encodingConfig.transform);
|
|
37424
37568
|
let needsRerender = width !== originalWidth || height !== originalHeight || totalRotation !== 0 && (!canUseRotationMetadata || trackOptions.process !== void 0) || !!crop || squarePixelWidth !== await track.getCodedWidth() || squarePixelHeight !== await track.getCodedHeight();
|
|
37425
37569
|
if (!needsRerender) {
|
|
37426
|
-
|
|
37427
|
-
|
|
37428
|
-
|
|
37429
|
-
|
|
37430
|
-
|
|
37431
|
-
|
|
37432
|
-
|
|
37433
|
-
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
37438
|
-
|
|
37439
|
-
|
|
37440
|
-
|
|
37441
|
-
|
|
37442
|
-
|
|
37443
|
-
|
|
37444
|
-
|
|
37445
|
-
|
|
37446
|
-
|
|
37447
|
-
|
|
37448
|
-
|
|
37570
|
+
var _stack = [];
|
|
37571
|
+
try {
|
|
37572
|
+
const tempOutput = new Output({
|
|
37573
|
+
format: new Mp4OutputFormat(),
|
|
37574
|
+
// Supports all video codecs
|
|
37575
|
+
target: new NullTarget()
|
|
37576
|
+
});
|
|
37577
|
+
const tempSource = new VideoSampleSource(encodingConfig);
|
|
37578
|
+
tempOutput.addVideoTrack(tempSource);
|
|
37579
|
+
await tempOutput.start();
|
|
37580
|
+
const sink = new VideoSampleSink(track);
|
|
37581
|
+
const firstSample = __using(_stack, await sink.getSample(firstTimestamp));
|
|
37582
|
+
if (firstSample) {
|
|
37583
|
+
try {
|
|
37584
|
+
await tempSource.add(firstSample);
|
|
37585
|
+
firstSample.close();
|
|
37586
|
+
await tempOutput.finalize();
|
|
37587
|
+
} catch (error) {
|
|
37588
|
+
Logging._warn(
|
|
37589
|
+
"An error occurred when probing encoder support. Falling back to rerender path.",
|
|
37590
|
+
error
|
|
37591
|
+
);
|
|
37592
|
+
void tempOutput.cancel();
|
|
37593
|
+
needsRerender = true;
|
|
37594
|
+
encodingConfig.transform.force = true;
|
|
37595
|
+
}
|
|
37596
|
+
} else {
|
|
37597
|
+
await tempOutput.cancel();
|
|
37449
37598
|
}
|
|
37450
|
-
}
|
|
37451
|
-
|
|
37599
|
+
} catch (_) {
|
|
37600
|
+
var _error = _, _hasError = true;
|
|
37601
|
+
} finally {
|
|
37602
|
+
__callDispose(_stack, _error, _hasError);
|
|
37452
37603
|
}
|
|
37453
37604
|
}
|
|
37454
37605
|
if (trackOptions.frameRate) {
|
|
@@ -37472,31 +37623,38 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37472
37623
|
};
|
|
37473
37624
|
const source = new VideoSampleSource(encodingConfig);
|
|
37474
37625
|
videoSource = source;
|
|
37475
|
-
this.
|
|
37476
|
-
await this._started;
|
|
37626
|
+
this._registerTrackPump(async (pump) => {
|
|
37477
37627
|
const sink = new VideoSampleSink(track);
|
|
37478
|
-
for await (
|
|
37479
|
-
|
|
37628
|
+
for await (var _sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
37629
|
+
var _stack2 = [];
|
|
37630
|
+
try {
|
|
37631
|
+
const sample = __using(_stack2, _sample);
|
|
37632
|
+
if (this.state === "canceled") {
|
|
37633
|
+
break;
|
|
37634
|
+
}
|
|
37635
|
+
const adjustedSampleTimestamp = Math.max(sample.timestamp - this._startTimestamp, 0);
|
|
37636
|
+
sample.setTimestamp(adjustedSampleTimestamp);
|
|
37637
|
+
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
37638
|
+
await source.add(sample);
|
|
37480
37639
|
sample.close();
|
|
37481
|
-
|
|
37482
|
-
|
|
37483
|
-
|
|
37484
|
-
|
|
37485
|
-
|
|
37486
|
-
await source.add(sample);
|
|
37487
|
-
if (lastSampleTimestamp !== null) {
|
|
37488
|
-
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
37489
|
-
await this._synchronizer.wait(lastSampleTimestamp);
|
|
37640
|
+
if (lastSampleTimestamp !== null) {
|
|
37641
|
+
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
37642
|
+
await this._synchronizer.wait(lastSampleTimestamp);
|
|
37643
|
+
}
|
|
37644
|
+
await this._checkpoint(pump, lastSampleTimestamp);
|
|
37490
37645
|
}
|
|
37646
|
+
} catch (_2) {
|
|
37647
|
+
var _error2 = _2, _hasError2 = true;
|
|
37648
|
+
} finally {
|
|
37649
|
+
__callDispose(_stack2, _error2, _hasError2);
|
|
37491
37650
|
}
|
|
37492
|
-
sample.close();
|
|
37493
37651
|
}
|
|
37494
37652
|
source.close();
|
|
37495
37653
|
this._synchronizer.closeTrack(outputTrackId);
|
|
37496
|
-
})
|
|
37654
|
+
});
|
|
37497
37655
|
}
|
|
37498
37656
|
let ownGroup = null;
|
|
37499
|
-
if (!trackOptions.group) {
|
|
37657
|
+
if (!trackOptions.group && !this._composable) {
|
|
37500
37658
|
ownGroup = new OutputTrackGroup();
|
|
37501
37659
|
}
|
|
37502
37660
|
const videoTrackLanguageCode = await track.getLanguageCode();
|
|
@@ -37509,8 +37667,6 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37509
37667
|
rotation: outputTrackRotation,
|
|
37510
37668
|
group: ownGroup ?? trackOptions.group
|
|
37511
37669
|
});
|
|
37512
|
-
this._addedCounts.video++;
|
|
37513
|
-
this._totalTrackCount++;
|
|
37514
37670
|
this.utilizedTracks.push(track);
|
|
37515
37671
|
this._outputTrackIds.push(outputTrackId);
|
|
37516
37672
|
this._outputOwnTrackGroups.push(ownGroup);
|
|
@@ -37538,14 +37694,13 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37538
37694
|
if (!trackOptions.forceTranscode && !trackOptions.bitrate && numberOfChannels === originalNumberOfChannels && sampleRate === originalSampleRate && !needsTrimming && !needsPadding && audioCodecs.includes(sourceCodec) && (!trackOptions.codec || trackOptions.codec === sourceCodec) && !trackOptions.process && trackOptions.sampleFormat === void 0) {
|
|
37539
37695
|
const source = new EncodedAudioPacketSource(sourceCodec);
|
|
37540
37696
|
audioSource = source;
|
|
37541
|
-
this.
|
|
37542
|
-
await this._started;
|
|
37697
|
+
this._registerTrackPump(async (pump) => {
|
|
37543
37698
|
const sink = new EncodedPacketSink(track);
|
|
37544
37699
|
const decoderConfig = await track.getDecoderConfig();
|
|
37545
37700
|
const meta = { decoderConfig: decoderConfig ?? void 0 };
|
|
37546
37701
|
for await (const packet of sink.packets()) {
|
|
37547
|
-
if (this.
|
|
37548
|
-
|
|
37702
|
+
if (this.state === "canceled") {
|
|
37703
|
+
break;
|
|
37549
37704
|
}
|
|
37550
37705
|
if (packet.timestamp >= this._endTimestamp) {
|
|
37551
37706
|
break;
|
|
@@ -37559,10 +37714,11 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37559
37714
|
if (this._synchronizer.shouldWait(outputTrackId, modifiedPacket.timestamp)) {
|
|
37560
37715
|
await this._synchronizer.wait(modifiedPacket.timestamp);
|
|
37561
37716
|
}
|
|
37717
|
+
await this._checkpoint(pump, modifiedPacket.timestamp);
|
|
37562
37718
|
}
|
|
37563
37719
|
source.close();
|
|
37564
37720
|
this._synchronizer.closeTrack(outputTrackId);
|
|
37565
|
-
})
|
|
37721
|
+
});
|
|
37566
37722
|
} else {
|
|
37567
37723
|
const canDecode2 = await track.canDecode();
|
|
37568
37724
|
if (!canDecode2) {
|
|
@@ -37627,60 +37783,96 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37627
37783
|
};
|
|
37628
37784
|
const source = new AudioSampleSource(encodingConfig);
|
|
37629
37785
|
audioSource = source;
|
|
37630
|
-
this.
|
|
37631
|
-
await this._started;
|
|
37786
|
+
this._registerTrackPump(async (pump) => {
|
|
37632
37787
|
const sink = new AudioSampleSink(track);
|
|
37633
|
-
for await (
|
|
37634
|
-
|
|
37635
|
-
|
|
37636
|
-
|
|
37637
|
-
|
|
37638
|
-
|
|
37639
|
-
|
|
37640
|
-
|
|
37641
|
-
|
|
37642
|
-
|
|
37643
|
-
|
|
37644
|
-
|
|
37645
|
-
|
|
37646
|
-
|
|
37647
|
-
|
|
37648
|
-
|
|
37649
|
-
|
|
37650
|
-
|
|
37651
|
-
|
|
37652
|
-
|
|
37653
|
-
|
|
37654
|
-
|
|
37655
|
-
|
|
37656
|
-
|
|
37657
|
-
|
|
37658
|
-
|
|
37659
|
-
|
|
37660
|
-
|
|
37661
|
-
|
|
37662
|
-
|
|
37663
|
-
|
|
37664
|
-
|
|
37665
|
-
|
|
37666
|
-
|
|
37667
|
-
|
|
37668
|
-
|
|
37669
|
-
|
|
37670
|
-
|
|
37671
|
-
|
|
37672
|
-
|
|
37788
|
+
for await (var _sample of sink.samples(this._startTimestamp, this._endTimestamp)) {
|
|
37789
|
+
var _stack3 = [];
|
|
37790
|
+
try {
|
|
37791
|
+
const sample = __using(_stack3, _sample);
|
|
37792
|
+
var _stack2 = [];
|
|
37793
|
+
try {
|
|
37794
|
+
if (this.state === "canceled") {
|
|
37795
|
+
break;
|
|
37796
|
+
}
|
|
37797
|
+
if (needsPadding) {
|
|
37798
|
+
var _stack = [];
|
|
37799
|
+
try {
|
|
37800
|
+
const paddingLength = firstTimestamp - this._startTimestamp;
|
|
37801
|
+
const paddingLengthSamples = Math.round(paddingLength * originalSampleRate);
|
|
37802
|
+
const bytesPerSample = getBytesPerSample(sample.format);
|
|
37803
|
+
const data = new Uint8Array(bytesPerSample * paddingLengthSamples * originalNumberOfChannels);
|
|
37804
|
+
if (sample.format === "u8" || sample.format === "u8-planar") {
|
|
37805
|
+
data.fill(2 ** 7);
|
|
37806
|
+
}
|
|
37807
|
+
const silentSample = __using(_stack, new AudioSample({
|
|
37808
|
+
data,
|
|
37809
|
+
// Use the same format the decoder is spitting out. This avoids feeding changing sample
|
|
37810
|
+
// formats to the audio encoder.
|
|
37811
|
+
format: sample.format,
|
|
37812
|
+
numberOfChannels: originalNumberOfChannels,
|
|
37813
|
+
sampleRate: originalSampleRate,
|
|
37814
|
+
timestamp: 0
|
|
37815
|
+
}));
|
|
37816
|
+
await this._registerAudioSample(
|
|
37817
|
+
pump,
|
|
37818
|
+
silentSample,
|
|
37819
|
+
source,
|
|
37820
|
+
outputTrackId,
|
|
37821
|
+
() => lastSampleTimestamp
|
|
37822
|
+
);
|
|
37823
|
+
needsPadding = false;
|
|
37824
|
+
} catch (_) {
|
|
37825
|
+
var _error = _, _hasError = true;
|
|
37826
|
+
} finally {
|
|
37827
|
+
__callDispose(_stack, _error, _hasError);
|
|
37828
|
+
}
|
|
37829
|
+
}
|
|
37830
|
+
let startFrame = 0;
|
|
37831
|
+
let endFrame = sample.numberOfFrames;
|
|
37832
|
+
if (sample.timestamp < this._startTimestamp) {
|
|
37833
|
+
startFrame = Math.round((this._startTimestamp - sample.timestamp) * sample.sampleRate);
|
|
37834
|
+
}
|
|
37835
|
+
if (sample.timestamp + sample.duration > this._endTimestamp) {
|
|
37836
|
+
endFrame = Math.round((this._endTimestamp - sample.timestamp) * sample.sampleRate);
|
|
37837
|
+
}
|
|
37838
|
+
let finalSampleLet;
|
|
37839
|
+
if (startFrame > 0 || endFrame < sample.numberOfFrames) {
|
|
37840
|
+
const trimmedSample = sample.trim(startFrame, endFrame);
|
|
37841
|
+
sample.close();
|
|
37842
|
+
finalSampleLet = trimmedSample;
|
|
37843
|
+
if (trimmedSample.numberOfFrames === 0) {
|
|
37844
|
+
trimmedSample.close();
|
|
37845
|
+
continue;
|
|
37846
|
+
}
|
|
37847
|
+
} else {
|
|
37848
|
+
finalSampleLet = sample;
|
|
37849
|
+
}
|
|
37850
|
+
const finalSample = __using(_stack2, finalSampleLet);
|
|
37851
|
+
finalSample.setTimestamp(finalSample.timestamp - this._startTimestamp);
|
|
37852
|
+
await this._registerAudioSample(
|
|
37853
|
+
pump,
|
|
37854
|
+
finalSample,
|
|
37855
|
+
source,
|
|
37856
|
+
outputTrackId,
|
|
37857
|
+
() => lastSampleTimestamp
|
|
37858
|
+
);
|
|
37859
|
+
} catch (_2) {
|
|
37860
|
+
var _error2 = _2, _hasError2 = true;
|
|
37861
|
+
} finally {
|
|
37862
|
+
__callDispose(_stack2, _error2, _hasError2);
|
|
37673
37863
|
}
|
|
37864
|
+
} catch (_3) {
|
|
37865
|
+
var _error3 = _3, _hasError3 = true;
|
|
37866
|
+
} finally {
|
|
37867
|
+
__callDispose(_stack3, _error3, _hasError3);
|
|
37674
37868
|
}
|
|
37675
|
-
sample.setTimestamp(sample.timestamp - this._startTimestamp);
|
|
37676
|
-
await this._registerAudioSample(sample, source, outputTrackId, () => lastSampleTimestamp);
|
|
37677
37869
|
}
|
|
37678
37870
|
source.close();
|
|
37679
37871
|
this._synchronizer.closeTrack(outputTrackId);
|
|
37680
|
-
})
|
|
37872
|
+
});
|
|
37681
37873
|
}
|
|
37682
37874
|
let ownGroup = null;
|
|
37683
|
-
if (!trackOptions.group) {
|
|
37875
|
+
if (!trackOptions.group && !this._composable) {
|
|
37684
37876
|
ownGroup = new OutputTrackGroup();
|
|
37685
37877
|
}
|
|
37686
37878
|
const audioTrackLanguageCode = await track.getLanguageCode();
|
|
@@ -37691,14 +37883,12 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37691
37883
|
disposition: await track.getDisposition(),
|
|
37692
37884
|
group: ownGroup ?? trackOptions.group
|
|
37693
37885
|
});
|
|
37694
|
-
this._addedCounts.audio++;
|
|
37695
|
-
this._totalTrackCount++;
|
|
37696
37886
|
this.utilizedTracks.push(track);
|
|
37697
37887
|
this._outputTrackIds.push(outputTrackId);
|
|
37698
37888
|
this._outputOwnTrackGroups.push(ownGroup);
|
|
37699
37889
|
}
|
|
37700
37890
|
/** @internal */
|
|
37701
|
-
async _registerAudioSample(sample, source, outputTrackId, getLastSampleTimestamp) {
|
|
37891
|
+
async _registerAudioSample(pump, sample, source, outputTrackId, getLastSampleTimestamp) {
|
|
37702
37892
|
this._reportProgress(outputTrackId, sample.timestamp + sample.duration);
|
|
37703
37893
|
await source.add(sample);
|
|
37704
37894
|
sample.close();
|
|
@@ -37707,6 +37897,33 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37707
37897
|
if (this._synchronizer.shouldWait(outputTrackId, lastSampleTimestamp)) {
|
|
37708
37898
|
await this._synchronizer.wait(lastSampleTimestamp);
|
|
37709
37899
|
}
|
|
37900
|
+
await this._checkpoint(pump, lastSampleTimestamp);
|
|
37901
|
+
}
|
|
37902
|
+
}
|
|
37903
|
+
/** @internal */
|
|
37904
|
+
_registerTrackPump(fn) {
|
|
37905
|
+
const pump = {
|
|
37906
|
+
done: false,
|
|
37907
|
+
resolvers: promiseWithResolvers(),
|
|
37908
|
+
wake: null,
|
|
37909
|
+
start: () => {
|
|
37910
|
+
void fn(pump).then(() => {
|
|
37911
|
+
pump.done = true;
|
|
37912
|
+
pump.resolvers.resolve();
|
|
37913
|
+
}, (error) => {
|
|
37914
|
+
pump.resolvers.reject(error);
|
|
37915
|
+
});
|
|
37916
|
+
}
|
|
37917
|
+
};
|
|
37918
|
+
this._trackPumps.push(pump);
|
|
37919
|
+
}
|
|
37920
|
+
/** @internal */
|
|
37921
|
+
async _checkpoint(pump, timestamp) {
|
|
37922
|
+
while (this.state !== "canceled" && (timestamp >= this._executionUntil || this._pauseRequested)) {
|
|
37923
|
+
pump.resolvers.resolve();
|
|
37924
|
+
const { promise, resolve } = promiseWithResolvers();
|
|
37925
|
+
pump.wake = resolve;
|
|
37926
|
+
await promise;
|
|
37710
37927
|
}
|
|
37711
37928
|
}
|
|
37712
37929
|
/** @internal */
|
|
@@ -37736,10 +37953,11 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37736
37953
|
};
|
|
37737
37954
|
var MAX_TIMESTAMP_GAP = 1;
|
|
37738
37955
|
var TrackSynchronizer = class {
|
|
37739
|
-
constructor() {
|
|
37956
|
+
constructor(conversion) {
|
|
37740
37957
|
this.maxTimestamps = /* @__PURE__ */ new Map();
|
|
37741
37958
|
// Track ID -> timestamp
|
|
37742
37959
|
this.resolvers = [];
|
|
37960
|
+
this.conversion = conversion;
|
|
37743
37961
|
}
|
|
37744
37962
|
declareTrack(trackId) {
|
|
37745
37963
|
this.maxTimestamps.set(trackId, 0);
|
|
@@ -37749,6 +37967,9 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37749
37967
|
assert(currentValue !== void 0);
|
|
37750
37968
|
this.maxTimestamps.set(trackId, Math.max(timestamp, currentValue));
|
|
37751
37969
|
const newMin = this.computeMinAndMaybeResolve();
|
|
37970
|
+
if (this.conversion.state === "canceled" || this.conversion._pauseRequested || timestamp >= this.conversion._executionUntil) {
|
|
37971
|
+
return false;
|
|
37972
|
+
}
|
|
37752
37973
|
return timestamp - newMin > MAX_TIMESTAMP_GAP;
|
|
37753
37974
|
}
|
|
37754
37975
|
wait(timestamp) {
|
|
@@ -37763,6 +37984,12 @@ The @mediabunny/mp3-encoder extension package provides support for encoding MP3.
|
|
|
37763
37984
|
this.maxTimestamps.delete(trackId);
|
|
37764
37985
|
this.computeMinAndMaybeResolve();
|
|
37765
37986
|
}
|
|
37987
|
+
resolveAll() {
|
|
37988
|
+
for (const entry of this.resolvers) {
|
|
37989
|
+
entry.resolve();
|
|
37990
|
+
}
|
|
37991
|
+
this.resolvers.length = 0;
|
|
37992
|
+
}
|
|
37766
37993
|
computeMinAndMaybeResolve() {
|
|
37767
37994
|
let newMin = Infinity;
|
|
37768
37995
|
for (const [, timestamp] of this.maxTimestamps) {
|