@remotion/web-renderer 4.0.383 → 4.0.385
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/add-sample.d.ts +3 -0
- package/dist/add-sample.js +20 -0
- package/dist/artifact.d.ts +4 -5
- package/dist/artifact.js +12 -15
- package/dist/audio.d.ts +6 -0
- package/dist/audio.js +38 -0
- package/dist/border-radius.d.ts +31 -0
- package/dist/border-radius.js +152 -0
- package/dist/calculate-transforms.d.ts +2 -0
- package/dist/calculate-transforms.js +17 -0
- package/dist/composable.d.ts +2 -8
- package/dist/compose-canvas.js +28 -4
- package/dist/compose.d.ts +1 -6
- package/dist/compose.js +16 -11
- package/dist/drawing/border-radius.d.ts +31 -0
- package/dist/drawing/border-radius.js +152 -0
- package/dist/drawing/calculate-transforms.d.ts +10 -0
- package/dist/drawing/calculate-transforms.js +81 -0
- package/dist/drawing/compose-canvas.d.ts +1 -0
- package/dist/drawing/compose-canvas.js +36 -0
- package/dist/drawing/compose-svg.d.ts +1 -0
- package/dist/drawing/compose-svg.js +34 -0
- package/dist/drawing/compose.d.ts +5 -0
- package/dist/drawing/compose.js +6 -0
- package/dist/drawing/draw-border.d.ts +10 -0
- package/dist/drawing/draw-border.js +101 -0
- package/dist/drawing/draw-element-to-canvas.d.ts +4 -0
- package/dist/drawing/draw-element-to-canvas.js +72 -0
- package/dist/drawing/get-computed-style-cache.d.ts +0 -0
- package/dist/drawing/get-computed-style-cache.js +1 -0
- package/dist/drawing/opacity.d.ts +4 -0
- package/dist/drawing/opacity.js +7 -0
- package/dist/drawing/parse-transform-origin.d.ts +4 -0
- package/dist/drawing/parse-transform-origin.js +7 -0
- package/dist/drawing/transform.d.ts +4 -0
- package/dist/drawing/transform.js +6 -0
- package/dist/drawing/turn-svg-into-drawable.d.ts +1 -0
- package/dist/drawing/turn-svg-into-drawable.js +34 -0
- package/dist/esm/index.mjs +1630 -125
- package/dist/find-capturable-elements.d.ts +1 -1
- package/dist/find-capturable-elements.js +20 -22
- package/dist/get-audio-encoding-config.d.ts +2 -0
- package/dist/get-audio-encoding-config.js +18 -0
- package/dist/opacity.d.ts +4 -0
- package/dist/opacity.js +7 -0
- package/dist/render-media-on-web.js +42 -11
- package/dist/render-still-on-web.js +5 -5
- package/dist/take-screenshot.js +7 -8
- package/dist/transform.d.ts +4 -0
- package/dist/transform.js +6 -0
- package/package.json +6 -6
package/dist/esm/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/misc.js
|
|
2
2
|
/*!
|
|
3
3
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
4
4
|
*
|
|
@@ -186,6 +186,29 @@ var promiseWithResolvers = () => {
|
|
|
186
186
|
var assertNever = (x) => {
|
|
187
187
|
throw new Error(`Unexpected value: ${x}`);
|
|
188
188
|
};
|
|
189
|
+
var setUint24 = (view, byteOffset, value, littleEndian) => {
|
|
190
|
+
value = value >>> 0;
|
|
191
|
+
value = value & 16777215;
|
|
192
|
+
if (littleEndian) {
|
|
193
|
+
view.setUint8(byteOffset, value & 255);
|
|
194
|
+
view.setUint8(byteOffset + 1, value >>> 8 & 255);
|
|
195
|
+
view.setUint8(byteOffset + 2, value >>> 16 & 255);
|
|
196
|
+
} else {
|
|
197
|
+
view.setUint8(byteOffset, value >>> 16 & 255);
|
|
198
|
+
view.setUint8(byteOffset + 1, value >>> 8 & 255);
|
|
199
|
+
view.setUint8(byteOffset + 2, value & 255);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var setInt24 = (view, byteOffset, value, littleEndian) => {
|
|
203
|
+
value = clamp(value, -8388608, 8388607);
|
|
204
|
+
if (value < 0) {
|
|
205
|
+
value = value + 16777216 & 16777215;
|
|
206
|
+
}
|
|
207
|
+
setUint24(view, byteOffset, value, littleEndian);
|
|
208
|
+
};
|
|
209
|
+
var clamp = (value, min, max) => {
|
|
210
|
+
return Math.max(min, Math.min(max, value));
|
|
211
|
+
};
|
|
189
212
|
var UNDETERMINED_LANGUAGE = "und";
|
|
190
213
|
var roundToMultiple = (value, multiple) => {
|
|
191
214
|
return Math.round(value / multiple) * multiple;
|
|
@@ -291,7 +314,7 @@ var polyfillSymbolDispose = () => {
|
|
|
291
314
|
Symbol.dispose ??= Symbol("Symbol.dispose");
|
|
292
315
|
};
|
|
293
316
|
|
|
294
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
317
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/metadata.js
|
|
295
318
|
/*!
|
|
296
319
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
297
320
|
*
|
|
@@ -430,7 +453,7 @@ var validateTrackDisposition = (disposition) => {
|
|
|
430
453
|
}
|
|
431
454
|
};
|
|
432
455
|
|
|
433
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
456
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/codec.js
|
|
434
457
|
/*!
|
|
435
458
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
436
459
|
*
|
|
@@ -640,6 +663,109 @@ var generateAv1CodecConfigurationFromCodecString = (codecString) => {
|
|
|
640
663
|
const fourthByte = initialPresentationDelayPresent;
|
|
641
664
|
return [firstByte, secondByte, thirdByte, fourthByte];
|
|
642
665
|
};
|
|
666
|
+
var buildAudioCodecString = (codec, numberOfChannels, sampleRate) => {
|
|
667
|
+
if (codec === "aac") {
|
|
668
|
+
if (numberOfChannels >= 2 && sampleRate <= 24000) {
|
|
669
|
+
return "mp4a.40.29";
|
|
670
|
+
}
|
|
671
|
+
if (sampleRate <= 24000) {
|
|
672
|
+
return "mp4a.40.5";
|
|
673
|
+
}
|
|
674
|
+
return "mp4a.40.2";
|
|
675
|
+
} else if (codec === "mp3") {
|
|
676
|
+
return "mp3";
|
|
677
|
+
} else if (codec === "opus") {
|
|
678
|
+
return "opus";
|
|
679
|
+
} else if (codec === "vorbis") {
|
|
680
|
+
return "vorbis";
|
|
681
|
+
} else if (codec === "flac") {
|
|
682
|
+
return "flac";
|
|
683
|
+
} else if (PCM_AUDIO_CODECS.includes(codec)) {
|
|
684
|
+
return codec;
|
|
685
|
+
}
|
|
686
|
+
throw new TypeError(`Unhandled codec '${codec}'.`);
|
|
687
|
+
};
|
|
688
|
+
var aacFrequencyTable = [
|
|
689
|
+
96000,
|
|
690
|
+
88200,
|
|
691
|
+
64000,
|
|
692
|
+
48000,
|
|
693
|
+
44100,
|
|
694
|
+
32000,
|
|
695
|
+
24000,
|
|
696
|
+
22050,
|
|
697
|
+
16000,
|
|
698
|
+
12000,
|
|
699
|
+
11025,
|
|
700
|
+
8000,
|
|
701
|
+
7350
|
|
702
|
+
];
|
|
703
|
+
var aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8];
|
|
704
|
+
var parseAacAudioSpecificConfig = (bytes) => {
|
|
705
|
+
if (!bytes || bytes.byteLength < 2) {
|
|
706
|
+
throw new TypeError("AAC description must be at least 2 bytes long.");
|
|
707
|
+
}
|
|
708
|
+
const bitstream = new Bitstream(bytes);
|
|
709
|
+
let objectType = bitstream.readBits(5);
|
|
710
|
+
if (objectType === 31) {
|
|
711
|
+
objectType = 32 + bitstream.readBits(6);
|
|
712
|
+
}
|
|
713
|
+
const frequencyIndex = bitstream.readBits(4);
|
|
714
|
+
let sampleRate = null;
|
|
715
|
+
if (frequencyIndex === 15) {
|
|
716
|
+
sampleRate = bitstream.readBits(24);
|
|
717
|
+
} else {
|
|
718
|
+
if (frequencyIndex < aacFrequencyTable.length) {
|
|
719
|
+
sampleRate = aacFrequencyTable[frequencyIndex];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
const channelConfiguration = bitstream.readBits(4);
|
|
723
|
+
let numberOfChannels = null;
|
|
724
|
+
if (channelConfiguration >= 1 && channelConfiguration <= 7) {
|
|
725
|
+
numberOfChannels = aacChannelMap[channelConfiguration];
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
objectType,
|
|
729
|
+
frequencyIndex,
|
|
730
|
+
sampleRate,
|
|
731
|
+
channelConfiguration,
|
|
732
|
+
numberOfChannels
|
|
733
|
+
};
|
|
734
|
+
};
|
|
735
|
+
var buildAacAudioSpecificConfig = (config) => {
|
|
736
|
+
let frequencyIndex = aacFrequencyTable.indexOf(config.sampleRate);
|
|
737
|
+
let customSampleRate = null;
|
|
738
|
+
if (frequencyIndex === -1) {
|
|
739
|
+
frequencyIndex = 15;
|
|
740
|
+
customSampleRate = config.sampleRate;
|
|
741
|
+
}
|
|
742
|
+
const channelConfiguration = aacChannelMap.indexOf(config.numberOfChannels);
|
|
743
|
+
if (channelConfiguration === -1) {
|
|
744
|
+
throw new TypeError(`Unsupported number of channels: ${config.numberOfChannels}`);
|
|
745
|
+
}
|
|
746
|
+
let bitCount = 5 + 4 + 4;
|
|
747
|
+
if (config.objectType >= 32) {
|
|
748
|
+
bitCount += 6;
|
|
749
|
+
}
|
|
750
|
+
if (frequencyIndex === 15) {
|
|
751
|
+
bitCount += 24;
|
|
752
|
+
}
|
|
753
|
+
const byteCount = Math.ceil(bitCount / 8);
|
|
754
|
+
const bytes = new Uint8Array(byteCount);
|
|
755
|
+
const bitstream = new Bitstream(bytes);
|
|
756
|
+
if (config.objectType < 32) {
|
|
757
|
+
bitstream.writeBits(5, config.objectType);
|
|
758
|
+
} else {
|
|
759
|
+
bitstream.writeBits(5, 31);
|
|
760
|
+
bitstream.writeBits(6, config.objectType - 32);
|
|
761
|
+
}
|
|
762
|
+
bitstream.writeBits(4, frequencyIndex);
|
|
763
|
+
if (frequencyIndex === 15) {
|
|
764
|
+
bitstream.writeBits(24, customSampleRate);
|
|
765
|
+
}
|
|
766
|
+
bitstream.writeBits(4, channelConfiguration);
|
|
767
|
+
return bytes;
|
|
768
|
+
};
|
|
643
769
|
var OPUS_SAMPLE_RATE = 48000;
|
|
644
770
|
var PCM_CODEC_REGEX = /^pcm-([usf])(\d+)+(be)?$/;
|
|
645
771
|
var parsePcmCodec = (codec) => {
|
|
@@ -714,6 +840,22 @@ var getVideoEncoderConfigExtension = (codec) => {
|
|
|
714
840
|
}
|
|
715
841
|
return {};
|
|
716
842
|
};
|
|
843
|
+
var getAudioEncoderConfigExtension = (codec) => {
|
|
844
|
+
if (codec === "aac") {
|
|
845
|
+
return {
|
|
846
|
+
aac: {
|
|
847
|
+
format: "aac"
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
} else if (codec === "opus") {
|
|
851
|
+
return {
|
|
852
|
+
opus: {
|
|
853
|
+
format: "opus"
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
return {};
|
|
858
|
+
};
|
|
717
859
|
var VALID_VIDEO_CODEC_STRING_PREFIXES = ["avc1", "avc3", "hev1", "hvc1", "vp8", "vp09", "av01"];
|
|
718
860
|
var AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\.[0-9a-fA-F]{6}$/;
|
|
719
861
|
var HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\.(?:[ABC]?\d+)\.[0-9a-fA-F]{1,8}\.[LH]\d+(?:\.[0-9a-fA-F]{1,2}){0,6}$/;
|
|
@@ -881,7 +1023,7 @@ var validateSubtitleMetadata = (metadata) => {
|
|
|
881
1023
|
}
|
|
882
1024
|
};
|
|
883
1025
|
|
|
884
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1026
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/muxer.js
|
|
885
1027
|
/*!
|
|
886
1028
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
887
1029
|
*
|
|
@@ -925,7 +1067,7 @@ class Muxer {
|
|
|
925
1067
|
}
|
|
926
1068
|
}
|
|
927
1069
|
|
|
928
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1070
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/codec-data.js
|
|
929
1071
|
/*!
|
|
930
1072
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
931
1073
|
*
|
|
@@ -936,6 +1078,7 @@ class Muxer {
|
|
|
936
1078
|
var AvcNalUnitType;
|
|
937
1079
|
(function(AvcNalUnitType2) {
|
|
938
1080
|
AvcNalUnitType2[AvcNalUnitType2["IDR"] = 5] = "IDR";
|
|
1081
|
+
AvcNalUnitType2[AvcNalUnitType2["SEI"] = 6] = "SEI";
|
|
939
1082
|
AvcNalUnitType2[AvcNalUnitType2["SPS"] = 7] = "SPS";
|
|
940
1083
|
AvcNalUnitType2[AvcNalUnitType2["PPS"] = 8] = "PPS";
|
|
941
1084
|
AvcNalUnitType2[AvcNalUnitType2["SPS_EXT"] = 13] = "SPS_EXT";
|
|
@@ -1630,7 +1773,7 @@ var FlacBlockType;
|
|
|
1630
1773
|
FlacBlockType2[FlacBlockType2["PICTURE"] = 6] = "PICTURE";
|
|
1631
1774
|
})(FlacBlockType || (FlacBlockType = {}));
|
|
1632
1775
|
|
|
1633
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1776
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/custom-coder.js
|
|
1634
1777
|
/*!
|
|
1635
1778
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
1636
1779
|
*
|
|
@@ -1639,8 +1782,9 @@ var FlacBlockType;
|
|
|
1639
1782
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1640
1783
|
*/
|
|
1641
1784
|
var customVideoEncoders = [];
|
|
1785
|
+
var customAudioEncoders = [];
|
|
1642
1786
|
|
|
1643
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1787
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/packet.js
|
|
1644
1788
|
/*!
|
|
1645
1789
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
1646
1790
|
*
|
|
@@ -1772,7 +1916,60 @@ class EncodedPacket {
|
|
|
1772
1916
|
}
|
|
1773
1917
|
}
|
|
1774
1918
|
|
|
1775
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
1919
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/pcm.js
|
|
1920
|
+
/*!
|
|
1921
|
+
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
1922
|
+
*
|
|
1923
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
1924
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
1925
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1926
|
+
*/
|
|
1927
|
+
var toUlaw = (s16) => {
|
|
1928
|
+
const MULAW_MAX = 8191;
|
|
1929
|
+
const MULAW_BIAS = 33;
|
|
1930
|
+
let number = s16;
|
|
1931
|
+
let mask = 4096;
|
|
1932
|
+
let sign = 0;
|
|
1933
|
+
let position = 12;
|
|
1934
|
+
let lsb = 0;
|
|
1935
|
+
if (number < 0) {
|
|
1936
|
+
number = -number;
|
|
1937
|
+
sign = 128;
|
|
1938
|
+
}
|
|
1939
|
+
number += MULAW_BIAS;
|
|
1940
|
+
if (number > MULAW_MAX) {
|
|
1941
|
+
number = MULAW_MAX;
|
|
1942
|
+
}
|
|
1943
|
+
while ((number & mask) !== mask && position >= 5) {
|
|
1944
|
+
mask >>= 1;
|
|
1945
|
+
position--;
|
|
1946
|
+
}
|
|
1947
|
+
lsb = number >> position - 4 & 15;
|
|
1948
|
+
return ~(sign | position - 5 << 4 | lsb) & 255;
|
|
1949
|
+
};
|
|
1950
|
+
var toAlaw = (s16) => {
|
|
1951
|
+
const ALAW_MAX = 4095;
|
|
1952
|
+
let mask = 2048;
|
|
1953
|
+
let sign = 0;
|
|
1954
|
+
let position = 11;
|
|
1955
|
+
let lsb = 0;
|
|
1956
|
+
let number = s16;
|
|
1957
|
+
if (number < 0) {
|
|
1958
|
+
number = -number;
|
|
1959
|
+
sign = 128;
|
|
1960
|
+
}
|
|
1961
|
+
if (number > ALAW_MAX) {
|
|
1962
|
+
number = ALAW_MAX;
|
|
1963
|
+
}
|
|
1964
|
+
while ((number & mask) !== mask && position >= 5) {
|
|
1965
|
+
mask >>= 1;
|
|
1966
|
+
position--;
|
|
1967
|
+
}
|
|
1968
|
+
lsb = number >> (position === 4 ? 1 : position - 4) & 15;
|
|
1969
|
+
return (sign | position - 4 << 4 | lsb) ^ 85;
|
|
1970
|
+
};
|
|
1971
|
+
|
|
1972
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/sample.js
|
|
1776
1973
|
/*!
|
|
1777
1974
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
1778
1975
|
*
|
|
@@ -1781,6 +1978,31 @@ class EncodedPacket {
|
|
|
1781
1978
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
1782
1979
|
*/
|
|
1783
1980
|
polyfillSymbolDispose();
|
|
1981
|
+
var lastVideoGcErrorLog = -Infinity;
|
|
1982
|
+
var lastAudioGcErrorLog = -Infinity;
|
|
1983
|
+
var finalizationRegistry = null;
|
|
1984
|
+
if (typeof FinalizationRegistry !== "undefined") {
|
|
1985
|
+
finalizationRegistry = new FinalizationRegistry((value) => {
|
|
1986
|
+
const now = Date.now();
|
|
1987
|
+
if (value.type === "video") {
|
|
1988
|
+
if (now - lastVideoGcErrorLog >= 1000) {
|
|
1989
|
+
console.error(`A VideoSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your VideoSamples as soon as you're done using them.`);
|
|
1990
|
+
lastVideoGcErrorLog = now;
|
|
1991
|
+
}
|
|
1992
|
+
if (typeof VideoFrame !== "undefined" && value.data instanceof VideoFrame) {
|
|
1993
|
+
value.data.close();
|
|
1994
|
+
}
|
|
1995
|
+
} else {
|
|
1996
|
+
if (now - lastAudioGcErrorLog >= 1000) {
|
|
1997
|
+
console.error(`An AudioSample was garbage collected without first being closed. For proper resource management,` + ` make sure to call close() on all your AudioSamples as soon as you're done using them.`);
|
|
1998
|
+
lastAudioGcErrorLog = now;
|
|
1999
|
+
}
|
|
2000
|
+
if (typeof AudioData !== "undefined" && value.data instanceof AudioData) {
|
|
2001
|
+
value.data.close();
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
1784
2006
|
|
|
1785
2007
|
class VideoSample {
|
|
1786
2008
|
get displayWidth() {
|
|
@@ -1800,7 +2022,7 @@ class VideoSample {
|
|
|
1800
2022
|
}
|
|
1801
2023
|
constructor(data, init) {
|
|
1802
2024
|
this._closed = false;
|
|
1803
|
-
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
|
|
2025
|
+
if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) {
|
|
1804
2026
|
if (!init || typeof init !== "object") {
|
|
1805
2027
|
throw new TypeError("init must be an object.");
|
|
1806
2028
|
}
|
|
@@ -1905,6 +2127,7 @@ class VideoSample {
|
|
|
1905
2127
|
} else {
|
|
1906
2128
|
throw new TypeError("Invalid data type: Must be a BufferSource or CanvasImageSource.");
|
|
1907
2129
|
}
|
|
2130
|
+
finalizationRegistry?.register(this, { type: "video", data: this._data }, this);
|
|
1908
2131
|
}
|
|
1909
2132
|
clone() {
|
|
1910
2133
|
if (this._closed) {
|
|
@@ -1943,6 +2166,7 @@ class VideoSample {
|
|
|
1943
2166
|
if (this._closed) {
|
|
1944
2167
|
return;
|
|
1945
2168
|
}
|
|
2169
|
+
finalizationRegistry?.unregister(this);
|
|
1946
2170
|
if (isVideoFrame(this._data)) {
|
|
1947
2171
|
this._data.close();
|
|
1948
2172
|
} else {
|
|
@@ -2222,7 +2446,465 @@ var validateCropRectangle = (crop, prefix) => {
|
|
|
2222
2446
|
};
|
|
2223
2447
|
var AUDIO_SAMPLE_FORMATS = new Set(["f32", "f32-planar", "s16", "s16-planar", "s32", "s32-planar", "u8", "u8-planar"]);
|
|
2224
2448
|
|
|
2225
|
-
|
|
2449
|
+
class AudioSample {
|
|
2450
|
+
get microsecondTimestamp() {
|
|
2451
|
+
return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp);
|
|
2452
|
+
}
|
|
2453
|
+
get microsecondDuration() {
|
|
2454
|
+
return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration);
|
|
2455
|
+
}
|
|
2456
|
+
constructor(init) {
|
|
2457
|
+
this._closed = false;
|
|
2458
|
+
if (isAudioData(init)) {
|
|
2459
|
+
if (init.format === null) {
|
|
2460
|
+
throw new TypeError("AudioData with null format is not supported.");
|
|
2461
|
+
}
|
|
2462
|
+
this._data = init;
|
|
2463
|
+
this.format = init.format;
|
|
2464
|
+
this.sampleRate = init.sampleRate;
|
|
2465
|
+
this.numberOfFrames = init.numberOfFrames;
|
|
2466
|
+
this.numberOfChannels = init.numberOfChannels;
|
|
2467
|
+
this.timestamp = init.timestamp / 1e6;
|
|
2468
|
+
this.duration = init.numberOfFrames / init.sampleRate;
|
|
2469
|
+
} else {
|
|
2470
|
+
if (!init || typeof init !== "object") {
|
|
2471
|
+
throw new TypeError("Invalid AudioDataInit: must be an object.");
|
|
2472
|
+
}
|
|
2473
|
+
if (!AUDIO_SAMPLE_FORMATS.has(init.format)) {
|
|
2474
|
+
throw new TypeError("Invalid AudioDataInit: invalid format.");
|
|
2475
|
+
}
|
|
2476
|
+
if (!Number.isFinite(init.sampleRate) || init.sampleRate <= 0) {
|
|
2477
|
+
throw new TypeError("Invalid AudioDataInit: sampleRate must be > 0.");
|
|
2478
|
+
}
|
|
2479
|
+
if (!Number.isInteger(init.numberOfChannels) || init.numberOfChannels === 0) {
|
|
2480
|
+
throw new TypeError("Invalid AudioDataInit: numberOfChannels must be an integer > 0.");
|
|
2481
|
+
}
|
|
2482
|
+
if (!Number.isFinite(init?.timestamp)) {
|
|
2483
|
+
throw new TypeError("init.timestamp must be a number.");
|
|
2484
|
+
}
|
|
2485
|
+
const numberOfFrames = init.data.byteLength / (getBytesPerSample(init.format) * init.numberOfChannels);
|
|
2486
|
+
if (!Number.isInteger(numberOfFrames)) {
|
|
2487
|
+
throw new TypeError("Invalid AudioDataInit: data size is not a multiple of frame size.");
|
|
2488
|
+
}
|
|
2489
|
+
this.format = init.format;
|
|
2490
|
+
this.sampleRate = init.sampleRate;
|
|
2491
|
+
this.numberOfFrames = numberOfFrames;
|
|
2492
|
+
this.numberOfChannels = init.numberOfChannels;
|
|
2493
|
+
this.timestamp = init.timestamp;
|
|
2494
|
+
this.duration = numberOfFrames / init.sampleRate;
|
|
2495
|
+
let dataBuffer;
|
|
2496
|
+
if (init.data instanceof ArrayBuffer) {
|
|
2497
|
+
dataBuffer = new Uint8Array(init.data);
|
|
2498
|
+
} else if (ArrayBuffer.isView(init.data)) {
|
|
2499
|
+
dataBuffer = new Uint8Array(init.data.buffer, init.data.byteOffset, init.data.byteLength);
|
|
2500
|
+
} else {
|
|
2501
|
+
throw new TypeError("Invalid AudioDataInit: data is not a BufferSource.");
|
|
2502
|
+
}
|
|
2503
|
+
const expectedSize = this.numberOfFrames * this.numberOfChannels * getBytesPerSample(this.format);
|
|
2504
|
+
if (dataBuffer.byteLength < expectedSize) {
|
|
2505
|
+
throw new TypeError("Invalid AudioDataInit: insufficient data size.");
|
|
2506
|
+
}
|
|
2507
|
+
this._data = dataBuffer;
|
|
2508
|
+
}
|
|
2509
|
+
finalizationRegistry?.register(this, { type: "audio", data: this._data }, this);
|
|
2510
|
+
}
|
|
2511
|
+
allocationSize(options) {
|
|
2512
|
+
if (!options || typeof options !== "object") {
|
|
2513
|
+
throw new TypeError("options must be an object.");
|
|
2514
|
+
}
|
|
2515
|
+
if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {
|
|
2516
|
+
throw new TypeError("planeIndex must be a non-negative integer.");
|
|
2517
|
+
}
|
|
2518
|
+
if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {
|
|
2519
|
+
throw new TypeError("Invalid format.");
|
|
2520
|
+
}
|
|
2521
|
+
if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {
|
|
2522
|
+
throw new TypeError("frameOffset must be a non-negative integer.");
|
|
2523
|
+
}
|
|
2524
|
+
if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {
|
|
2525
|
+
throw new TypeError("frameCount must be a non-negative integer.");
|
|
2526
|
+
}
|
|
2527
|
+
if (this._closed) {
|
|
2528
|
+
throw new Error("AudioSample is closed.");
|
|
2529
|
+
}
|
|
2530
|
+
const destFormat = options.format ?? this.format;
|
|
2531
|
+
const frameOffset = options.frameOffset ?? 0;
|
|
2532
|
+
if (frameOffset >= this.numberOfFrames) {
|
|
2533
|
+
throw new RangeError("frameOffset out of range");
|
|
2534
|
+
}
|
|
2535
|
+
const copyFrameCount = options.frameCount !== undefined ? options.frameCount : this.numberOfFrames - frameOffset;
|
|
2536
|
+
if (copyFrameCount > this.numberOfFrames - frameOffset) {
|
|
2537
|
+
throw new RangeError("frameCount out of range");
|
|
2538
|
+
}
|
|
2539
|
+
const bytesPerSample = getBytesPerSample(destFormat);
|
|
2540
|
+
const isPlanar = formatIsPlanar(destFormat);
|
|
2541
|
+
if (isPlanar && options.planeIndex >= this.numberOfChannels) {
|
|
2542
|
+
throw new RangeError("planeIndex out of range");
|
|
2543
|
+
}
|
|
2544
|
+
if (!isPlanar && options.planeIndex !== 0) {
|
|
2545
|
+
throw new RangeError("planeIndex out of range");
|
|
2546
|
+
}
|
|
2547
|
+
const elementCount = isPlanar ? copyFrameCount : copyFrameCount * this.numberOfChannels;
|
|
2548
|
+
return elementCount * bytesPerSample;
|
|
2549
|
+
}
|
|
2550
|
+
copyTo(destination, options) {
|
|
2551
|
+
if (!isAllowSharedBufferSource(destination)) {
|
|
2552
|
+
throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view.");
|
|
2553
|
+
}
|
|
2554
|
+
if (!options || typeof options !== "object") {
|
|
2555
|
+
throw new TypeError("options must be an object.");
|
|
2556
|
+
}
|
|
2557
|
+
if (!Number.isInteger(options.planeIndex) || options.planeIndex < 0) {
|
|
2558
|
+
throw new TypeError("planeIndex must be a non-negative integer.");
|
|
2559
|
+
}
|
|
2560
|
+
if (options.format !== undefined && !AUDIO_SAMPLE_FORMATS.has(options.format)) {
|
|
2561
|
+
throw new TypeError("Invalid format.");
|
|
2562
|
+
}
|
|
2563
|
+
if (options.frameOffset !== undefined && (!Number.isInteger(options.frameOffset) || options.frameOffset < 0)) {
|
|
2564
|
+
throw new TypeError("frameOffset must be a non-negative integer.");
|
|
2565
|
+
}
|
|
2566
|
+
if (options.frameCount !== undefined && (!Number.isInteger(options.frameCount) || options.frameCount < 0)) {
|
|
2567
|
+
throw new TypeError("frameCount must be a non-negative integer.");
|
|
2568
|
+
}
|
|
2569
|
+
if (this._closed) {
|
|
2570
|
+
throw new Error("AudioSample is closed.");
|
|
2571
|
+
}
|
|
2572
|
+
const { planeIndex, format, frameCount: optFrameCount, frameOffset: optFrameOffset } = options;
|
|
2573
|
+
const destFormat = format ?? this.format;
|
|
2574
|
+
if (!destFormat)
|
|
2575
|
+
throw new Error("Destination format not determined");
|
|
2576
|
+
const numFrames = this.numberOfFrames;
|
|
2577
|
+
const numChannels = this.numberOfChannels;
|
|
2578
|
+
const frameOffset = optFrameOffset ?? 0;
|
|
2579
|
+
if (frameOffset >= numFrames) {
|
|
2580
|
+
throw new RangeError("frameOffset out of range");
|
|
2581
|
+
}
|
|
2582
|
+
const copyFrameCount = optFrameCount !== undefined ? optFrameCount : numFrames - frameOffset;
|
|
2583
|
+
if (copyFrameCount > numFrames - frameOffset) {
|
|
2584
|
+
throw new RangeError("frameCount out of range");
|
|
2585
|
+
}
|
|
2586
|
+
const destBytesPerSample = getBytesPerSample(destFormat);
|
|
2587
|
+
const destIsPlanar = formatIsPlanar(destFormat);
|
|
2588
|
+
if (destIsPlanar && planeIndex >= numChannels) {
|
|
2589
|
+
throw new RangeError("planeIndex out of range");
|
|
2590
|
+
}
|
|
2591
|
+
if (!destIsPlanar && planeIndex !== 0) {
|
|
2592
|
+
throw new RangeError("planeIndex out of range");
|
|
2593
|
+
}
|
|
2594
|
+
const destElementCount = destIsPlanar ? copyFrameCount : copyFrameCount * numChannels;
|
|
2595
|
+
const requiredSize = destElementCount * destBytesPerSample;
|
|
2596
|
+
if (destination.byteLength < requiredSize) {
|
|
2597
|
+
throw new RangeError("Destination buffer is too small");
|
|
2598
|
+
}
|
|
2599
|
+
const destView = toDataView(destination);
|
|
2600
|
+
const writeFn = getWriteFunction(destFormat);
|
|
2601
|
+
if (isAudioData(this._data)) {
|
|
2602
|
+
if (destIsPlanar) {
|
|
2603
|
+
if (destFormat === "f32-planar") {
|
|
2604
|
+
this._data.copyTo(destination, {
|
|
2605
|
+
planeIndex,
|
|
2606
|
+
frameOffset,
|
|
2607
|
+
frameCount: copyFrameCount,
|
|
2608
|
+
format: "f32-planar"
|
|
2609
|
+
});
|
|
2610
|
+
} else {
|
|
2611
|
+
const tempBuffer = new ArrayBuffer(copyFrameCount * 4);
|
|
2612
|
+
const tempArray = new Float32Array(tempBuffer);
|
|
2613
|
+
this._data.copyTo(tempArray, {
|
|
2614
|
+
planeIndex,
|
|
2615
|
+
frameOffset,
|
|
2616
|
+
frameCount: copyFrameCount,
|
|
2617
|
+
format: "f32-planar"
|
|
2618
|
+
});
|
|
2619
|
+
const tempView = new DataView(tempBuffer);
|
|
2620
|
+
for (let i = 0;i < copyFrameCount; i++) {
|
|
2621
|
+
const destOffset = i * destBytesPerSample;
|
|
2622
|
+
const sample = tempView.getFloat32(i * 4, true);
|
|
2623
|
+
writeFn(destView, destOffset, sample);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
} else {
|
|
2627
|
+
const numCh = numChannels;
|
|
2628
|
+
const temp = new Float32Array(copyFrameCount);
|
|
2629
|
+
for (let ch = 0;ch < numCh; ch++) {
|
|
2630
|
+
this._data.copyTo(temp, {
|
|
2631
|
+
planeIndex: ch,
|
|
2632
|
+
frameOffset,
|
|
2633
|
+
frameCount: copyFrameCount,
|
|
2634
|
+
format: "f32-planar"
|
|
2635
|
+
});
|
|
2636
|
+
for (let i = 0;i < copyFrameCount; i++) {
|
|
2637
|
+
const destIndex = i * numCh + ch;
|
|
2638
|
+
const destOffset = destIndex * destBytesPerSample;
|
|
2639
|
+
writeFn(destView, destOffset, temp[i]);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
} else {
|
|
2644
|
+
const uint8Data = this._data;
|
|
2645
|
+
const srcView = toDataView(uint8Data);
|
|
2646
|
+
const srcFormat = this.format;
|
|
2647
|
+
const readFn = getReadFunction(srcFormat);
|
|
2648
|
+
const srcBytesPerSample = getBytesPerSample(srcFormat);
|
|
2649
|
+
const srcIsPlanar = formatIsPlanar(srcFormat);
|
|
2650
|
+
for (let i = 0;i < copyFrameCount; i++) {
|
|
2651
|
+
if (destIsPlanar) {
|
|
2652
|
+
const destOffset = i * destBytesPerSample;
|
|
2653
|
+
let srcOffset;
|
|
2654
|
+
if (srcIsPlanar) {
|
|
2655
|
+
srcOffset = (planeIndex * numFrames + (i + frameOffset)) * srcBytesPerSample;
|
|
2656
|
+
} else {
|
|
2657
|
+
srcOffset = ((i + frameOffset) * numChannels + planeIndex) * srcBytesPerSample;
|
|
2658
|
+
}
|
|
2659
|
+
const normalized = readFn(srcView, srcOffset);
|
|
2660
|
+
writeFn(destView, destOffset, normalized);
|
|
2661
|
+
} else {
|
|
2662
|
+
for (let ch = 0;ch < numChannels; ch++) {
|
|
2663
|
+
const destIndex = i * numChannels + ch;
|
|
2664
|
+
const destOffset = destIndex * destBytesPerSample;
|
|
2665
|
+
let srcOffset;
|
|
2666
|
+
if (srcIsPlanar) {
|
|
2667
|
+
srcOffset = (ch * numFrames + (i + frameOffset)) * srcBytesPerSample;
|
|
2668
|
+
} else {
|
|
2669
|
+
srcOffset = ((i + frameOffset) * numChannels + ch) * srcBytesPerSample;
|
|
2670
|
+
}
|
|
2671
|
+
const normalized = readFn(srcView, srcOffset);
|
|
2672
|
+
writeFn(destView, destOffset, normalized);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
clone() {
|
|
2679
|
+
if (this._closed) {
|
|
2680
|
+
throw new Error("AudioSample is closed.");
|
|
2681
|
+
}
|
|
2682
|
+
if (isAudioData(this._data)) {
|
|
2683
|
+
const sample = new AudioSample(this._data.clone());
|
|
2684
|
+
sample.setTimestamp(this.timestamp);
|
|
2685
|
+
return sample;
|
|
2686
|
+
} else {
|
|
2687
|
+
return new AudioSample({
|
|
2688
|
+
format: this.format,
|
|
2689
|
+
sampleRate: this.sampleRate,
|
|
2690
|
+
numberOfFrames: this.numberOfFrames,
|
|
2691
|
+
numberOfChannels: this.numberOfChannels,
|
|
2692
|
+
timestamp: this.timestamp,
|
|
2693
|
+
data: this._data
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
close() {
|
|
2698
|
+
if (this._closed) {
|
|
2699
|
+
return;
|
|
2700
|
+
}
|
|
2701
|
+
finalizationRegistry?.unregister(this);
|
|
2702
|
+
if (isAudioData(this._data)) {
|
|
2703
|
+
this._data.close();
|
|
2704
|
+
} else {
|
|
2705
|
+
this._data = new Uint8Array(0);
|
|
2706
|
+
}
|
|
2707
|
+
this._closed = true;
|
|
2708
|
+
}
|
|
2709
|
+
toAudioData() {
|
|
2710
|
+
if (this._closed) {
|
|
2711
|
+
throw new Error("AudioSample is closed.");
|
|
2712
|
+
}
|
|
2713
|
+
if (isAudioData(this._data)) {
|
|
2714
|
+
if (this._data.timestamp === this.microsecondTimestamp) {
|
|
2715
|
+
return this._data.clone();
|
|
2716
|
+
} else {
|
|
2717
|
+
if (formatIsPlanar(this.format)) {
|
|
2718
|
+
const size = this.allocationSize({ planeIndex: 0, format: this.format });
|
|
2719
|
+
const data = new ArrayBuffer(size * this.numberOfChannels);
|
|
2720
|
+
for (let i = 0;i < this.numberOfChannels; i++) {
|
|
2721
|
+
this.copyTo(new Uint8Array(data, i * size, size), { planeIndex: i, format: this.format });
|
|
2722
|
+
}
|
|
2723
|
+
return new AudioData({
|
|
2724
|
+
format: this.format,
|
|
2725
|
+
sampleRate: this.sampleRate,
|
|
2726
|
+
numberOfFrames: this.numberOfFrames,
|
|
2727
|
+
numberOfChannels: this.numberOfChannels,
|
|
2728
|
+
timestamp: this.microsecondTimestamp,
|
|
2729
|
+
data
|
|
2730
|
+
});
|
|
2731
|
+
} else {
|
|
2732
|
+
const data = new ArrayBuffer(this.allocationSize({ planeIndex: 0, format: this.format }));
|
|
2733
|
+
this.copyTo(data, { planeIndex: 0, format: this.format });
|
|
2734
|
+
return new AudioData({
|
|
2735
|
+
format: this.format,
|
|
2736
|
+
sampleRate: this.sampleRate,
|
|
2737
|
+
numberOfFrames: this.numberOfFrames,
|
|
2738
|
+
numberOfChannels: this.numberOfChannels,
|
|
2739
|
+
timestamp: this.microsecondTimestamp,
|
|
2740
|
+
data
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
} else {
|
|
2745
|
+
return new AudioData({
|
|
2746
|
+
format: this.format,
|
|
2747
|
+
sampleRate: this.sampleRate,
|
|
2748
|
+
numberOfFrames: this.numberOfFrames,
|
|
2749
|
+
numberOfChannels: this.numberOfChannels,
|
|
2750
|
+
timestamp: this.microsecondTimestamp,
|
|
2751
|
+
data: this._data.buffer instanceof ArrayBuffer ? this._data.buffer : this._data.slice()
|
|
2752
|
+
});
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
toAudioBuffer() {
|
|
2756
|
+
if (this._closed) {
|
|
2757
|
+
throw new Error("AudioSample is closed.");
|
|
2758
|
+
}
|
|
2759
|
+
const audioBuffer = new AudioBuffer({
|
|
2760
|
+
numberOfChannels: this.numberOfChannels,
|
|
2761
|
+
length: this.numberOfFrames,
|
|
2762
|
+
sampleRate: this.sampleRate
|
|
2763
|
+
});
|
|
2764
|
+
const dataBytes = new Float32Array(this.allocationSize({ planeIndex: 0, format: "f32-planar" }) / 4);
|
|
2765
|
+
for (let i = 0;i < this.numberOfChannels; i++) {
|
|
2766
|
+
this.copyTo(dataBytes, { planeIndex: i, format: "f32-planar" });
|
|
2767
|
+
audioBuffer.copyToChannel(dataBytes, i);
|
|
2768
|
+
}
|
|
2769
|
+
return audioBuffer;
|
|
2770
|
+
}
|
|
2771
|
+
setTimestamp(newTimestamp) {
|
|
2772
|
+
if (!Number.isFinite(newTimestamp)) {
|
|
2773
|
+
throw new TypeError("newTimestamp must be a number.");
|
|
2774
|
+
}
|
|
2775
|
+
this.timestamp = newTimestamp;
|
|
2776
|
+
}
|
|
2777
|
+
[Symbol.dispose]() {
|
|
2778
|
+
this.close();
|
|
2779
|
+
}
|
|
2780
|
+
static *_fromAudioBuffer(audioBuffer, timestamp) {
|
|
2781
|
+
if (!(audioBuffer instanceof AudioBuffer)) {
|
|
2782
|
+
throw new TypeError("audioBuffer must be an AudioBuffer.");
|
|
2783
|
+
}
|
|
2784
|
+
const MAX_FLOAT_COUNT = 48000 * 5;
|
|
2785
|
+
const numberOfChannels = audioBuffer.numberOfChannels;
|
|
2786
|
+
const sampleRate = audioBuffer.sampleRate;
|
|
2787
|
+
const totalFrames = audioBuffer.length;
|
|
2788
|
+
const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);
|
|
2789
|
+
let currentRelativeFrame = 0;
|
|
2790
|
+
let remainingFrames = totalFrames;
|
|
2791
|
+
while (remainingFrames > 0) {
|
|
2792
|
+
const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);
|
|
2793
|
+
const chunkData = new Float32Array(numberOfChannels * framesToCopy);
|
|
2794
|
+
for (let channel = 0;channel < numberOfChannels; channel++) {
|
|
2795
|
+
audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);
|
|
2796
|
+
}
|
|
2797
|
+
yield new AudioSample({
|
|
2798
|
+
format: "f32-planar",
|
|
2799
|
+
sampleRate,
|
|
2800
|
+
numberOfFrames: framesToCopy,
|
|
2801
|
+
numberOfChannels,
|
|
2802
|
+
timestamp: timestamp + currentRelativeFrame / sampleRate,
|
|
2803
|
+
data: chunkData
|
|
2804
|
+
});
|
|
2805
|
+
currentRelativeFrame += framesToCopy;
|
|
2806
|
+
remainingFrames -= framesToCopy;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
static fromAudioBuffer(audioBuffer, timestamp) {
|
|
2810
|
+
if (!(audioBuffer instanceof AudioBuffer)) {
|
|
2811
|
+
throw new TypeError("audioBuffer must be an AudioBuffer.");
|
|
2812
|
+
}
|
|
2813
|
+
const MAX_FLOAT_COUNT = 48000 * 5;
|
|
2814
|
+
const numberOfChannels = audioBuffer.numberOfChannels;
|
|
2815
|
+
const sampleRate = audioBuffer.sampleRate;
|
|
2816
|
+
const totalFrames = audioBuffer.length;
|
|
2817
|
+
const maxFramesPerChunk = Math.floor(MAX_FLOAT_COUNT / numberOfChannels);
|
|
2818
|
+
let currentRelativeFrame = 0;
|
|
2819
|
+
let remainingFrames = totalFrames;
|
|
2820
|
+
const result = [];
|
|
2821
|
+
while (remainingFrames > 0) {
|
|
2822
|
+
const framesToCopy = Math.min(maxFramesPerChunk, remainingFrames);
|
|
2823
|
+
const chunkData = new Float32Array(numberOfChannels * framesToCopy);
|
|
2824
|
+
for (let channel = 0;channel < numberOfChannels; channel++) {
|
|
2825
|
+
audioBuffer.copyFromChannel(chunkData.subarray(channel * framesToCopy, (channel + 1) * framesToCopy), channel, currentRelativeFrame);
|
|
2826
|
+
}
|
|
2827
|
+
const audioSample = new AudioSample({
|
|
2828
|
+
format: "f32-planar",
|
|
2829
|
+
sampleRate,
|
|
2830
|
+
numberOfFrames: framesToCopy,
|
|
2831
|
+
numberOfChannels,
|
|
2832
|
+
timestamp: timestamp + currentRelativeFrame / sampleRate,
|
|
2833
|
+
data: chunkData
|
|
2834
|
+
});
|
|
2835
|
+
result.push(audioSample);
|
|
2836
|
+
currentRelativeFrame += framesToCopy;
|
|
2837
|
+
remainingFrames -= framesToCopy;
|
|
2838
|
+
}
|
|
2839
|
+
return result;
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
var getBytesPerSample = (format) => {
|
|
2843
|
+
switch (format) {
|
|
2844
|
+
case "u8":
|
|
2845
|
+
case "u8-planar":
|
|
2846
|
+
return 1;
|
|
2847
|
+
case "s16":
|
|
2848
|
+
case "s16-planar":
|
|
2849
|
+
return 2;
|
|
2850
|
+
case "s32":
|
|
2851
|
+
case "s32-planar":
|
|
2852
|
+
return 4;
|
|
2853
|
+
case "f32":
|
|
2854
|
+
case "f32-planar":
|
|
2855
|
+
return 4;
|
|
2856
|
+
default:
|
|
2857
|
+
throw new Error("Unknown AudioSampleFormat");
|
|
2858
|
+
}
|
|
2859
|
+
};
|
|
2860
|
+
var formatIsPlanar = (format) => {
|
|
2861
|
+
switch (format) {
|
|
2862
|
+
case "u8-planar":
|
|
2863
|
+
case "s16-planar":
|
|
2864
|
+
case "s32-planar":
|
|
2865
|
+
case "f32-planar":
|
|
2866
|
+
return true;
|
|
2867
|
+
default:
|
|
2868
|
+
return false;
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
var getReadFunction = (format) => {
|
|
2872
|
+
switch (format) {
|
|
2873
|
+
case "u8":
|
|
2874
|
+
case "u8-planar":
|
|
2875
|
+
return (view, offset) => (view.getUint8(offset) - 128) / 128;
|
|
2876
|
+
case "s16":
|
|
2877
|
+
case "s16-planar":
|
|
2878
|
+
return (view, offset) => view.getInt16(offset, true) / 32768;
|
|
2879
|
+
case "s32":
|
|
2880
|
+
case "s32-planar":
|
|
2881
|
+
return (view, offset) => view.getInt32(offset, true) / 2147483648;
|
|
2882
|
+
case "f32":
|
|
2883
|
+
case "f32-planar":
|
|
2884
|
+
return (view, offset) => view.getFloat32(offset, true);
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
var getWriteFunction = (format) => {
|
|
2888
|
+
switch (format) {
|
|
2889
|
+
case "u8":
|
|
2890
|
+
case "u8-planar":
|
|
2891
|
+
return (view, offset, value) => view.setUint8(offset, clamp((value + 1) * 127.5, 0, 255));
|
|
2892
|
+
case "s16":
|
|
2893
|
+
case "s16-planar":
|
|
2894
|
+
return (view, offset, value) => view.setInt16(offset, clamp(Math.round(value * 32767), -32768, 32767), true);
|
|
2895
|
+
case "s32":
|
|
2896
|
+
case "s32-planar":
|
|
2897
|
+
return (view, offset, value) => view.setInt32(offset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), true);
|
|
2898
|
+
case "f32":
|
|
2899
|
+
case "f32-planar":
|
|
2900
|
+
return (view, offset, value) => view.setFloat32(offset, value, true);
|
|
2901
|
+
}
|
|
2902
|
+
};
|
|
2903
|
+
var isAudioData = (x) => {
|
|
2904
|
+
return typeof AudioData !== "undefined" && x instanceof AudioData;
|
|
2905
|
+
};
|
|
2906
|
+
|
|
2907
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-misc.js
|
|
2226
2908
|
/*!
|
|
2227
2909
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2228
2910
|
*
|
|
@@ -2240,7 +2922,7 @@ var buildIsobmffMimeType = (info) => {
|
|
|
2240
2922
|
return string;
|
|
2241
2923
|
};
|
|
2242
2924
|
|
|
2243
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
2925
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-reader.js
|
|
2244
2926
|
/*!
|
|
2245
2927
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2246
2928
|
*
|
|
@@ -2251,7 +2933,7 @@ var buildIsobmffMimeType = (info) => {
|
|
|
2251
2933
|
var MIN_BOX_HEADER_SIZE = 8;
|
|
2252
2934
|
var MAX_BOX_HEADER_SIZE = 16;
|
|
2253
2935
|
|
|
2254
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
2936
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/matroska/ebml.js
|
|
2255
2937
|
/*!
|
|
2256
2938
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2257
2939
|
*
|
|
@@ -2661,7 +3343,7 @@ var CODEC_STRING_MAP = {
|
|
|
2661
3343
|
webvtt: "S_TEXT/WEBVTT"
|
|
2662
3344
|
};
|
|
2663
3345
|
|
|
2664
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
3346
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/matroska/matroska-misc.js
|
|
2665
3347
|
/*!
|
|
2666
3348
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2667
3349
|
*
|
|
@@ -2679,7 +3361,7 @@ var buildMatroskaMimeType = (info) => {
|
|
|
2679
3361
|
return string;
|
|
2680
3362
|
};
|
|
2681
3363
|
|
|
2682
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
3364
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/subtitles.js
|
|
2683
3365
|
/*!
|
|
2684
3366
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2685
3367
|
*
|
|
@@ -2703,7 +3385,7 @@ var formatSubtitleTimestamp = (timestamp) => {
|
|
|
2703
3385
|
return hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0") + "." + milliseconds.toString().padStart(3, "0");
|
|
2704
3386
|
};
|
|
2705
3387
|
|
|
2706
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
3388
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-boxes.js
|
|
2707
3389
|
/*!
|
|
2708
3390
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
2709
3391
|
*
|
|
@@ -4037,7 +4719,7 @@ var getLanguageCodeInt = (code) => {
|
|
|
4037
4719
|
return language;
|
|
4038
4720
|
};
|
|
4039
4721
|
|
|
4040
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
4722
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/writer.js
|
|
4041
4723
|
/*!
|
|
4042
4724
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
4043
4725
|
*
|
|
@@ -4165,7 +4847,7 @@ class BufferTargetWriter extends Writer {
|
|
|
4165
4847
|
}
|
|
4166
4848
|
var DEFAULT_CHUNK_SIZE = 2 ** 24;
|
|
4167
4849
|
|
|
4168
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
4850
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/target.js
|
|
4169
4851
|
var nodeAlias = (() => ({}));
|
|
4170
4852
|
/*!
|
|
4171
4853
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
@@ -4191,7 +4873,7 @@ class BufferTarget extends Target {
|
|
|
4191
4873
|
}
|
|
4192
4874
|
}
|
|
4193
4875
|
|
|
4194
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
4876
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/isobmff/isobmff-muxer.js
|
|
4195
4877
|
/*!
|
|
4196
4878
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
4197
4879
|
*
|
|
@@ -5018,7 +5700,7 @@ class IsobmffMuxer extends Muxer {
|
|
|
5018
5700
|
}
|
|
5019
5701
|
}
|
|
5020
5702
|
|
|
5021
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
5703
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/matroska/matroska-muxer.js
|
|
5022
5704
|
/*!
|
|
5023
5705
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
5024
5706
|
*
|
|
@@ -5901,7 +6583,7 @@ ${cue.notes ?? ""}`;
|
|
|
5901
6583
|
}
|
|
5902
6584
|
}
|
|
5903
6585
|
|
|
5904
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
6586
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/output-format.js
|
|
5905
6587
|
/*!
|
|
5906
6588
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
5907
6589
|
*
|
|
@@ -6119,7 +6801,7 @@ class WebMOutputFormat extends MkvOutputFormat {
|
|
|
6119
6801
|
}
|
|
6120
6802
|
}
|
|
6121
6803
|
|
|
6122
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
6804
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/encode.js
|
|
6123
6805
|
/*!
|
|
6124
6806
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
6125
6807
|
*
|
|
@@ -6197,6 +6879,53 @@ var buildVideoEncoderConfig = (options) => {
|
|
|
6197
6879
|
...getVideoEncoderConfigExtension(options.codec)
|
|
6198
6880
|
};
|
|
6199
6881
|
};
|
|
6882
|
+
var validateAudioEncodingConfig = (config) => {
|
|
6883
|
+
if (!config || typeof config !== "object") {
|
|
6884
|
+
throw new TypeError("Encoding config must be an object.");
|
|
6885
|
+
}
|
|
6886
|
+
if (!AUDIO_CODECS.includes(config.codec)) {
|
|
6887
|
+
throw new TypeError(`Invalid audio codec '${config.codec}'. Must be one of: ${AUDIO_CODECS.join(", ")}.`);
|
|
6888
|
+
}
|
|
6889
|
+
if (config.bitrate === undefined && (!PCM_AUDIO_CODECS.includes(config.codec) || config.codec === "flac")) {
|
|
6890
|
+
throw new TypeError("config.bitrate must be provided for compressed audio codecs.");
|
|
6891
|
+
}
|
|
6892
|
+
if (config.bitrate !== undefined && !(config.bitrate instanceof Quality) && (!Number.isInteger(config.bitrate) || config.bitrate <= 0)) {
|
|
6893
|
+
throw new TypeError("config.bitrate, when provided, must be a positive integer or a quality.");
|
|
6894
|
+
}
|
|
6895
|
+
if (config.onEncodedPacket !== undefined && typeof config.onEncodedPacket !== "function") {
|
|
6896
|
+
throw new TypeError("config.onEncodedChunk, when provided, must be a function.");
|
|
6897
|
+
}
|
|
6898
|
+
if (config.onEncoderConfig !== undefined && typeof config.onEncoderConfig !== "function") {
|
|
6899
|
+
throw new TypeError("config.onEncoderConfig, when provided, must be a function.");
|
|
6900
|
+
}
|
|
6901
|
+
validateAudioEncodingAdditionalOptions(config.codec, config);
|
|
6902
|
+
};
|
|
6903
|
+
var validateAudioEncodingAdditionalOptions = (codec, options) => {
|
|
6904
|
+
if (!options || typeof options !== "object") {
|
|
6905
|
+
throw new TypeError("Encoding options must be an object.");
|
|
6906
|
+
}
|
|
6907
|
+
if (options.bitrateMode !== undefined && !["constant", "variable"].includes(options.bitrateMode)) {
|
|
6908
|
+
throw new TypeError("bitrateMode, when provided, must be 'constant' or 'variable'.");
|
|
6909
|
+
}
|
|
6910
|
+
if (options.fullCodecString !== undefined && typeof options.fullCodecString !== "string") {
|
|
6911
|
+
throw new TypeError("fullCodecString, when provided, must be a string.");
|
|
6912
|
+
}
|
|
6913
|
+
if (options.fullCodecString !== undefined && inferCodecFromCodecString(options.fullCodecString) !== codec) {
|
|
6914
|
+
throw new TypeError(`fullCodecString, when provided, must be a string that matches the specified codec (${codec}).`);
|
|
6915
|
+
}
|
|
6916
|
+
};
|
|
6917
|
+
var buildAudioEncoderConfig = (options) => {
|
|
6918
|
+
const resolvedBitrate = options.bitrate instanceof Quality ? options.bitrate._toAudioBitrate(options.codec) : options.bitrate;
|
|
6919
|
+
return {
|
|
6920
|
+
codec: options.fullCodecString ?? buildAudioCodecString(options.codec, options.numberOfChannels, options.sampleRate),
|
|
6921
|
+
numberOfChannels: options.numberOfChannels,
|
|
6922
|
+
sampleRate: options.sampleRate,
|
|
6923
|
+
bitrate: resolvedBitrate,
|
|
6924
|
+
bitrateMode: options.bitrateMode,
|
|
6925
|
+
...getAudioEncoderConfigExtension(options.codec)
|
|
6926
|
+
};
|
|
6927
|
+
};
|
|
6928
|
+
|
|
6200
6929
|
class Quality {
|
|
6201
6930
|
constructor(factor) {
|
|
6202
6931
|
this._factor = factor;
|
|
@@ -6267,8 +6996,52 @@ var QUALITY_LOW = /* @__PURE__ */ new Quality(0.6);
|
|
|
6267
6996
|
var QUALITY_MEDIUM = /* @__PURE__ */ new Quality(1);
|
|
6268
6997
|
var QUALITY_HIGH = /* @__PURE__ */ new Quality(2);
|
|
6269
6998
|
var QUALITY_VERY_HIGH = /* @__PURE__ */ new Quality(4);
|
|
6999
|
+
var canEncodeAudio = async (codec, options = {}) => {
|
|
7000
|
+
const { numberOfChannels = 2, sampleRate = 48000, bitrate = 128000, ...restOptions } = options;
|
|
7001
|
+
if (!AUDIO_CODECS.includes(codec)) {
|
|
7002
|
+
return false;
|
|
7003
|
+
}
|
|
7004
|
+
if (!Number.isInteger(numberOfChannels) || numberOfChannels <= 0) {
|
|
7005
|
+
throw new TypeError("numberOfChannels must be a positive integer.");
|
|
7006
|
+
}
|
|
7007
|
+
if (!Number.isInteger(sampleRate) || sampleRate <= 0) {
|
|
7008
|
+
throw new TypeError("sampleRate must be a positive integer.");
|
|
7009
|
+
}
|
|
7010
|
+
if (!(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) {
|
|
7011
|
+
throw new TypeError("bitrate must be a positive integer.");
|
|
7012
|
+
}
|
|
7013
|
+
validateAudioEncodingAdditionalOptions(codec, restOptions);
|
|
7014
|
+
let encoderConfig = null;
|
|
7015
|
+
if (customAudioEncoders.length > 0) {
|
|
7016
|
+
encoderConfig ??= buildAudioEncoderConfig({
|
|
7017
|
+
codec,
|
|
7018
|
+
numberOfChannels,
|
|
7019
|
+
sampleRate,
|
|
7020
|
+
bitrate,
|
|
7021
|
+
...restOptions
|
|
7022
|
+
});
|
|
7023
|
+
if (customAudioEncoders.some((x) => x.supports(codec, encoderConfig))) {
|
|
7024
|
+
return true;
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
if (PCM_AUDIO_CODECS.includes(codec)) {
|
|
7028
|
+
return true;
|
|
7029
|
+
}
|
|
7030
|
+
if (typeof AudioEncoder === "undefined") {
|
|
7031
|
+
return false;
|
|
7032
|
+
}
|
|
7033
|
+
encoderConfig ??= buildAudioEncoderConfig({
|
|
7034
|
+
codec,
|
|
7035
|
+
numberOfChannels,
|
|
7036
|
+
sampleRate,
|
|
7037
|
+
bitrate,
|
|
7038
|
+
...restOptions
|
|
7039
|
+
});
|
|
7040
|
+
const support = await AudioEncoder.isConfigSupported(encoderConfig);
|
|
7041
|
+
return support.supported === true;
|
|
7042
|
+
};
|
|
6270
7043
|
|
|
6271
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
7044
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/media-source.js
|
|
6272
7045
|
/*!
|
|
6273
7046
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
6274
7047
|
*
|
|
@@ -6898,18 +7671,371 @@ class AudioSource extends MediaSource {
|
|
|
6898
7671
|
this._codec = codec;
|
|
6899
7672
|
}
|
|
6900
7673
|
}
|
|
6901
|
-
class
|
|
6902
|
-
constructor(
|
|
6903
|
-
|
|
6904
|
-
this.
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
this.
|
|
7674
|
+
class AudioEncoderWrapper {
|
|
7675
|
+
constructor(source, encodingConfig) {
|
|
7676
|
+
this.source = source;
|
|
7677
|
+
this.encodingConfig = encodingConfig;
|
|
7678
|
+
this.ensureEncoderPromise = null;
|
|
7679
|
+
this.encoderInitialized = false;
|
|
7680
|
+
this.encoder = null;
|
|
7681
|
+
this.muxer = null;
|
|
7682
|
+
this.lastNumberOfChannels = null;
|
|
7683
|
+
this.lastSampleRate = null;
|
|
7684
|
+
this.isPcmEncoder = false;
|
|
7685
|
+
this.outputSampleSize = null;
|
|
7686
|
+
this.writeOutputValue = null;
|
|
7687
|
+
this.customEncoder = null;
|
|
7688
|
+
this.customEncoderCallSerializer = new CallSerializer;
|
|
7689
|
+
this.customEncoderQueueSize = 0;
|
|
7690
|
+
this.lastEndSampleIndex = null;
|
|
7691
|
+
this.error = null;
|
|
7692
|
+
this.errorNeedsNewStack = true;
|
|
6909
7693
|
}
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
7694
|
+
async add(audioSample, shouldClose) {
|
|
7695
|
+
try {
|
|
7696
|
+
this.checkForEncoderError();
|
|
7697
|
+
this.source._ensureValidAdd();
|
|
7698
|
+
if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) {
|
|
7699
|
+
if (audioSample.numberOfChannels !== this.lastNumberOfChannels || audioSample.sampleRate !== this.lastSampleRate) {
|
|
7700
|
+
throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at` + ` ${this.lastSampleRate} Hz, got ${audioSample.numberOfChannels} channels at` + ` ${audioSample.sampleRate} Hz.`);
|
|
7701
|
+
}
|
|
7702
|
+
} else {
|
|
7703
|
+
this.lastNumberOfChannels = audioSample.numberOfChannels;
|
|
7704
|
+
this.lastSampleRate = audioSample.sampleRate;
|
|
7705
|
+
}
|
|
7706
|
+
if (!this.encoderInitialized) {
|
|
7707
|
+
if (!this.ensureEncoderPromise) {
|
|
7708
|
+
this.ensureEncoder(audioSample);
|
|
7709
|
+
}
|
|
7710
|
+
if (!this.encoderInitialized) {
|
|
7711
|
+
await this.ensureEncoderPromise;
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7714
|
+
assert(this.encoderInitialized);
|
|
7715
|
+
{
|
|
7716
|
+
const startSampleIndex = Math.round(audioSample.timestamp * audioSample.sampleRate);
|
|
7717
|
+
const endSampleIndex = Math.round((audioSample.timestamp + audioSample.duration) * audioSample.sampleRate);
|
|
7718
|
+
if (this.lastEndSampleIndex === null) {
|
|
7719
|
+
this.lastEndSampleIndex = endSampleIndex;
|
|
7720
|
+
} else {
|
|
7721
|
+
const sampleDiff = startSampleIndex - this.lastEndSampleIndex;
|
|
7722
|
+
if (sampleDiff >= 64) {
|
|
7723
|
+
const fillSample = new AudioSample({
|
|
7724
|
+
data: new Float32Array(sampleDiff * audioSample.numberOfChannels),
|
|
7725
|
+
format: "f32-planar",
|
|
7726
|
+
sampleRate: audioSample.sampleRate,
|
|
7727
|
+
numberOfChannels: audioSample.numberOfChannels,
|
|
7728
|
+
numberOfFrames: sampleDiff,
|
|
7729
|
+
timestamp: this.lastEndSampleIndex / audioSample.sampleRate
|
|
7730
|
+
});
|
|
7731
|
+
await this.add(fillSample, true);
|
|
7732
|
+
}
|
|
7733
|
+
this.lastEndSampleIndex += audioSample.numberOfFrames;
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
if (this.customEncoder) {
|
|
7737
|
+
this.customEncoderQueueSize++;
|
|
7738
|
+
const clonedSample = audioSample.clone();
|
|
7739
|
+
const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample)).then(() => this.customEncoderQueueSize--).catch((error) => this.error ??= error).finally(() => {
|
|
7740
|
+
clonedSample.close();
|
|
7741
|
+
});
|
|
7742
|
+
if (this.customEncoderQueueSize >= 4) {
|
|
7743
|
+
await promise;
|
|
7744
|
+
}
|
|
7745
|
+
await this.muxer.mutex.currentPromise;
|
|
7746
|
+
} else if (this.isPcmEncoder) {
|
|
7747
|
+
await this.doPcmEncoding(audioSample, shouldClose);
|
|
7748
|
+
} else {
|
|
7749
|
+
assert(this.encoder);
|
|
7750
|
+
const audioData = audioSample.toAudioData();
|
|
7751
|
+
this.encoder.encode(audioData);
|
|
7752
|
+
audioData.close();
|
|
7753
|
+
if (shouldClose) {
|
|
7754
|
+
audioSample.close();
|
|
7755
|
+
}
|
|
7756
|
+
if (this.encoder.encodeQueueSize >= 4) {
|
|
7757
|
+
await new Promise((resolve) => this.encoder.addEventListener("dequeue", resolve, { once: true }));
|
|
7758
|
+
}
|
|
7759
|
+
await this.muxer.mutex.currentPromise;
|
|
7760
|
+
}
|
|
7761
|
+
} finally {
|
|
7762
|
+
if (shouldClose) {
|
|
7763
|
+
audioSample.close();
|
|
7764
|
+
}
|
|
7765
|
+
}
|
|
7766
|
+
}
|
|
7767
|
+
async doPcmEncoding(audioSample, shouldClose) {
|
|
7768
|
+
assert(this.outputSampleSize);
|
|
7769
|
+
assert(this.writeOutputValue);
|
|
7770
|
+
const { numberOfChannels, numberOfFrames, sampleRate, timestamp } = audioSample;
|
|
7771
|
+
const CHUNK_SIZE = 2048;
|
|
7772
|
+
const outputs = [];
|
|
7773
|
+
for (let frame = 0;frame < numberOfFrames; frame += CHUNK_SIZE) {
|
|
7774
|
+
const frameCount = Math.min(CHUNK_SIZE, audioSample.numberOfFrames - frame);
|
|
7775
|
+
const outputSize = frameCount * numberOfChannels * this.outputSampleSize;
|
|
7776
|
+
const outputBuffer = new ArrayBuffer(outputSize);
|
|
7777
|
+
const outputView = new DataView(outputBuffer);
|
|
7778
|
+
outputs.push({ frameCount, view: outputView });
|
|
7779
|
+
}
|
|
7780
|
+
const allocationSize = audioSample.allocationSize({ planeIndex: 0, format: "f32-planar" });
|
|
7781
|
+
const floats = new Float32Array(allocationSize / Float32Array.BYTES_PER_ELEMENT);
|
|
7782
|
+
for (let i = 0;i < numberOfChannels; i++) {
|
|
7783
|
+
audioSample.copyTo(floats, { planeIndex: i, format: "f32-planar" });
|
|
7784
|
+
for (let j = 0;j < outputs.length; j++) {
|
|
7785
|
+
const { frameCount, view: view2 } = outputs[j];
|
|
7786
|
+
for (let k = 0;k < frameCount; k++) {
|
|
7787
|
+
this.writeOutputValue(view2, (k * numberOfChannels + i) * this.outputSampleSize, floats[j * CHUNK_SIZE + k]);
|
|
7788
|
+
}
|
|
7789
|
+
}
|
|
7790
|
+
}
|
|
7791
|
+
if (shouldClose) {
|
|
7792
|
+
audioSample.close();
|
|
7793
|
+
}
|
|
7794
|
+
const meta = {
|
|
7795
|
+
decoderConfig: {
|
|
7796
|
+
codec: this.encodingConfig.codec,
|
|
7797
|
+
numberOfChannels,
|
|
7798
|
+
sampleRate
|
|
7799
|
+
}
|
|
7800
|
+
};
|
|
7801
|
+
for (let i = 0;i < outputs.length; i++) {
|
|
7802
|
+
const { frameCount, view: view2 } = outputs[i];
|
|
7803
|
+
const outputBuffer = view2.buffer;
|
|
7804
|
+
const startFrame = i * CHUNK_SIZE;
|
|
7805
|
+
const packet = new EncodedPacket(new Uint8Array(outputBuffer), "key", timestamp + startFrame / sampleRate, frameCount / sampleRate);
|
|
7806
|
+
this.encodingConfig.onEncodedPacket?.(packet, meta);
|
|
7807
|
+
await this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta);
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7810
|
+
ensureEncoder(audioSample) {
|
|
7811
|
+
const encoderError = new Error;
|
|
7812
|
+
this.ensureEncoderPromise = (async () => {
|
|
7813
|
+
const { numberOfChannels, sampleRate } = audioSample;
|
|
7814
|
+
const encoderConfig = buildAudioEncoderConfig({
|
|
7815
|
+
numberOfChannels,
|
|
7816
|
+
sampleRate,
|
|
7817
|
+
...this.encodingConfig
|
|
7818
|
+
});
|
|
7819
|
+
this.encodingConfig.onEncoderConfig?.(encoderConfig);
|
|
7820
|
+
const MatchingCustomEncoder = customAudioEncoders.find((x) => x.supports(this.encodingConfig.codec, encoderConfig));
|
|
7821
|
+
if (MatchingCustomEncoder) {
|
|
7822
|
+
this.customEncoder = new MatchingCustomEncoder;
|
|
7823
|
+
this.customEncoder.codec = this.encodingConfig.codec;
|
|
7824
|
+
this.customEncoder.config = encoderConfig;
|
|
7825
|
+
this.customEncoder.onPacket = (packet, meta) => {
|
|
7826
|
+
if (!(packet instanceof EncodedPacket)) {
|
|
7827
|
+
throw new TypeError("The first argument passed to onPacket must be an EncodedPacket.");
|
|
7828
|
+
}
|
|
7829
|
+
if (meta !== undefined && (!meta || typeof meta !== "object")) {
|
|
7830
|
+
throw new TypeError("The second argument passed to onPacket must be an object or undefined.");
|
|
7831
|
+
}
|
|
7832
|
+
this.encodingConfig.onEncodedPacket?.(packet, meta);
|
|
7833
|
+
this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {
|
|
7834
|
+
this.error ??= error;
|
|
7835
|
+
this.errorNeedsNewStack = false;
|
|
7836
|
+
});
|
|
7837
|
+
};
|
|
7838
|
+
await this.customEncoder.init();
|
|
7839
|
+
} else if (PCM_AUDIO_CODECS.includes(this.encodingConfig.codec)) {
|
|
7840
|
+
this.initPcmEncoder();
|
|
7841
|
+
} else {
|
|
7842
|
+
if (typeof AudioEncoder === "undefined") {
|
|
7843
|
+
throw new Error("AudioEncoder is not supported by this browser.");
|
|
7844
|
+
}
|
|
7845
|
+
const support = await AudioEncoder.isConfigSupported(encoderConfig);
|
|
7846
|
+
if (!support.supported) {
|
|
7847
|
+
throw new Error(`This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,` + ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not` + ` supported by this browser. Consider using another codec or changing your audio parameters.`);
|
|
7848
|
+
}
|
|
7849
|
+
this.encoder = new AudioEncoder({
|
|
7850
|
+
output: (chunk, meta) => {
|
|
7851
|
+
if (this.encodingConfig.codec === "aac" && meta?.decoderConfig) {
|
|
7852
|
+
let needsDescriptionOverwrite = false;
|
|
7853
|
+
if (!meta.decoderConfig.description || meta.decoderConfig.description.byteLength < 2) {
|
|
7854
|
+
needsDescriptionOverwrite = true;
|
|
7855
|
+
} else {
|
|
7856
|
+
const audioSpecificConfig = parseAacAudioSpecificConfig(toUint8Array(meta.decoderConfig.description));
|
|
7857
|
+
needsDescriptionOverwrite = audioSpecificConfig.objectType === 0;
|
|
7858
|
+
}
|
|
7859
|
+
if (needsDescriptionOverwrite) {
|
|
7860
|
+
const objectType = Number(last(encoderConfig.codec.split(".")));
|
|
7861
|
+
meta.decoderConfig.description = buildAacAudioSpecificConfig({
|
|
7862
|
+
objectType,
|
|
7863
|
+
numberOfChannels: meta.decoderConfig.numberOfChannels,
|
|
7864
|
+
sampleRate: meta.decoderConfig.sampleRate
|
|
7865
|
+
});
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
const packet = EncodedPacket.fromEncodedChunk(chunk);
|
|
7869
|
+
this.encodingConfig.onEncodedPacket?.(packet, meta);
|
|
7870
|
+
this.muxer.addEncodedAudioPacket(this.source._connectedTrack, packet, meta).catch((error) => {
|
|
7871
|
+
this.error ??= error;
|
|
7872
|
+
this.errorNeedsNewStack = false;
|
|
7873
|
+
});
|
|
7874
|
+
},
|
|
7875
|
+
error: (error) => {
|
|
7876
|
+
error.stack = encoderError.stack;
|
|
7877
|
+
this.error ??= error;
|
|
7878
|
+
}
|
|
7879
|
+
});
|
|
7880
|
+
this.encoder.configure(encoderConfig);
|
|
7881
|
+
}
|
|
7882
|
+
assert(this.source._connectedTrack);
|
|
7883
|
+
this.muxer = this.source._connectedTrack.output._muxer;
|
|
7884
|
+
this.encoderInitialized = true;
|
|
7885
|
+
})();
|
|
7886
|
+
}
|
|
7887
|
+
initPcmEncoder() {
|
|
7888
|
+
this.isPcmEncoder = true;
|
|
7889
|
+
const codec = this.encodingConfig.codec;
|
|
7890
|
+
const { dataType, sampleSize, littleEndian } = parsePcmCodec(codec);
|
|
7891
|
+
this.outputSampleSize = sampleSize;
|
|
7892
|
+
switch (sampleSize) {
|
|
7893
|
+
case 1:
|
|
7894
|
+
{
|
|
7895
|
+
if (dataType === "unsigned") {
|
|
7896
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setUint8(byteOffset, clamp((value + 1) * 127.5, 0, 255));
|
|
7897
|
+
} else if (dataType === "signed") {
|
|
7898
|
+
this.writeOutputValue = (view2, byteOffset, value) => {
|
|
7899
|
+
view2.setInt8(byteOffset, clamp(Math.round(value * 128), -128, 127));
|
|
7900
|
+
};
|
|
7901
|
+
} else if (dataType === "ulaw") {
|
|
7902
|
+
this.writeOutputValue = (view2, byteOffset, value) => {
|
|
7903
|
+
const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
|
|
7904
|
+
view2.setUint8(byteOffset, toUlaw(int16));
|
|
7905
|
+
};
|
|
7906
|
+
} else if (dataType === "alaw") {
|
|
7907
|
+
this.writeOutputValue = (view2, byteOffset, value) => {
|
|
7908
|
+
const int16 = clamp(Math.floor(value * 32767), -32768, 32767);
|
|
7909
|
+
view2.setUint8(byteOffset, toAlaw(int16));
|
|
7910
|
+
};
|
|
7911
|
+
} else {
|
|
7912
|
+
assert(false);
|
|
7913
|
+
}
|
|
7914
|
+
}
|
|
7915
|
+
;
|
|
7916
|
+
break;
|
|
7917
|
+
case 2:
|
|
7918
|
+
{
|
|
7919
|
+
if (dataType === "unsigned") {
|
|
7920
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setUint16(byteOffset, clamp((value + 1) * 32767.5, 0, 65535), littleEndian);
|
|
7921
|
+
} else if (dataType === "signed") {
|
|
7922
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setInt16(byteOffset, clamp(Math.round(value * 32767), -32768, 32767), littleEndian);
|
|
7923
|
+
} else {
|
|
7924
|
+
assert(false);
|
|
7925
|
+
}
|
|
7926
|
+
}
|
|
7927
|
+
;
|
|
7928
|
+
break;
|
|
7929
|
+
case 3:
|
|
7930
|
+
{
|
|
7931
|
+
if (dataType === "unsigned") {
|
|
7932
|
+
this.writeOutputValue = (view2, byteOffset, value) => setUint24(view2, byteOffset, clamp((value + 1) * 8388607.5, 0, 16777215), littleEndian);
|
|
7933
|
+
} else if (dataType === "signed") {
|
|
7934
|
+
this.writeOutputValue = (view2, byteOffset, value) => setInt24(view2, byteOffset, clamp(Math.round(value * 8388607), -8388608, 8388607), littleEndian);
|
|
7935
|
+
} else {
|
|
7936
|
+
assert(false);
|
|
7937
|
+
}
|
|
7938
|
+
}
|
|
7939
|
+
;
|
|
7940
|
+
break;
|
|
7941
|
+
case 4:
|
|
7942
|
+
{
|
|
7943
|
+
if (dataType === "unsigned") {
|
|
7944
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setUint32(byteOffset, clamp((value + 1) * 2147483647.5, 0, 4294967295), littleEndian);
|
|
7945
|
+
} else if (dataType === "signed") {
|
|
7946
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setInt32(byteOffset, clamp(Math.round(value * 2147483647), -2147483648, 2147483647), littleEndian);
|
|
7947
|
+
} else if (dataType === "float") {
|
|
7948
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setFloat32(byteOffset, value, littleEndian);
|
|
7949
|
+
} else {
|
|
7950
|
+
assert(false);
|
|
7951
|
+
}
|
|
7952
|
+
}
|
|
7953
|
+
;
|
|
7954
|
+
break;
|
|
7955
|
+
case 8:
|
|
7956
|
+
{
|
|
7957
|
+
if (dataType === "float") {
|
|
7958
|
+
this.writeOutputValue = (view2, byteOffset, value) => view2.setFloat64(byteOffset, value, littleEndian);
|
|
7959
|
+
} else {
|
|
7960
|
+
assert(false);
|
|
7961
|
+
}
|
|
7962
|
+
}
|
|
7963
|
+
;
|
|
7964
|
+
break;
|
|
7965
|
+
default:
|
|
7966
|
+
{
|
|
7967
|
+
assertNever(sampleSize);
|
|
7968
|
+
assert(false);
|
|
7969
|
+
}
|
|
7970
|
+
;
|
|
7971
|
+
}
|
|
7972
|
+
}
|
|
7973
|
+
async flushAndClose(forceClose) {
|
|
7974
|
+
if (!forceClose)
|
|
7975
|
+
this.checkForEncoderError();
|
|
7976
|
+
if (this.customEncoder) {
|
|
7977
|
+
if (!forceClose) {
|
|
7978
|
+
this.customEncoderCallSerializer.call(() => this.customEncoder.flush());
|
|
7979
|
+
}
|
|
7980
|
+
await this.customEncoderCallSerializer.call(() => this.customEncoder.close());
|
|
7981
|
+
} else if (this.encoder) {
|
|
7982
|
+
if (!forceClose) {
|
|
7983
|
+
await this.encoder.flush();
|
|
7984
|
+
}
|
|
7985
|
+
if (this.encoder.state !== "closed") {
|
|
7986
|
+
this.encoder.close();
|
|
7987
|
+
}
|
|
7988
|
+
}
|
|
7989
|
+
if (!forceClose)
|
|
7990
|
+
this.checkForEncoderError();
|
|
7991
|
+
}
|
|
7992
|
+
getQueueSize() {
|
|
7993
|
+
if (this.customEncoder) {
|
|
7994
|
+
return this.customEncoderQueueSize;
|
|
7995
|
+
} else if (this.isPcmEncoder) {
|
|
7996
|
+
return 0;
|
|
7997
|
+
} else {
|
|
7998
|
+
return this.encoder?.encodeQueueSize ?? 0;
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
checkForEncoderError() {
|
|
8002
|
+
if (this.error) {
|
|
8003
|
+
if (this.errorNeedsNewStack) {
|
|
8004
|
+
this.error.stack = new Error().stack;
|
|
8005
|
+
}
|
|
8006
|
+
throw this.error;
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
|
|
8011
|
+
class AudioSampleSource extends AudioSource {
|
|
8012
|
+
constructor(encodingConfig) {
|
|
8013
|
+
validateAudioEncodingConfig(encodingConfig);
|
|
8014
|
+
super(encodingConfig.codec);
|
|
8015
|
+
this._encoder = new AudioEncoderWrapper(this, encodingConfig);
|
|
8016
|
+
}
|
|
8017
|
+
add(audioSample) {
|
|
8018
|
+
if (!(audioSample instanceof AudioSample)) {
|
|
8019
|
+
throw new TypeError("audioSample must be an AudioSample.");
|
|
8020
|
+
}
|
|
8021
|
+
return this._encoder.add(audioSample, false);
|
|
8022
|
+
}
|
|
8023
|
+
_flushAndClose(forceClose) {
|
|
8024
|
+
return this._encoder.flushAndClose(forceClose);
|
|
8025
|
+
}
|
|
8026
|
+
}
|
|
8027
|
+
class SubtitleSource extends MediaSource {
|
|
8028
|
+
constructor(codec) {
|
|
8029
|
+
super();
|
|
8030
|
+
this._connectedTrack = null;
|
|
8031
|
+
if (!SUBTITLE_CODECS.includes(codec)) {
|
|
8032
|
+
throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(", ")}.`);
|
|
8033
|
+
}
|
|
8034
|
+
this._codec = codec;
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
|
|
8038
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/output.js
|
|
6913
8039
|
/*!
|
|
6914
8040
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
6915
8041
|
*
|
|
@@ -7123,7 +8249,7 @@ class Output {
|
|
|
7123
8249
|
})();
|
|
7124
8250
|
}
|
|
7125
8251
|
}
|
|
7126
|
-
// ../../node_modules/.bun/mediabunny@1.25.
|
|
8252
|
+
// ../../node_modules/.bun/mediabunny@1.25.8/node_modules/mediabunny/dist/modules/src/index.js
|
|
7127
8253
|
/*!
|
|
7128
8254
|
* Copyright (c) 2025-present, Vanilagy and contributors
|
|
7129
8255
|
*
|
|
@@ -7135,6 +8261,25 @@ class Output {
|
|
|
7135
8261
|
// src/render-media-on-web.tsx
|
|
7136
8262
|
import { Internals as Internals3 } from "remotion";
|
|
7137
8263
|
|
|
8264
|
+
// src/add-sample.ts
|
|
8265
|
+
var addVideoSampleAndCloseFrame = async (frameToEncode, videoSampleSource) => {
|
|
8266
|
+
const sample = new VideoSample(frameToEncode);
|
|
8267
|
+
try {
|
|
8268
|
+
await videoSampleSource.add(sample);
|
|
8269
|
+
} finally {
|
|
8270
|
+
sample.close();
|
|
8271
|
+
frameToEncode.close();
|
|
8272
|
+
}
|
|
8273
|
+
};
|
|
8274
|
+
var addAudioSample = async (audio, audioSampleSource) => {
|
|
8275
|
+
const sample = new AudioSample(audio);
|
|
8276
|
+
try {
|
|
8277
|
+
await audioSampleSource.add(sample);
|
|
8278
|
+
} finally {
|
|
8279
|
+
sample.close();
|
|
8280
|
+
}
|
|
8281
|
+
};
|
|
8282
|
+
|
|
7138
8283
|
// src/artifact.ts
|
|
7139
8284
|
import { NoReactInternals } from "remotion/no-react";
|
|
7140
8285
|
var onlyArtifact = async ({
|
|
@@ -7172,34 +8317,72 @@ var onlyArtifact = async ({
|
|
|
7172
8317
|
}
|
|
7173
8318
|
return result.filter(NoReactInternals.truthy);
|
|
7174
8319
|
};
|
|
7175
|
-
var handleArtifacts = ({
|
|
7176
|
-
ref,
|
|
7177
|
-
onArtifact
|
|
7178
|
-
}) => {
|
|
8320
|
+
var handleArtifacts = () => {
|
|
7179
8321
|
const previousArtifacts = [];
|
|
7180
8322
|
const handle = async ({
|
|
7181
8323
|
imageData,
|
|
7182
|
-
frame
|
|
8324
|
+
frame,
|
|
8325
|
+
assets: artifactAssets,
|
|
8326
|
+
onArtifact
|
|
7183
8327
|
}) => {
|
|
7184
|
-
const
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
if (previousArtifact) {
|
|
7193
|
-
throw new Error(`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`);
|
|
7194
|
-
}
|
|
7195
|
-
onArtifact(artifact);
|
|
7196
|
-
previousArtifacts.push({ frame, filename: artifact.filename });
|
|
8328
|
+
const artifacts = await onlyArtifact({
|
|
8329
|
+
assets: artifactAssets,
|
|
8330
|
+
frameBuffer: imageData
|
|
8331
|
+
});
|
|
8332
|
+
for (const artifact of artifacts) {
|
|
8333
|
+
const previousArtifact = previousArtifacts.find((a) => a.filename === artifact.filename);
|
|
8334
|
+
if (previousArtifact) {
|
|
8335
|
+
throw new Error(`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`);
|
|
7197
8336
|
}
|
|
8337
|
+
onArtifact(artifact);
|
|
8338
|
+
previousArtifacts.push({ frame, filename: artifact.filename });
|
|
7198
8339
|
}
|
|
7199
8340
|
};
|
|
7200
8341
|
return { handle };
|
|
7201
8342
|
};
|
|
7202
8343
|
|
|
8344
|
+
// src/audio.ts
|
|
8345
|
+
var TARGET_NUMBER_OF_CHANNELS = 2;
|
|
8346
|
+
var TARGET_SAMPLE_RATE = 48000;
|
|
8347
|
+
function mixAudio(waves, length) {
|
|
8348
|
+
if (waves.length === 1) {
|
|
8349
|
+
return waves[0];
|
|
8350
|
+
}
|
|
8351
|
+
const mixed = new Int16Array(length);
|
|
8352
|
+
for (let i = 0;i < length; i++) {
|
|
8353
|
+
const sum = waves.reduce((acc, wave2) => {
|
|
8354
|
+
return acc + (wave2[i] ?? 0);
|
|
8355
|
+
}, 0);
|
|
8356
|
+
mixed[i] = Math.max(-32768, Math.min(32767, sum));
|
|
8357
|
+
}
|
|
8358
|
+
return mixed;
|
|
8359
|
+
}
|
|
8360
|
+
var onlyInlineAudio = ({
|
|
8361
|
+
assets,
|
|
8362
|
+
fps,
|
|
8363
|
+
frame
|
|
8364
|
+
}) => {
|
|
8365
|
+
const inlineAudio = assets.filter((asset) => asset.type === "inline-audio");
|
|
8366
|
+
if (inlineAudio.length === 0) {
|
|
8367
|
+
return null;
|
|
8368
|
+
}
|
|
8369
|
+
const expectedLength = Math.round(TARGET_NUMBER_OF_CHANNELS * TARGET_SAMPLE_RATE / fps);
|
|
8370
|
+
for (const asset of inlineAudio) {
|
|
8371
|
+
if (asset.toneFrequency !== 1) {
|
|
8372
|
+
throw new Error("Setting the toneFrequency is not supported yet in web rendering.");
|
|
8373
|
+
}
|
|
8374
|
+
}
|
|
8375
|
+
const mixedAudio = mixAudio(inlineAudio.map((asset) => asset.audio), expectedLength);
|
|
8376
|
+
return new AudioData({
|
|
8377
|
+
data: mixedAudio,
|
|
8378
|
+
format: "s16",
|
|
8379
|
+
numberOfChannels: TARGET_NUMBER_OF_CHANNELS,
|
|
8380
|
+
numberOfFrames: expectedLength / TARGET_NUMBER_OF_CHANNELS,
|
|
8381
|
+
sampleRate: TARGET_SAMPLE_RATE,
|
|
8382
|
+
timestamp: frame / fps * 1e6
|
|
8383
|
+
});
|
|
8384
|
+
};
|
|
8385
|
+
|
|
7203
8386
|
// src/create-scaffold.tsx
|
|
7204
8387
|
import { createRef } from "react";
|
|
7205
8388
|
import { flushSync as flushSync2 } from "react-dom";
|
|
@@ -7399,6 +8582,25 @@ var getRealFrameRange = (durationInFrames, frameRange) => {
|
|
|
7399
8582
|
return frameRange;
|
|
7400
8583
|
};
|
|
7401
8584
|
|
|
8585
|
+
// src/get-audio-encoding-config.ts
|
|
8586
|
+
var getDefaultAudioEncodingConfig = async () => {
|
|
8587
|
+
const preferredDefaultAudioEncodingConfig = {
|
|
8588
|
+
codec: "aac",
|
|
8589
|
+
bitrate: QUALITY_MEDIUM
|
|
8590
|
+
};
|
|
8591
|
+
if (await canEncodeAudio(preferredDefaultAudioEncodingConfig.codec, preferredDefaultAudioEncodingConfig)) {
|
|
8592
|
+
return preferredDefaultAudioEncodingConfig;
|
|
8593
|
+
}
|
|
8594
|
+
const backupDefaultAudioEncodingConfig = {
|
|
8595
|
+
codec: "opus",
|
|
8596
|
+
bitrate: QUALITY_MEDIUM
|
|
8597
|
+
};
|
|
8598
|
+
if (await canEncodeAudio(backupDefaultAudioEncodingConfig.codec, backupDefaultAudioEncodingConfig)) {
|
|
8599
|
+
return backupDefaultAudioEncodingConfig;
|
|
8600
|
+
}
|
|
8601
|
+
return null;
|
|
8602
|
+
};
|
|
8603
|
+
|
|
7402
8604
|
// src/mediabunny-mappings.ts
|
|
7403
8605
|
var codecToMediabunnyCodec = (codec) => {
|
|
7404
8606
|
switch (codec) {
|
|
@@ -7453,7 +8655,143 @@ var getQualityForWebRendererQuality = (quality) => {
|
|
|
7453
8655
|
}
|
|
7454
8656
|
};
|
|
7455
8657
|
|
|
7456
|
-
// src/
|
|
8658
|
+
// src/drawing/border-radius.ts
|
|
8659
|
+
function parseValue({
|
|
8660
|
+
value,
|
|
8661
|
+
reference
|
|
8662
|
+
}) {
|
|
8663
|
+
value = value.trim();
|
|
8664
|
+
if (value.endsWith("%")) {
|
|
8665
|
+
const percentage = parseFloat(value);
|
|
8666
|
+
return percentage / 100 * reference;
|
|
8667
|
+
}
|
|
8668
|
+
if (value.endsWith("px")) {
|
|
8669
|
+
return parseFloat(value);
|
|
8670
|
+
}
|
|
8671
|
+
return parseFloat(value);
|
|
8672
|
+
}
|
|
8673
|
+
function expandShorthand(values) {
|
|
8674
|
+
if (values.length === 1) {
|
|
8675
|
+
return [values[0], values[0], values[0], values[0]];
|
|
8676
|
+
}
|
|
8677
|
+
if (values.length === 2) {
|
|
8678
|
+
return [values[0], values[1], values[0], values[1]];
|
|
8679
|
+
}
|
|
8680
|
+
if (values.length === 3) {
|
|
8681
|
+
return [values[0], values[1], values[2], values[1]];
|
|
8682
|
+
}
|
|
8683
|
+
return [values[0], values[1], values[2], values[3]];
|
|
8684
|
+
}
|
|
8685
|
+
function clampBorderRadius({
|
|
8686
|
+
borderRadius,
|
|
8687
|
+
width,
|
|
8688
|
+
height
|
|
8689
|
+
}) {
|
|
8690
|
+
const clamped = {
|
|
8691
|
+
topLeft: { ...borderRadius.topLeft },
|
|
8692
|
+
topRight: { ...borderRadius.topRight },
|
|
8693
|
+
bottomRight: { ...borderRadius.bottomRight },
|
|
8694
|
+
bottomLeft: { ...borderRadius.bottomLeft }
|
|
8695
|
+
};
|
|
8696
|
+
const topSum = clamped.topLeft.horizontal + clamped.topRight.horizontal;
|
|
8697
|
+
if (topSum > width) {
|
|
8698
|
+
const factor = width / topSum;
|
|
8699
|
+
clamped.topLeft.horizontal *= factor;
|
|
8700
|
+
clamped.topRight.horizontal *= factor;
|
|
8701
|
+
}
|
|
8702
|
+
const rightSum = clamped.topRight.vertical + clamped.bottomRight.vertical;
|
|
8703
|
+
if (rightSum > height) {
|
|
8704
|
+
const factor = height / rightSum;
|
|
8705
|
+
clamped.topRight.vertical *= factor;
|
|
8706
|
+
clamped.bottomRight.vertical *= factor;
|
|
8707
|
+
}
|
|
8708
|
+
const bottomSum = clamped.bottomRight.horizontal + clamped.bottomLeft.horizontal;
|
|
8709
|
+
if (bottomSum > width) {
|
|
8710
|
+
const factor = width / bottomSum;
|
|
8711
|
+
clamped.bottomRight.horizontal *= factor;
|
|
8712
|
+
clamped.bottomLeft.horizontal *= factor;
|
|
8713
|
+
}
|
|
8714
|
+
const leftSum = clamped.bottomLeft.vertical + clamped.topLeft.vertical;
|
|
8715
|
+
if (leftSum > height) {
|
|
8716
|
+
const factor = height / leftSum;
|
|
8717
|
+
clamped.bottomLeft.vertical *= factor;
|
|
8718
|
+
clamped.topLeft.vertical *= factor;
|
|
8719
|
+
}
|
|
8720
|
+
return clamped;
|
|
8721
|
+
}
|
|
8722
|
+
function parseBorderRadius({
|
|
8723
|
+
borderRadius,
|
|
8724
|
+
width,
|
|
8725
|
+
height
|
|
8726
|
+
}) {
|
|
8727
|
+
const parts = borderRadius.split("/").map((part) => part.trim());
|
|
8728
|
+
const horizontalPart = parts[0];
|
|
8729
|
+
const verticalPart = parts[1];
|
|
8730
|
+
const horizontalValues = horizontalPart.split(/\s+/).filter((v) => v);
|
|
8731
|
+
const verticalValues = verticalPart ? verticalPart.split(/\s+/).filter((v) => v) : horizontalValues;
|
|
8732
|
+
const [hTopLeft, hTopRight, hBottomRight, hBottomLeft] = expandShorthand(horizontalValues);
|
|
8733
|
+
const [vTopLeft, vTopRight, vBottomRight, vBottomLeft] = expandShorthand(verticalValues);
|
|
8734
|
+
return clampBorderRadius({
|
|
8735
|
+
borderRadius: {
|
|
8736
|
+
topLeft: {
|
|
8737
|
+
horizontal: parseValue({ value: hTopLeft, reference: width }),
|
|
8738
|
+
vertical: parseValue({ value: vTopLeft, reference: height })
|
|
8739
|
+
},
|
|
8740
|
+
topRight: {
|
|
8741
|
+
horizontal: parseValue({ value: hTopRight, reference: width }),
|
|
8742
|
+
vertical: parseValue({ value: vTopRight, reference: height })
|
|
8743
|
+
},
|
|
8744
|
+
bottomRight: {
|
|
8745
|
+
horizontal: parseValue({ value: hBottomRight, reference: width }),
|
|
8746
|
+
vertical: parseValue({ value: vBottomRight, reference: height })
|
|
8747
|
+
},
|
|
8748
|
+
bottomLeft: {
|
|
8749
|
+
horizontal: parseValue({ value: hBottomLeft, reference: width }),
|
|
8750
|
+
vertical: parseValue({ value: vBottomLeft, reference: height })
|
|
8751
|
+
}
|
|
8752
|
+
},
|
|
8753
|
+
width,
|
|
8754
|
+
height
|
|
8755
|
+
});
|
|
8756
|
+
}
|
|
8757
|
+
function setBorderRadius({
|
|
8758
|
+
ctx,
|
|
8759
|
+
x,
|
|
8760
|
+
y,
|
|
8761
|
+
width,
|
|
8762
|
+
height,
|
|
8763
|
+
borderRadius
|
|
8764
|
+
}) {
|
|
8765
|
+
if (borderRadius.topLeft.horizontal === 0 && borderRadius.topLeft.vertical === 0 && borderRadius.topRight.horizontal === 0 && borderRadius.topRight.vertical === 0 && borderRadius.bottomRight.horizontal === 0 && borderRadius.bottomRight.vertical === 0 && borderRadius.bottomLeft.horizontal === 0 && borderRadius.bottomLeft.vertical === 0) {
|
|
8766
|
+
return () => {};
|
|
8767
|
+
}
|
|
8768
|
+
ctx.save();
|
|
8769
|
+
ctx.beginPath();
|
|
8770
|
+
ctx.moveTo(x + borderRadius.topLeft.horizontal, y);
|
|
8771
|
+
ctx.lineTo(x + width - borderRadius.topRight.horizontal, y);
|
|
8772
|
+
if (borderRadius.topRight.horizontal > 0 || borderRadius.topRight.vertical > 0) {
|
|
8773
|
+
ctx.ellipse(x + width - borderRadius.topRight.horizontal, y + borderRadius.topRight.vertical, borderRadius.topRight.horizontal, borderRadius.topRight.vertical, 0, -Math.PI / 2, 0);
|
|
8774
|
+
}
|
|
8775
|
+
ctx.lineTo(x + width, y + height - borderRadius.bottomRight.vertical);
|
|
8776
|
+
if (borderRadius.bottomRight.horizontal > 0 || borderRadius.bottomRight.vertical > 0) {
|
|
8777
|
+
ctx.ellipse(x + width - borderRadius.bottomRight.horizontal, y + height - borderRadius.bottomRight.vertical, borderRadius.bottomRight.horizontal, borderRadius.bottomRight.vertical, 0, 0, Math.PI / 2);
|
|
8778
|
+
}
|
|
8779
|
+
ctx.lineTo(x + borderRadius.bottomLeft.horizontal, y + height);
|
|
8780
|
+
if (borderRadius.bottomLeft.horizontal > 0 || borderRadius.bottomLeft.vertical > 0) {
|
|
8781
|
+
ctx.ellipse(x + borderRadius.bottomLeft.horizontal, y + height - borderRadius.bottomLeft.vertical, borderRadius.bottomLeft.horizontal, borderRadius.bottomLeft.vertical, 0, Math.PI / 2, Math.PI);
|
|
8782
|
+
}
|
|
8783
|
+
ctx.lineTo(x, y + borderRadius.topLeft.vertical);
|
|
8784
|
+
if (borderRadius.topLeft.horizontal > 0 || borderRadius.topLeft.vertical > 0) {
|
|
8785
|
+
ctx.ellipse(x + borderRadius.topLeft.horizontal, y + borderRadius.topLeft.vertical, borderRadius.topLeft.horizontal, borderRadius.topLeft.vertical, 0, Math.PI, Math.PI * 3 / 2);
|
|
8786
|
+
}
|
|
8787
|
+
ctx.closePath();
|
|
8788
|
+
ctx.clip();
|
|
8789
|
+
return () => {
|
|
8790
|
+
ctx.restore();
|
|
8791
|
+
};
|
|
8792
|
+
}
|
|
8793
|
+
|
|
8794
|
+
// src/drawing/parse-transform-origin.ts
|
|
7457
8795
|
var parseTransformOrigin = (transformOrigin) => {
|
|
7458
8796
|
if (transformOrigin.trim() === "") {
|
|
7459
8797
|
return null;
|
|
@@ -7462,7 +8800,7 @@ var parseTransformOrigin = (transformOrigin) => {
|
|
|
7462
8800
|
return { x: parseFloat(x), y: parseFloat(y) };
|
|
7463
8801
|
};
|
|
7464
8802
|
|
|
7465
|
-
// src/calculate-transforms.ts
|
|
8803
|
+
// src/drawing/calculate-transforms.ts
|
|
7466
8804
|
var getInternalTransformOrigin = (transform) => {
|
|
7467
8805
|
const centerX = transform.boundingClientRect.width / 2;
|
|
7468
8806
|
const centerY = transform.boundingClientRect.height / 2;
|
|
@@ -7483,8 +8821,13 @@ var calculateTransforms = (element) => {
|
|
|
7483
8821
|
let parent = element;
|
|
7484
8822
|
const transforms = [];
|
|
7485
8823
|
const toReset = [];
|
|
8824
|
+
let opacity = 1;
|
|
7486
8825
|
while (parent) {
|
|
7487
8826
|
const computedStyle = getComputedStyle(parent);
|
|
8827
|
+
const parentOpacity = computedStyle.opacity;
|
|
8828
|
+
if (parentOpacity && parentOpacity !== "") {
|
|
8829
|
+
opacity *= parseFloat(parentOpacity);
|
|
8830
|
+
}
|
|
7488
8831
|
if (computedStyle.transform && computedStyle.transform !== "none" || parent === element) {
|
|
7489
8832
|
const toParse = computedStyle.transform === "none" || computedStyle.transform === "" ? undefined : computedStyle.transform;
|
|
7490
8833
|
const matrix = new DOMMatrix(toParse);
|
|
@@ -7525,11 +8868,122 @@ var calculateTransforms = (element) => {
|
|
|
7525
8868
|
reset();
|
|
7526
8869
|
}
|
|
7527
8870
|
},
|
|
7528
|
-
nativeTransformOrigin
|
|
8871
|
+
nativeTransformOrigin,
|
|
8872
|
+
opacity
|
|
8873
|
+
};
|
|
8874
|
+
};
|
|
8875
|
+
|
|
8876
|
+
// src/drawing/draw-border.ts
|
|
8877
|
+
var drawBorder = ({
|
|
8878
|
+
ctx,
|
|
8879
|
+
x,
|
|
8880
|
+
y,
|
|
8881
|
+
width,
|
|
8882
|
+
height,
|
|
8883
|
+
borderRadius,
|
|
8884
|
+
computedStyle
|
|
8885
|
+
}) => {
|
|
8886
|
+
const {
|
|
8887
|
+
borderStyle,
|
|
8888
|
+
borderColor,
|
|
8889
|
+
borderWidth: computedBorderWidth
|
|
8890
|
+
} = computedStyle;
|
|
8891
|
+
const borderWidths = computedBorderWidth.split(/\s+/).map((w) => parseFloat(w));
|
|
8892
|
+
const borderTop = borderWidths[0] || 0;
|
|
8893
|
+
const borderRight = borderWidths[1] || borderTop;
|
|
8894
|
+
const borderBottom = borderWidths[2] || borderTop;
|
|
8895
|
+
const borderLeft = borderWidths[3] || borderRight;
|
|
8896
|
+
const hasBorder = borderStyle && borderStyle !== "none" && borderStyle !== "hidden" && (borderTop > 0 || borderRight > 0 || borderBottom > 0 || borderLeft > 0);
|
|
8897
|
+
if (!hasBorder) {
|
|
8898
|
+
return;
|
|
8899
|
+
}
|
|
8900
|
+
const originalStrokeStyle = ctx.strokeStyle;
|
|
8901
|
+
const originalLineWidth = ctx.lineWidth;
|
|
8902
|
+
const originalLineDash = ctx.getLineDash();
|
|
8903
|
+
ctx.strokeStyle = borderColor;
|
|
8904
|
+
if (borderStyle === "dashed") {
|
|
8905
|
+
const max = Math.max(borderTop, borderRight, borderBottom, borderLeft);
|
|
8906
|
+
ctx.setLineDash([max * 2, max]);
|
|
8907
|
+
} else if (borderStyle === "dotted") {
|
|
8908
|
+
ctx.setLineDash([
|
|
8909
|
+
Math.max(borderTop, borderRight, borderBottom, borderLeft)
|
|
8910
|
+
]);
|
|
8911
|
+
} else {
|
|
8912
|
+
ctx.setLineDash([]);
|
|
8913
|
+
}
|
|
8914
|
+
const maxBorderWidth = Math.max(borderTop, borderRight, borderBottom, borderLeft);
|
|
8915
|
+
ctx.beginPath();
|
|
8916
|
+
const borderX = x + maxBorderWidth / 2;
|
|
8917
|
+
const borderY = y + maxBorderWidth / 2;
|
|
8918
|
+
const borderWidth = width - maxBorderWidth;
|
|
8919
|
+
const borderHeight = height - maxBorderWidth;
|
|
8920
|
+
const adjustedBorderRadius = {
|
|
8921
|
+
topLeft: {
|
|
8922
|
+
horizontal: Math.max(0, borderRadius.topLeft.horizontal - maxBorderWidth / 2),
|
|
8923
|
+
vertical: Math.max(0, borderRadius.topLeft.vertical - maxBorderWidth / 2)
|
|
8924
|
+
},
|
|
8925
|
+
topRight: {
|
|
8926
|
+
horizontal: Math.max(0, borderRadius.topRight.horizontal - maxBorderWidth / 2),
|
|
8927
|
+
vertical: Math.max(0, borderRadius.topRight.vertical - maxBorderWidth / 2)
|
|
8928
|
+
},
|
|
8929
|
+
bottomRight: {
|
|
8930
|
+
horizontal: Math.max(0, borderRadius.bottomRight.horizontal - maxBorderWidth / 2),
|
|
8931
|
+
vertical: Math.max(0, borderRadius.bottomRight.vertical - maxBorderWidth / 2)
|
|
8932
|
+
},
|
|
8933
|
+
bottomLeft: {
|
|
8934
|
+
horizontal: Math.max(0, borderRadius.bottomLeft.horizontal - maxBorderWidth / 2),
|
|
8935
|
+
vertical: Math.max(0, borderRadius.bottomLeft.vertical - maxBorderWidth / 2)
|
|
8936
|
+
}
|
|
8937
|
+
};
|
|
8938
|
+
ctx.moveTo(borderX + adjustedBorderRadius.topLeft.horizontal, borderY);
|
|
8939
|
+
ctx.lineTo(borderX + borderWidth - adjustedBorderRadius.topRight.horizontal, borderY);
|
|
8940
|
+
if (adjustedBorderRadius.topRight.horizontal > 0 || adjustedBorderRadius.topRight.vertical > 0) {
|
|
8941
|
+
ctx.ellipse(borderX + borderWidth - adjustedBorderRadius.topRight.horizontal, borderY + adjustedBorderRadius.topRight.vertical, adjustedBorderRadius.topRight.horizontal, adjustedBorderRadius.topRight.vertical, 0, -Math.PI / 2, 0);
|
|
8942
|
+
}
|
|
8943
|
+
ctx.lineTo(borderX + borderWidth, borderY + borderHeight - adjustedBorderRadius.bottomRight.vertical);
|
|
8944
|
+
if (adjustedBorderRadius.bottomRight.horizontal > 0 || adjustedBorderRadius.bottomRight.vertical > 0) {
|
|
8945
|
+
ctx.ellipse(borderX + borderWidth - adjustedBorderRadius.bottomRight.horizontal, borderY + borderHeight - adjustedBorderRadius.bottomRight.vertical, adjustedBorderRadius.bottomRight.horizontal, adjustedBorderRadius.bottomRight.vertical, 0, 0, Math.PI / 2);
|
|
8946
|
+
}
|
|
8947
|
+
ctx.lineTo(borderX + adjustedBorderRadius.bottomLeft.horizontal, borderY + borderHeight);
|
|
8948
|
+
if (adjustedBorderRadius.bottomLeft.horizontal > 0 || adjustedBorderRadius.bottomLeft.vertical > 0) {
|
|
8949
|
+
ctx.ellipse(borderX + adjustedBorderRadius.bottomLeft.horizontal, borderY + borderHeight - adjustedBorderRadius.bottomLeft.vertical, adjustedBorderRadius.bottomLeft.horizontal, adjustedBorderRadius.bottomLeft.vertical, 0, Math.PI / 2, Math.PI);
|
|
8950
|
+
}
|
|
8951
|
+
ctx.lineTo(borderX, borderY + adjustedBorderRadius.topLeft.vertical);
|
|
8952
|
+
if (adjustedBorderRadius.topLeft.horizontal > 0 || adjustedBorderRadius.topLeft.vertical > 0) {
|
|
8953
|
+
ctx.ellipse(borderX + adjustedBorderRadius.topLeft.horizontal, borderY + adjustedBorderRadius.topLeft.vertical, adjustedBorderRadius.topLeft.horizontal, adjustedBorderRadius.topLeft.vertical, 0, Math.PI, Math.PI * 3 / 2);
|
|
8954
|
+
}
|
|
8955
|
+
ctx.closePath();
|
|
8956
|
+
ctx.lineWidth = maxBorderWidth;
|
|
8957
|
+
ctx.stroke();
|
|
8958
|
+
ctx.strokeStyle = originalStrokeStyle;
|
|
8959
|
+
ctx.lineWidth = originalLineWidth;
|
|
8960
|
+
ctx.setLineDash(originalLineDash);
|
|
8961
|
+
};
|
|
8962
|
+
|
|
8963
|
+
// src/drawing/opacity.ts
|
|
8964
|
+
var setOpacity = ({
|
|
8965
|
+
ctx,
|
|
8966
|
+
opacity
|
|
8967
|
+
}) => {
|
|
8968
|
+
const previousAlpha = ctx.globalAlpha;
|
|
8969
|
+
ctx.globalAlpha = previousAlpha * opacity;
|
|
8970
|
+
return () => {
|
|
8971
|
+
ctx.globalAlpha = previousAlpha;
|
|
8972
|
+
};
|
|
8973
|
+
};
|
|
8974
|
+
|
|
8975
|
+
// src/drawing/transform.ts
|
|
8976
|
+
var setTransform = ({
|
|
8977
|
+
ctx,
|
|
8978
|
+
transform
|
|
8979
|
+
}) => {
|
|
8980
|
+
ctx.setTransform(transform);
|
|
8981
|
+
return () => {
|
|
8982
|
+
ctx.setTransform(new DOMMatrix);
|
|
7529
8983
|
};
|
|
7530
8984
|
};
|
|
7531
8985
|
|
|
7532
|
-
// src/
|
|
8986
|
+
// src/drawing/turn-svg-into-drawable.ts
|
|
7533
8987
|
var turnSvgIntoDrawable = (svg) => {
|
|
7534
8988
|
const originalTransform = svg.style.transform;
|
|
7535
8989
|
const originalTransformOrigin = svg.style.transformOrigin;
|
|
@@ -7563,59 +9017,83 @@ var turnSvgIntoDrawable = (svg) => {
|
|
|
7563
9017
|
});
|
|
7564
9018
|
};
|
|
7565
9019
|
|
|
7566
|
-
// src/
|
|
7567
|
-
var
|
|
7568
|
-
|
|
7569
|
-
context
|
|
7570
|
-
|
|
7571
|
-
|
|
7572
|
-
|
|
9020
|
+
// src/drawing/draw-element-to-canvas.ts
|
|
9021
|
+
var drawElementToCanvas = async ({
|
|
9022
|
+
element,
|
|
9023
|
+
context
|
|
9024
|
+
}) => {
|
|
9025
|
+
const { totalMatrix, reset, dimensions, opacity } = calculateTransforms(element);
|
|
9026
|
+
if (opacity === 0) {
|
|
9027
|
+
reset();
|
|
9028
|
+
return;
|
|
9029
|
+
}
|
|
9030
|
+
if (dimensions.width <= 0 || dimensions.height <= 0) {
|
|
9031
|
+
reset();
|
|
9032
|
+
return;
|
|
9033
|
+
}
|
|
9034
|
+
const computedStyle = getComputedStyle(element);
|
|
9035
|
+
const background = computedStyle.backgroundColor;
|
|
9036
|
+
const borderRadius = parseBorderRadius({
|
|
9037
|
+
borderRadius: computedStyle.borderRadius,
|
|
9038
|
+
width: dimensions.width,
|
|
9039
|
+
height: dimensions.height
|
|
9040
|
+
});
|
|
9041
|
+
const finishTransform = setTransform({
|
|
9042
|
+
ctx: context,
|
|
9043
|
+
transform: totalMatrix
|
|
9044
|
+
});
|
|
9045
|
+
const finishBorderRadius = setBorderRadius({
|
|
9046
|
+
ctx: context,
|
|
9047
|
+
x: dimensions.left,
|
|
9048
|
+
y: dimensions.top,
|
|
9049
|
+
width: dimensions.width,
|
|
9050
|
+
height: dimensions.height,
|
|
9051
|
+
borderRadius
|
|
9052
|
+
});
|
|
9053
|
+
const finishOpacity = setOpacity({
|
|
9054
|
+
ctx: context,
|
|
9055
|
+
opacity
|
|
9056
|
+
});
|
|
9057
|
+
const drawable = element instanceof SVGSVGElement ? await turnSvgIntoDrawable(element) : element instanceof HTMLImageElement ? element : element instanceof HTMLCanvasElement ? element : null;
|
|
9058
|
+
if (background && background !== "transparent" && !(background.startsWith("rgba") && (background.endsWith(", 0)") || background.endsWith(",0")))) {
|
|
9059
|
+
const originalFillStyle = context.fillStyle;
|
|
9060
|
+
context.fillStyle = background;
|
|
9061
|
+
context.fillRect(dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
9062
|
+
context.fillStyle = originalFillStyle;
|
|
9063
|
+
}
|
|
9064
|
+
if (drawable) {
|
|
9065
|
+
context.drawImage(drawable, dimensions.left, dimensions.top, dimensions.width, dimensions.height);
|
|
9066
|
+
}
|
|
9067
|
+
drawBorder({
|
|
9068
|
+
ctx: context,
|
|
9069
|
+
x: dimensions.left,
|
|
9070
|
+
y: dimensions.top,
|
|
9071
|
+
width: dimensions.width,
|
|
9072
|
+
height: dimensions.height,
|
|
9073
|
+
borderRadius,
|
|
9074
|
+
computedStyle
|
|
9075
|
+
});
|
|
9076
|
+
finishOpacity();
|
|
9077
|
+
finishBorderRadius();
|
|
9078
|
+
finishTransform();
|
|
7573
9079
|
reset();
|
|
7574
9080
|
};
|
|
7575
9081
|
|
|
7576
9082
|
// src/compose.ts
|
|
7577
|
-
var compose = async ({
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
const canvas = new OffscreenCanvas(width, height);
|
|
7583
|
-
const context = canvas.getContext("2d");
|
|
7584
|
-
if (!context) {
|
|
7585
|
-
throw new Error("Could not get context");
|
|
7586
|
-
}
|
|
7587
|
-
for (const composable of composables) {
|
|
7588
|
-
await composeCanvas(composable.element, context);
|
|
7589
|
-
}
|
|
7590
|
-
return canvas;
|
|
7591
|
-
};
|
|
7592
|
-
|
|
7593
|
-
// src/find-capturable-elements.ts
|
|
7594
|
-
var findCapturableElements = (element) => {
|
|
7595
|
-
const canvasAndSvgElements = element.querySelectorAll("svg,canvas,img");
|
|
7596
|
-
const composables = [];
|
|
7597
|
-
Array.from(canvasAndSvgElements).forEach((capturableElement) => {
|
|
7598
|
-
if (capturableElement.tagName.toLocaleLowerCase() === "canvas") {
|
|
7599
|
-
const canvas = capturableElement;
|
|
7600
|
-
composables.push({
|
|
7601
|
-
type: "canvas",
|
|
7602
|
-
element: canvas
|
|
7603
|
-
});
|
|
7604
|
-
} else if (capturableElement.tagName.toLocaleLowerCase() === "svg") {
|
|
7605
|
-
const svg = capturableElement;
|
|
7606
|
-
composables.push({
|
|
7607
|
-
type: "svg",
|
|
7608
|
-
element: svg
|
|
7609
|
-
});
|
|
7610
|
-
} else if (capturableElement.tagName.toLocaleLowerCase() === "img") {
|
|
7611
|
-
const img = capturableElement;
|
|
7612
|
-
composables.push({
|
|
7613
|
-
type: "img",
|
|
7614
|
-
element: img
|
|
7615
|
-
});
|
|
9083
|
+
var compose = async (element, context) => {
|
|
9084
|
+
const treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, (node) => {
|
|
9085
|
+
if (node instanceof Element) {
|
|
9086
|
+
const computedStyle = getComputedStyle(node);
|
|
9087
|
+
return computedStyle.display === "none" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT;
|
|
7616
9088
|
}
|
|
9089
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
7617
9090
|
});
|
|
7618
|
-
|
|
9091
|
+
while (treeWalker.nextNode()) {
|
|
9092
|
+
const node = treeWalker.currentNode;
|
|
9093
|
+
if (node instanceof HTMLElement || node instanceof SVGElement) {
|
|
9094
|
+
await drawElementToCanvas({ element: node, context });
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
7619
9097
|
};
|
|
7620
9098
|
|
|
7621
9099
|
// src/take-screenshot.ts
|
|
@@ -7624,13 +9102,13 @@ var createFrame = async ({
|
|
|
7624
9102
|
width,
|
|
7625
9103
|
height
|
|
7626
9104
|
}) => {
|
|
7627
|
-
const
|
|
7628
|
-
const
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
return
|
|
9105
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
9106
|
+
const context = canvas.getContext("2d");
|
|
9107
|
+
if (!context) {
|
|
9108
|
+
throw new Error("Could not get context");
|
|
9109
|
+
}
|
|
9110
|
+
await compose(div, context);
|
|
9111
|
+
return canvas;
|
|
7634
9112
|
};
|
|
7635
9113
|
var takeScreenshot = async ({
|
|
7636
9114
|
div,
|
|
@@ -7807,10 +9285,7 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7807
9285
|
defaultCodec: resolved.defaultCodec,
|
|
7808
9286
|
defaultOutName: resolved.defaultOutName
|
|
7809
9287
|
});
|
|
7810
|
-
const artifactsHandler = handleArtifacts(
|
|
7811
|
-
ref: collectAssets,
|
|
7812
|
-
onArtifact
|
|
7813
|
-
});
|
|
9288
|
+
const artifactsHandler = handleArtifacts();
|
|
7814
9289
|
cleanupFns.push(() => {
|
|
7815
9290
|
cleanupScaffold();
|
|
7816
9291
|
});
|
|
@@ -7850,6 +9325,15 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7850
9325
|
videoSampleSource.close();
|
|
7851
9326
|
});
|
|
7852
9327
|
output.addVideoTrack(videoSampleSource);
|
|
9328
|
+
const defaultAudioEncodingConfig = await getDefaultAudioEncodingConfig();
|
|
9329
|
+
if (!defaultAudioEncodingConfig) {
|
|
9330
|
+
return Promise.reject(new Error("No default audio encoding config found"));
|
|
9331
|
+
}
|
|
9332
|
+
const audioSampleSource = new AudioSampleSource(defaultAudioEncodingConfig);
|
|
9333
|
+
cleanupFns.push(() => {
|
|
9334
|
+
audioSampleSource.close();
|
|
9335
|
+
});
|
|
9336
|
+
output.addAudioTrack(audioSampleSource);
|
|
7853
9337
|
await output.start();
|
|
7854
9338
|
if (signal?.aborted) {
|
|
7855
9339
|
throw new Error("renderMediaOnWeb() was cancelled");
|
|
@@ -7859,8 +9343,11 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7859
9343
|
encodedFrames: 0
|
|
7860
9344
|
};
|
|
7861
9345
|
const throttledOnProgress = createThrottledProgressCallback(onProgress);
|
|
7862
|
-
for (let
|
|
7863
|
-
|
|
9346
|
+
for (let frame = realFrameRange[0];frame <= realFrameRange[1]; frame++) {
|
|
9347
|
+
if (signal?.aborted) {
|
|
9348
|
+
throw new Error("renderMediaOnWeb() was cancelled");
|
|
9349
|
+
}
|
|
9350
|
+
timeUpdater.current?.update(frame);
|
|
7864
9351
|
await waitForReady({
|
|
7865
9352
|
timeoutInMilliseconds: delayRenderTimeoutInMilliseconds,
|
|
7866
9353
|
scope: delayRenderScope,
|
|
@@ -7875,11 +9362,23 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7875
9362
|
width: resolved.width,
|
|
7876
9363
|
height: resolved.height
|
|
7877
9364
|
});
|
|
7878
|
-
await artifactsHandler.handle({ imageData, frame: i });
|
|
7879
9365
|
if (signal?.aborted) {
|
|
7880
9366
|
throw new Error("renderMediaOnWeb() was cancelled");
|
|
7881
9367
|
}
|
|
7882
|
-
const
|
|
9368
|
+
const assets = collectAssets.current.collectAssets();
|
|
9369
|
+
if (onArtifact) {
|
|
9370
|
+
await artifactsHandler.handle({
|
|
9371
|
+
imageData,
|
|
9372
|
+
frame,
|
|
9373
|
+
assets,
|
|
9374
|
+
onArtifact
|
|
9375
|
+
});
|
|
9376
|
+
}
|
|
9377
|
+
if (signal?.aborted) {
|
|
9378
|
+
throw new Error("renderMediaOnWeb() was cancelled");
|
|
9379
|
+
}
|
|
9380
|
+
const audio = onlyInlineAudio({ assets, fps: resolved.fps, frame });
|
|
9381
|
+
const timestamp = Math.round((frame - realFrameRange[0]) / resolved.fps * 1e6);
|
|
7883
9382
|
const videoFrame = new VideoFrame(imageData, {
|
|
7884
9383
|
timestamp
|
|
7885
9384
|
});
|
|
@@ -7888,6 +9387,9 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7888
9387
|
let frameToEncode = videoFrame;
|
|
7889
9388
|
if (onFrame) {
|
|
7890
9389
|
const returnedFrame = await onFrame(videoFrame);
|
|
9390
|
+
if (signal?.aborted) {
|
|
9391
|
+
throw new Error("renderMediaOnWeb() was cancelled");
|
|
9392
|
+
}
|
|
7891
9393
|
frameToEncode = validateVideoFrame({
|
|
7892
9394
|
originalFrame: videoFrame,
|
|
7893
9395
|
returnedFrame,
|
|
@@ -7896,16 +9398,19 @@ var internalRenderMediaOnWeb = async ({
|
|
|
7896
9398
|
expectedTimestamp: timestamp
|
|
7897
9399
|
});
|
|
7898
9400
|
}
|
|
7899
|
-
await
|
|
9401
|
+
await Promise.all([
|
|
9402
|
+
addVideoSampleAndCloseFrame(frameToEncode, videoSampleSource),
|
|
9403
|
+
audio ? addAudioSample(audio, audioSampleSource) : Promise.resolve()
|
|
9404
|
+
]);
|
|
7900
9405
|
progress.encodedFrames++;
|
|
7901
9406
|
throttledOnProgress?.({ ...progress });
|
|
7902
|
-
frameToEncode.close();
|
|
7903
9407
|
if (signal?.aborted) {
|
|
7904
9408
|
throw new Error("renderMediaOnWeb() was cancelled");
|
|
7905
9409
|
}
|
|
7906
9410
|
}
|
|
7907
9411
|
onProgress?.({ ...progress });
|
|
7908
9412
|
videoSampleSource.close();
|
|
9413
|
+
audioSampleSource.close();
|
|
7909
9414
|
await output.finalize();
|
|
7910
9415
|
return output.target.buffer;
|
|
7911
9416
|
} finally {
|
|
@@ -7982,10 +9487,7 @@ async function internalRenderStillOnWeb({
|
|
|
7982
9487
|
defaultCodec: resolved.defaultCodec,
|
|
7983
9488
|
defaultOutName: resolved.defaultOutName
|
|
7984
9489
|
});
|
|
7985
|
-
const artifactsHandler = handleArtifacts(
|
|
7986
|
-
ref: collectAssets,
|
|
7987
|
-
onArtifact
|
|
7988
|
-
});
|
|
9490
|
+
const artifactsHandler = handleArtifacts();
|
|
7989
9491
|
try {
|
|
7990
9492
|
if (signal?.aborted) {
|
|
7991
9493
|
throw new Error("renderStillOnWeb() was cancelled");
|
|
@@ -8005,7 +9507,10 @@ async function internalRenderStillOnWeb({
|
|
|
8005
9507
|
height: resolved.height,
|
|
8006
9508
|
imageFormat
|
|
8007
9509
|
});
|
|
8008
|
-
|
|
9510
|
+
const assets = collectAssets.current.collectAssets();
|
|
9511
|
+
if (onArtifact) {
|
|
9512
|
+
await artifactsHandler.handle({ imageData, frame, assets, onArtifact });
|
|
9513
|
+
}
|
|
8009
9514
|
return imageData;
|
|
8010
9515
|
} finally {
|
|
8011
9516
|
cleanupScaffold();
|