@remotion/media 4.0.402 → 4.0.404
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/audio/audio-for-preview.d.ts +2 -0
- package/dist/audio/audio-preview-iterator.d.ts +8 -3
- package/dist/audio/props.d.ts +2 -0
- package/dist/audio-extraction/audio-iterator.d.ts +5 -4
- package/dist/audio-extraction/audio-manager.d.ts +23 -7
- package/dist/audio-extraction/extract-audio.d.ts +2 -4
- package/dist/audio-iterator-manager.d.ts +2 -2
- package/dist/caches.d.ts +34 -17
- package/dist/convert-audiodata/apply-volume.d.ts +1 -1
- package/dist/convert-audiodata/resample-audiodata.d.ts +2 -2
- package/dist/debug-overlay/preview-overlay.d.ts +83 -5
- package/dist/esm/index.mjs +524 -476
- package/dist/extract-frame-and-audio.d.ts +1 -2
- package/dist/get-sink.d.ts +1 -2
- package/dist/index.d.ts +2 -1
- package/dist/media-player.d.ts +1 -1
- package/dist/on-error.d.ts +12 -0
- package/dist/show-in-timeline.d.ts +2 -2
- package/dist/use-media-in-timeline.d.ts +2 -2
- package/dist/video/props.d.ts +5 -0
- package/dist/video/video-for-preview.d.ts +2 -0
- package/dist/video/video-for-rendering.d.ts +2 -0
- package/dist/video/video-preview-iterator.d.ts +7 -2
- package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +1 -2
- package/dist/video-extraction/extract-frame.d.ts +2 -4
- package/dist/video-extraction/get-frames-since-keyframe.d.ts +2 -12
- package/dist/video-extraction/keyframe-bank.d.ts +14 -12
- package/dist/video-extraction/keyframe-manager.d.ts +7 -9
- package/dist/video-iterator-manager.d.ts +4 -5
- package/package.json +8 -7
- package/dist/audio/allow-wait.js +0 -15
- package/dist/audio/audio-for-preview.js +0 -304
- package/dist/audio/audio-for-rendering.js +0 -194
- package/dist/audio/audio-preview-iterator.js +0 -176
- package/dist/audio/audio.js +0 -20
- package/dist/audio/props.js +0 -1
- package/dist/audio-extraction/audio-cache.js +0 -66
- package/dist/audio-extraction/audio-iterator.js +0 -132
- package/dist/audio-extraction/audio-manager.js +0 -113
- package/dist/audio-extraction/extract-audio.js +0 -132
- package/dist/audio-iterator-manager.js +0 -228
- package/dist/browser-can-use-webgl2.js +0 -13
- package/dist/caches.js +0 -61
- package/dist/calculate-playbacktime.js +0 -4
- package/dist/convert-audiodata/apply-volume.js +0 -17
- package/dist/convert-audiodata/combine-audiodata.js +0 -23
- package/dist/convert-audiodata/convert-audiodata.js +0 -73
- package/dist/convert-audiodata/resample-audiodata.js +0 -94
- package/dist/debug-overlay/preview-overlay.js +0 -44
- package/dist/extract-frame-and-audio.js +0 -101
- package/dist/get-sink.js +0 -15
- package/dist/get-time-in-seconds.js +0 -40
- package/dist/helpers/round-to-4-digits.js +0 -4
- package/dist/index.js +0 -12
- package/dist/is-type-of-error.js +0 -20
- package/dist/looped-frame.js +0 -10
- package/dist/media-player.js +0 -445
- package/dist/nonce-manager.js +0 -13
- package/dist/prewarm-iterator-for-looping.js +0 -56
- package/dist/render-timestamp-range.js +0 -9
- package/dist/show-in-timeline.js +0 -31
- package/dist/use-media-in-timeline.js +0 -103
- package/dist/video/props.js +0 -1
- package/dist/video/video-for-preview.js +0 -329
- package/dist/video/video-for-rendering.js +0 -263
- package/dist/video/video-preview-iterator.js +0 -122
- package/dist/video/video.js +0 -35
- package/dist/video-extraction/add-broadcast-channel-listener.js +0 -125
- package/dist/video-extraction/extract-frame-via-broadcast-channel.js +0 -113
- package/dist/video-extraction/extract-frame.js +0 -85
- package/dist/video-extraction/get-allocation-size.js +0 -6
- package/dist/video-extraction/get-frames-since-keyframe.js +0 -108
- package/dist/video-extraction/keyframe-bank.js +0 -159
- package/dist/video-extraction/keyframe-manager.js +0 -206
- package/dist/video-extraction/remember-actual-matroska-timestamps.js +0 -19
- package/dist/video-extraction/rotate-frame.js +0 -34
- package/dist/video-iterator-manager.js +0 -109
package/dist/esm/index.mjs
CHANGED
|
@@ -946,7 +946,7 @@ class MediaPlayer {
|
|
|
946
946
|
}) {
|
|
947
947
|
this.canvas = canvas ?? null;
|
|
948
948
|
this.src = src;
|
|
949
|
-
this.logLevel = logLevel
|
|
949
|
+
this.logLevel = logLevel;
|
|
950
950
|
this.sharedAudioContext = sharedAudioContext;
|
|
951
951
|
this.playbackRate = playbackRate;
|
|
952
952
|
this.globalPlaybackRate = globalPlaybackRate;
|
|
@@ -1344,6 +1344,27 @@ class MediaPlayer {
|
|
|
1344
1344
|
};
|
|
1345
1345
|
}
|
|
1346
1346
|
|
|
1347
|
+
// src/on-error.ts
|
|
1348
|
+
var callOnErrorAndResolve = ({
|
|
1349
|
+
onError,
|
|
1350
|
+
error,
|
|
1351
|
+
disallowFallback,
|
|
1352
|
+
isClientSideRendering,
|
|
1353
|
+
clientSideError
|
|
1354
|
+
}) => {
|
|
1355
|
+
const result = onError?.(error);
|
|
1356
|
+
if (isClientSideRendering) {
|
|
1357
|
+
return ["fail", clientSideError];
|
|
1358
|
+
}
|
|
1359
|
+
if (result) {
|
|
1360
|
+
return [result, error];
|
|
1361
|
+
}
|
|
1362
|
+
if (disallowFallback) {
|
|
1363
|
+
return ["fail", error];
|
|
1364
|
+
}
|
|
1365
|
+
return ["fallback", error];
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1347
1368
|
// src/show-in-timeline.ts
|
|
1348
1369
|
import { useMemo } from "react";
|
|
1349
1370
|
import { Internals as Internals4, useVideoConfig } from "remotion";
|
|
@@ -1538,7 +1559,8 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
1538
1559
|
disallowFallbackToHtml5Audio,
|
|
1539
1560
|
toneFrequency,
|
|
1540
1561
|
audioStreamIndex,
|
|
1541
|
-
fallbackHtml5AudioProps
|
|
1562
|
+
fallbackHtml5AudioProps,
|
|
1563
|
+
onError
|
|
1542
1564
|
}) => {
|
|
1543
1565
|
const videoConfig = useUnsafeVideoConfig();
|
|
1544
1566
|
const frame = useCurrentFrame2();
|
|
@@ -1637,36 +1659,35 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
1637
1659
|
if (result.type === "disposed") {
|
|
1638
1660
|
return;
|
|
1639
1661
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1662
|
+
const handleError = (error, fallbackMessage) => {
|
|
1663
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
1664
|
+
onError,
|
|
1665
|
+
error,
|
|
1666
|
+
disallowFallback: disallowFallbackToHtml5Audio,
|
|
1667
|
+
isClientSideRendering: false,
|
|
1668
|
+
clientSideError: error
|
|
1669
|
+
});
|
|
1670
|
+
if (action === "fail") {
|
|
1671
|
+
throw errorToUse;
|
|
1672
|
+
} else {
|
|
1673
|
+
Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
|
|
1674
|
+
setShouldFallbackToNativeAudio(true);
|
|
1643
1675
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1676
|
+
};
|
|
1677
|
+
if (result.type === "unknown-container-format") {
|
|
1678
|
+
handleError(new Error(`Unknown container format ${preloadedSrc}.`), `Unknown container format for ${preloadedSrc} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <Html5Audio>`);
|
|
1646
1679
|
return;
|
|
1647
1680
|
}
|
|
1648
1681
|
if (result.type === "network-error") {
|
|
1649
|
-
|
|
1650
|
-
throw new Error(`Network error fetching ${preloadedSrc}, and 'disallowFallbackToHtml5Audio' was set.`);
|
|
1651
|
-
}
|
|
1652
|
-
Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, `Network error fetching ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1653
|
-
setShouldFallbackToNativeAudio(true);
|
|
1682
|
+
handleError(new Error(`Network error fetching ${preloadedSrc}.`), `Network error fetching ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1654
1683
|
return;
|
|
1655
1684
|
}
|
|
1656
1685
|
if (result.type === "cannot-decode") {
|
|
1657
|
-
|
|
1658
|
-
throw new Error(`Cannot decode ${preloadedSrc}, and 'disallowFallbackToHtml5Audio' was set.`);
|
|
1659
|
-
}
|
|
1660
|
-
Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, `Cannot decode ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1661
|
-
setShouldFallbackToNativeAudio(true);
|
|
1686
|
+
handleError(new Error(`Cannot decode ${preloadedSrc}.`), `Cannot decode ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1662
1687
|
return;
|
|
1663
1688
|
}
|
|
1664
1689
|
if (result.type === "no-tracks") {
|
|
1665
|
-
|
|
1666
|
-
throw new Error(`No video or audio tracks found for ${preloadedSrc}, and 'disallowFallbackToHtml5Audio' was set.`);
|
|
1667
|
-
}
|
|
1668
|
-
Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, `No video or audio tracks found for ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1669
|
-
setShouldFallbackToNativeAudio(true);
|
|
1690
|
+
handleError(new Error(`No video or audio tracks found for ${preloadedSrc}.`), `No video or audio tracks found for ${preloadedSrc}, falling back to <Html5Audio>`);
|
|
1670
1691
|
return;
|
|
1671
1692
|
}
|
|
1672
1693
|
if (result.type === "success") {
|
|
@@ -1675,11 +1696,32 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
1675
1696
|
Internals6.Log.trace({ logLevel, tag: "@remotion/media" }, `[AudioForPreview] MediaPlayer initialized successfully`);
|
|
1676
1697
|
}
|
|
1677
1698
|
}).catch((error) => {
|
|
1678
|
-
|
|
1679
|
-
|
|
1699
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
1700
|
+
onError,
|
|
1701
|
+
error,
|
|
1702
|
+
disallowFallback: disallowFallbackToHtml5Audio,
|
|
1703
|
+
isClientSideRendering: false,
|
|
1704
|
+
clientSideError: error
|
|
1705
|
+
});
|
|
1706
|
+
if (action === "fail") {
|
|
1707
|
+
throw errorToUse;
|
|
1708
|
+
} else {
|
|
1709
|
+
Internals6.Log.error({ logLevel, tag: "@remotion/media" }, "[AudioForPreview] Failed to initialize MediaPlayer", error);
|
|
1710
|
+
setShouldFallbackToNativeAudio(true);
|
|
1711
|
+
}
|
|
1680
1712
|
});
|
|
1681
1713
|
} catch (error) {
|
|
1682
|
-
|
|
1714
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
1715
|
+
error,
|
|
1716
|
+
onError,
|
|
1717
|
+
disallowFallback: disallowFallbackToHtml5Audio,
|
|
1718
|
+
isClientSideRendering: false,
|
|
1719
|
+
clientSideError: error
|
|
1720
|
+
});
|
|
1721
|
+
if (action === "fail") {
|
|
1722
|
+
throw errorToUse;
|
|
1723
|
+
}
|
|
1724
|
+
Internals6.Log.error({ logLevel, tag: "@remotion/media" }, "[AudioForPreview] MediaPlayer initialization failed", errorToUse);
|
|
1683
1725
|
setShouldFallbackToNativeAudio(true);
|
|
1684
1726
|
}
|
|
1685
1727
|
return () => {
|
|
@@ -1700,7 +1742,8 @@ var AudioForPreviewAssertedShowing = ({
|
|
|
1700
1742
|
videoConfig.fps,
|
|
1701
1743
|
audioStreamIndex,
|
|
1702
1744
|
disallowFallbackToHtml5Audio,
|
|
1703
|
-
buffer
|
|
1745
|
+
buffer,
|
|
1746
|
+
onError
|
|
1704
1747
|
]);
|
|
1705
1748
|
useLayoutEffect(() => {
|
|
1706
1749
|
const audioPlayer = mediaPlayerRef.current;
|
|
@@ -1827,9 +1870,11 @@ var AudioForPreview = ({
|
|
|
1827
1870
|
disallowFallbackToHtml5Audio,
|
|
1828
1871
|
toneFrequency,
|
|
1829
1872
|
audioStreamIndex,
|
|
1830
|
-
fallbackHtml5AudioProps
|
|
1873
|
+
fallbackHtml5AudioProps,
|
|
1874
|
+
onError
|
|
1831
1875
|
}) => {
|
|
1832
1876
|
const preloadedSrc = usePreload(src);
|
|
1877
|
+
const defaultLogLevel = Internals6.useLogLevel();
|
|
1833
1878
|
const frame = useCurrentFrame2();
|
|
1834
1879
|
const videoConfig = useVideoConfig2();
|
|
1835
1880
|
const currentTime = frame / videoConfig.fps;
|
|
@@ -1861,7 +1906,7 @@ var AudioForPreview = ({
|
|
|
1861
1906
|
audioStreamIndex: audioStreamIndex ?? 0,
|
|
1862
1907
|
src: preloadedSrc,
|
|
1863
1908
|
playbackRate: playbackRate ?? 1,
|
|
1864
|
-
logLevel: logLevel ??
|
|
1909
|
+
logLevel: logLevel ?? defaultLogLevel,
|
|
1865
1910
|
muted: muted ?? false,
|
|
1866
1911
|
volume: volume ?? 1,
|
|
1867
1912
|
loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? "repeat",
|
|
@@ -1873,6 +1918,7 @@ var AudioForPreview = ({
|
|
|
1873
1918
|
stack,
|
|
1874
1919
|
disallowFallbackToHtml5Audio: disallowFallbackToHtml5Audio ?? false,
|
|
1875
1920
|
toneFrequency,
|
|
1921
|
+
onError,
|
|
1876
1922
|
fallbackHtml5AudioProps
|
|
1877
1923
|
});
|
|
1878
1924
|
};
|
|
@@ -2011,7 +2057,7 @@ var makeAudioIterator2 = ({
|
|
|
2011
2057
|
const getSamples = async (timestamp, durationInSeconds) => {
|
|
2012
2058
|
lastUsed = Date.now();
|
|
2013
2059
|
if (fullDuration !== null && timestamp > fullDuration) {
|
|
2014
|
-
cache.clearBeforeThreshold(fullDuration -
|
|
2060
|
+
cache.clearBeforeThreshold(fullDuration - SAFE_WINDOW_OF_MONOTONICITY);
|
|
2015
2061
|
return [];
|
|
2016
2062
|
}
|
|
2017
2063
|
const samples = cache.getSamples(timestamp, durationInSeconds);
|
|
@@ -2024,7 +2070,7 @@ var makeAudioIterator2 = ({
|
|
|
2024
2070
|
while (true) {
|
|
2025
2071
|
const sample = await getNextSample();
|
|
2026
2072
|
const deleteBefore = fullDuration === null ? timestamp : Math.min(timestamp, fullDuration);
|
|
2027
|
-
cache.clearBeforeThreshold(deleteBefore -
|
|
2073
|
+
cache.clearBeforeThreshold(deleteBefore - SAFE_WINDOW_OF_MONOTONICITY);
|
|
2028
2074
|
if (sample === null) {
|
|
2029
2075
|
break;
|
|
2030
2076
|
}
|
|
@@ -2154,8 +2200,14 @@ var makeAudioManager = () => {
|
|
|
2154
2200
|
logLevel,
|
|
2155
2201
|
maxCacheSize
|
|
2156
2202
|
}) => {
|
|
2157
|
-
|
|
2203
|
+
let attempts = 0;
|
|
2204
|
+
const maxAttempts = 3;
|
|
2205
|
+
while ((await getTotalCacheStats()).totalSize > maxCacheSize && attempts < maxAttempts) {
|
|
2158
2206
|
deleteOldestIterator();
|
|
2207
|
+
attempts++;
|
|
2208
|
+
}
|
|
2209
|
+
if ((await getTotalCacheStats()).totalSize > maxCacheSize && attempts >= maxAttempts) {
|
|
2210
|
+
Internals8.Log.warn({ logLevel, tag: "@remotion/media" }, `Audio cache: Exceeded max cache size after ${maxAttempts} attempts. Still ${(await getTotalCacheStats()).totalSize} bytes used, target was ${maxCacheSize} bytes.`);
|
|
2159
2211
|
}
|
|
2160
2212
|
for (const iterator of iterators) {
|
|
2161
2213
|
if (iterator.src === src && await iterator.waitForCompletion() && iterator.canSatisfyRequestedTime(timeInSeconds)) {
|
|
@@ -2226,21 +2278,6 @@ var makeAudioManager = () => {
|
|
|
2226
2278
|
// src/video-extraction/keyframe-manager.ts
|
|
2227
2279
|
import { Internals as Internals10 } from "remotion";
|
|
2228
2280
|
|
|
2229
|
-
// src/browser-can-use-webgl2.ts
|
|
2230
|
-
var browserCanUseWebGl2 = null;
|
|
2231
|
-
var browserCanUseWebGl2Uncached = () => {
|
|
2232
|
-
const canvas = new OffscreenCanvas(1, 1);
|
|
2233
|
-
const context = canvas.getContext("webgl2");
|
|
2234
|
-
return context !== null;
|
|
2235
|
-
};
|
|
2236
|
-
var canBrowserUseWebGl2 = () => {
|
|
2237
|
-
if (browserCanUseWebGl2 !== null) {
|
|
2238
|
-
return browserCanUseWebGl2;
|
|
2239
|
-
}
|
|
2240
|
-
browserCanUseWebGl2 = browserCanUseWebGl2Uncached();
|
|
2241
|
-
return browserCanUseWebGl2;
|
|
2242
|
-
};
|
|
2243
|
-
|
|
2244
2281
|
// src/render-timestamp-range.ts
|
|
2245
2282
|
var renderTimestampRange = (timestamps) => {
|
|
2246
2283
|
if (timestamps.length === 0) {
|
|
@@ -2252,18 +2289,6 @@ var renderTimestampRange = (timestamps) => {
|
|
|
2252
2289
|
return `${timestamps[0].toFixed(3)}...${timestamps[timestamps.length - 1].toFixed(3)}`;
|
|
2253
2290
|
};
|
|
2254
2291
|
|
|
2255
|
-
// src/video-extraction/get-frames-since-keyframe.ts
|
|
2256
|
-
import {
|
|
2257
|
-
ALL_FORMATS as ALL_FORMATS2,
|
|
2258
|
-
AudioSampleSink,
|
|
2259
|
-
EncodedPacketSink,
|
|
2260
|
-
Input as Input2,
|
|
2261
|
-
MATROSKA,
|
|
2262
|
-
UrlSource as UrlSource2,
|
|
2263
|
-
VideoSampleSink,
|
|
2264
|
-
WEBM
|
|
2265
|
-
} from "mediabunny";
|
|
2266
|
-
|
|
2267
2292
|
// src/video-extraction/keyframe-bank.ts
|
|
2268
2293
|
import { Internals as Internals9 } from "remotion";
|
|
2269
2294
|
|
|
@@ -2276,36 +2301,42 @@ var getAllocationSize = (sample) => {
|
|
|
2276
2301
|
};
|
|
2277
2302
|
|
|
2278
2303
|
// src/video-extraction/keyframe-bank.ts
|
|
2279
|
-
var
|
|
2280
|
-
|
|
2281
|
-
endTimestampInSeconds,
|
|
2282
|
-
sampleIterator,
|
|
2304
|
+
var BIGGEST_ALLOWED_JUMP_FORWARD_SECONDS = 3;
|
|
2305
|
+
var makeKeyframeBank = async ({
|
|
2283
2306
|
logLevel: parentLogLevel,
|
|
2284
|
-
src
|
|
2307
|
+
src,
|
|
2308
|
+
videoSampleSink,
|
|
2309
|
+
requestedTimestamp
|
|
2285
2310
|
}) => {
|
|
2286
|
-
|
|
2311
|
+
const sampleIterator = videoSampleSink.samples(roundTo4Digits(requestedTimestamp));
|
|
2287
2312
|
const frames = {};
|
|
2288
2313
|
const frameTimestamps = [];
|
|
2314
|
+
let hasReachedEndOfVideo = false;
|
|
2289
2315
|
let lastUsed = Date.now();
|
|
2290
2316
|
let allocationSize = 0;
|
|
2317
|
+
const deleteFrameAtTimestamp = (timestamp) => {
|
|
2318
|
+
allocationSize -= getAllocationSize(frames[timestamp]);
|
|
2319
|
+
frameTimestamps.splice(frameTimestamps.indexOf(timestamp), 1);
|
|
2320
|
+
frames[timestamp].close();
|
|
2321
|
+
delete frames[timestamp];
|
|
2322
|
+
};
|
|
2291
2323
|
const deleteFramesBeforeTimestamp = ({
|
|
2292
2324
|
logLevel,
|
|
2293
2325
|
timestampInSeconds
|
|
2294
2326
|
}) => {
|
|
2295
2327
|
const deletedTimestamps = [];
|
|
2296
2328
|
for (const frameTimestamp of frameTimestamps.slice()) {
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2329
|
+
if (hasReachedEndOfVideo) {
|
|
2330
|
+
const isLast = frameTimestamp === frameTimestamps[frameTimestamps.length - 1];
|
|
2331
|
+
if (isLast) {
|
|
2332
|
+
continue;
|
|
2333
|
+
}
|
|
2300
2334
|
}
|
|
2301
2335
|
if (frameTimestamp < timestampInSeconds) {
|
|
2302
2336
|
if (!frames[frameTimestamp]) {
|
|
2303
2337
|
continue;
|
|
2304
2338
|
}
|
|
2305
|
-
|
|
2306
|
-
frameTimestamps.splice(frameTimestamps.indexOf(frameTimestamp), 1);
|
|
2307
|
-
frames[frameTimestamp].close();
|
|
2308
|
-
delete frames[frameTimestamp];
|
|
2339
|
+
deleteFrameAtTimestamp(frameTimestamp);
|
|
2309
2340
|
deletedTimestamps.push(frameTimestamp);
|
|
2310
2341
|
}
|
|
2311
2342
|
}
|
|
@@ -2322,32 +2353,31 @@ var makeKeyframeBank = ({
|
|
|
2322
2353
|
if (!lastFrame) {
|
|
2323
2354
|
return true;
|
|
2324
2355
|
}
|
|
2325
|
-
return roundTo4Digits(lastFrame.timestamp + lastFrame.duration) > roundTo4Digits(timestamp) +
|
|
2356
|
+
return roundTo4Digits(lastFrame.timestamp + lastFrame.duration) > roundTo4Digits(timestamp) + SAFE_WINDOW_OF_MONOTONICITY;
|
|
2326
2357
|
};
|
|
2327
|
-
const addFrame = (frame) => {
|
|
2358
|
+
const addFrame = (frame, logLevel) => {
|
|
2328
2359
|
if (frames[frame.timestamp]) {
|
|
2329
|
-
|
|
2330
|
-
frameTimestamps.splice(frameTimestamps.indexOf(frame.timestamp), 1);
|
|
2331
|
-
frames[frame.timestamp].close();
|
|
2332
|
-
delete frames[frame.timestamp];
|
|
2360
|
+
deleteFrameAtTimestamp(frame.timestamp);
|
|
2333
2361
|
}
|
|
2334
2362
|
frames[frame.timestamp] = frame;
|
|
2335
2363
|
frameTimestamps.push(frame.timestamp);
|
|
2336
2364
|
allocationSize += getAllocationSize(frame);
|
|
2337
2365
|
lastUsed = Date.now();
|
|
2366
|
+
Internals9.Log.trace({ logLevel, tag: "@remotion/media" }, `Added frame at ${frame.timestamp}sec to bank`);
|
|
2338
2367
|
};
|
|
2339
|
-
const ensureEnoughFramesForTimestamp = async (timestampInSeconds) => {
|
|
2368
|
+
const ensureEnoughFramesForTimestamp = async (timestampInSeconds, logLevel) => {
|
|
2340
2369
|
while (!hasDecodedEnoughForTimestamp(timestampInSeconds)) {
|
|
2341
2370
|
const sample = await sampleIterator.next();
|
|
2342
2371
|
if (sample.value) {
|
|
2343
|
-
addFrame(sample.value);
|
|
2372
|
+
addFrame(sample.value, logLevel);
|
|
2344
2373
|
}
|
|
2345
2374
|
if (sample.done) {
|
|
2375
|
+
hasReachedEndOfVideo = true;
|
|
2346
2376
|
break;
|
|
2347
2377
|
}
|
|
2348
2378
|
deleteFramesBeforeTimestamp({
|
|
2349
2379
|
logLevel: parentLogLevel,
|
|
2350
|
-
timestampInSeconds: timestampInSeconds -
|
|
2380
|
+
timestampInSeconds: timestampInSeconds - SAFE_WINDOW_OF_MONOTONICITY
|
|
2351
2381
|
});
|
|
2352
2382
|
}
|
|
2353
2383
|
lastUsed = Date.now();
|
|
@@ -2355,13 +2385,10 @@ var makeKeyframeBank = ({
|
|
|
2355
2385
|
const getFrameFromTimestamp = async (timestampInSeconds) => {
|
|
2356
2386
|
lastUsed = Date.now();
|
|
2357
2387
|
let adjustedTimestamp = timestampInSeconds;
|
|
2358
|
-
if (roundTo4Digits(
|
|
2359
|
-
adjustedTimestamp =
|
|
2388
|
+
if (hasReachedEndOfVideo && roundTo4Digits(adjustedTimestamp) > roundTo4Digits(frameTimestamps[frameTimestamps.length - 1])) {
|
|
2389
|
+
adjustedTimestamp = frameTimestamps[frameTimestamps.length - 1];
|
|
2360
2390
|
}
|
|
2361
|
-
|
|
2362
|
-
adjustedTimestamp = endTimestampInSeconds;
|
|
2363
|
-
}
|
|
2364
|
-
await ensureEnoughFramesForTimestamp(adjustedTimestamp);
|
|
2391
|
+
await ensureEnoughFramesForTimestamp(adjustedTimestamp, parentLogLevel);
|
|
2365
2392
|
for (let i = frameTimestamps.length - 1;i >= 0; i--) {
|
|
2366
2393
|
const sample = frames[frameTimestamps[i]];
|
|
2367
2394
|
if (!sample) {
|
|
@@ -2371,32 +2398,11 @@ var makeKeyframeBank = ({
|
|
|
2371
2398
|
return sample;
|
|
2372
2399
|
}
|
|
2373
2400
|
}
|
|
2374
|
-
return null;
|
|
2401
|
+
return frames[frameTimestamps[0]] ?? null;
|
|
2375
2402
|
};
|
|
2376
2403
|
const hasTimestampInSecond = async (timestamp) => {
|
|
2377
2404
|
return await getFrameFromTimestamp(timestamp) !== null;
|
|
2378
2405
|
};
|
|
2379
|
-
const prepareForDeletion = (logLevel) => {
|
|
2380
|
-
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, `Preparing for deletion of keyframe bank from ${startTimestampInSeconds}sec to ${endTimestampInSeconds}sec`);
|
|
2381
|
-
sampleIterator.return().then((result) => {
|
|
2382
|
-
if (result.value) {
|
|
2383
|
-
result.value.close();
|
|
2384
|
-
}
|
|
2385
|
-
return null;
|
|
2386
|
-
});
|
|
2387
|
-
let framesDeleted = 0;
|
|
2388
|
-
for (const frameTimestamp of frameTimestamps) {
|
|
2389
|
-
if (!frames[frameTimestamp]) {
|
|
2390
|
-
continue;
|
|
2391
|
-
}
|
|
2392
|
-
allocationSize -= getAllocationSize(frames[frameTimestamp]);
|
|
2393
|
-
frames[frameTimestamp].close();
|
|
2394
|
-
delete frames[frameTimestamp];
|
|
2395
|
-
framesDeleted++;
|
|
2396
|
-
}
|
|
2397
|
-
frameTimestamps.length = 0;
|
|
2398
|
-
return { framesDeleted };
|
|
2399
|
-
};
|
|
2400
2406
|
const getOpenFrameCount = () => {
|
|
2401
2407
|
return {
|
|
2402
2408
|
size: allocationSize,
|
|
@@ -2407,173 +2413,91 @@ var makeKeyframeBank = ({
|
|
|
2407
2413
|
return lastUsed;
|
|
2408
2414
|
};
|
|
2409
2415
|
let queue = Promise.resolve(undefined);
|
|
2410
|
-
const
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
getFrameFromTimestamp: (timestamp) => {
|
|
2414
|
-
queue = queue.then(() => getFrameFromTimestamp(timestamp));
|
|
2415
|
-
return queue;
|
|
2416
|
-
},
|
|
2417
|
-
prepareForDeletion,
|
|
2418
|
-
hasTimestampInSecond,
|
|
2419
|
-
addFrame,
|
|
2420
|
-
deleteFramesBeforeTimestamp,
|
|
2421
|
-
src,
|
|
2422
|
-
getOpenFrameCount,
|
|
2423
|
-
getLastUsed
|
|
2424
|
-
};
|
|
2425
|
-
return keyframeBank;
|
|
2426
|
-
};
|
|
2427
|
-
|
|
2428
|
-
// src/video-extraction/remember-actual-matroska-timestamps.ts
|
|
2429
|
-
var rememberActualMatroskaTimestamps = (isMatroska) => {
|
|
2430
|
-
const observations = [];
|
|
2431
|
-
const observeTimestamp = (startTime) => {
|
|
2432
|
-
if (!isMatroska) {
|
|
2433
|
-
return;
|
|
2434
|
-
}
|
|
2435
|
-
observations.push(startTime);
|
|
2436
|
-
};
|
|
2437
|
-
const getRealTimestamp = (observedTimestamp) => {
|
|
2438
|
-
if (!isMatroska) {
|
|
2439
|
-
return observedTimestamp;
|
|
2440
|
-
}
|
|
2441
|
-
return observations.find((observation) => Math.abs(observedTimestamp - observation) < 0.001) ?? null;
|
|
2442
|
-
};
|
|
2443
|
-
return {
|
|
2444
|
-
observeTimestamp,
|
|
2445
|
-
getRealTimestamp
|
|
2446
|
-
};
|
|
2447
|
-
};
|
|
2448
|
-
|
|
2449
|
-
// src/video-extraction/get-frames-since-keyframe.ts
|
|
2450
|
-
var getRetryDelay = () => {
|
|
2451
|
-
return null;
|
|
2452
|
-
};
|
|
2453
|
-
var getFormatOrNullOrNetworkError = async (input) => {
|
|
2454
|
-
try {
|
|
2455
|
-
return await input.getFormat();
|
|
2456
|
-
} catch (err) {
|
|
2457
|
-
if (isNetworkError(err)) {
|
|
2458
|
-
return "network-error";
|
|
2459
|
-
}
|
|
2460
|
-
return null;
|
|
2416
|
+
const firstFrame = await sampleIterator.next();
|
|
2417
|
+
if (!firstFrame.value) {
|
|
2418
|
+
throw new Error("No first frame found");
|
|
2461
2419
|
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
})
|
|
2469
|
-
});
|
|
2470
|
-
const format = await getFormatOrNullOrNetworkError(input);
|
|
2471
|
-
const isMatroska = format === MATROSKA || format === WEBM;
|
|
2472
|
-
const getVideoSinks = async () => {
|
|
2473
|
-
if (format === "network-error") {
|
|
2474
|
-
return "network-error";
|
|
2475
|
-
}
|
|
2476
|
-
if (format === null) {
|
|
2477
|
-
return "unknown-container-format";
|
|
2478
|
-
}
|
|
2479
|
-
const videoTrack = await input.getPrimaryVideoTrack();
|
|
2480
|
-
if (!videoTrack) {
|
|
2481
|
-
return "no-video-track";
|
|
2482
|
-
}
|
|
2483
|
-
const canDecode = await videoTrack.canDecode();
|
|
2484
|
-
if (!canDecode) {
|
|
2485
|
-
return "cannot-decode";
|
|
2420
|
+
const startTimestampInSeconds = firstFrame.value.timestamp;
|
|
2421
|
+
Internals9.Log.verbose({ logLevel: parentLogLevel, tag: "@remotion/media" }, `Creating keyframe bank from ${startTimestampInSeconds}sec`);
|
|
2422
|
+
addFrame(firstFrame.value, parentLogLevel);
|
|
2423
|
+
const getRangeOfTimestamps = () => {
|
|
2424
|
+
if (frameTimestamps.length === 0) {
|
|
2425
|
+
return null;
|
|
2486
2426
|
}
|
|
2487
2427
|
return {
|
|
2488
|
-
|
|
2489
|
-
|
|
2428
|
+
firstTimestamp: frameTimestamps[0],
|
|
2429
|
+
lastTimestamp: frameTimestamps[frameTimestamps.length - 1]
|
|
2490
2430
|
};
|
|
2491
2431
|
};
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
if (
|
|
2495
|
-
|
|
2432
|
+
const prepareForDeletion = (logLevel, reason) => {
|
|
2433
|
+
const range = getRangeOfTimestamps();
|
|
2434
|
+
if (range) {
|
|
2435
|
+
Internals9.Log.verbose({ logLevel, tag: "@remotion/media" }, `Preparing for deletion (${reason}) of keyframe bank from ${range?.firstTimestamp}sec to ${range?.lastTimestamp}sec`);
|
|
2496
2436
|
}
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2437
|
+
let framesDeleted = 0;
|
|
2438
|
+
for (const frameTimestamp of frameTimestamps.slice()) {
|
|
2439
|
+
if (!frames[frameTimestamp]) {
|
|
2440
|
+
continue;
|
|
2441
|
+
}
|
|
2442
|
+
deleteFrameAtTimestamp(frameTimestamp);
|
|
2443
|
+
framesDeleted++;
|
|
2504
2444
|
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2445
|
+
sampleIterator.return();
|
|
2446
|
+
frameTimestamps.length = 0;
|
|
2447
|
+
return { framesDeleted };
|
|
2448
|
+
};
|
|
2449
|
+
const canSatisfyTimestamp = (timestamp) => {
|
|
2450
|
+
if (frameTimestamps.length === 0) {
|
|
2451
|
+
return false;
|
|
2507
2452
|
}
|
|
2508
|
-
const
|
|
2509
|
-
const
|
|
2510
|
-
|
|
2511
|
-
|
|
2453
|
+
const roundedTimestamp = roundTo4Digits(timestamp);
|
|
2454
|
+
const firstFrameTimestamp = roundTo4Digits(frameTimestamps[0]);
|
|
2455
|
+
const lastFrameTimestamp = roundTo4Digits(frameTimestamps[frameTimestamps.length - 1]);
|
|
2456
|
+
if (hasReachedEndOfVideo && roundedTimestamp > lastFrameTimestamp) {
|
|
2457
|
+
return true;
|
|
2512
2458
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
return "cannot-decode-audio";
|
|
2459
|
+
if (roundedTimestamp < firstFrameTimestamp) {
|
|
2460
|
+
return false;
|
|
2516
2461
|
}
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
};
|
|
2520
|
-
};
|
|
2521
|
-
const getAudioSinksPromise = (index) => {
|
|
2522
|
-
if (audioSinksPromise[index]) {
|
|
2523
|
-
return audioSinksPromise[index];
|
|
2462
|
+
if (roundedTimestamp - BIGGEST_ALLOWED_JUMP_FORWARD_SECONDS > lastFrameTimestamp) {
|
|
2463
|
+
return false;
|
|
2524
2464
|
}
|
|
2525
|
-
|
|
2526
|
-
return audioSinksPromise[index];
|
|
2465
|
+
return true;
|
|
2527
2466
|
};
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2467
|
+
const keyframeBank = {
|
|
2468
|
+
getFrameFromTimestamp: (timestamp) => {
|
|
2469
|
+
queue = queue.then(() => getFrameFromTimestamp(timestamp));
|
|
2470
|
+
return queue;
|
|
2471
|
+
},
|
|
2472
|
+
prepareForDeletion,
|
|
2473
|
+
hasTimestampInSecond: (timestamp) => {
|
|
2474
|
+
queue = queue.then(() => hasTimestampInSecond(timestamp));
|
|
2475
|
+
return queue;
|
|
2476
|
+
},
|
|
2477
|
+
addFrame,
|
|
2478
|
+
deleteFramesBeforeTimestamp,
|
|
2479
|
+
src,
|
|
2480
|
+
getOpenFrameCount,
|
|
2481
|
+
getLastUsed,
|
|
2482
|
+
canSatisfyTimestamp,
|
|
2483
|
+
getRangeOfTimestamps
|
|
2536
2484
|
};
|
|
2537
|
-
};
|
|
2538
|
-
var getFramesSinceKeyframe = async ({
|
|
2539
|
-
packetSink,
|
|
2540
|
-
videoSampleSink,
|
|
2541
|
-
startPacket,
|
|
2542
|
-
logLevel,
|
|
2543
|
-
src
|
|
2544
|
-
}) => {
|
|
2545
|
-
const nextKeyPacket = await packetSink.getNextKeyPacket(startPacket, {
|
|
2546
|
-
verifyKeyPackets: true
|
|
2547
|
-
});
|
|
2548
|
-
const sampleIterator = videoSampleSink.samples(startPacket.timestamp, nextKeyPacket ? nextKeyPacket.timestamp : Infinity);
|
|
2549
|
-
const keyframeBank = makeKeyframeBank({
|
|
2550
|
-
startTimestampInSeconds: startPacket.timestamp,
|
|
2551
|
-
endTimestampInSeconds: nextKeyPacket ? nextKeyPacket.timestamp : Infinity,
|
|
2552
|
-
sampleIterator,
|
|
2553
|
-
logLevel,
|
|
2554
|
-
src
|
|
2555
|
-
});
|
|
2556
2485
|
return keyframeBank;
|
|
2557
2486
|
};
|
|
2558
2487
|
|
|
2559
2488
|
// src/video-extraction/keyframe-manager.ts
|
|
2560
2489
|
var makeKeyframeManager = () => {
|
|
2561
|
-
|
|
2562
|
-
const addKeyframeBank = ({
|
|
2563
|
-
src
|
|
2564
|
-
bank
|
|
2565
|
-
startTimestampInSeconds
|
|
2566
|
-
}) => {
|
|
2567
|
-
sources[src] = sources[src] ?? {};
|
|
2568
|
-
sources[src][startTimestampInSeconds] = bank;
|
|
2490
|
+
let sources = {};
|
|
2491
|
+
const addKeyframeBank = ({ src, bank }) => {
|
|
2492
|
+
sources[src] = sources[src] ?? [];
|
|
2493
|
+
sources[src].push(bank);
|
|
2569
2494
|
};
|
|
2570
|
-
const logCacheStats =
|
|
2495
|
+
const logCacheStats = (logLevel) => {
|
|
2571
2496
|
let count = 0;
|
|
2572
2497
|
let totalSize = 0;
|
|
2573
2498
|
for (const src in sources) {
|
|
2574
|
-
for (const bank
|
|
2575
|
-
const
|
|
2576
|
-
const { size, timestamps } = v.getOpenFrameCount();
|
|
2499
|
+
for (const bank of sources[src]) {
|
|
2500
|
+
const { size, timestamps } = bank.getOpenFrameCount();
|
|
2577
2501
|
count += timestamps.length;
|
|
2578
2502
|
totalSize += size;
|
|
2579
2503
|
if (size === 0) {
|
|
@@ -2584,13 +2508,12 @@ var makeKeyframeManager = () => {
|
|
|
2584
2508
|
}
|
|
2585
2509
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Video cache stats: ${count} open frames, ${totalSize} bytes`);
|
|
2586
2510
|
};
|
|
2587
|
-
const getCacheStats =
|
|
2511
|
+
const getCacheStats = () => {
|
|
2588
2512
|
let count = 0;
|
|
2589
2513
|
let totalSize = 0;
|
|
2590
2514
|
for (const src in sources) {
|
|
2591
|
-
for (const bank
|
|
2592
|
-
const
|
|
2593
|
-
const { timestamps, size } = v.getOpenFrameCount();
|
|
2515
|
+
for (const bank of sources[src]) {
|
|
2516
|
+
const { timestamps, size } = bank.getOpenFrameCount();
|
|
2594
2517
|
count += timestamps.length;
|
|
2595
2518
|
totalSize += size;
|
|
2596
2519
|
if (size === 0) {
|
|
@@ -2600,17 +2523,17 @@ var makeKeyframeManager = () => {
|
|
|
2600
2523
|
}
|
|
2601
2524
|
return { count, totalSize };
|
|
2602
2525
|
};
|
|
2603
|
-
const getTheKeyframeBankMostInThePast =
|
|
2526
|
+
const getTheKeyframeBankMostInThePast = () => {
|
|
2604
2527
|
let mostInThePast = null;
|
|
2605
2528
|
let mostInThePastBank = null;
|
|
2606
2529
|
let numberOfBanks = 0;
|
|
2607
2530
|
for (const src in sources) {
|
|
2608
|
-
for (const
|
|
2609
|
-
const
|
|
2531
|
+
for (const bank of sources[src]) {
|
|
2532
|
+
const index = sources[src].indexOf(bank);
|
|
2610
2533
|
const lastUsed = bank.getLastUsed();
|
|
2611
2534
|
if (mostInThePast === null || lastUsed < mostInThePast) {
|
|
2612
2535
|
mostInThePast = lastUsed;
|
|
2613
|
-
mostInThePastBank = { src, bank };
|
|
2536
|
+
mostInThePastBank = { src, bank, index };
|
|
2614
2537
|
}
|
|
2615
2538
|
numberOfBanks++;
|
|
2616
2539
|
}
|
|
@@ -2620,49 +2543,66 @@ var makeKeyframeManager = () => {
|
|
|
2620
2543
|
}
|
|
2621
2544
|
return { mostInThePastBank, numberOfBanks };
|
|
2622
2545
|
};
|
|
2623
|
-
const deleteOldestKeyframeBank =
|
|
2546
|
+
const deleteOldestKeyframeBank = (logLevel) => {
|
|
2624
2547
|
const {
|
|
2625
|
-
mostInThePastBank: {
|
|
2548
|
+
mostInThePastBank: {
|
|
2549
|
+
bank: mostInThePastBank,
|
|
2550
|
+
src: mostInThePastSrc,
|
|
2551
|
+
index: mostInThePastIndex
|
|
2552
|
+
},
|
|
2626
2553
|
numberOfBanks
|
|
2627
|
-
} =
|
|
2554
|
+
} = getTheKeyframeBankMostInThePast();
|
|
2628
2555
|
if (numberOfBanks < 2) {
|
|
2629
2556
|
return { finish: true };
|
|
2630
2557
|
}
|
|
2631
2558
|
if (mostInThePastBank) {
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
|
|
2559
|
+
const range = mostInThePastBank.getRangeOfTimestamps();
|
|
2560
|
+
const { framesDeleted } = mostInThePastBank.prepareForDeletion(logLevel, "deleted oldest keyframe bank to stay under max cache size");
|
|
2561
|
+
sources[mostInThePastSrc].splice(mostInThePastIndex, 1);
|
|
2562
|
+
if (range) {
|
|
2563
|
+
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Deleted ${framesDeleted} frames for src ${mostInThePastSrc} from ${range?.firstTimestamp}sec to ${range?.lastTimestamp}sec to free up memory.`);
|
|
2564
|
+
}
|
|
2635
2565
|
}
|
|
2636
2566
|
return { finish: false };
|
|
2637
2567
|
};
|
|
2638
|
-
const ensureToStayUnderMaxCacheSize =
|
|
2639
|
-
let cacheStats =
|
|
2640
|
-
|
|
2641
|
-
|
|
2568
|
+
const ensureToStayUnderMaxCacheSize = (logLevel, maxCacheSize) => {
|
|
2569
|
+
let cacheStats = getTotalCacheStats();
|
|
2570
|
+
let attempts = 0;
|
|
2571
|
+
const maxAttempts = 3;
|
|
2572
|
+
while (cacheStats.totalSize > maxCacheSize && attempts < maxAttempts) {
|
|
2573
|
+
const { finish } = deleteOldestKeyframeBank(logLevel);
|
|
2642
2574
|
if (finish) {
|
|
2643
2575
|
break;
|
|
2644
2576
|
}
|
|
2645
2577
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, "Deleted oldest keyframe bank to stay under max cache size", (cacheStats.totalSize / 1024 / 1024).toFixed(1), "out of", (maxCacheSize / 1024 / 1024).toFixed(1));
|
|
2646
|
-
cacheStats =
|
|
2578
|
+
cacheStats = getTotalCacheStats();
|
|
2579
|
+
attempts++;
|
|
2580
|
+
}
|
|
2581
|
+
if (cacheStats.totalSize > maxCacheSize && attempts >= maxAttempts) {
|
|
2582
|
+
Internals10.Log.warn({ logLevel, tag: "@remotion/media" }, `Exceeded max cache size after ${maxAttempts} attempts. Remaining cache size: ${(cacheStats.totalSize / 1024 / 1024).toFixed(1)} MB, target was ${(maxCacheSize / 1024 / 1024).toFixed(1)} MB.`);
|
|
2647
2583
|
}
|
|
2648
2584
|
};
|
|
2649
|
-
const clearKeyframeBanksBeforeTime =
|
|
2585
|
+
const clearKeyframeBanksBeforeTime = ({
|
|
2650
2586
|
timestampInSeconds,
|
|
2651
2587
|
src,
|
|
2652
2588
|
logLevel
|
|
2653
2589
|
}) => {
|
|
2654
|
-
const threshold = timestampInSeconds -
|
|
2590
|
+
const threshold = timestampInSeconds - SAFE_WINDOW_OF_MONOTONICITY;
|
|
2655
2591
|
if (!sources[src]) {
|
|
2656
2592
|
return;
|
|
2657
2593
|
}
|
|
2658
|
-
const banks =
|
|
2659
|
-
for (const
|
|
2660
|
-
const
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2594
|
+
const banks = sources[src];
|
|
2595
|
+
for (const bank of banks) {
|
|
2596
|
+
const range = bank.getRangeOfTimestamps();
|
|
2597
|
+
if (!range) {
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
const { lastTimestamp } = range;
|
|
2601
|
+
if (lastTimestamp < threshold) {
|
|
2602
|
+
bank.prepareForDeletion(logLevel, "cleared before threshold");
|
|
2603
|
+
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `[Video] Cleared frames for src ${src} from ${range.firstTimestamp}sec to ${range.lastTimestamp}sec`);
|
|
2604
|
+
const bankIndex = banks.indexOf(bank);
|
|
2605
|
+
delete sources[src][bankIndex];
|
|
2666
2606
|
} else {
|
|
2667
2607
|
bank.deleteFramesBeforeTimestamp({
|
|
2668
2608
|
timestampInSeconds: threshold,
|
|
@@ -2670,70 +2610,58 @@ var makeKeyframeManager = () => {
|
|
|
2670
2610
|
});
|
|
2671
2611
|
}
|
|
2672
2612
|
}
|
|
2673
|
-
|
|
2613
|
+
sources[src] = sources[src].filter((bank) => bank !== undefined);
|
|
2614
|
+
logCacheStats(logLevel);
|
|
2674
2615
|
};
|
|
2675
2616
|
const getKeyframeBankOrRefetch = async ({
|
|
2676
|
-
packetSink,
|
|
2677
2617
|
timestamp,
|
|
2678
2618
|
videoSampleSink,
|
|
2679
2619
|
src,
|
|
2680
2620
|
logLevel
|
|
2681
2621
|
}) => {
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
}) ?? await packetSink.getFirstPacket({ verifyKeyPackets: true });
|
|
2685
|
-
const hasAlpha = startPacket?.sideData.alpha;
|
|
2686
|
-
if (hasAlpha && !canBrowserUseWebGl2()) {
|
|
2687
|
-
return "has-alpha";
|
|
2688
|
-
}
|
|
2689
|
-
if (!startPacket) {
|
|
2690
|
-
return null;
|
|
2691
|
-
}
|
|
2692
|
-
const startTimestampInSeconds = startPacket.timestamp;
|
|
2693
|
-
const existingBank = sources[src]?.[startTimestampInSeconds];
|
|
2622
|
+
const existingBanks = sources[src] ?? [];
|
|
2623
|
+
const existingBank = existingBanks?.find((bank) => bank.canSatisfyTimestamp(timestamp));
|
|
2694
2624
|
if (!existingBank) {
|
|
2695
|
-
|
|
2696
|
-
|
|
2625
|
+
Internals10.Log.trace({ logLevel, tag: "@remotion/media" }, `Creating new keyframe bank for src ${src} at timestamp ${timestamp}`);
|
|
2626
|
+
const newKeyframeBank = await makeKeyframeBank({
|
|
2697
2627
|
videoSampleSink,
|
|
2698
|
-
startPacket,
|
|
2699
2628
|
logLevel,
|
|
2700
|
-
src
|
|
2629
|
+
src,
|
|
2630
|
+
requestedTimestamp: timestamp
|
|
2701
2631
|
});
|
|
2702
|
-
addKeyframeBank({ src, bank: newKeyframeBank
|
|
2632
|
+
addKeyframeBank({ src, bank: newKeyframeBank });
|
|
2703
2633
|
return newKeyframeBank;
|
|
2704
2634
|
}
|
|
2705
|
-
if (
|
|
2635
|
+
if (existingBank.canSatisfyTimestamp(timestamp)) {
|
|
2636
|
+
Internals10.Log.trace({ logLevel, tag: "@remotion/media" }, `Keyframe bank exists and satisfies timestamp ${timestamp}`);
|
|
2706
2637
|
return existingBank;
|
|
2707
2638
|
}
|
|
2708
2639
|
Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Keyframe bank exists but frame at time ${timestamp} does not exist anymore.`);
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
const replacementKeybank =
|
|
2712
|
-
packetSink,
|
|
2640
|
+
existingBank.prepareForDeletion(logLevel, "already existed but evicted");
|
|
2641
|
+
sources[src] = sources[src].filter((bank) => bank !== existingBank);
|
|
2642
|
+
const replacementKeybank = await makeKeyframeBank({
|
|
2713
2643
|
videoSampleSink,
|
|
2714
|
-
|
|
2644
|
+
requestedTimestamp: timestamp,
|
|
2715
2645
|
logLevel,
|
|
2716
2646
|
src
|
|
2717
2647
|
});
|
|
2718
|
-
addKeyframeBank({ src, bank: replacementKeybank
|
|
2648
|
+
addKeyframeBank({ src, bank: replacementKeybank });
|
|
2719
2649
|
return replacementKeybank;
|
|
2720
2650
|
};
|
|
2721
2651
|
const requestKeyframeBank = async ({
|
|
2722
|
-
packetSink,
|
|
2723
2652
|
timestamp,
|
|
2724
2653
|
videoSampleSink,
|
|
2725
2654
|
src,
|
|
2726
2655
|
logLevel,
|
|
2727
2656
|
maxCacheSize
|
|
2728
2657
|
}) => {
|
|
2729
|
-
|
|
2730
|
-
|
|
2658
|
+
ensureToStayUnderMaxCacheSize(logLevel, maxCacheSize);
|
|
2659
|
+
clearKeyframeBanksBeforeTime({
|
|
2731
2660
|
timestampInSeconds: timestamp,
|
|
2732
2661
|
src,
|
|
2733
2662
|
logLevel
|
|
2734
2663
|
});
|
|
2735
2664
|
const keyframeBank = await getKeyframeBankOrRefetch({
|
|
2736
|
-
packetSink,
|
|
2737
2665
|
timestamp,
|
|
2738
2666
|
videoSampleSink,
|
|
2739
2667
|
src,
|
|
@@ -2741,21 +2669,20 @@ var makeKeyframeManager = () => {
|
|
|
2741
2669
|
});
|
|
2742
2670
|
return keyframeBank;
|
|
2743
2671
|
};
|
|
2744
|
-
const clearAll =
|
|
2672
|
+
const clearAll = (logLevel) => {
|
|
2745
2673
|
const srcs = Object.keys(sources);
|
|
2746
2674
|
for (const src of srcs) {
|
|
2747
|
-
const banks =
|
|
2748
|
-
for (const
|
|
2749
|
-
|
|
2750
|
-
bank.prepareForDeletion(logLevel);
|
|
2751
|
-
delete sources[src][startTimeInSeconds];
|
|
2675
|
+
const banks = sources[src];
|
|
2676
|
+
for (const bank of banks) {
|
|
2677
|
+
bank.prepareForDeletion(logLevel, "clearAll");
|
|
2752
2678
|
}
|
|
2679
|
+
sources[src] = [];
|
|
2753
2680
|
}
|
|
2681
|
+
sources = {};
|
|
2754
2682
|
};
|
|
2755
2683
|
let queue = Promise.resolve(undefined);
|
|
2756
2684
|
return {
|
|
2757
2685
|
requestKeyframeBank: ({
|
|
2758
|
-
packetSink,
|
|
2759
2686
|
timestamp,
|
|
2760
2687
|
videoSampleSink,
|
|
2761
2688
|
src,
|
|
@@ -2763,7 +2690,6 @@ var makeKeyframeManager = () => {
|
|
|
2763
2690
|
maxCacheSize
|
|
2764
2691
|
}) => {
|
|
2765
2692
|
queue = queue.then(() => requestKeyframeBank({
|
|
2766
|
-
packetSink,
|
|
2767
2693
|
timestamp,
|
|
2768
2694
|
videoSampleSink,
|
|
2769
2695
|
src,
|
|
@@ -2778,11 +2704,11 @@ var makeKeyframeManager = () => {
|
|
|
2778
2704
|
};
|
|
2779
2705
|
|
|
2780
2706
|
// src/caches.ts
|
|
2781
|
-
var
|
|
2707
|
+
var SAFE_WINDOW_OF_MONOTONICITY = 0.2;
|
|
2782
2708
|
var keyframeManager = makeKeyframeManager();
|
|
2783
2709
|
var audioManager = makeAudioManager();
|
|
2784
|
-
var getTotalCacheStats =
|
|
2785
|
-
const keyframeManagerCacheStats =
|
|
2710
|
+
var getTotalCacheStats = () => {
|
|
2711
|
+
const keyframeManagerCacheStats = keyframeManager.getCacheStats();
|
|
2786
2712
|
const audioManagerCacheStats = audioManager.getCacheStats();
|
|
2787
2713
|
return {
|
|
2788
2714
|
count: keyframeManagerCacheStats.count + audioManagerCacheStats.count,
|
|
@@ -3050,6 +2976,154 @@ var combineAudioDataAndClosePrevious = (audioDataArray) => {
|
|
|
3050
2976
|
|
|
3051
2977
|
// src/get-sink.ts
|
|
3052
2978
|
import { Internals as Internals12 } from "remotion";
|
|
2979
|
+
|
|
2980
|
+
// src/video-extraction/get-frames-since-keyframe.ts
|
|
2981
|
+
import {
|
|
2982
|
+
ALL_FORMATS as ALL_FORMATS2,
|
|
2983
|
+
AudioSampleSink,
|
|
2984
|
+
EncodedPacketSink,
|
|
2985
|
+
Input as Input2,
|
|
2986
|
+
MATROSKA,
|
|
2987
|
+
UrlSource as UrlSource2,
|
|
2988
|
+
VideoSampleSink,
|
|
2989
|
+
WEBM
|
|
2990
|
+
} from "mediabunny";
|
|
2991
|
+
|
|
2992
|
+
// src/browser-can-use-webgl2.ts
|
|
2993
|
+
var browserCanUseWebGl2 = null;
|
|
2994
|
+
var browserCanUseWebGl2Uncached = () => {
|
|
2995
|
+
const canvas = new OffscreenCanvas(1, 1);
|
|
2996
|
+
const context = canvas.getContext("webgl2");
|
|
2997
|
+
return context !== null;
|
|
2998
|
+
};
|
|
2999
|
+
var canBrowserUseWebGl2 = () => {
|
|
3000
|
+
if (browserCanUseWebGl2 !== null) {
|
|
3001
|
+
return browserCanUseWebGl2;
|
|
3002
|
+
}
|
|
3003
|
+
browserCanUseWebGl2 = browserCanUseWebGl2Uncached();
|
|
3004
|
+
return browserCanUseWebGl2;
|
|
3005
|
+
};
|
|
3006
|
+
|
|
3007
|
+
// src/video-extraction/remember-actual-matroska-timestamps.ts
|
|
3008
|
+
var rememberActualMatroskaTimestamps = (isMatroska) => {
|
|
3009
|
+
const observations = [];
|
|
3010
|
+
const observeTimestamp = (startTime) => {
|
|
3011
|
+
if (!isMatroska) {
|
|
3012
|
+
return;
|
|
3013
|
+
}
|
|
3014
|
+
observations.push(startTime);
|
|
3015
|
+
};
|
|
3016
|
+
const getRealTimestamp = (observedTimestamp) => {
|
|
3017
|
+
if (!isMatroska) {
|
|
3018
|
+
return observedTimestamp;
|
|
3019
|
+
}
|
|
3020
|
+
return observations.find((observation) => Math.abs(observedTimestamp - observation) < 0.001) ?? null;
|
|
3021
|
+
};
|
|
3022
|
+
return {
|
|
3023
|
+
observeTimestamp,
|
|
3024
|
+
getRealTimestamp
|
|
3025
|
+
};
|
|
3026
|
+
};
|
|
3027
|
+
|
|
3028
|
+
// src/video-extraction/get-frames-since-keyframe.ts
|
|
3029
|
+
var getRetryDelay = () => {
|
|
3030
|
+
return null;
|
|
3031
|
+
};
|
|
3032
|
+
var getFormatOrNullOrNetworkError = async (input) => {
|
|
3033
|
+
try {
|
|
3034
|
+
return await input.getFormat();
|
|
3035
|
+
} catch (err) {
|
|
3036
|
+
if (isNetworkError(err)) {
|
|
3037
|
+
return "network-error";
|
|
3038
|
+
}
|
|
3039
|
+
return null;
|
|
3040
|
+
}
|
|
3041
|
+
};
|
|
3042
|
+
var getSinks = async (src) => {
|
|
3043
|
+
const input = new Input2({
|
|
3044
|
+
formats: ALL_FORMATS2,
|
|
3045
|
+
source: new UrlSource2(src, {
|
|
3046
|
+
getRetryDelay
|
|
3047
|
+
})
|
|
3048
|
+
});
|
|
3049
|
+
const format = await getFormatOrNullOrNetworkError(input);
|
|
3050
|
+
const isMatroska = format === MATROSKA || format === WEBM;
|
|
3051
|
+
const getVideoSinks = async () => {
|
|
3052
|
+
if (format === "network-error") {
|
|
3053
|
+
return "network-error";
|
|
3054
|
+
}
|
|
3055
|
+
if (format === null) {
|
|
3056
|
+
return "unknown-container-format";
|
|
3057
|
+
}
|
|
3058
|
+
const videoTrack = await input.getPrimaryVideoTrack();
|
|
3059
|
+
if (!videoTrack) {
|
|
3060
|
+
return "no-video-track";
|
|
3061
|
+
}
|
|
3062
|
+
const canDecode = await videoTrack.canDecode();
|
|
3063
|
+
if (!canDecode) {
|
|
3064
|
+
return "cannot-decode";
|
|
3065
|
+
}
|
|
3066
|
+
const sampleSink = new VideoSampleSink(videoTrack);
|
|
3067
|
+
const packetSink = new EncodedPacketSink(videoTrack);
|
|
3068
|
+
const startPacket = await packetSink.getFirstPacket({
|
|
3069
|
+
verifyKeyPackets: true
|
|
3070
|
+
});
|
|
3071
|
+
const hasAlpha = startPacket?.sideData.alpha;
|
|
3072
|
+
if (hasAlpha && !canBrowserUseWebGl2()) {
|
|
3073
|
+
return "cannot-decode-alpha";
|
|
3074
|
+
}
|
|
3075
|
+
return {
|
|
3076
|
+
sampleSink
|
|
3077
|
+
};
|
|
3078
|
+
};
|
|
3079
|
+
let videoSinksPromise = null;
|
|
3080
|
+
const getVideoSinksPromise = () => {
|
|
3081
|
+
if (videoSinksPromise) {
|
|
3082
|
+
return videoSinksPromise;
|
|
3083
|
+
}
|
|
3084
|
+
videoSinksPromise = getVideoSinks();
|
|
3085
|
+
return videoSinksPromise;
|
|
3086
|
+
};
|
|
3087
|
+
const audioSinksPromise = {};
|
|
3088
|
+
const getAudioSinks = async (index) => {
|
|
3089
|
+
if (format === null) {
|
|
3090
|
+
return "unknown-container-format";
|
|
3091
|
+
}
|
|
3092
|
+
if (format === "network-error") {
|
|
3093
|
+
return "network-error";
|
|
3094
|
+
}
|
|
3095
|
+
const audioTracks = await input.getAudioTracks();
|
|
3096
|
+
const audioTrack = audioTracks[index];
|
|
3097
|
+
if (!audioTrack) {
|
|
3098
|
+
return "no-audio-track";
|
|
3099
|
+
}
|
|
3100
|
+
const canDecode = await audioTrack.canDecode();
|
|
3101
|
+
if (!canDecode) {
|
|
3102
|
+
return "cannot-decode-audio";
|
|
3103
|
+
}
|
|
3104
|
+
return {
|
|
3105
|
+
sampleSink: new AudioSampleSink(audioTrack)
|
|
3106
|
+
};
|
|
3107
|
+
};
|
|
3108
|
+
const getAudioSinksPromise = (index) => {
|
|
3109
|
+
if (audioSinksPromise[index]) {
|
|
3110
|
+
return audioSinksPromise[index];
|
|
3111
|
+
}
|
|
3112
|
+
audioSinksPromise[index] = getAudioSinks(index);
|
|
3113
|
+
return audioSinksPromise[index];
|
|
3114
|
+
};
|
|
3115
|
+
return {
|
|
3116
|
+
getVideo: () => getVideoSinksPromise(),
|
|
3117
|
+
getAudio: (index) => getAudioSinksPromise(index),
|
|
3118
|
+
actualMatroskaTimestamps: rememberActualMatroskaTimestamps(isMatroska),
|
|
3119
|
+
isMatroska,
|
|
3120
|
+
getDuration: () => {
|
|
3121
|
+
return input.computeDuration();
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
};
|
|
3125
|
+
|
|
3126
|
+
// src/get-sink.ts
|
|
3053
3127
|
var sinkPromises = {};
|
|
3054
3128
|
var getSink = (src, logLevel) => {
|
|
3055
3129
|
let promise = sinkPromises[src];
|
|
@@ -3224,6 +3298,12 @@ var extractFrameInternal = async ({
|
|
|
3224
3298
|
if (video === "network-error") {
|
|
3225
3299
|
return { type: "network-error" };
|
|
3226
3300
|
}
|
|
3301
|
+
if (video === "cannot-decode-alpha") {
|
|
3302
|
+
return {
|
|
3303
|
+
type: "cannot-decode-alpha",
|
|
3304
|
+
durationInSeconds: mediaDurationInSeconds
|
|
3305
|
+
};
|
|
3306
|
+
}
|
|
3227
3307
|
const timeInSeconds = getTimeInSeconds({
|
|
3228
3308
|
loop,
|
|
3229
3309
|
mediaDurationInSeconds,
|
|
@@ -3238,36 +3318,29 @@ var extractFrameInternal = async ({
|
|
|
3238
3318
|
if (timeInSeconds === null) {
|
|
3239
3319
|
return {
|
|
3240
3320
|
type: "success",
|
|
3241
|
-
|
|
3321
|
+
sample: null,
|
|
3242
3322
|
durationInSeconds: await sink.getDuration()
|
|
3243
3323
|
};
|
|
3244
3324
|
}
|
|
3245
3325
|
try {
|
|
3246
3326
|
const keyframeBank = await keyframeManager.requestKeyframeBank({
|
|
3247
|
-
packetSink: video.packetSink,
|
|
3248
3327
|
videoSampleSink: video.sampleSink,
|
|
3249
3328
|
timestamp: timeInSeconds,
|
|
3250
3329
|
src,
|
|
3251
3330
|
logLevel,
|
|
3252
3331
|
maxCacheSize
|
|
3253
3332
|
});
|
|
3254
|
-
if (keyframeBank === "has-alpha") {
|
|
3255
|
-
return {
|
|
3256
|
-
type: "cannot-decode-alpha",
|
|
3257
|
-
durationInSeconds: await sink.getDuration()
|
|
3258
|
-
};
|
|
3259
|
-
}
|
|
3260
3333
|
if (!keyframeBank) {
|
|
3261
3334
|
return {
|
|
3262
3335
|
type: "success",
|
|
3263
|
-
|
|
3336
|
+
sample: null,
|
|
3264
3337
|
durationInSeconds: await sink.getDuration()
|
|
3265
3338
|
};
|
|
3266
3339
|
}
|
|
3267
3340
|
const frame = await keyframeBank.getFrameFromTimestamp(timeInSeconds);
|
|
3268
3341
|
return {
|
|
3269
3342
|
type: "success",
|
|
3270
|
-
frame,
|
|
3343
|
+
sample: frame,
|
|
3271
3344
|
durationInSeconds: await sink.getDuration()
|
|
3272
3345
|
};
|
|
3273
3346
|
} catch (err) {
|
|
@@ -3331,7 +3404,7 @@ var extractFrameAndAudio = async ({
|
|
|
3331
3404
|
maxCacheSize
|
|
3332
3405
|
}) => {
|
|
3333
3406
|
try {
|
|
3334
|
-
const [
|
|
3407
|
+
const [video, audio] = await Promise.all([
|
|
3335
3408
|
includeVideo ? extractFrame({
|
|
3336
3409
|
src,
|
|
3337
3410
|
timeInSeconds,
|
|
@@ -3357,59 +3430,42 @@ var extractFrameAndAudio = async ({
|
|
|
3357
3430
|
maxCacheSize
|
|
3358
3431
|
}) : null
|
|
3359
3432
|
]);
|
|
3360
|
-
if (
|
|
3433
|
+
if (video?.type === "cannot-decode") {
|
|
3361
3434
|
return {
|
|
3362
3435
|
type: "cannot-decode",
|
|
3363
|
-
durationInSeconds:
|
|
3436
|
+
durationInSeconds: video.durationInSeconds
|
|
3364
3437
|
};
|
|
3365
3438
|
}
|
|
3366
|
-
if (
|
|
3439
|
+
if (video?.type === "unknown-container-format") {
|
|
3367
3440
|
return { type: "unknown-container-format" };
|
|
3368
3441
|
}
|
|
3369
|
-
if (
|
|
3442
|
+
if (video?.type === "cannot-decode-alpha") {
|
|
3370
3443
|
return {
|
|
3371
3444
|
type: "cannot-decode-alpha",
|
|
3372
|
-
durationInSeconds:
|
|
3445
|
+
durationInSeconds: video.durationInSeconds
|
|
3373
3446
|
};
|
|
3374
3447
|
}
|
|
3375
|
-
if (
|
|
3448
|
+
if (video?.type === "network-error") {
|
|
3376
3449
|
return { type: "network-error" };
|
|
3377
3450
|
}
|
|
3378
3451
|
if (audio === "unknown-container-format") {
|
|
3379
|
-
if (frame !== null) {
|
|
3380
|
-
frame?.frame?.close();
|
|
3381
|
-
}
|
|
3382
3452
|
return { type: "unknown-container-format" };
|
|
3383
3453
|
}
|
|
3384
3454
|
if (audio === "network-error") {
|
|
3385
|
-
if (frame !== null) {
|
|
3386
|
-
frame?.frame?.close();
|
|
3387
|
-
}
|
|
3388
3455
|
return { type: "network-error" };
|
|
3389
3456
|
}
|
|
3390
3457
|
if (audio === "cannot-decode") {
|
|
3391
|
-
if (frame?.type === "success" && frame.frame !== null) {
|
|
3392
|
-
frame?.frame.close();
|
|
3393
|
-
}
|
|
3394
3458
|
return {
|
|
3395
3459
|
type: "cannot-decode",
|
|
3396
|
-
durationInSeconds:
|
|
3397
|
-
};
|
|
3398
|
-
}
|
|
3399
|
-
if (!frame?.frame) {
|
|
3400
|
-
return {
|
|
3401
|
-
type: "success",
|
|
3402
|
-
frame: null,
|
|
3403
|
-
audio: audio?.data ?? null,
|
|
3404
|
-
durationInSeconds: audio?.durationInSeconds ?? null
|
|
3460
|
+
durationInSeconds: video?.type === "success" ? video.durationInSeconds : null
|
|
3405
3461
|
};
|
|
3406
3462
|
}
|
|
3407
3463
|
return {
|
|
3408
3464
|
type: "success",
|
|
3409
|
-
frame: await rotateFrame({
|
|
3410
|
-
frame:
|
|
3411
|
-
rotation:
|
|
3412
|
-
}),
|
|
3465
|
+
frame: video?.sample ? await rotateFrame({
|
|
3466
|
+
frame: video.sample.toVideoFrame(),
|
|
3467
|
+
rotation: video.sample.rotation
|
|
3468
|
+
}) : null,
|
|
3413
3469
|
audio: audio?.data ?? null,
|
|
3414
3470
|
durationInSeconds: audio?.durationInSeconds ?? null
|
|
3415
3471
|
};
|
|
@@ -3676,7 +3732,7 @@ var AudioForRendering = ({
|
|
|
3676
3732
|
loopVolumeCurveBehavior,
|
|
3677
3733
|
delayRenderRetries,
|
|
3678
3734
|
delayRenderTimeoutInMilliseconds,
|
|
3679
|
-
logLevel
|
|
3735
|
+
logLevel: overriddenLogLevel,
|
|
3680
3736
|
loop,
|
|
3681
3737
|
fallbackHtml5AudioProps,
|
|
3682
3738
|
audioStreamIndex,
|
|
@@ -3686,8 +3742,11 @@ var AudioForRendering = ({
|
|
|
3686
3742
|
disallowFallbackToHtml5Audio,
|
|
3687
3743
|
toneFrequency,
|
|
3688
3744
|
trimAfter,
|
|
3689
|
-
trimBefore
|
|
3745
|
+
trimBefore,
|
|
3746
|
+
onError
|
|
3690
3747
|
}) => {
|
|
3748
|
+
const defaultLogLevel = Internals14.useLogLevel();
|
|
3749
|
+
const logLevel = overriddenLogLevel ?? defaultLogLevel;
|
|
3691
3750
|
const frame = useCurrentFrame3();
|
|
3692
3751
|
const absoluteFrame = Internals14.useTimelinePosition();
|
|
3693
3752
|
const videoConfig = Internals14.useUnsafeVideoConfig();
|
|
@@ -3710,7 +3769,7 @@ var AudioForRendering = ({
|
|
|
3710
3769
|
sequenceContext?.relativeFrom,
|
|
3711
3770
|
sequenceContext?.durationInFrames
|
|
3712
3771
|
]);
|
|
3713
|
-
const maxCacheSize = useMaxMediaCacheSize(logLevel
|
|
3772
|
+
const maxCacheSize = useMaxMediaCacheSize(logLevel);
|
|
3714
3773
|
const audioEnabled = Internals14.useAudioEnabled();
|
|
3715
3774
|
useLayoutEffect2(() => {
|
|
3716
3775
|
const timestamp = frame / fps;
|
|
@@ -3739,7 +3798,7 @@ var AudioForRendering = ({
|
|
|
3739
3798
|
timeInSeconds: timestamp,
|
|
3740
3799
|
durationInSeconds,
|
|
3741
3800
|
playbackRate: playbackRate ?? 1,
|
|
3742
|
-
logLevel
|
|
3801
|
+
logLevel,
|
|
3743
3802
|
includeAudio: shouldRenderAudio,
|
|
3744
3803
|
includeVideo: false,
|
|
3745
3804
|
isClientSideRendering: environment.isClientSideRendering,
|
|
@@ -3750,52 +3809,33 @@ var AudioForRendering = ({
|
|
|
3750
3809
|
fps,
|
|
3751
3810
|
maxCacheSize
|
|
3752
3811
|
}).then((result) => {
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3812
|
+
const handleError = (error, clientSideError, fallbackMessage) => {
|
|
3813
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
3814
|
+
onError,
|
|
3815
|
+
error,
|
|
3816
|
+
disallowFallback: disallowFallbackToHtml5Audio ?? false,
|
|
3817
|
+
isClientSideRendering: environment.isClientSideRendering,
|
|
3818
|
+
clientSideError
|
|
3819
|
+
});
|
|
3820
|
+
if (action === "fail") {
|
|
3821
|
+
cancelRender2(errorToUse);
|
|
3760
3822
|
}
|
|
3761
|
-
Internals14.Log.warn({
|
|
3762
|
-
logLevel: logLevel ?? window.remotion_logLevel,
|
|
3763
|
-
tag: "@remotion/media"
|
|
3764
|
-
}, `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <Html5Audio>`);
|
|
3823
|
+
Internals14.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
|
|
3765
3824
|
setReplaceWithHtml5Audio(true);
|
|
3825
|
+
};
|
|
3826
|
+
if (result.type === "unknown-container-format") {
|
|
3827
|
+
handleError(new Error(`Unknown container format ${src}.`), new Error(`Cannot render audio "${src}": Unknown container format. See supported formats: https://www.remotion.dev/docs/mediabunny/formats`), `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <Html5Audio>`);
|
|
3766
3828
|
return;
|
|
3767
3829
|
}
|
|
3768
3830
|
if (result.type === "cannot-decode") {
|
|
3769
|
-
|
|
3770
|
-
cancelRender2(new Error(`Cannot render audio "${src}": The audio could not be decoded by the browser.`));
|
|
3771
|
-
return;
|
|
3772
|
-
}
|
|
3773
|
-
if (disallowFallbackToHtml5Audio) {
|
|
3774
|
-
cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
|
|
3775
|
-
}
|
|
3776
|
-
Internals14.Log.warn({
|
|
3777
|
-
logLevel: logLevel ?? window.remotion_logLevel,
|
|
3778
|
-
tag: "@remotion/media"
|
|
3779
|
-
}, `Cannot decode ${src}, falling back to <Html5Audio>`);
|
|
3780
|
-
setReplaceWithHtml5Audio(true);
|
|
3831
|
+
handleError(new Error(`Cannot decode ${src}.`), new Error(`Cannot render audio "${src}": The audio could not be decoded by the browser.`), `Cannot decode ${src}, falling back to <Html5Audio>`);
|
|
3781
3832
|
return;
|
|
3782
3833
|
}
|
|
3783
3834
|
if (result.type === "cannot-decode-alpha") {
|
|
3784
3835
|
throw new Error(`Cannot decode alpha component for ${src}, and 'disallowFallbackToHtml5Audio' was set. But this should never happen, since you used the <Audio> tag. Please report this as a bug.`);
|
|
3785
3836
|
}
|
|
3786
3837
|
if (result.type === "network-error") {
|
|
3787
|
-
|
|
3788
|
-
cancelRender2(new Error(`Cannot render audio "${src}": Network error while fetching the audio (possibly CORS).`));
|
|
3789
|
-
return;
|
|
3790
|
-
}
|
|
3791
|
-
if (disallowFallbackToHtml5Audio) {
|
|
3792
|
-
cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
|
|
3793
|
-
}
|
|
3794
|
-
Internals14.Log.warn({
|
|
3795
|
-
logLevel: logLevel ?? window.remotion_logLevel,
|
|
3796
|
-
tag: "@remotion/media"
|
|
3797
|
-
}, `Network error fetching ${src}, falling back to <Html5Audio>`);
|
|
3798
|
-
setReplaceWithHtml5Audio(true);
|
|
3838
|
+
handleError(new Error(`Network error fetching ${src}.`), new Error(`Cannot render audio "${src}": Network error while fetching the audio (possibly CORS).`), `Network error fetching ${src}, falling back to <Html5Audio>`);
|
|
3799
3839
|
return;
|
|
3800
3840
|
}
|
|
3801
3841
|
const { audio, durationInSeconds: assetDurationInSeconds } = result;
|
|
@@ -3860,7 +3900,8 @@ var AudioForRendering = ({
|
|
|
3860
3900
|
trimBefore,
|
|
3861
3901
|
replaceWithHtml5Audio,
|
|
3862
3902
|
maxCacheSize,
|
|
3863
|
-
audioEnabled
|
|
3903
|
+
audioEnabled,
|
|
3904
|
+
onError
|
|
3864
3905
|
]);
|
|
3865
3906
|
if (replaceWithHtml5Audio) {
|
|
3866
3907
|
return /* @__PURE__ */ jsx2(Html5Audio, {
|
|
@@ -3961,7 +4002,8 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
3961
4002
|
fallbackOffthreadVideoProps,
|
|
3962
4003
|
audioStreamIndex,
|
|
3963
4004
|
debugOverlay,
|
|
3964
|
-
headless
|
|
4005
|
+
headless,
|
|
4006
|
+
onError
|
|
3965
4007
|
}) => {
|
|
3966
4008
|
const src = usePreload2(unpreloadedSrc);
|
|
3967
4009
|
const canvasRef = useRef2(null);
|
|
@@ -4060,36 +4102,34 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4060
4102
|
if (result.type === "disposed") {
|
|
4061
4103
|
return;
|
|
4062
4104
|
}
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4105
|
+
const handleError = (error, fallbackMessage) => {
|
|
4106
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
4107
|
+
onError,
|
|
4108
|
+
error,
|
|
4109
|
+
disallowFallback: disallowFallbackToOffthreadVideo,
|
|
4110
|
+
isClientSideRendering: false,
|
|
4111
|
+
clientSideError: error
|
|
4112
|
+
});
|
|
4113
|
+
if (action === "fail") {
|
|
4114
|
+
throw errorToUse;
|
|
4066
4115
|
}
|
|
4067
|
-
Internals16.Log.warn({ logLevel, tag: "@remotion/media" },
|
|
4116
|
+
Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
|
|
4068
4117
|
setShouldFallbackToNativeVideo(true);
|
|
4118
|
+
};
|
|
4119
|
+
if (result.type === "unknown-container-format") {
|
|
4120
|
+
handleError(new Error(`Unknown container format ${preloadedSrc}.`), `Unknown container format for ${preloadedSrc} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`);
|
|
4069
4121
|
return;
|
|
4070
4122
|
}
|
|
4071
4123
|
if (result.type === "network-error") {
|
|
4072
|
-
|
|
4073
|
-
throw new Error(`Network error fetching ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
4074
|
-
}
|
|
4075
|
-
Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Network error fetching ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4076
|
-
setShouldFallbackToNativeVideo(true);
|
|
4124
|
+
handleError(new Error(`Network error fetching ${preloadedSrc}.`), `Network error fetching ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4077
4125
|
return;
|
|
4078
4126
|
}
|
|
4079
4127
|
if (result.type === "cannot-decode") {
|
|
4080
|
-
|
|
4081
|
-
throw new Error(`Cannot decode ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
4082
|
-
}
|
|
4083
|
-
Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Cannot decode ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4084
|
-
setShouldFallbackToNativeVideo(true);
|
|
4128
|
+
handleError(new Error(`Cannot decode ${preloadedSrc}.`), `Cannot decode ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4085
4129
|
return;
|
|
4086
4130
|
}
|
|
4087
4131
|
if (result.type === "no-tracks") {
|
|
4088
|
-
|
|
4089
|
-
throw new Error(`No video or audio tracks found for ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
|
|
4090
|
-
}
|
|
4091
|
-
Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `No video or audio tracks found for ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4092
|
-
setShouldFallbackToNativeVideo(true);
|
|
4132
|
+
handleError(new Error(`No video or audio tracks found for ${preloadedSrc}.`), `No video or audio tracks found for ${preloadedSrc}, falling back to <OffthreadVideo>`);
|
|
4093
4133
|
return;
|
|
4094
4134
|
}
|
|
4095
4135
|
if (result.type === "success") {
|
|
@@ -4097,11 +4137,31 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4097
4137
|
setMediaDurationInSeconds(result.durationInSeconds);
|
|
4098
4138
|
}
|
|
4099
4139
|
}).catch((error) => {
|
|
4100
|
-
|
|
4140
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
4141
|
+
onError,
|
|
4142
|
+
error,
|
|
4143
|
+
disallowFallback: disallowFallbackToOffthreadVideo,
|
|
4144
|
+
isClientSideRendering: false,
|
|
4145
|
+
clientSideError: error
|
|
4146
|
+
});
|
|
4147
|
+
if (action === "fail") {
|
|
4148
|
+
throw errorToUse;
|
|
4149
|
+
}
|
|
4150
|
+
Internals16.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] Failed to initialize MediaPlayer", errorToUse);
|
|
4101
4151
|
setShouldFallbackToNativeVideo(true);
|
|
4102
4152
|
});
|
|
4103
4153
|
} catch (error) {
|
|
4104
|
-
|
|
4154
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
4155
|
+
error,
|
|
4156
|
+
onError,
|
|
4157
|
+
disallowFallback: disallowFallbackToOffthreadVideo,
|
|
4158
|
+
isClientSideRendering: false,
|
|
4159
|
+
clientSideError: error
|
|
4160
|
+
});
|
|
4161
|
+
if (action === "fail") {
|
|
4162
|
+
throw errorToUse;
|
|
4163
|
+
}
|
|
4164
|
+
Internals16.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] MediaPlayer initialization failed", errorToUse);
|
|
4105
4165
|
setShouldFallbackToNativeVideo(true);
|
|
4106
4166
|
}
|
|
4107
4167
|
return () => {
|
|
@@ -4122,7 +4182,8 @@ var VideoForPreviewAssertedShowing = ({
|
|
|
4122
4182
|
loop,
|
|
4123
4183
|
preloadedSrc,
|
|
4124
4184
|
sharedAudioContext,
|
|
4125
|
-
videoConfig.fps
|
|
4185
|
+
videoConfig.fps,
|
|
4186
|
+
onError
|
|
4126
4187
|
]);
|
|
4127
4188
|
const classNameValue = useMemo4(() => {
|
|
4128
4189
|
return [Internals16.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals16.truthy).join(" ");
|
|
@@ -4335,7 +4396,8 @@ var VideoForRendering = ({
|
|
|
4335
4396
|
toneFrequency,
|
|
4336
4397
|
trimAfterValue,
|
|
4337
4398
|
trimBeforeValue,
|
|
4338
|
-
headless
|
|
4399
|
+
headless,
|
|
4400
|
+
onError
|
|
4339
4401
|
}) => {
|
|
4340
4402
|
if (!src) {
|
|
4341
4403
|
throw new TypeError("No `src` was passed to <Video>.");
|
|
@@ -4404,64 +4466,43 @@ var VideoForRendering = ({
|
|
|
4404
4466
|
fps,
|
|
4405
4467
|
maxCacheSize
|
|
4406
4468
|
}).then((result) => {
|
|
4407
|
-
|
|
4469
|
+
const handleError = (err, clientSideError, fallbackMessage, mediaDurationInSeconds) => {
|
|
4408
4470
|
if (environment.isClientSideRendering) {
|
|
4409
|
-
cancelRender3(
|
|
4471
|
+
cancelRender3(clientSideError);
|
|
4410
4472
|
return;
|
|
4411
4473
|
}
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
if (result.type === "cannot-decode") {
|
|
4422
|
-
if (environment.isClientSideRendering) {
|
|
4423
|
-
cancelRender3(new Error(`Cannot render video "${src}": The video could not be decoded by the browser.`));
|
|
4474
|
+
const [action, errorToUse] = callOnErrorAndResolve({
|
|
4475
|
+
onError,
|
|
4476
|
+
error: err,
|
|
4477
|
+
disallowFallback: disallowFallbackToOffthreadVideo,
|
|
4478
|
+
isClientSideRendering: environment.isClientSideRendering,
|
|
4479
|
+
clientSideError: err
|
|
4480
|
+
});
|
|
4481
|
+
if (action === "fail") {
|
|
4482
|
+
cancelRender3(errorToUse);
|
|
4424
4483
|
return;
|
|
4425
4484
|
}
|
|
4426
|
-
if (disallowFallbackToOffthreadVideo) {
|
|
4427
|
-
cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
4428
|
-
}
|
|
4429
4485
|
if (window.remotion_isMainTab) {
|
|
4430
|
-
Internals17.Log.warn({ logLevel, tag: "@remotion/media" },
|
|
4486
|
+
Internals17.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
|
|
4431
4487
|
}
|
|
4432
4488
|
setReplaceWithOffthreadVideo({
|
|
4433
|
-
durationInSeconds:
|
|
4489
|
+
durationInSeconds: mediaDurationInSeconds
|
|
4434
4490
|
});
|
|
4491
|
+
};
|
|
4492
|
+
if (result.type === "unknown-container-format") {
|
|
4493
|
+
handleError(new Error(`Unknown container format ${src}.`), new Error(`Cannot render video "${src}": Unknown container format. See supported formats: https://www.remotion.dev/docs/mediabunny/formats`), `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`, null);
|
|
4494
|
+
return;
|
|
4495
|
+
}
|
|
4496
|
+
if (result.type === "cannot-decode") {
|
|
4497
|
+
handleError(new Error(`Cannot decode ${src}.`), new Error(`Cannot render video "${src}": The video could not be decoded by the browser.`), `Cannot decode ${src}, falling back to <OffthreadVideo>`, result.durationInSeconds);
|
|
4435
4498
|
return;
|
|
4436
4499
|
}
|
|
4437
4500
|
if (result.type === "cannot-decode-alpha") {
|
|
4438
|
-
|
|
4439
|
-
cancelRender3(new Error(`Cannot render video "${src}": The alpha channel could not be decoded by the browser.`));
|
|
4440
|
-
return;
|
|
4441
|
-
}
|
|
4442
|
-
if (disallowFallbackToOffthreadVideo) {
|
|
4443
|
-
cancelRender3(new Error(`Cannot decode alpha component for ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
4444
|
-
}
|
|
4445
|
-
if (window.remotion_isMainTab) {
|
|
4446
|
-
Internals17.Log.info({ logLevel, tag: "@remotion/media" }, `Cannot decode alpha component for ${src}, falling back to <OffthreadVideo>`);
|
|
4447
|
-
}
|
|
4448
|
-
setReplaceWithOffthreadVideo({
|
|
4449
|
-
durationInSeconds: result.durationInSeconds
|
|
4450
|
-
});
|
|
4501
|
+
handleError(new Error(`Cannot decode alpha component for ${src}.`), new Error(`Cannot render video "${src}": The alpha channel could not be decoded by the browser.`), `Cannot decode alpha component for ${src}, falling back to <OffthreadVideo>`, result.durationInSeconds);
|
|
4451
4502
|
return;
|
|
4452
4503
|
}
|
|
4453
4504
|
if (result.type === "network-error") {
|
|
4454
|
-
|
|
4455
|
-
cancelRender3(new Error(`Cannot render video "${src}": Network error while fetching the video (possibly CORS).`));
|
|
4456
|
-
return;
|
|
4457
|
-
}
|
|
4458
|
-
if (disallowFallbackToOffthreadVideo) {
|
|
4459
|
-
cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
|
|
4460
|
-
}
|
|
4461
|
-
if (window.remotion_isMainTab) {
|
|
4462
|
-
Internals17.Log.warn({ logLevel, tag: "@remotion/media" }, `Network error fetching ${src} (no CORS?), falling back to <OffthreadVideo>`);
|
|
4463
|
-
}
|
|
4464
|
-
setReplaceWithOffthreadVideo({ durationInSeconds: null });
|
|
4505
|
+
handleError(new Error(`Network error fetching ${src}.`), new Error(`Cannot render video "${src}": Network error while fetching the video (possibly CORS).`), `Network error fetching ${src} (no CORS?), falling back to <OffthreadVideo>`, null);
|
|
4465
4506
|
return;
|
|
4466
4507
|
}
|
|
4467
4508
|
const {
|
|
@@ -4554,7 +4595,8 @@ var VideoForRendering = ({
|
|
|
4554
4595
|
videoEnabled,
|
|
4555
4596
|
maxCacheSize,
|
|
4556
4597
|
cancelRender3,
|
|
4557
|
-
headless
|
|
4598
|
+
headless,
|
|
4599
|
+
onError
|
|
4558
4600
|
]);
|
|
4559
4601
|
const classNameValue = useMemo5(() => {
|
|
4560
4602
|
return [Internals17.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals17.truthy).join(" ");
|
|
@@ -4649,7 +4691,8 @@ var InnerVideo = ({
|
|
|
4649
4691
|
toneFrequency,
|
|
4650
4692
|
showInTimeline,
|
|
4651
4693
|
debugOverlay,
|
|
4652
|
-
headless
|
|
4694
|
+
headless,
|
|
4695
|
+
onError
|
|
4653
4696
|
}) => {
|
|
4654
4697
|
const environment = useRemotionEnvironment4();
|
|
4655
4698
|
if (typeof src !== "string") {
|
|
@@ -4690,7 +4733,8 @@ var InnerVideo = ({
|
|
|
4690
4733
|
toneFrequency,
|
|
4691
4734
|
trimAfterValue,
|
|
4692
4735
|
trimBeforeValue,
|
|
4693
|
-
headless
|
|
4736
|
+
headless,
|
|
4737
|
+
onError
|
|
4694
4738
|
});
|
|
4695
4739
|
}
|
|
4696
4740
|
return /* @__PURE__ */ jsx6(VideoForPreview, {
|
|
@@ -4713,7 +4757,8 @@ var InnerVideo = ({
|
|
|
4713
4757
|
disallowFallbackToOffthreadVideo,
|
|
4714
4758
|
fallbackOffthreadVideoProps,
|
|
4715
4759
|
debugOverlay: debugOverlay ?? false,
|
|
4716
|
-
headless: headless ?? false
|
|
4760
|
+
headless: headless ?? false,
|
|
4761
|
+
onError
|
|
4717
4762
|
});
|
|
4718
4763
|
};
|
|
4719
4764
|
var Video = ({
|
|
@@ -4739,8 +4784,10 @@ var Video = ({
|
|
|
4739
4784
|
stack,
|
|
4740
4785
|
toneFrequency,
|
|
4741
4786
|
debugOverlay,
|
|
4742
|
-
headless
|
|
4787
|
+
headless,
|
|
4788
|
+
onError
|
|
4743
4789
|
}) => {
|
|
4790
|
+
const fallbackLogLevel = Internals18.useLogLevel();
|
|
4744
4791
|
return /* @__PURE__ */ jsx6(InnerVideo, {
|
|
4745
4792
|
audioStreamIndex: audioStreamIndex ?? 0,
|
|
4746
4793
|
className,
|
|
@@ -4748,7 +4795,7 @@ var Video = ({
|
|
|
4748
4795
|
delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? null,
|
|
4749
4796
|
disallowFallbackToOffthreadVideo: disallowFallbackToOffthreadVideo ?? false,
|
|
4750
4797
|
fallbackOffthreadVideoProps: fallbackOffthreadVideoProps ?? {},
|
|
4751
|
-
logLevel: logLevel ??
|
|
4798
|
+
logLevel: logLevel ?? fallbackLogLevel,
|
|
4752
4799
|
loop: loop ?? false,
|
|
4753
4800
|
loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? "repeat",
|
|
4754
4801
|
muted: muted ?? false,
|
|
@@ -4764,7 +4811,8 @@ var Video = ({
|
|
|
4764
4811
|
toneFrequency: toneFrequency ?? 1,
|
|
4765
4812
|
stack,
|
|
4766
4813
|
debugOverlay: debugOverlay ?? false,
|
|
4767
|
-
headless: headless ?? false
|
|
4814
|
+
headless: headless ?? false,
|
|
4815
|
+
onError
|
|
4768
4816
|
});
|
|
4769
4817
|
};
|
|
4770
4818
|
Internals18.addSequenceStackTraces(Video);
|