@remotion/media 4.0.358 → 4.0.361

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.
@@ -81,6 +81,9 @@ export const AudioForRendering = ({ volume: volumeProp, playbackRate, src, muted
81
81
  setReplaceWithHtml5Audio(true);
82
82
  return;
83
83
  }
84
+ if (result.type === 'cannot-decode-alpha') {
85
+ 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.`);
86
+ }
84
87
  if (result.type === 'network-error') {
85
88
  if (disallowFallbackToHtml5Audio) {
86
89
  cancelRender(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
@@ -0,0 +1 @@
1
+ export declare const canBrowserUseWebGl2: () => boolean;
@@ -0,0 +1,13 @@
1
+ let browserCanUseWebGl2 = null;
2
+ const browserCanUseWebGl2Uncached = () => {
3
+ const canvas = new OffscreenCanvas(1, 1);
4
+ const context = canvas.getContext('webgl2');
5
+ return context !== null;
6
+ };
7
+ export const canBrowserUseWebGl2 = () => {
8
+ if (browserCanUseWebGl2 !== null) {
9
+ return browserCanUseWebGl2;
10
+ }
11
+ browserCanUseWebGl2 = browserCanUseWebGl2Uncached();
12
+ return browserCanUseWebGl2;
13
+ };
package/dist/caches.d.ts CHANGED
@@ -7,7 +7,7 @@ export declare const keyframeManager: {
7
7
  videoSampleSink: import("mediabunny").VideoSampleSink;
8
8
  src: string;
9
9
  logLevel: LogLevel;
10
- }) => Promise<import("./video-extraction/keyframe-bank").KeyframeBank | null>;
10
+ }) => Promise<import("./video-extraction/keyframe-bank").KeyframeBank | "has-alpha" | null>;
11
11
  getCacheStats: () => Promise<{
12
12
  count: number;
13
13
  totalSize: number;
package/dist/caches.js CHANGED
@@ -28,9 +28,9 @@ const getUncachedMaxCacheSize = (logLevel) => {
28
28
  if (window.remotion_initialMemoryAvailable !== undefined &&
29
29
  window.remotion_initialMemoryAvailable !== null) {
30
30
  const value = window.remotion_initialMemoryAvailable / 2;
31
- if (value < 240 * 1024 * 1024) {
32
- Internals.Log.verbose({ logLevel, tag: '@remotion/media' }, `Using cache size set based on minimum value of 240MB (which is more than half of the available system memory!)`);
33
- return 240 * 1024 * 1024;
31
+ if (value < 500 * 1024 * 1024) {
32
+ Internals.Log.verbose({ logLevel, tag: '@remotion/media' }, `Using cache size set based on minimum value of 500MB (which is more than half of the available system memory!)`);
33
+ return 500 * 1024 * 1024;
34
34
  }
35
35
  if (value > 20000 * 1024 * 1024) {
36
36
  Internals.Log.verbose({ logLevel, tag: '@remotion/media' }, `Using cache size set based on maximum value of 20GB (which is less than half of the available system memory)`);
@@ -361,7 +361,8 @@ class MediaPlayer {
361
361
  }
362
362
  this.canvasSink = new CanvasSink(videoTrack, {
363
363
  poolSize: 2,
364
- fit: "contain"
364
+ fit: "contain",
365
+ alpha: true
365
366
  });
366
367
  this.canvas.width = videoTrack.displayWidth;
367
368
  this.canvas.height = videoTrack.displayHeight;
@@ -582,6 +583,7 @@ class MediaPlayer {
582
583
  }
583
584
  drawCurrentFrame() {
584
585
  if (this.context && this.nextFrame) {
586
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
585
587
  this.context.drawImage(this.nextFrame.canvas, 0, 0);
586
588
  }
587
589
  if (this.onVideoFrameCallback && this.canvas) {
@@ -1527,6 +1529,21 @@ var makeAudioManager = () => {
1527
1529
  // src/video-extraction/keyframe-manager.ts
1528
1530
  import { Internals as Internals9 } from "remotion";
1529
1531
 
1532
+ // src/browser-can-use-webgl2.ts
1533
+ var browserCanUseWebGl2 = null;
1534
+ var browserCanUseWebGl2Uncached = () => {
1535
+ const canvas = new OffscreenCanvas(1, 1);
1536
+ const context = canvas.getContext("webgl2");
1537
+ return context !== null;
1538
+ };
1539
+ var canBrowserUseWebGl2 = () => {
1540
+ if (browserCanUseWebGl2 !== null) {
1541
+ return browserCanUseWebGl2;
1542
+ }
1543
+ browserCanUseWebGl2 = browserCanUseWebGl2Uncached();
1544
+ return browserCanUseWebGl2;
1545
+ };
1546
+
1530
1547
  // src/render-timestamp-range.ts
1531
1548
  var renderTimestampRange = (timestamps) => {
1532
1549
  if (timestamps.length === 0) {
@@ -1927,6 +1944,10 @@ var makeKeyframeManager = () => {
1927
1944
  const startPacket = await packetSink.getKeyPacket(timestamp, {
1928
1945
  verifyKeyPackets: true
1929
1946
  });
1947
+ const hasAlpha = startPacket?.sideData.alpha;
1948
+ if (hasAlpha && !canBrowserUseWebGl2()) {
1949
+ return "has-alpha";
1950
+ }
1930
1951
  if (!startPacket) {
1931
1952
  return null;
1932
1953
  }
@@ -2038,9 +2059,9 @@ var getUncachedMaxCacheSize = (logLevel) => {
2038
2059
  }
2039
2060
  if (window.remotion_initialMemoryAvailable !== undefined && window.remotion_initialMemoryAvailable !== null) {
2040
2061
  const value = window.remotion_initialMemoryAvailable / 2;
2041
- if (value < 240 * 1024 * 1024) {
2042
- Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Using cache size set based on minimum value of 240MB (which is more than half of the available system memory!)`);
2043
- return 240 * 1024 * 1024;
2062
+ if (value < 500 * 1024 * 1024) {
2063
+ Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Using cache size set based on minimum value of 500MB (which is more than half of the available system memory!)`);
2064
+ return 500 * 1024 * 1024;
2044
2065
  }
2045
2066
  if (value > 20000 * 1024 * 1024) {
2046
2067
  Internals10.Log.verbose({ logLevel, tag: "@remotion/media" }, `Using cache size set based on maximum value of 20GB (which is less than half of the available system memory)`);
@@ -2302,6 +2323,12 @@ var extractFrameInternal = async ({
2302
2323
  src,
2303
2324
  logLevel
2304
2325
  });
2326
+ if (keyframeBank === "has-alpha") {
2327
+ return {
2328
+ type: "cannot-decode-alpha",
2329
+ durationInSeconds: await sink.getDuration()
2330
+ };
2331
+ }
2305
2332
  if (!keyframeBank) {
2306
2333
  return {
2307
2334
  type: "success",
@@ -2367,6 +2394,12 @@ var extractFrameAndAudio = async ({
2367
2394
  if (frame?.type === "unknown-container-format") {
2368
2395
  return { type: "unknown-container-format" };
2369
2396
  }
2397
+ if (frame?.type === "cannot-decode-alpha") {
2398
+ return {
2399
+ type: "cannot-decode-alpha",
2400
+ durationInSeconds: frame.durationInSeconds
2401
+ };
2402
+ }
2370
2403
  if (audio === "unknown-container-format") {
2371
2404
  if (frame !== null) {
2372
2405
  frame?.frame?.close();
@@ -2426,6 +2459,15 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
2426
2459
  window.remotion_broadcastChannel.postMessage(cannotDecodeResponse);
2427
2460
  return;
2428
2461
  }
2462
+ if (result.type === "cannot-decode-alpha") {
2463
+ const cannotDecodeAlphaResponse = {
2464
+ type: "response-cannot-decode-alpha",
2465
+ id: data.id,
2466
+ durationInSeconds: result.durationInSeconds
2467
+ };
2468
+ window.remotion_broadcastChannel.postMessage(cannotDecodeAlphaResponse);
2469
+ return;
2470
+ }
2429
2471
  if (result.type === "network-error") {
2430
2472
  const networkErrorResponse = {
2431
2473
  type: "response-network-error",
@@ -2544,6 +2586,14 @@ var extractFrameViaBroadcastChannel = ({
2544
2586
  window.remotion_broadcastChannel.removeEventListener("message", onMessage);
2545
2587
  return;
2546
2588
  }
2589
+ if (data.type === "response-cannot-decode-alpha") {
2590
+ resolve({
2591
+ type: "cannot-decode-alpha",
2592
+ durationInSeconds: data.durationInSeconds
2593
+ });
2594
+ window.remotion_broadcastChannel.removeEventListener("message", onMessage);
2595
+ return;
2596
+ }
2547
2597
  throw new Error(`Invalid message: ${JSON.stringify(data)}`);
2548
2598
  };
2549
2599
  window.remotion_broadcastChannel.addEventListener("message", onMessage);
@@ -2673,6 +2723,9 @@ var AudioForRendering = ({
2673
2723
  setReplaceWithHtml5Audio(true);
2674
2724
  return;
2675
2725
  }
2726
+ if (result.type === "cannot-decode-alpha") {
2727
+ 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.`);
2728
+ }
2676
2729
  if (result.type === "network-error") {
2677
2730
  if (disallowFallbackToHtml5Audio) {
2678
2731
  cancelRender2(new Error(`Cannot decode ${src}, and 'disallowFallbackToHtml5Audio' was set. Failing the render.`));
@@ -3236,6 +3289,18 @@ var VideoForRendering = ({
3236
3289
  });
3237
3290
  return;
3238
3291
  }
3292
+ if (result.type === "cannot-decode-alpha") {
3293
+ if (disallowFallbackToOffthreadVideo) {
3294
+ cancelRender3(new Error(`Cannot decode alpha component for ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
3295
+ }
3296
+ if (window.remotion_isMainTab) {
3297
+ Internals15.Log.info({ logLevel, tag: "@remotion/media" }, `Cannot decode alpha component for ${src}, falling back to <OffthreadVideo>`);
3298
+ }
3299
+ setReplaceWithOffthreadVideo({
3300
+ durationInSeconds: result.durationInSeconds
3301
+ });
3302
+ return;
3303
+ }
3239
3304
  if (result.type === "network-error") {
3240
3305
  if (disallowFallbackToOffthreadVideo) {
3241
3306
  cancelRender3(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
@@ -3253,7 +3318,9 @@ var VideoForRendering = ({
3253
3318
  } = result;
3254
3319
  if (imageBitmap) {
3255
3320
  onVideoFrame?.(imageBitmap);
3256
- const context = canvasRef.current?.getContext("2d");
3321
+ const context = canvasRef.current?.getContext("2d", {
3322
+ alpha: true
3323
+ });
3257
3324
  if (!context) {
3258
3325
  return;
3259
3326
  }
@@ -3263,7 +3330,9 @@ var VideoForRendering = ({
3263
3330
  context.drawImage(imageBitmap, 0, 0);
3264
3331
  imageBitmap.close();
3265
3332
  } else if (window.remotion_videoEnabled) {
3266
- const context = canvasRef.current?.getContext("2d");
3333
+ const context = canvasRef.current?.getContext("2d", {
3334
+ alpha: true
3335
+ });
3267
3336
  if (context) {
3268
3337
  context.clearRect(0, 0, context.canvas.width, context.canvas.height);
3269
3338
  }
@@ -3344,7 +3413,7 @@ var VideoForRendering = ({
3344
3413
  delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined,
3345
3414
  style,
3346
3415
  allowAmplificationDuringRender: true,
3347
- transparent: fallbackOffthreadVideoProps?.transparent ?? false,
3416
+ transparent: fallbackOffthreadVideoProps?.transparent ?? true,
3348
3417
  toneMapped: fallbackOffthreadVideoProps?.toneMapped ?? true,
3349
3418
  audioStreamIndex: audioStreamIndex ?? 0,
3350
3419
  name,
@@ -40,6 +40,12 @@ export const extractFrameAndAudio = async ({ src, timeInSeconds, logLevel, durat
40
40
  if (frame?.type === 'unknown-container-format') {
41
41
  return { type: 'unknown-container-format' };
42
42
  }
43
+ if (frame?.type === 'cannot-decode-alpha') {
44
+ return {
45
+ type: 'cannot-decode-alpha',
46
+ durationInSeconds: frame.durationInSeconds,
47
+ };
48
+ }
43
49
  if (audio === 'unknown-container-format') {
44
50
  if (frame !== null) {
45
51
  frame?.frame?.close();
@@ -258,6 +258,7 @@ export class MediaPlayer {
258
258
  this.canvasSink = new CanvasSink(videoTrack, {
259
259
  poolSize: 2,
260
260
  fit: 'contain',
261
+ alpha: true,
261
262
  });
262
263
  this.canvas.width = videoTrack.displayWidth;
263
264
  this.canvas.height = videoTrack.displayHeight;
@@ -473,6 +474,7 @@ export class MediaPlayer {
473
474
  }
474
475
  drawCurrentFrame() {
475
476
  if (this.context && this.nextFrame) {
477
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
476
478
  this.context.drawImage(this.nextFrame.canvas, 0, 0);
477
479
  }
478
480
  if (this.onVideoFrameCallback && this.canvas) {
@@ -88,6 +88,18 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
88
88
  });
89
89
  return;
90
90
  }
91
+ if (result.type === 'cannot-decode-alpha') {
92
+ if (disallowFallbackToOffthreadVideo) {
93
+ cancelRender(new Error(`Cannot decode alpha component for ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
94
+ }
95
+ if (window.remotion_isMainTab) {
96
+ Internals.Log.info({ logLevel, tag: '@remotion/media' }, `Cannot decode alpha component for ${src}, falling back to <OffthreadVideo>`);
97
+ }
98
+ setReplaceWithOffthreadVideo({
99
+ durationInSeconds: result.durationInSeconds,
100
+ });
101
+ return;
102
+ }
91
103
  if (result.type === 'network-error') {
92
104
  if (disallowFallbackToOffthreadVideo) {
93
105
  cancelRender(new Error(`Cannot decode ${src}, and 'disallowFallbackToOffthreadVideo' was set. Failing the render.`));
@@ -101,7 +113,9 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
101
113
  const { frame: imageBitmap, audio, durationInSeconds: assetDurationInSeconds, } = result;
102
114
  if (imageBitmap) {
103
115
  onVideoFrame?.(imageBitmap);
104
- const context = canvasRef.current?.getContext('2d');
116
+ const context = canvasRef.current?.getContext('2d', {
117
+ alpha: true,
118
+ });
105
119
  if (!context) {
106
120
  return;
107
121
  }
@@ -121,7 +135,9 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
121
135
  // In the case of https://discord.com/channels/809501355504959528/809501355504959531/1424400511070765086
122
136
  // A video that only starts at time 0.033sec
123
137
  // we shall not crash here but clear the canvas
124
- const context = canvasRef.current?.getContext('2d');
138
+ const context = canvasRef.current?.getContext('2d', {
139
+ alpha: true,
140
+ });
125
141
  if (context) {
126
142
  context.clearRect(0, 0, context.canvas.width, context.canvas.height);
127
143
  }
@@ -195,7 +211,7 @@ export const VideoForRendering = ({ volume: volumeProp, playbackRate, src, muted
195
211
  .join(' ');
196
212
  }, [className]);
197
213
  if (replaceWithOffthreadVideo) {
198
- const fallback = (_jsx(Internals.InnerOffthreadVideo, { src: src, playbackRate: playbackRate ?? 1, muted: muted ?? false, acceptableTimeShiftInSeconds: fallbackOffthreadVideoProps?.acceptableTimeShiftInSeconds, loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? 'repeat', delayRenderRetries: delayRenderRetries ?? undefined, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined, style: style, allowAmplificationDuringRender: true, transparent: fallbackOffthreadVideoProps?.transparent ?? false, toneMapped: fallbackOffthreadVideoProps?.toneMapped ?? true, audioStreamIndex: audioStreamIndex ?? 0, name: name, className: className, onVideoFrame: onVideoFrame, volume: volumeProp, id: id, onError: fallbackOffthreadVideoProps?.onError, toneFrequency: fallbackOffthreadVideoProps?.toneFrequency ?? 1,
214
+ const fallback = (_jsx(Internals.InnerOffthreadVideo, { src: src, playbackRate: playbackRate ?? 1, muted: muted ?? false, acceptableTimeShiftInSeconds: fallbackOffthreadVideoProps?.acceptableTimeShiftInSeconds, loopVolumeCurveBehavior: loopVolumeCurveBehavior ?? 'repeat', delayRenderRetries: delayRenderRetries ?? undefined, delayRenderTimeoutInMilliseconds: delayRenderTimeoutInMilliseconds ?? undefined, style: style, allowAmplificationDuringRender: true, transparent: fallbackOffthreadVideoProps?.transparent ?? true, toneMapped: fallbackOffthreadVideoProps?.toneMapped ?? true, audioStreamIndex: audioStreamIndex ?? 0, name: name, className: className, onVideoFrame: onVideoFrame, volume: volumeProp, id: id, onError: fallbackOffthreadVideoProps?.onError, toneFrequency: fallbackOffthreadVideoProps?.toneFrequency ?? 1,
199
215
  // these shouldn't matter during rendering / should not appear at all
200
216
  showInTimeline: false, crossOrigin: undefined, onAutoPlayError: () => undefined, pauseWhenBuffering: false, trimAfter: undefined, trimBefore: undefined, useWebAudioApi: false, startFrom: undefined, endAt: undefined, stack: stack, _remotionInternalNativeLoopPassed: false }));
201
217
  if (loop) {
@@ -8,6 +8,9 @@ export type ExtractFrameViaBroadcastChannelResult = {
8
8
  } | {
9
9
  type: 'cannot-decode';
10
10
  durationInSeconds: number | null;
11
+ } | {
12
+ type: 'cannot-decode-alpha';
13
+ durationInSeconds: number | null;
11
14
  } | {
12
15
  type: 'network-error';
13
16
  } | {
@@ -28,6 +28,15 @@ if (window.remotion_broadcastChannel && window.remotion_isMainTab) {
28
28
  window.remotion_broadcastChannel.postMessage(cannotDecodeResponse);
29
29
  return;
30
30
  }
31
+ if (result.type === 'cannot-decode-alpha') {
32
+ const cannotDecodeAlphaResponse = {
33
+ type: 'response-cannot-decode-alpha',
34
+ id: data.id,
35
+ durationInSeconds: result.durationInSeconds,
36
+ };
37
+ window.remotion_broadcastChannel.postMessage(cannotDecodeAlphaResponse);
38
+ return;
39
+ }
31
40
  if (result.type === 'network-error') {
32
41
  const networkErrorResponse = {
33
42
  type: 'response-network-error',
@@ -138,6 +147,14 @@ export const extractFrameViaBroadcastChannel = ({ src, timeInSeconds, logLevel,
138
147
  window.remotion_broadcastChannel.removeEventListener('message', onMessage);
139
148
  return;
140
149
  }
150
+ if (data.type === 'response-cannot-decode-alpha') {
151
+ resolve({
152
+ type: 'cannot-decode-alpha',
153
+ durationInSeconds: data.durationInSeconds,
154
+ });
155
+ window.remotion_broadcastChannel.removeEventListener('message', onMessage);
156
+ return;
157
+ }
141
158
  throw new Error(`Invalid message: ${JSON.stringify(data)}`);
142
159
  };
143
160
  window.remotion_broadcastChannel.addEventListener('message', onMessage);
@@ -7,6 +7,9 @@ type ExtractFrameResult = {
7
7
  } | {
8
8
  type: 'cannot-decode';
9
9
  durationInSeconds: number | null;
10
+ } | {
11
+ type: 'cannot-decode-alpha';
12
+ durationInSeconds: number | null;
10
13
  } | {
11
14
  type: 'unknown-container-format';
12
15
  };
@@ -42,6 +42,12 @@ const extractFrameInternal = async ({ src, timeInSeconds: unloopedTimeInSeconds,
42
42
  src,
43
43
  logLevel,
44
44
  });
45
+ if (keyframeBank === 'has-alpha') {
46
+ return {
47
+ type: 'cannot-decode-alpha',
48
+ durationInSeconds: await sink.getDuration(),
49
+ };
50
+ }
45
51
  if (!keyframeBank) {
46
52
  return {
47
53
  type: 'success',
@@ -8,7 +8,7 @@ export declare const makeKeyframeManager: () => {
8
8
  videoSampleSink: VideoSampleSink;
9
9
  src: string;
10
10
  logLevel: LogLevel;
11
- }) => Promise<KeyframeBank | null>;
11
+ }) => Promise<KeyframeBank | "has-alpha" | null>;
12
12
  getCacheStats: () => Promise<{
13
13
  count: number;
14
14
  totalSize: number;
@@ -1,4 +1,5 @@
1
1
  import { Internals } from 'remotion';
2
+ import { canBrowserUseWebGl2 } from '../browser-can-use-webgl2';
2
3
  import { getMaxVideoCacheSize, getTotalCacheStats, SAFE_BACK_WINDOW_IN_SECONDS, } from '../caches';
3
4
  import { renderTimestampRange } from '../render-timestamp-range';
4
5
  import { getFramesSinceKeyframe } from './get-frames-since-keyframe';
@@ -104,6 +105,10 @@ export const makeKeyframeManager = () => {
104
105
  const startPacket = await packetSink.getKeyPacket(timestamp, {
105
106
  verifyKeyPackets: true,
106
107
  });
108
+ const hasAlpha = startPacket?.sideData.alpha;
109
+ if (hasAlpha && !canBrowserUseWebGl2()) {
110
+ return 'has-alpha';
111
+ }
107
112
  if (!startPacket) {
108
113
  // e.g. https://discord.com/channels/809501355504959528/809501355504959531/1424400511070765086
109
114
  // The video has an offset and the first frame is at time 0.033sec
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/media",
3
- "version": "4.0.358",
3
+ "version": "4.0.361",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/esm/index.mjs",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "mediabunny": "1.23.0",
25
- "remotion": "4.0.357",
25
+ "remotion": "4.0.361",
26
26
  "webdriverio": "9.19.2"
27
27
  },
28
28
  "peerDependencies": {
@@ -30,7 +30,7 @@
30
30
  "react-dom": ">=16.8.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@remotion/eslint-config-internal": "4.0.357",
33
+ "@remotion/eslint-config-internal": "4.0.361",
34
34
  "@vitest/browser": "^3.2.4",
35
35
  "eslint": "9.19.0",
36
36
  "react": "19.0.0",