@remotion/media 4.0.403 → 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.
Files changed (76) hide show
  1. package/dist/audio/audio-for-preview.d.ts +2 -0
  2. package/dist/audio/audio-preview-iterator.d.ts +8 -3
  3. package/dist/audio/props.d.ts +2 -0
  4. package/dist/audio-extraction/audio-iterator.d.ts +5 -4
  5. package/dist/audio-extraction/audio-manager.d.ts +23 -7
  6. package/dist/audio-extraction/extract-audio.d.ts +2 -4
  7. package/dist/audio-iterator-manager.d.ts +2 -2
  8. package/dist/caches.d.ts +29 -11
  9. package/dist/convert-audiodata/apply-volume.d.ts +1 -1
  10. package/dist/convert-audiodata/resample-audiodata.d.ts +2 -2
  11. package/dist/debug-overlay/preview-overlay.d.ts +83 -5
  12. package/dist/esm/index.mjs +180 -146
  13. package/dist/extract-frame-and-audio.d.ts +1 -2
  14. package/dist/get-sink.d.ts +1 -2
  15. package/dist/index.d.ts +2 -1
  16. package/dist/media-player.d.ts +1 -1
  17. package/dist/on-error.d.ts +12 -0
  18. package/dist/show-in-timeline.d.ts +2 -2
  19. package/dist/use-media-in-timeline.d.ts +2 -2
  20. package/dist/video/props.d.ts +5 -0
  21. package/dist/video/video-for-preview.d.ts +2 -0
  22. package/dist/video/video-for-rendering.d.ts +2 -0
  23. package/dist/video/video-preview-iterator.d.ts +7 -2
  24. package/dist/video-extraction/extract-frame-via-broadcast-channel.d.ts +1 -2
  25. package/dist/video-extraction/extract-frame.d.ts +1 -3
  26. package/dist/video-extraction/keyframe-bank.d.ts +2 -2
  27. package/dist/video-extraction/keyframe-manager.d.ts +2 -3
  28. package/dist/video-iterator-manager.d.ts +4 -5
  29. package/package.json +8 -7
  30. package/dist/audio/allow-wait.js +0 -15
  31. package/dist/audio/audio-for-preview.js +0 -302
  32. package/dist/audio/audio-for-rendering.js +0 -196
  33. package/dist/audio/audio-preview-iterator.js +0 -176
  34. package/dist/audio/audio.js +0 -20
  35. package/dist/audio/props.js +0 -1
  36. package/dist/audio-extraction/audio-cache.js +0 -66
  37. package/dist/audio-extraction/audio-iterator.js +0 -132
  38. package/dist/audio-extraction/audio-manager.js +0 -121
  39. package/dist/audio-extraction/extract-audio.js +0 -132
  40. package/dist/audio-iterator-manager.js +0 -228
  41. package/dist/browser-can-use-webgl2.js +0 -13
  42. package/dist/caches.js +0 -61
  43. package/dist/calculate-playbacktime.js +0 -4
  44. package/dist/convert-audiodata/apply-volume.js +0 -17
  45. package/dist/convert-audiodata/combine-audiodata.js +0 -23
  46. package/dist/convert-audiodata/convert-audiodata.js +0 -73
  47. package/dist/convert-audiodata/resample-audiodata.js +0 -94
  48. package/dist/debug-overlay/preview-overlay.js +0 -44
  49. package/dist/extract-frame-and-audio.js +0 -86
  50. package/dist/get-sink.js +0 -15
  51. package/dist/get-time-in-seconds.js +0 -40
  52. package/dist/helpers/round-to-4-digits.js +0 -4
  53. package/dist/index.js +0 -12
  54. package/dist/is-type-of-error.js +0 -20
  55. package/dist/looped-frame.js +0 -10
  56. package/dist/media-player.js +0 -445
  57. package/dist/nonce-manager.js +0 -13
  58. package/dist/prewarm-iterator-for-looping.js +0 -56
  59. package/dist/render-timestamp-range.js +0 -9
  60. package/dist/show-in-timeline.js +0 -31
  61. package/dist/use-media-in-timeline.js +0 -103
  62. package/dist/video/props.js +0 -1
  63. package/dist/video/video-for-preview.js +0 -329
  64. package/dist/video/video-for-rendering.js +0 -263
  65. package/dist/video/video-preview-iterator.js +0 -122
  66. package/dist/video/video.js +0 -33
  67. package/dist/video-extraction/add-broadcast-channel-listener.js +0 -125
  68. package/dist/video-extraction/extract-frame-via-broadcast-channel.js +0 -113
  69. package/dist/video-extraction/extract-frame.js +0 -84
  70. package/dist/video-extraction/get-allocation-size.js +0 -6
  71. package/dist/video-extraction/get-frames-since-keyframe.js +0 -105
  72. package/dist/video-extraction/keyframe-bank.js +0 -200
  73. package/dist/video-extraction/keyframe-manager.js +0 -201
  74. package/dist/video-extraction/remember-actual-matroska-timestamps.js +0 -19
  75. package/dist/video-extraction/rotate-frame.js +0 -34
  76. package/dist/video-iterator-manager.js +0 -109
@@ -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
- if (result.type === "unknown-container-format") {
1641
- if (disallowFallbackToHtml5Audio) {
1642
- throw new Error(`Unknown container format ${preloadedSrc}, and 'disallowFallbackToHtml5Audio' was set.`);
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
- Internals6.Log.warn({ logLevel, tag: "@remotion/media" }, `Unknown container format for ${preloadedSrc} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <Html5Audio>`);
1645
- setShouldFallbackToNativeAudio(true);
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
- if (disallowFallbackToHtml5Audio) {
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
- if (disallowFallbackToHtml5Audio) {
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
- if (disallowFallbackToHtml5Audio) {
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
- Internals6.Log.error({ logLevel, tag: "@remotion/media" }, "[AudioForPreview] Failed to initialize MediaPlayer", error);
1679
- setShouldFallbackToNativeAudio(true);
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
- Internals6.Log.error({ logLevel, tag: "@remotion/media" }, "[AudioForPreview] MediaPlayer initialization failed", error);
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,7 +1870,8 @@ 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);
1833
1877
  const defaultLogLevel = Internals6.useLogLevel();
@@ -1874,6 +1918,7 @@ var AudioForPreview = ({
1874
1918
  stack,
1875
1919
  disallowFallbackToHtml5Audio: disallowFallbackToHtml5Audio ?? false,
1876
1920
  toneFrequency,
1921
+ onError,
1877
1922
  fallbackHtml5AudioProps
1878
1923
  });
1879
1924
  };
@@ -2498,7 +2543,7 @@ var makeKeyframeManager = () => {
2498
2543
  }
2499
2544
  return { mostInThePastBank, numberOfBanks };
2500
2545
  };
2501
- const deleteOldestKeyframeBank = async (logLevel) => {
2546
+ const deleteOldestKeyframeBank = (logLevel) => {
2502
2547
  const {
2503
2548
  mostInThePastBank: {
2504
2549
  bank: mostInThePastBank,
@@ -2506,31 +2551,31 @@ var makeKeyframeManager = () => {
2506
2551
  index: mostInThePastIndex
2507
2552
  },
2508
2553
  numberOfBanks
2509
- } = await getTheKeyframeBankMostInThePast();
2554
+ } = getTheKeyframeBankMostInThePast();
2510
2555
  if (numberOfBanks < 2) {
2511
2556
  return { finish: true };
2512
2557
  }
2513
2558
  if (mostInThePastBank) {
2514
2559
  const range = mostInThePastBank.getRangeOfTimestamps();
2515
2560
  const { framesDeleted } = mostInThePastBank.prepareForDeletion(logLevel, "deleted oldest keyframe bank to stay under max cache size");
2516
- delete sources[mostInThePastSrc][mostInThePastIndex];
2561
+ sources[mostInThePastSrc].splice(mostInThePastIndex, 1);
2517
2562
  if (range) {
2518
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.`);
2519
2564
  }
2520
2565
  }
2521
2566
  return { finish: false };
2522
2567
  };
2523
- const ensureToStayUnderMaxCacheSize = async (logLevel, maxCacheSize) => {
2524
- let cacheStats = await getTotalCacheStats();
2568
+ const ensureToStayUnderMaxCacheSize = (logLevel, maxCacheSize) => {
2569
+ let cacheStats = getTotalCacheStats();
2525
2570
  let attempts = 0;
2526
2571
  const maxAttempts = 3;
2527
2572
  while (cacheStats.totalSize > maxCacheSize && attempts < maxAttempts) {
2528
- const { finish } = await deleteOldestKeyframeBank(logLevel);
2573
+ const { finish } = deleteOldestKeyframeBank(logLevel);
2529
2574
  if (finish) {
2530
2575
  break;
2531
2576
  }
2532
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));
2533
- cacheStats = await getTotalCacheStats();
2578
+ cacheStats = getTotalCacheStats();
2534
2579
  attempts++;
2535
2580
  }
2536
2581
  if (cacheStats.totalSize > maxCacheSize && attempts >= maxAttempts) {
@@ -2610,7 +2655,7 @@ var makeKeyframeManager = () => {
2610
2655
  logLevel,
2611
2656
  maxCacheSize
2612
2657
  }) => {
2613
- await ensureToStayUnderMaxCacheSize(logLevel, maxCacheSize);
2658
+ ensureToStayUnderMaxCacheSize(logLevel, maxCacheSize);
2614
2659
  clearKeyframeBanksBeforeTime({
2615
2660
  timestampInSeconds: timestamp,
2616
2661
  src,
@@ -2662,8 +2707,8 @@ var makeKeyframeManager = () => {
2662
2707
  var SAFE_WINDOW_OF_MONOTONICITY = 0.2;
2663
2708
  var keyframeManager = makeKeyframeManager();
2664
2709
  var audioManager = makeAudioManager();
2665
- var getTotalCacheStats = async () => {
2666
- const keyframeManagerCacheStats = await keyframeManager.getCacheStats();
2710
+ var getTotalCacheStats = () => {
2711
+ const keyframeManagerCacheStats = keyframeManager.getCacheStats();
2667
2712
  const audioManagerCacheStats = audioManager.getCacheStats();
2668
2713
  return {
2669
2714
  count: keyframeManagerCacheStats.count + audioManagerCacheStats.count,
@@ -3697,7 +3742,8 @@ var AudioForRendering = ({
3697
3742
  disallowFallbackToHtml5Audio,
3698
3743
  toneFrequency,
3699
3744
  trimAfter,
3700
- trimBefore
3745
+ trimBefore,
3746
+ onError
3701
3747
  }) => {
3702
3748
  const defaultLogLevel = Internals14.useLogLevel();
3703
3749
  const logLevel = overriddenLogLevel ?? defaultLogLevel;
@@ -3763,52 +3809,33 @@ var AudioForRendering = ({
3763
3809
  fps,
3764
3810
  maxCacheSize
3765
3811
  }).then((result) => {
3766
- if (result.type === "unknown-container-format") {
3767
- if (environment.isClientSideRendering) {
3768
- cancelRender2(new Error(`Cannot render audio "${src}": Unknown container format. See supported formats: https://www.remotion.dev/docs/mediabunny/formats`));
3769
- return;
3770
- }
3771
- if (disallowFallbackToHtml5Audio) {
3772
- cancelRender2(new Error(`Unknown container format ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
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);
3773
3822
  }
3774
- Internals14.Log.warn({
3775
- logLevel,
3776
- tag: "@remotion/media"
3777
- }, `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);
3778
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>`);
3779
3828
  return;
3780
3829
  }
3781
3830
  if (result.type === "cannot-decode") {
3782
- if (environment.isClientSideRendering) {
3783
- cancelRender2(new Error(`Cannot render audio "${src}": The audio could not be decoded by the browser.`));
3784
- return;
3785
- }
3786
- if (disallowFallbackToHtml5Audio) {
3787
- cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
3788
- }
3789
- Internals14.Log.warn({
3790
- logLevel,
3791
- tag: "@remotion/media"
3792
- }, `Cannot decode ${src}, falling back to <Html5Audio>`);
3793
- 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>`);
3794
3832
  return;
3795
3833
  }
3796
3834
  if (result.type === "cannot-decode-alpha") {
3797
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.`);
3798
3836
  }
3799
3837
  if (result.type === "network-error") {
3800
- if (environment.isClientSideRendering) {
3801
- cancelRender2(new Error(`Cannot render audio "${src}": Network error while fetching the audio (possibly CORS).`));
3802
- return;
3803
- }
3804
- if (disallowFallbackToHtml5Audio) {
3805
- cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
3806
- }
3807
- Internals14.Log.warn({
3808
- logLevel,
3809
- tag: "@remotion/media"
3810
- }, `Network error fetching ${src}, falling back to <Html5Audio>`);
3811
- 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>`);
3812
3839
  return;
3813
3840
  }
3814
3841
  const { audio, durationInSeconds: assetDurationInSeconds } = result;
@@ -3873,7 +3900,8 @@ var AudioForRendering = ({
3873
3900
  trimBefore,
3874
3901
  replaceWithHtml5Audio,
3875
3902
  maxCacheSize,
3876
- audioEnabled
3903
+ audioEnabled,
3904
+ onError
3877
3905
  ]);
3878
3906
  if (replaceWithHtml5Audio) {
3879
3907
  return /* @__PURE__ */ jsx2(Html5Audio, {
@@ -3974,7 +4002,8 @@ var VideoForPreviewAssertedShowing = ({
3974
4002
  fallbackOffthreadVideoProps,
3975
4003
  audioStreamIndex,
3976
4004
  debugOverlay,
3977
- headless
4005
+ headless,
4006
+ onError
3978
4007
  }) => {
3979
4008
  const src = usePreload2(unpreloadedSrc);
3980
4009
  const canvasRef = useRef2(null);
@@ -4073,36 +4102,34 @@ var VideoForPreviewAssertedShowing = ({
4073
4102
  if (result.type === "disposed") {
4074
4103
  return;
4075
4104
  }
4076
- if (result.type === "unknown-container-format") {
4077
- if (disallowFallbackToOffthreadVideo) {
4078
- throw new Error(`Unknown container format ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
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;
4079
4115
  }
4080
- Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Unknown container format for ${preloadedSrc} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`);
4116
+ Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
4081
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>`);
4082
4121
  return;
4083
4122
  }
4084
4123
  if (result.type === "network-error") {
4085
- if (disallowFallbackToOffthreadVideo) {
4086
- throw new Error(`Network error fetching ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
4087
- }
4088
- Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Network error fetching ${preloadedSrc}, falling back to <OffthreadVideo>`);
4089
- setShouldFallbackToNativeVideo(true);
4124
+ handleError(new Error(`Network error fetching ${preloadedSrc}.`), `Network error fetching ${preloadedSrc}, falling back to <OffthreadVideo>`);
4090
4125
  return;
4091
4126
  }
4092
4127
  if (result.type === "cannot-decode") {
4093
- if (disallowFallbackToOffthreadVideo) {
4094
- throw new Error(`Cannot decode ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
4095
- }
4096
- Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `Cannot decode ${preloadedSrc}, falling back to <OffthreadVideo>`);
4097
- setShouldFallbackToNativeVideo(true);
4128
+ handleError(new Error(`Cannot decode ${preloadedSrc}.`), `Cannot decode ${preloadedSrc}, falling back to <OffthreadVideo>`);
4098
4129
  return;
4099
4130
  }
4100
4131
  if (result.type === "no-tracks") {
4101
- if (disallowFallbackToOffthreadVideo) {
4102
- throw new Error(`No video or audio tracks found for ${preloadedSrc}, and 'disallowFallbackToOffthreadVideo' was set.`);
4103
- }
4104
- Internals16.Log.warn({ logLevel, tag: "@remotion/media" }, `No video or audio tracks found for ${preloadedSrc}, falling back to <OffthreadVideo>`);
4105
- 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>`);
4106
4133
  return;
4107
4134
  }
4108
4135
  if (result.type === "success") {
@@ -4110,11 +4137,31 @@ var VideoForPreviewAssertedShowing = ({
4110
4137
  setMediaDurationInSeconds(result.durationInSeconds);
4111
4138
  }
4112
4139
  }).catch((error) => {
4113
- Internals16.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] Failed to initialize MediaPlayer", error);
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);
4114
4151
  setShouldFallbackToNativeVideo(true);
4115
4152
  });
4116
4153
  } catch (error) {
4117
- Internals16.Log.error({ logLevel, tag: "@remotion/media" }, "[VideoForPreview] MediaPlayer initialization failed", error);
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);
4118
4165
  setShouldFallbackToNativeVideo(true);
4119
4166
  }
4120
4167
  return () => {
@@ -4135,7 +4182,8 @@ var VideoForPreviewAssertedShowing = ({
4135
4182
  loop,
4136
4183
  preloadedSrc,
4137
4184
  sharedAudioContext,
4138
- videoConfig.fps
4185
+ videoConfig.fps,
4186
+ onError
4139
4187
  ]);
4140
4188
  const classNameValue = useMemo4(() => {
4141
4189
  return [Internals16.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals16.truthy).join(" ");
@@ -4348,7 +4396,8 @@ var VideoForRendering = ({
4348
4396
  toneFrequency,
4349
4397
  trimAfterValue,
4350
4398
  trimBeforeValue,
4351
- headless
4399
+ headless,
4400
+ onError
4352
4401
  }) => {
4353
4402
  if (!src) {
4354
4403
  throw new TypeError("No `src` was passed to <Video>.");
@@ -4417,64 +4466,43 @@ var VideoForRendering = ({
4417
4466
  fps,
4418
4467
  maxCacheSize
4419
4468
  }).then((result) => {
4420
- if (result.type === "unknown-container-format") {
4469
+ const handleError = (err, clientSideError, fallbackMessage, mediaDurationInSeconds) => {
4421
4470
  if (environment.isClientSideRendering) {
4422
- cancelRender3(new Error(`Cannot render video "${src}": Unknown container format. See supported formats: https://www.remotion.dev/docs/mediabunny/formats`));
4471
+ cancelRender3(clientSideError);
4423
4472
  return;
4424
4473
  }
4425
- if (disallowFallbackToOffthreadVideo) {
4426
- cancelRender3(new Error(`Unknown container format ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
4427
- }
4428
- if (window.remotion_isMainTab) {
4429
- Internals17.Log.info({ logLevel, tag: "@remotion/media" }, `Unknown container format for ${src} (Supported formats: https://www.remotion.dev/docs/mediabunny/formats), falling back to <OffthreadVideo>`);
4430
- }
4431
- setReplaceWithOffthreadVideo({ durationInSeconds: null });
4432
- return;
4433
- }
4434
- if (result.type === "cannot-decode") {
4435
- if (environment.isClientSideRendering) {
4436
- 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);
4437
4483
  return;
4438
4484
  }
4439
- if (disallowFallbackToOffthreadVideo) {
4440
- cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
4441
- }
4442
4485
  if (window.remotion_isMainTab) {
4443
- Internals17.Log.warn({ logLevel, tag: "@remotion/media" }, `Cannot decode ${src}, falling back to <OffthreadVideo>`);
4486
+ Internals17.Log.warn({ logLevel, tag: "@remotion/media" }, fallbackMessage);
4444
4487
  }
4445
4488
  setReplaceWithOffthreadVideo({
4446
- durationInSeconds: result.durationInSeconds
4489
+ durationInSeconds: mediaDurationInSeconds
4447
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);
4448
4498
  return;
4449
4499
  }
4450
4500
  if (result.type === "cannot-decode-alpha") {
4451
- if (environment.isClientSideRendering) {
4452
- cancelRender3(new Error(`Cannot render video "${src}": The alpha channel could not be decoded by the browser.`));
4453
- return;
4454
- }
4455
- if (disallowFallbackToOffthreadVideo) {
4456
- cancelRender3(new Error(`Cannot decode alpha component for ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
4457
- }
4458
- if (window.remotion_isMainTab) {
4459
- Internals17.Log.info({ logLevel, tag: "@remotion/media" }, `Cannot decode alpha component for ${src}, falling back to <OffthreadVideo>`);
4460
- }
4461
- setReplaceWithOffthreadVideo({
4462
- durationInSeconds: result.durationInSeconds
4463
- });
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);
4464
4502
  return;
4465
4503
  }
4466
4504
  if (result.type === "network-error") {
4467
- if (environment.isClientSideRendering) {
4468
- cancelRender3(new Error(`Cannot render video "${src}": Network error while fetching the video (possibly CORS).`));
4469
- return;
4470
- }
4471
- if (disallowFallbackToOffthreadVideo) {
4472
- cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
4473
- }
4474
- if (window.remotion_isMainTab) {
4475
- Internals17.Log.warn({ logLevel, tag: "@remotion/media" }, `Network error fetching ${src} (no CORS?), falling back to <OffthreadVideo>`);
4476
- }
4477
- 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);
4478
4506
  return;
4479
4507
  }
4480
4508
  const {
@@ -4567,7 +4595,8 @@ var VideoForRendering = ({
4567
4595
  videoEnabled,
4568
4596
  maxCacheSize,
4569
4597
  cancelRender3,
4570
- headless
4598
+ headless,
4599
+ onError
4571
4600
  ]);
4572
4601
  const classNameValue = useMemo5(() => {
4573
4602
  return [Internals17.OBJECTFIT_CONTAIN_CLASS_NAME, className].filter(Internals17.truthy).join(" ");
@@ -4662,7 +4691,8 @@ var InnerVideo = ({
4662
4691
  toneFrequency,
4663
4692
  showInTimeline,
4664
4693
  debugOverlay,
4665
- headless
4694
+ headless,
4695
+ onError
4666
4696
  }) => {
4667
4697
  const environment = useRemotionEnvironment4();
4668
4698
  if (typeof src !== "string") {
@@ -4703,7 +4733,8 @@ var InnerVideo = ({
4703
4733
  toneFrequency,
4704
4734
  trimAfterValue,
4705
4735
  trimBeforeValue,
4706
- headless
4736
+ headless,
4737
+ onError
4707
4738
  });
4708
4739
  }
4709
4740
  return /* @__PURE__ */ jsx6(VideoForPreview, {
@@ -4726,7 +4757,8 @@ var InnerVideo = ({
4726
4757
  disallowFallbackToOffthreadVideo,
4727
4758
  fallbackOffthreadVideoProps,
4728
4759
  debugOverlay: debugOverlay ?? false,
4729
- headless: headless ?? false
4760
+ headless: headless ?? false,
4761
+ onError
4730
4762
  });
4731
4763
  };
4732
4764
  var Video = ({
@@ -4752,7 +4784,8 @@ var Video = ({
4752
4784
  stack,
4753
4785
  toneFrequency,
4754
4786
  debugOverlay,
4755
- headless
4787
+ headless,
4788
+ onError
4756
4789
  }) => {
4757
4790
  const fallbackLogLevel = Internals18.useLogLevel();
4758
4791
  return /* @__PURE__ */ jsx6(InnerVideo, {
@@ -4778,7 +4811,8 @@ var Video = ({
4778
4811
  toneFrequency: toneFrequency ?? 1,
4779
4812
  stack,
4780
4813
  debugOverlay: debugOverlay ?? false,
4781
- headless: headless ?? false
4814
+ headless: headless ?? false,
4815
+ onError
4782
4816
  });
4783
4817
  };
4784
4818
  Internals18.addSequenceStackTraces(Video);
@@ -1,9 +1,8 @@
1
- import type { LogLevel } from 'remotion';
2
1
  import type { ExtractFrameViaBroadcastChannelResult } from './video-extraction/extract-frame-via-broadcast-channel';
3
2
  export declare const extractFrameAndAudio: ({ src, timeInSeconds, logLevel, durationInSeconds, playbackRate, includeAudio, includeVideo, loop, audioStreamIndex, trimAfter, trimBefore, fps, maxCacheSize, }: {
4
3
  src: string;
5
4
  timeInSeconds: number;
6
- logLevel: LogLevel;
5
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
7
6
  durationInSeconds: number;
8
7
  playbackRate: number;
9
8
  includeAudio: boolean;
@@ -1,7 +1,6 @@
1
- import type { LogLevel } from 'remotion';
2
1
  import type { GetSink } from './video-extraction/get-frames-since-keyframe';
3
2
  export declare const sinkPromises: Record<string, Promise<GetSink>>;
4
- export declare const getSink: (src: string, logLevel: LogLevel) => Promise<{
3
+ export declare const getSink: (src: string, logLevel: "error" | "info" | "trace" | "verbose" | "warn") => Promise<{
5
4
  getVideo: () => Promise<import("./video-extraction/get-frames-since-keyframe").VideoSinkResult>;
6
5
  getAudio: (index: number) => Promise<import("./video-extraction/get-frames-since-keyframe").AudioSinkResult>;
7
6
  actualMatroskaTimestamps: {
package/dist/index.d.ts CHANGED
@@ -10,5 +10,6 @@ export declare const experimental_Audio: import("react").FC<import(".").AudioPro
10
10
  export declare const experimental_Video: import("react").FC<import(".").VideoProps>;
11
11
  export { AudioForPreview } from './audio/audio-for-preview';
12
12
  export { AudioProps, FallbackHtml5AudioProps } from './audio/props';
13
- export { VideoProps } from './video/props';
13
+ export { MediaErrorAction } from './on-error';
14
+ export { FallbackOffthreadVideoProps, VideoProps } from './video/props';
14
15
  export { Audio, Video };
@@ -42,7 +42,7 @@ export declare class MediaPlayer {
42
42
  private isPremounting;
43
43
  private isPostmounting;
44
44
  private seekPromiseChain;
45
- constructor({ canvas, src, logLevel, sharedAudioContext, loop, trimBefore, trimAfter, playbackRate, globalPlaybackRate, audioStreamIndex, fps, debugOverlay, bufferState, isPremounting, isPostmounting, onVideoFrameCallback, playing, }: {
45
+ constructor({ canvas, src, logLevel, sharedAudioContext, loop, trimBefore, trimAfter, playbackRate, globalPlaybackRate, audioStreamIndex, fps, debugOverlay, bufferState, isPremounting, isPostmounting, onVideoFrameCallback, playing }: {
46
46
  canvas: HTMLCanvasElement | OffscreenCanvas | null;
47
47
  src: string;
48
48
  logLevel: LogLevel;
@@ -0,0 +1,12 @@
1
+ export type MediaErrorEvent = {
2
+ error: Error;
3
+ };
4
+ export type MediaErrorAction = 'fallback' | 'fail';
5
+ export type MediaOnError = (error: Error) => MediaErrorAction | undefined;
6
+ export declare const callOnErrorAndResolve: ({ onError, error, disallowFallback, isClientSideRendering, clientSideError, }: {
7
+ onError: MediaOnError | undefined;
8
+ error: Error;
9
+ clientSideError: Error;
10
+ disallowFallback: boolean;
11
+ isClientSideRendering: boolean;
12
+ }) => [MediaErrorAction, Error];
@@ -1,8 +1,8 @@
1
- import type { _InternalTypes } from 'remotion';
1
+ import type { LoopDisplay } from 'remotion';
2
2
  export declare const useLoopDisplay: ({ loop, mediaDurationInSeconds, playbackRate, trimAfter, trimBefore, }: {
3
3
  loop: boolean;
4
4
  mediaDurationInSeconds: number | null;
5
5
  trimAfter: number | undefined;
6
6
  trimBefore: number | undefined;
7
7
  playbackRate: number;
8
- }) => _InternalTypes["LoopDisplay"] | undefined;
8
+ }) => LoopDisplay | undefined;
@@ -1,4 +1,4 @@
1
- import type { _InternalTypes } from 'remotion';
1
+ import type { LoopDisplay } from 'remotion';
2
2
  import { type VolumeProp } from 'remotion';
3
3
  export declare const useMediaInTimeline: ({ volume, mediaVolume, src, mediaType, playbackRate, displayName, stack, showInTimeline, premountDisplay, postmountDisplay, loopDisplay, trimBefore, trimAfter, }: {
4
4
  volume: VolumeProp | undefined;
@@ -11,7 +11,7 @@ export declare const useMediaInTimeline: ({ volume, mediaVolume, src, mediaType,
11
11
  showInTimeline: boolean;
12
12
  premountDisplay: number | null;
13
13
  postmountDisplay: number | null;
14
- loopDisplay: _InternalTypes["LoopDisplay"] | undefined;
14
+ loopDisplay: LoopDisplay | undefined;
15
15
  trimBefore: number | undefined;
16
16
  trimAfter: number | undefined;
17
17
  }) => {