@remotion/web-renderer 4.0.431 → 4.0.433

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 (93) hide show
  1. package/dist/can-render-types.d.ts +2 -2
  2. package/dist/esm/index.mjs +401 -98
  3. package/dist/index.d.ts +1 -1
  4. package/dist/mediabunny-mappings.d.ts +6 -4
  5. package/dist/register-aac-encoder.d.ts +1 -0
  6. package/dist/register-flac-encoder.d.ts +1 -0
  7. package/dist/render-media-on-web.d.ts +1 -1
  8. package/package.json +11 -8
  9. package/dist/add-sample.js +0 -20
  10. package/dist/artifact.js +0 -56
  11. package/dist/audio.js +0 -42
  12. package/dist/calculate-transforms.d.ts +0 -9
  13. package/dist/calculate-transforms.js +0 -74
  14. package/dist/can-use-webfs-target.js +0 -19
  15. package/dist/composable.d.ts +0 -10
  16. package/dist/composable.js +0 -1
  17. package/dist/compose-canvas.d.ts +0 -1
  18. package/dist/compose-canvas.js +0 -12
  19. package/dist/compose-svg.d.ts +0 -1
  20. package/dist/compose-svg.js +0 -34
  21. package/dist/compose.js +0 -85
  22. package/dist/create-scaffold.js +0 -104
  23. package/dist/drawing/border-radius.js +0 -151
  24. package/dist/drawing/calculate-object-fit.js +0 -208
  25. package/dist/drawing/calculate-transforms.js +0 -127
  26. package/dist/drawing/clamp-rect-to-parent-bounds.js +0 -18
  27. package/dist/drawing/do-rects-intersect.js +0 -6
  28. package/dist/drawing/draw-background.js +0 -62
  29. package/dist/drawing/draw-border.js +0 -353
  30. package/dist/drawing/draw-box-shadow.js +0 -103
  31. package/dist/drawing/draw-dom-element.js +0 -85
  32. package/dist/drawing/draw-element-to-canvas.d.ts +0 -11
  33. package/dist/drawing/draw-element-to-canvas.js +0 -64
  34. package/dist/drawing/draw-element.js +0 -84
  35. package/dist/drawing/draw-outline.js +0 -93
  36. package/dist/drawing/draw-rounded.js +0 -34
  37. package/dist/drawing/drawn-fn.js +0 -1
  38. package/dist/drawing/fit-svg-into-its-dimensions.js +0 -35
  39. package/dist/drawing/get-clipped-background.d.ts +0 -23
  40. package/dist/drawing/get-clipped-background.js +0 -14
  41. package/dist/drawing/get-padding-box.js +0 -30
  42. package/dist/drawing/get-pretransform-rect.js +0 -49
  43. package/dist/drawing/go-rects-intersect.d.ts +0 -1
  44. package/dist/drawing/go-rects-intersect.js +0 -6
  45. package/dist/drawing/handle-3d-transform.js +0 -26
  46. package/dist/drawing/handle-mask.js +0 -21
  47. package/dist/drawing/has-transform.js +0 -14
  48. package/dist/drawing/mask-image.js +0 -14
  49. package/dist/drawing/opacity.js +0 -7
  50. package/dist/drawing/overflow.js +0 -14
  51. package/dist/drawing/parse-linear-gradient.js +0 -260
  52. package/dist/drawing/parse-transform-origin.js +0 -7
  53. package/dist/drawing/precompose.d.ts +0 -27
  54. package/dist/drawing/precompose.js +0 -14
  55. package/dist/drawing/process-node.js +0 -122
  56. package/dist/drawing/round-to-expand-rect.js +0 -7
  57. package/dist/drawing/text/apply-text-transform.js +0 -12
  58. package/dist/drawing/text/draw-text.js +0 -53
  59. package/dist/drawing/text/find-line-breaks.text.js +0 -118
  60. package/dist/drawing/text/get-base-height.d.ts +0 -1
  61. package/dist/drawing/text/get-base-height.js +0 -13
  62. package/dist/drawing/text/get-collapsed-text.d.ts +0 -1
  63. package/dist/drawing/text/get-collapsed-text.js +0 -46
  64. package/dist/drawing/text/handle-text-node.js +0 -24
  65. package/dist/drawing/transform-in-3d.js +0 -177
  66. package/dist/drawing/transform-rect-with-matrix.js +0 -19
  67. package/dist/drawing/transform.js +0 -10
  68. package/dist/drawing/turn-svg-into-drawable.js +0 -41
  69. package/dist/find-capturable-elements.d.ts +0 -2
  70. package/dist/find-capturable-elements.js +0 -28
  71. package/dist/frame-range.js +0 -15
  72. package/dist/get-audio-encoding-config.js +0 -18
  73. package/dist/get-biggest-bounding-client-rect.js +0 -43
  74. package/dist/index.js +0 -2
  75. package/dist/internal-state.js +0 -21
  76. package/dist/mediabunny-mappings.js +0 -63
  77. package/dist/output-target.js +0 -1
  78. package/dist/parse-transform-origin.d.ts +0 -4
  79. package/dist/parse-transform-origin.js +0 -7
  80. package/dist/props-if-has-props.js +0 -1
  81. package/dist/render-media-on-web.js +0 -297
  82. package/dist/render-operations-queue.js +0 -3
  83. package/dist/render-still-on-web.js +0 -109
  84. package/dist/send-telemetry-event.js +0 -22
  85. package/dist/take-screenshot.js +0 -30
  86. package/dist/throttle-progress.js +0 -43
  87. package/dist/tree-walker-cleanup-after-children.js +0 -33
  88. package/dist/update-time.js +0 -17
  89. package/dist/validate-video-frame.js +0 -34
  90. package/dist/wait-for-ready.js +0 -35
  91. package/dist/walk-tree.js +0 -14
  92. package/dist/web-fs-target.js +0 -41
  93. package/dist/with-resolvers.js +0 -9
@@ -1,3 +1,26 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __toESM = (mod, isNodeMode, target) => {
7
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
8
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
9
+ for (let key of __getOwnPropNames(mod))
10
+ if (!__hasOwnProp.call(to, key))
11
+ __defProp(to, key, {
12
+ get: () => mod[key],
13
+ enumerable: true
14
+ });
15
+ return to;
16
+ };
17
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
18
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
19
+ }) : x)(function(x) {
20
+ if (typeof require !== "undefined")
21
+ return require.apply(this, arguments);
22
+ throw Error('Dynamic require of "' + x + '" is not supported');
23
+ });
1
24
  var __dispose = Symbol.dispose || /* @__PURE__ */ Symbol.for("Symbol.dispose");
2
25
  var __asyncDispose = Symbol.asyncDispose || /* @__PURE__ */ Symbol.for("Symbol.asyncDispose");
3
26
  var __using = (stack, value, async) => {
@@ -91,14 +114,24 @@ var checkWebGLSupport = () => {
91
114
 
92
115
  // src/mediabunny-mappings.ts
93
116
  import {
117
+ AdtsOutputFormat,
118
+ FlacOutputFormat,
119
+ MkvOutputFormat,
120
+ MovOutputFormat,
121
+ Mp3OutputFormat,
94
122
  Mp4OutputFormat,
123
+ OggOutputFormat,
95
124
  QUALITY_HIGH,
96
125
  QUALITY_LOW,
97
126
  QUALITY_MEDIUM,
98
127
  QUALITY_VERY_HIGH,
99
128
  QUALITY_VERY_LOW,
129
+ WavOutputFormat,
100
130
  WebMOutputFormat
101
131
  } from "mediabunny";
132
+ var isAudioOnlyContainer = (container) => {
133
+ return container === "wav" || container === "mp3" || container === "aac" || container === "ogg" || container === "flac";
134
+ };
102
135
  var codecToMediabunnyCodec = (codec) => {
103
136
  switch (codec) {
104
137
  case "h264":
@@ -118,9 +151,23 @@ var codecToMediabunnyCodec = (codec) => {
118
151
  var containerToMediabunnyContainer = (container) => {
119
152
  switch (container) {
120
153
  case "mp4":
121
- return new Mp4OutputFormat;
154
+ return new Mp4OutputFormat({ fastStart: "reserve" });
122
155
  case "webm":
123
156
  return new WebMOutputFormat;
157
+ case "mkv":
158
+ return new MkvOutputFormat;
159
+ case "wav":
160
+ return new WavOutputFormat;
161
+ case "mp3":
162
+ return new Mp3OutputFormat;
163
+ case "aac":
164
+ return new AdtsOutputFormat;
165
+ case "ogg":
166
+ return new OggOutputFormat;
167
+ case "flac":
168
+ return new FlacOutputFormat;
169
+ case "mov":
170
+ return new MovOutputFormat({ fastStart: "reserve" });
124
171
  default:
125
172
  throw new Error(`Unsupported container: ${container}`);
126
173
  }
@@ -131,10 +178,32 @@ var getDefaultVideoCodecForContainer = (container) => {
131
178
  return "h264";
132
179
  case "webm":
133
180
  return "vp8";
181
+ case "mkv":
182
+ case "mov":
183
+ return "h264";
184
+ case "wav":
185
+ case "mp3":
186
+ case "aac":
187
+ case "ogg":
188
+ case "flac":
189
+ return null;
134
190
  default:
135
191
  throw new Error(`Unsupported container: ${container}`);
136
192
  }
137
193
  };
194
+ var getDefaultContainerForCodec = (codec) => {
195
+ switch (codec) {
196
+ case "h264":
197
+ case "h265":
198
+ case "av1":
199
+ return "mp4";
200
+ case "vp8":
201
+ case "vp9":
202
+ return "webm";
203
+ default:
204
+ throw new Error(`Unsupported codec: ${codec}`);
205
+ }
206
+ };
138
207
  var getQualityForWebRendererQuality = (quality) => {
139
208
  switch (quality) {
140
209
  case "very-low":
@@ -157,6 +226,20 @@ var getMimeType = (container) => {
157
226
  return "video/mp4";
158
227
  case "webm":
159
228
  return "video/webm";
229
+ case "mkv":
230
+ return "video/x-matroska";
231
+ case "wav":
232
+ return "audio/wav";
233
+ case "mp3":
234
+ return "audio/mpeg";
235
+ case "aac":
236
+ return "audio/aac";
237
+ case "ogg":
238
+ return "audio/ogg";
239
+ case "flac":
240
+ return "audio/flac";
241
+ case "mov":
242
+ return "video/quicktime";
160
243
  default:
161
244
  throw new Error(`Unsupported container: ${container}`);
162
245
  }
@@ -167,6 +250,20 @@ var getDefaultAudioCodecForContainer = (container) => {
167
250
  return "aac";
168
251
  case "webm":
169
252
  return "opus";
253
+ case "mkv":
254
+ return "aac";
255
+ case "wav":
256
+ return "pcm-s16";
257
+ case "mp3":
258
+ return "mp3";
259
+ case "aac":
260
+ return "aac";
261
+ case "ogg":
262
+ return "opus";
263
+ case "flac":
264
+ return "flac";
265
+ case "mov":
266
+ return "aac";
170
267
  default:
171
268
  throw new Error(`Unsupported container: ${container}`);
172
269
  }
@@ -179,22 +276,100 @@ var WEB_RENDERER_VIDEO_CODECS = [
179
276
  "av1"
180
277
  ];
181
278
  var getSupportedVideoCodecsForContainer = (container) => {
279
+ if (isAudioOnlyContainer(container)) {
280
+ return [];
281
+ }
182
282
  const format = containerToMediabunnyContainer(container);
183
283
  const allSupported = format.getSupportedVideoCodecs();
184
284
  return WEB_RENDERER_VIDEO_CODECS.filter((codec) => allSupported.includes(codecToMediabunnyCodec(codec)));
185
285
  };
186
- var WEB_RENDERER_AUDIO_CODECS = ["aac", "opus"];
286
+ var WEB_RENDERER_AUDIO_CODECS = [
287
+ "aac",
288
+ "opus",
289
+ "mp3",
290
+ "vorbis",
291
+ "pcm-s16",
292
+ "flac"
293
+ ];
294
+ var audioCodecToMediabunnyAudioCodec = (audioCodec) => {
295
+ switch (audioCodec) {
296
+ case "aac":
297
+ return "aac";
298
+ case "opus":
299
+ return "opus";
300
+ case "mp3":
301
+ return "mp3";
302
+ case "vorbis":
303
+ return "vorbis";
304
+ case "pcm-s16":
305
+ return "pcm-s16";
306
+ case "flac":
307
+ return "flac";
308
+ default:
309
+ throw new Error(`Unsupported audio codec: ${audioCodec}`);
310
+ }
311
+ };
187
312
  var getSupportedAudioCodecsForContainer = (container) => {
188
313
  const format = containerToMediabunnyContainer(container);
189
314
  const allSupported = format.getSupportedAudioCodecs();
190
- return WEB_RENDERER_AUDIO_CODECS.filter((codec) => allSupported.includes(codec));
191
- };
192
- var audioCodecToMediabunnyAudioCodec = (audioCodec) => {
193
- return audioCodec;
315
+ return WEB_RENDERER_AUDIO_CODECS.filter((codec) => allSupported.includes(audioCodecToMediabunnyAudioCodec(codec)));
194
316
  };
195
317
 
196
318
  // src/resolve-audio-codec.ts
319
+ import { canEncodeAudio as canEncodeAudio4 } from "mediabunny";
320
+
321
+ // src/register-aac-encoder.ts
197
322
  import { canEncodeAudio } from "mediabunny";
323
+ var registrationPromise = null;
324
+ var doRegister = async () => {
325
+ const nativeSupport = await canEncodeAudio("aac");
326
+ if (!nativeSupport) {
327
+ const { registerAacEncoder } = await import("@mediabunny/aac-encoder");
328
+ registerAacEncoder();
329
+ }
330
+ };
331
+ var ensureAacEncoderRegistered = () => {
332
+ if (!registrationPromise) {
333
+ registrationPromise = doRegister();
334
+ }
335
+ return registrationPromise;
336
+ };
337
+
338
+ // src/register-flac-encoder.ts
339
+ import { canEncodeAudio as canEncodeAudio2 } from "mediabunny";
340
+ var registrationPromise2 = null;
341
+ var doRegister2 = async () => {
342
+ const nativeSupport = await canEncodeAudio2("flac");
343
+ if (!nativeSupport) {
344
+ const { registerFlacEncoder } = await import("@mediabunny/flac-encoder");
345
+ registerFlacEncoder();
346
+ }
347
+ };
348
+ var ensureFlacEncoderRegistered = () => {
349
+ if (!registrationPromise2) {
350
+ registrationPromise2 = doRegister2();
351
+ }
352
+ return registrationPromise2;
353
+ };
354
+
355
+ // src/register-mp3-encoder.ts
356
+ import { canEncodeAudio as canEncodeAudio3 } from "mediabunny";
357
+ var registrationPromise3 = null;
358
+ var doRegister3 = async () => {
359
+ const nativeSupport = await canEncodeAudio3("mp3");
360
+ if (!nativeSupport) {
361
+ const { registerMp3Encoder } = await import("@mediabunny/mp3-encoder");
362
+ registerMp3Encoder();
363
+ }
364
+ };
365
+ var ensureMp3EncoderRegistered = () => {
366
+ if (!registrationPromise3) {
367
+ registrationPromise3 = doRegister3();
368
+ }
369
+ return registrationPromise3;
370
+ };
371
+
372
+ // src/resolve-audio-codec.ts
198
373
  var resolveAudioCodec = async (options) => {
199
374
  const issues = [];
200
375
  const { container, requestedCodec, userSpecifiedAudioCodec, bitrate } = options;
@@ -209,7 +384,16 @@ var resolveAudioCodec = async (options) => {
209
384
  return { codec: null, issues };
210
385
  }
211
386
  const mediabunnyAudioCodec = audioCodecToMediabunnyAudioCodec(audioCodec);
212
- const canEncode = await canEncodeAudio(mediabunnyAudioCodec, { bitrate });
387
+ if (audioCodec === "mp3") {
388
+ await ensureMp3EncoderRegistered();
389
+ }
390
+ if (audioCodec === "aac") {
391
+ await ensureAacEncoderRegistered();
392
+ }
393
+ if (audioCodec === "flac") {
394
+ await ensureFlacEncoderRegistered();
395
+ }
396
+ const canEncode = await canEncodeAudio4(mediabunnyAudioCodec, { bitrate });
213
397
  if (canEncode) {
214
398
  return { codec: audioCodec, issues };
215
399
  }
@@ -223,8 +407,17 @@ var resolveAudioCodec = async (options) => {
223
407
  }
224
408
  for (const fallbackCodec of supportedAudioCodecs) {
225
409
  if (fallbackCodec !== audioCodec) {
410
+ if (fallbackCodec === "mp3") {
411
+ await ensureMp3EncoderRegistered();
412
+ }
413
+ if (fallbackCodec === "aac") {
414
+ await ensureAacEncoderRegistered();
415
+ }
416
+ if (fallbackCodec === "flac") {
417
+ await ensureFlacEncoderRegistered();
418
+ }
226
419
  const fallbackMediabunnyCodec = audioCodecToMediabunnyAudioCodec(fallbackCodec);
227
- const canEncodeFallback = await canEncodeAudio(fallbackMediabunnyCodec, {
420
+ const canEncodeFallback = await canEncodeAudio4(fallbackMediabunnyCodec, {
228
421
  bitrate
229
422
  });
230
423
  if (canEncodeFallback) {
@@ -263,46 +456,61 @@ var validateDimensions = (options) => {
263
456
  // src/can-render-media-on-web.ts
264
457
  var canRenderMediaOnWeb = async (options) => {
265
458
  const issues = [];
266
- if (typeof VideoEncoder === "undefined") {
267
- issues.push({
268
- type: "webcodecs-unavailable",
269
- message: "WebCodecs API is not available in this browser. A modern browser with WebCodecs support is required.",
270
- severity: "error"
271
- });
272
- }
273
459
  const container = options.container ?? "mp4";
274
- const videoCodec = options.videoCodec ?? getDefaultVideoCodecForContainer(container);
460
+ const videoCodec = options.videoCodec ?? getDefaultVideoCodecForContainer(container) ?? null;
461
+ const videoEnabled = !isAudioOnlyContainer(container);
275
462
  const transparent = options.transparent ?? false;
276
463
  const muted = options.muted ?? false;
277
464
  const { width, height } = options;
278
465
  const resolvedVideoBitrate = typeof options.videoBitrate === "number" ? options.videoBitrate : getQualityForWebRendererQuality(options.videoBitrate ?? "medium");
279
466
  const resolvedAudioBitrate = typeof options.audioBitrate === "number" ? options.audioBitrate : getQualityForWebRendererQuality(options.audioBitrate ?? "medium");
280
- const format = containerToMediabunnyContainer(container);
281
- if (!format.getSupportedCodecs().includes(codecToMediabunnyCodec(videoCodec))) {
282
- issues.push({
283
- type: "container-codec-mismatch",
284
- message: `Codec ${videoCodec} is not supported for container ${container}`,
285
- severity: "error"
286
- });
287
- }
288
- const dimensionIssue = validateDimensions({ width, height, codec: videoCodec });
289
- if (dimensionIssue) {
290
- issues.push(dimensionIssue);
291
- }
292
- const canEncodeVideoResult = await canEncodeVideo(codecToMediabunnyCodec(videoCodec), { bitrate: resolvedVideoBitrate });
293
- if (!canEncodeVideoResult) {
294
- issues.push({
295
- type: "video-codec-unsupported",
296
- message: `Video codec "${videoCodec}" cannot be encoded by this browser`,
297
- severity: "error"
298
- });
299
- }
300
- if (transparent && !["vp8", "vp9"].includes(videoCodec)) {
301
- issues.push({
302
- type: "transparent-video-unsupported",
303
- message: `Transparent video requires VP8 or VP9 codec with WebM container. ${videoCodec} does not support alpha channel.`,
304
- severity: "error"
305
- });
467
+ if (videoEnabled) {
468
+ if (typeof VideoEncoder === "undefined") {
469
+ issues.push({
470
+ type: "webcodecs-unavailable",
471
+ message: "WebCodecs API is not available in this browser. A modern browser with WebCodecs support is required.",
472
+ severity: "error"
473
+ });
474
+ }
475
+ if (!videoCodec) {
476
+ issues.push({
477
+ type: "container-codec-mismatch",
478
+ message: `A video codec is required for container ${container}`,
479
+ severity: "error"
480
+ });
481
+ } else {
482
+ const format = containerToMediabunnyContainer(container);
483
+ if (!format.getSupportedCodecs().includes(codecToMediabunnyCodec(videoCodec))) {
484
+ issues.push({
485
+ type: "container-codec-mismatch",
486
+ message: `Codec ${videoCodec} is not supported for container ${container}`,
487
+ severity: "error"
488
+ });
489
+ }
490
+ const dimensionIssue = validateDimensions({
491
+ width,
492
+ height,
493
+ codec: videoCodec
494
+ });
495
+ if (dimensionIssue) {
496
+ issues.push(dimensionIssue);
497
+ }
498
+ const canEncodeVideoResult = await canEncodeVideo(codecToMediabunnyCodec(videoCodec), { bitrate: resolvedVideoBitrate });
499
+ if (!canEncodeVideoResult) {
500
+ issues.push({
501
+ type: "video-codec-unsupported",
502
+ message: `Video codec "${videoCodec}" cannot be encoded by this browser`,
503
+ severity: "error"
504
+ });
505
+ }
506
+ if (transparent && !["vp8", "vp9"].includes(videoCodec)) {
507
+ issues.push({
508
+ type: "transparent-video-unsupported",
509
+ message: `Transparent video requires VP8 or VP9 codec with WebM container. ${videoCodec} does not support alpha channel.`,
510
+ severity: "error"
511
+ });
512
+ }
513
+ }
306
514
  }
307
515
  let resolvedAudioCodec = null;
308
516
  if (!muted) {
@@ -338,7 +546,7 @@ var canRenderMediaOnWeb = async (options) => {
338
546
  return {
339
547
  canRender: issues.filter((i) => i.severity === "error").length === 0,
340
548
  issues,
341
- resolvedVideoCodec: videoCodec,
549
+ resolvedVideoCodec: videoEnabled ? videoCodec : null,
342
550
  resolvedAudioCodec,
343
551
  resolvedOutputTarget
344
552
  };
@@ -359,6 +567,15 @@ var getEncodableVideoCodecs = async (container, options) => {
359
567
  };
360
568
  var getEncodableAudioCodecs = async (container, options) => {
361
569
  const supported = getSupportedAudioCodecsForContainer(container);
570
+ if (supported.includes("mp3")) {
571
+ await ensureMp3EncoderRegistered();
572
+ }
573
+ if (supported.includes("aac")) {
574
+ await ensureAacEncoderRegistered();
575
+ }
576
+ if (supported.includes("flac")) {
577
+ await ensureFlacEncoderRegistered();
578
+ }
362
579
  const resolvedBitrate = options?.audioBitrate ? typeof options.audioBitrate === "number" ? options.audioBitrate : getQualityForWebRendererQuality(options.audioBitrate) : undefined;
363
580
  const encodable = await mediabunnyGetEncodableAudioCodecs(supported, {
364
581
  bitrate: resolvedBitrate
@@ -629,6 +846,7 @@ var UpdateTime = ({
629
846
  }
630
847
  }));
631
848
  return /* @__PURE__ */ jsx(Internals2.RemotionRootContexts, {
849
+ visualModeEnabled: false,
632
850
  audioEnabled,
633
851
  videoEnabled,
634
852
  logLevel,
@@ -637,13 +855,73 @@ var UpdateTime = ({
637
855
  frameState: {
638
856
  [compId]: frame
639
857
  },
640
- nonceContextSeed: 0,
641
858
  children
642
859
  });
643
860
  };
644
861
 
645
862
  // src/create-scaffold.tsx
646
863
  import { jsx as jsx2 } from "react/jsx-runtime";
864
+ var GENERIC_REACT_RENDER_ERROR_MESSAGES = new Set([
865
+ "Error thrown during rendering"
866
+ ]);
867
+ var isMessageLikeObject = (err) => {
868
+ return typeof err === "object" && err !== null && "message" in err && typeof err.message === "string";
869
+ };
870
+ var unknownErrorToMessage = (err) => {
871
+ if (typeof err === "string") {
872
+ return err;
873
+ }
874
+ if (isMessageLikeObject(err)) {
875
+ return err.message;
876
+ }
877
+ try {
878
+ const serialized = JSON.stringify(err);
879
+ if (serialized) {
880
+ return serialized;
881
+ }
882
+ } catch {}
883
+ return String(err);
884
+ };
885
+ var setErrorCause = (error, cause) => {
886
+ try {
887
+ Object.defineProperty(error, "cause", {
888
+ value: cause,
889
+ enumerable: false,
890
+ configurable: true,
891
+ writable: true
892
+ });
893
+ } catch {}
894
+ };
895
+ var appendComponentStack = (error, componentStack) => {
896
+ if (!componentStack?.trim()) {
897
+ return error;
898
+ }
899
+ const normalizedComponentStack = componentStack.trim();
900
+ const stack = error.stack ?? `${error.name}: ${error.message}`;
901
+ if (stack.includes(normalizedComponentStack)) {
902
+ return error;
903
+ }
904
+ const errorTitle = `${error.name}: ${error.message}`;
905
+ const stackWithoutTitle = stack.startsWith(errorTitle) ? stack.slice(errorTitle.length).trimStart() : stack;
906
+ const stackBody = stackWithoutTitle.length > 0 ? `
907
+ ${stackWithoutTitle}` : "";
908
+ error.stack = `${errorTitle}
909
+ For the likely root cause, see "React component stack:" after the JavaScript stack trace below.${stackBody}
910
+ React component stack:
911
+ ${normalizedComponentStack}`;
912
+ return error;
913
+ };
914
+ var normalizeUncaughtReactError = (err, componentStack) => {
915
+ if (err instanceof Error) {
916
+ const { cause } = err;
917
+ const shouldUnwrapCause = cause instanceof Error && GENERIC_REACT_RENDER_ERROR_MESSAGES.has(err.message);
918
+ const errorToThrow = shouldUnwrapCause ? cause : err;
919
+ return appendComponentStack(errorToThrow, componentStack);
920
+ }
921
+ const normalized = new Error(unknownErrorToMessage(err));
922
+ setErrorCause(normalized, err);
923
+ return appendComponentStack(normalized, componentStack);
924
+ };
647
925
  function checkForError(errorHolder) {
648
926
  if (errorHolder.error) {
649
927
  throw errorHolder.error;
@@ -670,28 +948,31 @@ function createScaffold({
670
948
  if (!ReactDOM.createRoot) {
671
949
  throw new Error("@remotion/web-renderer requires React 18 or higher");
672
950
  }
951
+ const wrapper = document.createElement("div");
952
+ wrapper.style.position = "fixed";
953
+ wrapper.style.inset = "0";
954
+ wrapper.style.overflow = "hidden";
955
+ wrapper.style.visibility = "hidden";
956
+ wrapper.style.pointerEvents = "none";
957
+ wrapper.style.zIndex = "-9999";
673
958
  const div = document.createElement("div");
674
- div.style.position = "fixed";
959
+ div.style.position = "absolute";
960
+ div.style.top = "0";
961
+ div.style.left = "0";
675
962
  div.style.display = "flex";
676
963
  div.style.flexDirection = "column";
677
964
  div.style.backgroundColor = "transparent";
678
965
  div.style.width = `${width}px`;
679
966
  div.style.height = `${height}px`;
680
- div.style.zIndex = "-9999";
681
- div.style.top = "0";
682
- div.style.left = "0";
683
- div.style.right = "0";
684
- div.style.bottom = "0";
685
- div.style.visibility = "hidden";
686
- div.style.pointerEvents = "none";
687
967
  const scaffoldClassName = `remotion-scaffold-${Math.random().toString(36).substring(2, 15)}`;
688
968
  div.className = scaffoldClassName;
689
969
  const cleanupCSS = Internals3.CSSUtils.injectCSS(Internals3.CSSUtils.makeDefaultPreviewCSS(`.${scaffoldClassName}`, "white"));
690
- document.body.appendChild(div);
970
+ wrapper.appendChild(div);
971
+ document.body.appendChild(wrapper);
691
972
  const errorHolder = { error: null };
692
973
  const root = ReactDOM.createRoot(div, {
693
- onUncaughtError: (err) => {
694
- errorHolder.error = err instanceof Error ? err : new Error(String(err));
974
+ onUncaughtError: (err, errorInfo) => {
975
+ errorHolder.error = normalizeUncaughtReactError(err, errorInfo?.componentStack);
695
976
  }
696
977
  });
697
978
  const delayRenderScope = {
@@ -722,7 +1003,7 @@ function createScaffold({
722
1003
  {
723
1004
  id,
724
1005
  component: Component,
725
- nonce: 0,
1006
+ nonce: [[0, 0]],
726
1007
  defaultProps: {},
727
1008
  folderName: null,
728
1009
  parentFolderName: null,
@@ -781,6 +1062,7 @@ function createScaffold({
781
1062
  [Symbol.dispose]: () => {
782
1063
  root.unmount();
783
1064
  div.remove();
1065
+ wrapper.remove();
784
1066
  cleanupCSS();
785
1067
  },
786
1068
  timeUpdater,
@@ -3815,7 +4097,8 @@ var internalRenderMediaOnWeb = async ({
3815
4097
  await cleanupStaleOpfsFiles();
3816
4098
  }
3817
4099
  const format = containerToMediabunnyContainer(container);
3818
- if (codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
4100
+ const videoEnabled = !isAudioOnlyContainer(container);
4101
+ if (videoEnabled && codec && !format.getSupportedCodecs().includes(codecToMediabunnyCodec(codec))) {
3819
4102
  return Promise.reject(new Error(`Codec ${codec} is not supported for container ${container}`));
3820
4103
  }
3821
4104
  const resolvedAudioBitrate = typeof audioBitrate === "number" ? audioBitrate : getQualityForWebRendererQuality(audioBitrate);
@@ -3863,7 +4146,7 @@ var internalRenderMediaOnWeb = async ({
3863
4146
  mediaCacheSizeInBytes,
3864
4147
  schema: schema ?? null,
3865
4148
  audioEnabled: !muted,
3866
- videoEnabled: true,
4149
+ videoEnabled,
3867
4150
  initialFrame: 0,
3868
4151
  defaultCodec: resolved.defaultCodec,
3869
4152
  defaultOutName: resolved.defaultOutName
@@ -3904,7 +4187,7 @@ var internalRenderMediaOnWeb = async ({
3904
4187
  if (signal?.aborted) {
3905
4188
  throw new Error("renderMediaOnWeb() was cancelled");
3906
4189
  }
3907
- const videoSampleSource = __using(__stack, makeVideoSampleSourceCleanup({
4190
+ const videoSampleSource = __using(__stack, videoEnabled && codec ? makeVideoSampleSourceCleanup({
3908
4191
  codec: codecToMediabunnyCodec(codec),
3909
4192
  bitrate: typeof videoBitrate === "number" ? videoBitrate : getQualityForWebRendererQuality(videoBitrate),
3910
4193
  sizeChangeBehavior: "deny",
@@ -3912,15 +4195,23 @@ var internalRenderMediaOnWeb = async ({
3912
4195
  latencyMode: "quality",
3913
4196
  keyFrameInterval: keyframeIntervalInSeconds,
3914
4197
  alpha: transparent ? "keep" : "discard"
3915
- }), 0);
3916
- outputWithCleanup.output.addVideoTrack(videoSampleSource.videoSampleSource);
4198
+ }) : null, 0);
4199
+ const totalFrames = realFrameRange[1] - realFrameRange[0] + 1;
4200
+ const durationInSeconds = totalFrames / resolved.fps;
4201
+ if (videoSampleSource) {
4202
+ outputWithCleanup.output.addVideoTrack(videoSampleSource.videoSampleSource, {
4203
+ maximumPacketCount: Math.ceil(totalFrames * 1.33)
4204
+ });
4205
+ }
3917
4206
  const audioSampleSource = __using(__stack, createAudioSampleSource({
3918
4207
  muted,
3919
4208
  codec: finalAudioCodec ? audioCodecToMediabunnyAudioCodec(finalAudioCodec) : null,
3920
4209
  bitrate: resolvedAudioBitrate
3921
4210
  }), 0);
3922
4211
  if (audioSampleSource) {
3923
- outputWithCleanup.output.addAudioTrack(audioSampleSource.audioSampleSource);
4212
+ outputWithCleanup.output.addAudioTrack(audioSampleSource.audioSampleSource, {
4213
+ maximumPacketCount: Math.ceil(durationInSeconds * 100 * 1.33)
4214
+ });
3924
4215
  }
3925
4216
  await outputWithCleanup.output.start();
3926
4217
  if (signal?.aborted) {
@@ -3947,44 +4238,49 @@ var internalRenderMediaOnWeb = async ({
3947
4238
  if (signal?.aborted) {
3948
4239
  throw new Error("renderMediaOnWeb() was cancelled");
3949
4240
  }
3950
- const createFrameStart = performance.now();
3951
- const layer = await createLayer({
3952
- element: div,
3953
- scale,
3954
- logLevel,
3955
- internalState,
3956
- onlyBackgroundClipText: false,
3957
- cutout: new DOMRect(0, 0, resolved.width, resolved.height)
3958
- });
3959
- internalState.addCreateFrameTime(performance.now() - createFrameStart);
3960
- if (signal?.aborted) {
3961
- throw new Error("renderMediaOnWeb() was cancelled");
3962
- }
3963
4241
  const timestamp = Math.round((frame - realFrameRange[0]) / resolved.fps * 1e6);
3964
- const videoFrame = new VideoFrame(layer.canvas, {
3965
- timestamp
3966
- });
3967
- progress.renderedFrames++;
3968
- throttledOnProgress?.({ ...progress });
3969
- let frameToEncode = videoFrame;
3970
- if (onFrame) {
3971
- const returnedFrame = await onFrame(videoFrame);
4242
+ let frameToEncode = null;
4243
+ let layerCanvas = null;
4244
+ if (videoEnabled) {
4245
+ const createFrameStart = performance.now();
4246
+ const layer = await createLayer({
4247
+ element: div,
4248
+ scale,
4249
+ logLevel,
4250
+ internalState,
4251
+ onlyBackgroundClipText: false,
4252
+ cutout: new DOMRect(0, 0, resolved.width, resolved.height)
4253
+ });
4254
+ internalState.addCreateFrameTime(performance.now() - createFrameStart);
4255
+ layerCanvas = layer.canvas;
3972
4256
  if (signal?.aborted) {
3973
4257
  throw new Error("renderMediaOnWeb() was cancelled");
3974
4258
  }
3975
- frameToEncode = validateVideoFrame({
3976
- originalFrame: videoFrame,
3977
- returnedFrame,
3978
- expectedWidth: Math.round(resolved.width * scale),
3979
- expectedHeight: Math.round(resolved.height * scale),
3980
- expectedTimestamp: timestamp
4259
+ const videoFrame = new VideoFrame(layer.canvas, {
4260
+ timestamp
3981
4261
  });
4262
+ frameToEncode = videoFrame;
4263
+ if (onFrame) {
4264
+ const returnedFrame = await onFrame(videoFrame);
4265
+ if (signal?.aborted) {
4266
+ throw new Error("renderMediaOnWeb() was cancelled");
4267
+ }
4268
+ frameToEncode = validateVideoFrame({
4269
+ originalFrame: videoFrame,
4270
+ returnedFrame,
4271
+ expectedWidth: Math.round(resolved.width * scale),
4272
+ expectedHeight: Math.round(resolved.height * scale),
4273
+ expectedTimestamp: timestamp
4274
+ });
4275
+ }
3982
4276
  }
4277
+ progress.renderedFrames++;
4278
+ throttledOnProgress?.({ ...progress });
3983
4279
  const audioCombineStart = performance.now();
3984
4280
  const assets = collectAssets.current.collectAssets();
3985
4281
  if (onArtifact) {
3986
4282
  await artifactsHandler.handle({
3987
- imageData: layer.canvas,
4283
+ imageData: layerCanvas,
3988
4284
  frame,
3989
4285
  assets,
3990
4286
  onArtifact
@@ -3996,10 +4292,14 @@ var internalRenderMediaOnWeb = async ({
3996
4292
  const audio = muted ? null : onlyInlineAudio({ assets, fps: resolved.fps, timestamp });
3997
4293
  internalState.addAudioMixingTime(performance.now() - audioCombineStart);
3998
4294
  const addSampleStart = performance.now();
3999
- await Promise.all([
4000
- addVideoSampleAndCloseFrame(frameToEncode, videoSampleSource.videoSampleSource),
4001
- audio && audioSampleSource ? addAudioSample(audio, audioSampleSource.audioSampleSource) : Promise.resolve()
4002
- ]);
4295
+ const encodingPromises = [];
4296
+ if (frameToEncode && videoSampleSource) {
4297
+ encodingPromises.push(addVideoSampleAndCloseFrame(frameToEncode, videoSampleSource.videoSampleSource));
4298
+ }
4299
+ if (audio && audioSampleSource) {
4300
+ encodingPromises.push(addAudioSample(audio, audioSampleSource.audioSampleSource));
4301
+ }
4302
+ await Promise.all(encodingPromises);
4003
4303
  internalState.addAddSampleTime(performance.now() - addSampleStart);
4004
4304
  progress.encodedFrames++;
4005
4305
  throttledOnProgress?.({ ...progress });
@@ -4008,7 +4308,7 @@ var internalRenderMediaOnWeb = async ({
4008
4308
  }
4009
4309
  }
4010
4310
  onProgress?.({ ...progress });
4011
- videoSampleSource.videoSampleSource.close();
4311
+ videoSampleSource?.videoSampleSource.close();
4012
4312
  audioSampleSource?.audioSampleSource.close();
4013
4313
  await outputWithCleanup.output.finalize();
4014
4314
  Internals8.Log.verbose({ logLevel, tag: "web-renderer" }, `Render timings: waitForReady=${internalState.getWaitForReadyTime().toFixed(2)}ms, createFrame=${internalState.getCreateFrameTime().toFixed(2)}ms, addSample=${internalState.getAddSampleTime().toFixed(2)}ms, audioMixing=${internalState.getAudioMixingTime().toFixed(2)}ms`);
@@ -4022,8 +4322,9 @@ var internalRenderMediaOnWeb = async ({
4022
4322
  });
4023
4323
  await webFsTarget.close();
4024
4324
  return {
4025
- getBlob: () => {
4026
- return webFsTarget.getBlob();
4325
+ getBlob: async () => {
4326
+ const file = await webFsTarget.getBlob();
4327
+ return new Blob([file], { type: getMimeType(container) });
4027
4328
  },
4028
4329
  internalState
4029
4330
  };
@@ -4074,7 +4375,7 @@ var internalRenderMediaOnWeb = async ({
4074
4375
  };
4075
4376
  var renderMediaOnWeb = (options) => {
4076
4377
  const container = options.container ?? "mp4";
4077
- const codec = options.videoCodec ?? getDefaultVideoCodecForContainer(container);
4378
+ const codec = options.videoCodec ?? getDefaultVideoCodecForContainer(container) ?? null;
4078
4379
  onlyOneRenderAtATimeQueue.ref = onlyOneRenderAtATimeQueue.ref.catch(() => Promise.resolve()).then(() => internalRenderMediaOnWeb({
4079
4380
  ...options,
4080
4381
  delayRenderTimeoutInMilliseconds: options.delayRenderTimeoutInMilliseconds ?? 30000,
@@ -4236,11 +4537,13 @@ var renderStillOnWeb = (options) => {
4236
4537
  export {
4237
4538
  renderStillOnWeb,
4238
4539
  renderMediaOnWeb,
4540
+ isAudioOnlyContainer,
4239
4541
  getSupportedVideoCodecsForContainer,
4240
4542
  getSupportedAudioCodecsForContainer,
4241
4543
  getEncodableVideoCodecs,
4242
4544
  getEncodableAudioCodecs,
4243
4545
  getDefaultVideoCodecForContainer,
4546
+ getDefaultContainerForCodec,
4244
4547
  getDefaultAudioCodecForContainer,
4245
4548
  canRenderMediaOnWeb
4246
4549
  };