@remotion/renderer 4.0.529 → 4.0.531

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.
@@ -18238,7 +18238,9 @@ async function puppeteerEvaluateWithCatch({
18238
18238
  `)),
18239
18239
  chunk: null
18240
18240
  });
18241
- page.close();
18241
+ page.close().catch(() => {
18242
+ return;
18243
+ });
18242
18244
  throw err;
18243
18245
  }
18244
18246
  return { size, value: valueFromRemoteObject2(remoteObject) };
@@ -19270,7 +19272,8 @@ var getExtensionOfFilename = (filename) => {
19270
19272
  if (filename === null) {
19271
19273
  return null;
19272
19274
  }
19273
- const filenameArr = pathNormalize(filename).split(".");
19275
+ const segments = pathNormalize(filename).split(/[/\\]/);
19276
+ const filenameArr = segments[segments.length - 1].split(".");
19274
19277
  const hasExtension = filenameArr.length >= 2;
19275
19278
  const filenameArrLength = filenameArr.length;
19276
19279
  const extension = hasExtension ? filenameArr[filenameArrLength - 1] : null;
@@ -21640,6 +21643,9 @@ var validateQualitySettings = ({
21640
21643
  if (encodingMaxRate && !encodingBufferSize) {
21641
21644
  throw new Error('"encodingMaxRate" can not be set without also setting "encodingBufferSize".');
21642
21645
  }
21646
+ if ((encodingMaxRate || encodingBufferSize) && !videoBitrate && (codec === "vp8" || codec === "vp9" || codec === "av1")) {
21647
+ throw new Error(`The ${codec} codec needs "videoBitrate" when "encodingMaxRate" or "encodingBufferSize" is set.`);
21648
+ }
21643
21649
  const bufSizeArray = encodingBufferSize ? ["-bufsize", encodingBufferSize] : [];
21644
21650
  const maxRateArray = encodingMaxRate ? ["-maxrate", encodingMaxRate] : [];
21645
21651
  if (videoBitrate) {
@@ -21960,7 +21966,9 @@ var firstEncodingStepOnly = ({
21960
21966
  encodingMaxRate,
21961
21967
  encodingBufferSize,
21962
21968
  hardwareAcceleration,
21963
- hardwareAccelerated
21969
+ hardwareAccelerated,
21970
+ cpuCount,
21971
+ lambdaMemoryInBytes
21964
21972
  }) => {
21965
21973
  if (hasPreencoded || codec === "gif") {
21966
21974
  return [];
@@ -21970,6 +21978,7 @@ var firstEncodingStepOnly = ({
21970
21978
  proResProfileName ? ["-profile:v", proResProfileName] : null,
21971
21979
  ["-pix_fmt", pixelFormat],
21972
21980
  pixelFormat === "yuva420p" ? ["-auto-alt-ref", "0"] : null,
21981
+ codec === "vp9" && (cpuCount > 2 || lambdaMemoryInBytes !== null && lambdaMemoryInBytes >= 2048 * 1024 * 1024) ? ["-row-mt", "1"] : null,
21973
21982
  x264Preset ? ["-preset", x264Preset] : null,
21974
21983
  gopSize === null ? null : ["-g", String(gopSize)],
21975
21984
  ["-video_track_timescale", "90000"],
@@ -21998,7 +22007,9 @@ var generateFfmpegArgs = ({
21998
22007
  colorSpace,
21999
22008
  hardwareAcceleration,
22000
22009
  indent,
22001
- logLevel
22010
+ logLevel,
22011
+ cpuCount,
22012
+ lambdaMemoryInBytes
22002
22013
  }) => {
22003
22014
  const encoderSettings = getCodecName({
22004
22015
  codec,
@@ -22054,7 +22065,9 @@ var generateFfmpegArgs = ({
22054
22065
  x264Preset,
22055
22066
  gopSize,
22056
22067
  hardwareAcceleration,
22057
- hardwareAccelerated
22068
+ hardwareAccelerated,
22069
+ cpuCount,
22070
+ lambdaMemoryInBytes
22058
22071
  })
22059
22072
  ].filter(truthy);
22060
22073
  };
@@ -22264,7 +22277,9 @@ var prespawnFfmpeg = (options2) => {
22264
22277
  colorSpace: options2.colorSpace,
22265
22278
  hardwareAcceleration: resolvedHardwareAcceleration,
22266
22279
  indent: options2.indent,
22267
- logLevel: options2.logLevel
22280
+ logLevel: options2.logLevel,
22281
+ cpuCount: getCpuCount(),
22282
+ lambdaMemoryInBytes: getMaxLambdaMemory()
22268
22283
  }),
22269
22284
  "-y",
22270
22285
  options2.outputLocation
@@ -22376,6 +22391,24 @@ var validateSelectedCodecAndProResCombination = ({
22376
22391
  import { cpSync as cpSync2, promises as promises5, rmSync as rmSync4 } from "node:fs";
22377
22392
  import path29 from "node:path";
22378
22393
 
22394
+ // src/aac-priming.ts
22395
+ var LIBFDK_AAC_LC_ENCODER_DELAY_IN_SAMPLES = 2048;
22396
+ var getAacPrimingInputArgs = ({
22397
+ audioCodec,
22398
+ sampleRate,
22399
+ outputExtension
22400
+ }) => {
22401
+ if (audioCodec !== "aac") {
22402
+ return [];
22403
+ }
22404
+ const extension = outputExtension?.toLowerCase();
22405
+ if (extension !== "mp4" && extension !== "mov") {
22406
+ return [];
22407
+ }
22408
+ const delayInMicroseconds = Math.round(LIBFDK_AAC_LC_ENCODER_DELAY_IN_SAMPLES / sampleRate * 1e6);
22409
+ return ["-itsoffset", `-${delayInMicroseconds}us`];
22410
+ };
22411
+
22379
22412
  // src/convert-number-of-gif-loops-to-ffmpeg.ts
22380
22413
  var convertNumberOfGifLoopsToFfmpegSyntax = (loops) => {
22381
22414
  if (loops === null) {
@@ -23756,7 +23789,17 @@ var innerStitchFramesToVideo = async ({
23756
23789
  ["-i", assetsInfo.imageSequenceName],
23757
23790
  codec === "gif" ? ["-filter_complex", "split[v],palettegen,[v]paletteuse"] : null
23758
23791
  ],
23759
- audio && !separateAudioTo ? ["-i", audio, "-c:a", "copy"] : ["-an"],
23792
+ audio && !separateAudioTo ? [
23793
+ ...getAacPrimingInputArgs({
23794
+ audioCodec: resolvedAudioCodec,
23795
+ sampleRate,
23796
+ outputExtension
23797
+ }),
23798
+ "-i",
23799
+ audio,
23800
+ "-c:a",
23801
+ "copy"
23802
+ ] : ["-an"],
23760
23803
  numberOfGifLoops === null ? null : ["-loop", convertNumberOfGifLoopsToFfmpegSyntax(numberOfGifLoops)],
23761
23804
  ...generateFfmpegArgs({
23762
23805
  codec,
@@ -23772,7 +23815,9 @@ var innerStitchFramesToVideo = async ({
23772
23815
  colorSpace,
23773
23816
  hardwareAcceleration: resolvedHardwareAcceleration,
23774
23817
  indent,
23775
- logLevel
23818
+ logLevel,
23819
+ cpuCount: getCpuCount(),
23820
+ lambdaMemoryInBytes: getMaxLambdaMemory()
23776
23821
  }),
23777
23822
  ["-map_metadata", "-1"],
23778
23823
  ...makeMetadataArgs(metadata ?? {}),
@@ -24400,12 +24445,12 @@ var internalRenderMediaRaw = ({
24400
24445
  let timeOfLastFrame = Date.now();
24401
24446
  const renderFramesProc = internalRenderFrames({
24402
24447
  composition,
24403
- onFrameUpdate: (frame, frameIndex) => {
24448
+ onFrameUpdate: (frame, frameIndex, timeToRenderInMilliseconds) => {
24404
24449
  renderedFrames = frame;
24405
24450
  const now = Date.now();
24406
- const timeToRenderInMilliseconds = now - timeOfLastFrame;
24451
+ const timeSinceLastFrame = now - timeOfLastFrame;
24407
24452
  timeOfLastFrame = now;
24408
- recentFrameTimings.push(timeToRenderInMilliseconds);
24453
+ recentFrameTimings.push(timeSinceLastFrame);
24409
24454
  if (recentFrameTimings.length > MAX_RECENT_FRAME_TIMINGS) {
24410
24455
  recentFrameTimings.shift();
24411
24456
  }
@@ -25619,7 +25664,9 @@ var muxVideoAndAudio = async ({
25619
25664
  fps,
25620
25665
  cancelSignal,
25621
25666
  metadata,
25622
- numberOfGifLoops
25667
+ numberOfGifLoops,
25668
+ audioCodec,
25669
+ sampleRate
25623
25670
  }) => {
25624
25671
  const startTime = Date.now();
25625
25672
  Log.verbose({ indent, logLevel }, "Muxing video and audio together");
@@ -25629,6 +25676,11 @@ var muxVideoAndAudio = async ({
25629
25676
  "-hide_banner",
25630
25677
  videoOutput ? "-i" : null,
25631
25678
  videoOutput,
25679
+ ...audioOutput ? getAacPrimingInputArgs({
25680
+ audioCodec,
25681
+ sampleRate,
25682
+ outputExtension
25683
+ }) : [],
25632
25684
  audioOutput ? "-i" : null,
25633
25685
  audioOutput,
25634
25686
  videoOutput ? "-c:v" : null,
@@ -25777,7 +25829,9 @@ var internalCombineChunks = async ({
25777
25829
  fps,
25778
25830
  cancelSignal,
25779
25831
  metadata,
25780
- numberOfGifLoops
25832
+ numberOfGifLoops,
25833
+ audioCodec: resolvedAudioCodec,
25834
+ sampleRate
25781
25835
  });
25782
25836
  onProgress({ totalProgress: 1, frames: numberOfFrames });
25783
25837
  rmSync6(filelistDir, { recursive: true });
package/dist/esm/pure.mjs CHANGED
@@ -303,7 +303,8 @@ var getExtensionOfFilename = (filename) => {
303
303
  if (filename === null) {
304
304
  return null;
305
305
  }
306
- const filenameArr = pathNormalize(filename).split(".");
306
+ const segments = pathNormalize(filename).split(/[/\\]/);
307
+ const filenameArr = segments[segments.length - 1].split(".");
307
308
  const hasExtension = filenameArr.length >= 2;
308
309
  const filenameArrLength = filenameArr.length;
309
310
  const extension = hasExtension ? filenameArr[filenameArrLength - 1] : null;
@@ -1,4 +1,4 @@
1
- export declare const generateFfmpegArgs: ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, colorSpace, hardwareAcceleration, indent, logLevel, }: {
1
+ export declare const generateFfmpegArgs: ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, colorSpace, hardwareAcceleration, indent, logLevel, cpuCount, lambdaMemoryInBytes, }: {
2
2
  hasPreencoded: boolean;
3
3
  proResProfileName: string | null;
4
4
  pixelFormat: "yuv420p" | "yuv420p10le" | "yuv422p" | "yuv422p10le" | "yuv444p" | "yuv444p10le" | "yuva420p" | "yuva444p10le";
@@ -13,4 +13,6 @@ export declare const generateFfmpegArgs: ({ hasPreencoded, proResProfileName, pi
13
13
  hardwareAcceleration: "disable" | "if-possible" | "required";
14
14
  indent: boolean;
15
15
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
16
+ cpuCount: number;
17
+ lambdaMemoryInBytes: number | null;
16
18
  }) => string[][];
@@ -7,7 +7,7 @@ const logger_1 = require("./logger");
7
7
  const color_space_1 = require("./options/color-space");
8
8
  const gop_size_1 = require("./options/gop-size");
9
9
  const truthy_1 = require("./truthy");
10
- const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, hardwareAccelerated, }) => {
10
+ const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, hardwareAcceleration, hardwareAccelerated, cpuCount, lambdaMemoryInBytes, }) => {
11
11
  if (hasPreencoded || codec === 'gif') {
12
12
  return [];
13
13
  }
@@ -18,6 +18,16 @@ const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat,
18
18
  // Without explicitly disabling auto-alt-ref,
19
19
  // transparent WebM generation doesn't work
20
20
  pixelFormat === 'yuva420p' ? ['-auto-alt-ref', '0'] : null,
21
+ // Without row-based multithreading, libvpx only splits VP9 work by
22
+ // tile columns (4 at 1080p), which leaves most cores idle. It is slower
23
+ // with 2 CPUs outside Lambda. On Lambda, it is faster with the default
24
+ // 2048 MB allocation even though only 2 CPUs are reported.
25
+ codec === 'vp9' &&
26
+ (cpuCount > 2 ||
27
+ (lambdaMemoryInBytes !== null &&
28
+ lambdaMemoryInBytes >= 2048 * 1024 * 1024))
29
+ ? ['-row-mt', '1']
30
+ : null,
21
31
  x264Preset ? ['-preset', x264Preset] : null,
22
32
  gopSize === null ? null : ['-g', String(gopSize)],
23
33
  // Apply a fixed a timescale across all environments:
@@ -34,7 +44,7 @@ const firstEncodingStepOnly = ({ hasPreencoded, proResProfileName, pixelFormat,
34
44
  }),
35
45
  ].filter(truthy_1.truthy);
36
46
  };
37
- const generateFfmpegArgs = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, colorSpace, hardwareAcceleration, indent, logLevel, }) => {
47
+ const generateFfmpegArgs = ({ hasPreencoded, proResProfileName, pixelFormat, x264Preset, gopSize, codec, crf, videoBitrate, encodingMaxRate, encodingBufferSize, colorSpace, hardwareAcceleration, indent, logLevel, cpuCount, lambdaMemoryInBytes, }) => {
38
48
  const encoderSettings = (0, get_codec_name_1.getCodecName)({
39
49
  codec,
40
50
  encodingMaxRate,
@@ -103,6 +113,8 @@ const generateFfmpegArgs = ({ hasPreencoded, proResProfileName, pixelFormat, x26
103
113
  gopSize,
104
114
  hardwareAcceleration,
105
115
  hardwareAccelerated,
116
+ cpuCount,
117
+ lambdaMemoryInBytes,
106
118
  }),
107
119
  ].filter(truthy_1.truthy);
108
120
  };
@@ -6,7 +6,10 @@ const getExtensionOfFilename = (filename) => {
6
6
  if (filename === null) {
7
7
  return null;
8
8
  }
9
- const filenameArr = (0, path_normalize_1.pathNormalize)(filename).split('.');
9
+ // Only the last path segment can have an extension: the dots in
10
+ // `my.project/frames` or `../frames` belong to folders.
11
+ const segments = (0, path_normalize_1.pathNormalize)(filename).split(/[/\\]/);
12
+ const filenameArr = segments[segments.length - 1].split('.');
10
13
  const hasExtension = filenameArr.length >= 2;
11
14
  const filenameArrLength = filenameArr.length;
12
15
  const extension = hasExtension ? filenameArr[filenameArrLength - 1] : null;
@@ -1,5 +1,5 @@
1
1
  import type { CancelSignal } from './make-cancel-signal';
2
- export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, }: {
2
+ export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, audioCodec, sampleRate, }: {
3
3
  videoOutput: string | null;
4
4
  audioOutput: string | null;
5
5
  output: string;
@@ -11,4 +11,6 @@ export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, inde
11
11
  cancelSignal: CancelSignal | undefined;
12
12
  metadata?: Record<string, string> | null | undefined;
13
13
  numberOfGifLoops: number | null;
14
+ audioCodec: "aac" | "mp3" | "opus" | "pcm-16" | null;
15
+ sampleRate: number;
14
16
  }) => Promise<void>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.muxVideoAndAudio = void 0;
4
+ const aac_priming_1 = require("./aac-priming");
4
5
  const call_ffmpeg_1 = require("./call-ffmpeg");
5
6
  const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif-loops-to-ffmpeg");
6
7
  const get_extension_of_filename_1 = require("./get-extension-of-filename");
@@ -9,7 +10,7 @@ const logger_1 = require("./logger");
9
10
  const make_metadata_args_1 = require("./make-metadata-args");
10
11
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
11
12
  const truthy_1 = require("./truthy");
12
- const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, }) => {
13
+ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, audioCodec, sampleRate, }) => {
13
14
  var _a;
14
15
  const startTime = Date.now();
15
16
  logger_1.Log.verbose({ indent, logLevel }, 'Muxing video and audio together');
@@ -21,6 +22,13 @@ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logL
21
22
  '-hide_banner',
22
23
  videoOutput ? '-i' : null,
23
24
  videoOutput,
25
+ ...(audioOutput
26
+ ? (0, aac_priming_1.getAacPrimingInputArgs)({
27
+ audioCodec,
28
+ sampleRate,
29
+ outputExtension,
30
+ })
31
+ : []),
24
32
  audioOutput ? '-i' : null,
25
33
  audioOutput,
26
34
  videoOutput ? '-c:v' : null,
@@ -0,0 +1,18 @@
1
+ export declare const canvasTabsOption: {
2
+ name: string;
3
+ cliFlag: "disable-canvas-tabs";
4
+ description: () => import("react/jsx-runtime").JSX.Element;
5
+ ssrName: null;
6
+ docLink: string;
7
+ type: boolean;
8
+ getValue: ({ commandLine }: {
9
+ commandLine: Record<string, unknown>;
10
+ }) => {
11
+ value: boolean;
12
+ source: string;
13
+ };
14
+ setConfig(value: boolean): void;
15
+ getConfigValue: () => boolean | null;
16
+ reset: () => void;
17
+ id: "disable-canvas-tabs";
18
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.canvasTabsOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ let canvasTabsEnabled = true;
6
+ let configuredCanvasTabsEnabled = null;
7
+ const cliFlag = 'disable-canvas-tabs';
8
+ exports.canvasTabsOption = {
9
+ name: 'Disable or enable Studio canvas tabs',
10
+ cliFlag,
11
+ description: () => jsx_runtime_1.jsx(jsx_runtime_1.Fragment, { children: "Enable or disable the tabs above the Studio canvas." }),
12
+ ssrName: null,
13
+ docLink: 'https://www.remotion.dev/docs/config#setcanvastabsenabled',
14
+ type: false,
15
+ getValue: ({ commandLine }) => {
16
+ if (commandLine[cliFlag] !== undefined && commandLine[cliFlag] !== null) {
17
+ canvasTabsEnabled = commandLine[cliFlag] === false;
18
+ return {
19
+ value: canvasTabsEnabled,
20
+ source: 'cli',
21
+ };
22
+ }
23
+ return {
24
+ value: canvasTabsEnabled,
25
+ source: 'config',
26
+ };
27
+ },
28
+ setConfig(value) {
29
+ canvasTabsEnabled = value;
30
+ configuredCanvasTabsEnabled = value;
31
+ },
32
+ getConfigValue: () => configuredCanvasTabsEnabled,
33
+ reset: () => {
34
+ canvasTabsEnabled = true;
35
+ configuredCanvasTabsEnabled = null;
36
+ },
37
+ id: cliFlag,
38
+ };
@@ -15,7 +15,7 @@ exports.encodingBufferSizeOption = {
15
15
  cliFlag,
16
16
  description: () => (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: ["The value for the ",
17
17
  jsx_runtime_1.jsx("code", { children: "-bufsize" }),
18
- " flag of FFmpeg. Should be used in conjunction with the encoding max rate flag."] })),
18
+ " flag of FFmpeg. Should be used in conjunction with the encoding max rate flag. With the VP8, VP9 and AV1 codecs, a video bitrate must also be set."] })),
19
19
  ssrName: 'encodingBufferSize',
20
20
  docLink: 'https://www.remotion.dev/docs/renderer/render-media#encodingbuffersize',
21
21
  type: '',
@@ -9,7 +9,7 @@ exports.encodingMaxRateOption = {
9
9
  cliFlag,
10
10
  description: () => (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: ["The value for the ",
11
11
  jsx_runtime_1.jsx("code", { children: "-maxrate" }),
12
- " flag of FFmpeg. Should be used in conjunction with the encoding buffer size flag."] })),
12
+ " flag of FFmpeg. Should be used in conjunction with the encoding buffer size flag. With the VP8, VP9 and AV1 codecs, a video bitrate must also be set."] })),
13
13
  ssrName: 'encodingMaxRate',
14
14
  docLink: 'https://www.remotion.dev/docs/renderer/render-media#encodingmaxrate',
15
15
  type: '',
@@ -0,0 +1,16 @@
1
+ export declare const experimentalClientSideRenderingOption: {
2
+ name: string;
3
+ cliFlag: "enable-experimental-client-side-rendering";
4
+ description: () => import("react/jsx-runtime").JSX.Element;
5
+ ssrName: null;
6
+ docLink: string;
7
+ type: boolean;
8
+ getValue: ({ commandLine }: {
9
+ commandLine: Record<string, unknown>;
10
+ }) => {
11
+ value: boolean;
12
+ source: string;
13
+ };
14
+ setConfig(value: boolean): void;
15
+ id: "enable-experimental-client-side-rendering";
16
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.experimentalClientSideRenderingOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ let experimentalClientSideRenderingEnabled = false;
6
+ const cliFlag = 'enable-experimental-client-side-rendering';
7
+ exports.experimentalClientSideRenderingOption = {
8
+ name: 'Enable Experimental Client-Side Rendering',
9
+ cliFlag,
10
+ description: () => (jsx_runtime_1.jsx(jsx_runtime_1.Fragment, { children: "Enable WIP client-side rendering in the Remotion Studio. See https://www.remotion.dev/docs/client-side-rendering/ for notes." })),
11
+ ssrName: null,
12
+ docLink: 'https://www.remotion.dev/docs/client-side-rendering',
13
+ type: false,
14
+ getValue: ({ commandLine }) => {
15
+ if (commandLine[cliFlag] !== null) {
16
+ return {
17
+ value: commandLine[cliFlag],
18
+ source: 'cli',
19
+ };
20
+ }
21
+ return {
22
+ value: experimentalClientSideRenderingEnabled,
23
+ source: 'config',
24
+ };
25
+ },
26
+ setConfig(value) {
27
+ experimentalClientSideRenderingEnabled = value;
28
+ },
29
+ id: cliFlag,
30
+ };
@@ -1190,6 +1190,24 @@ export declare const allOptions: {
1190
1190
  reset: () => void;
1191
1191
  id: "disable-keyboard-shortcuts";
1192
1192
  };
1193
+ canvasTabsOption: {
1194
+ name: string;
1195
+ cliFlag: "disable-canvas-tabs";
1196
+ description: () => import("react/jsx-runtime").JSX.Element;
1197
+ ssrName: null;
1198
+ docLink: string;
1199
+ type: boolean;
1200
+ getValue: ({ commandLine }: {
1201
+ commandLine: Record<string, unknown>;
1202
+ }) => {
1203
+ value: boolean;
1204
+ source: string;
1205
+ };
1206
+ setConfig(value: boolean): void;
1207
+ getConfigValue: () => boolean | null;
1208
+ reset: () => void;
1209
+ id: "disable-canvas-tabs";
1210
+ };
1193
1211
  framesOption: {
1194
1212
  name: string;
1195
1213
  cliFlag: "frames";
@@ -12,6 +12,7 @@ const binaries_directory_1 = require("./binaries-directory");
12
12
  const browser_1 = require("./browser");
13
13
  const browser_executable_1 = require("./browser-executable");
14
14
  const bundle_cache_1 = require("./bundle-cache");
15
+ const canvas_tabs_1 = require("./canvas-tabs");
15
16
  const chrome_mode_1 = require("./chrome-mode");
16
17
  const color_space_1 = require("./color-space");
17
18
  const concurrency_1 = require("./concurrency");
@@ -167,6 +168,7 @@ exports.allOptions = {
167
168
  askAIOption: ask_ai_1.askAIOption,
168
169
  interactivityOption: interactivity_1.interactivityOption,
169
170
  keyboardShortcutsOption: keyboard_shortcuts_1.keyboardShortcutsOption,
171
+ canvasTabsOption: canvas_tabs_1.canvasTabsOption,
170
172
  framesOption: frames_1.framesOption,
171
173
  forceNewStudioOption: force_new_studio_1.forceNewStudioOption,
172
174
  experimentalKeepAudioContextAliveOption: experimental_keep_audio_context_alive_1.experimentalKeepAudioContextAliveOption,
@@ -4,8 +4,10 @@ exports.prespawnFfmpeg = void 0;
4
4
  const call_ffmpeg_1 = require("./call-ffmpeg");
5
5
  const codec_1 = require("./codec");
6
6
  const ffmpeg_args_1 = require("./ffmpeg-args");
7
+ const get_cpu_count_1 = require("./get-cpu-count");
7
8
  const get_prores_profile_name_1 = require("./get-prores-profile-name");
8
9
  const logger_1 = require("./logger");
10
+ const from_lambda_env_1 = require("./memory/from-lambda-env");
9
11
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
10
12
  const pixel_format_1 = require("./pixel-format");
11
13
  const probe_encoder_1 = require("./probe-encoder");
@@ -66,6 +68,8 @@ const prespawnFfmpeg = (options) => {
66
68
  hardwareAcceleration: resolvedHardwareAcceleration,
67
69
  indent: options.indent,
68
70
  logLevel: options.logLevel,
71
+ cpuCount: (0, get_cpu_count_1.getCpuCount)(),
72
+ lambdaMemoryInBytes: (0, from_lambda_env_1.getMaxLambdaMemory)(),
69
73
  }),
70
74
  '-y',
71
75
  options.outputLocation,
@@ -109,7 +109,9 @@ async function puppeteerEvaluateWithCatch({ page, pageFunction, frame, args, })
109
109
  stackFrame: (0, parse_browser_error_stack_1.parseStack)(((_e = exceptionDetails.exception) === null || _e === void 0 ? void 0 : _e.description).split('\n')),
110
110
  chunk: null,
111
111
  });
112
- page.close();
112
+ // The browser may get closed before the page is, which rejects
113
+ // this with "Target closed". Unhandled, that ends the process.
114
+ page.close().catch(() => undefined);
113
115
  throw err;
114
116
  }
115
117
  return { size, value: valueFromRemoteObject(remoteObject) };
@@ -325,13 +325,13 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
325
325
  let timeOfLastFrame = Date.now();
326
326
  const renderFramesProc = (0, render_frames_1.internalRenderFrames)({
327
327
  composition,
328
- onFrameUpdate: (frame, frameIndex) => {
328
+ onFrameUpdate: (frame, frameIndex, timeToRenderInMilliseconds) => {
329
329
  renderedFrames = frame;
330
330
  const now = Date.now();
331
- const timeToRenderInMilliseconds = now - timeOfLastFrame;
331
+ const timeSinceLastFrame = now - timeOfLastFrame;
332
332
  timeOfLastFrame = now;
333
333
  // Track recent frame timings (at most 50)
334
- recentFrameTimings.push(timeToRenderInMilliseconds);
334
+ recentFrameTimings.push(timeSinceLastFrame);
335
335
  if (recentFrameTimings.length > MAX_RECENT_FRAME_TIMINGS) {
336
336
  recentFrameTimings.shift();
337
337
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.stitchFramesToVideo = exports.internalStitchFramesToVideo = void 0;
7
7
  const node_fs_1 = require("node:fs");
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
+ const aac_priming_1 = require("./aac-priming");
9
10
  const download_map_1 = require("./assets/download-map");
10
11
  const call_ffmpeg_1 = require("./call-ffmpeg");
11
12
  const codec_1 = require("./codec");
@@ -18,6 +19,7 @@ const delete_directory_1 = require("./delete-directory");
18
19
  const ffmpeg_args_1 = require("./ffmpeg-args");
19
20
  const finalize_fast_start_1 = require("./finalize-fast-start");
20
21
  const find_closest_package_json_1 = require("./find-closest-package-json");
22
+ const get_cpu_count_1 = require("./get-cpu-count");
21
23
  const get_extension_from_codec_1 = require("./get-extension-from-codec");
22
24
  const get_extension_of_filename_1 = require("./get-extension-of-filename");
23
25
  const get_fast_start_muxer_1 = require("./get-fast-start-muxer");
@@ -25,6 +27,7 @@ const get_prores_profile_name_1 = require("./get-prores-profile-name");
25
27
  const logger_1 = require("./logger");
26
28
  const make_cancel_signal_1 = require("./make-cancel-signal");
27
29
  const make_metadata_args_1 = require("./make-metadata-args");
30
+ const from_lambda_env_1 = require("./memory/from-lambda-env");
28
31
  const audio_codec_1 = require("./options/audio-codec");
29
32
  const color_space_1 = require("./options/color-space");
30
33
  const gop_size_1 = require("./options/gop-size");
@@ -222,7 +225,19 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
222
225
  ? ['-filter_complex', 'split[v],palettegen,[v]paletteuse']
223
226
  : null,
224
227
  ]),
225
- audio && !separateAudioTo ? ['-i', audio, '-c:a', 'copy'] : ['-an'],
228
+ audio && !separateAudioTo
229
+ ? [
230
+ ...(0, aac_priming_1.getAacPrimingInputArgs)({
231
+ audioCodec: resolvedAudioCodec,
232
+ sampleRate,
233
+ outputExtension,
234
+ }),
235
+ '-i',
236
+ audio,
237
+ '-c:a',
238
+ 'copy',
239
+ ]
240
+ : ['-an'],
226
241
  numberOfGifLoops === null
227
242
  ? null
228
243
  : ['-loop', (0, convert_number_of_gif_loops_to_ffmpeg_1.convertNumberOfGifLoopsToFfmpegSyntax)(numberOfGifLoops)],
@@ -241,6 +256,8 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
241
256
  hardwareAcceleration: resolvedHardwareAcceleration,
242
257
  indent,
243
258
  logLevel,
259
+ cpuCount: (0, get_cpu_count_1.getCpuCount)(),
260
+ lambdaMemoryInBytes: (0, from_lambda_env_1.getMaxLambdaMemory)(),
244
261
  }),
245
262
  // Ignore metadata that may come from remote media
246
263
  ['-map_metadata', '-1'],
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/renderer"
4
4
  },
5
5
  "name": "@remotion/renderer",
6
- "version": "4.0.529",
6
+ "version": "4.0.531",
7
7
  "description": "Render Remotion videos using Node.js or Bun",
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
@@ -22,11 +22,11 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "execa": "5.1.1",
25
- "remotion": "4.0.529",
26
- "@remotion/streaming": "4.0.529",
25
+ "remotion": "4.0.531",
26
+ "@remotion/streaming": "4.0.531",
27
27
  "source-map": "0.8.0",
28
28
  "ws": "8.21.0",
29
- "@remotion/licensing": "4.0.529"
29
+ "@remotion/licensing": "4.0.531"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=16.8.0",
@@ -40,19 +40,19 @@
40
40
  "react-dom": "19.2.3",
41
41
  "@typescript/native-preview": "7.0.0-dev.20260217.1",
42
42
  "@types/ws": "8.5.10",
43
- "@remotion/example-videos": "4.0.529",
44
- "@remotion/eslint-config-internal": "4.0.529",
43
+ "@remotion/example-videos": "4.0.531",
44
+ "@remotion/eslint-config-internal": "4.0.531",
45
45
  "eslint": "9.19.0",
46
46
  "@types/node": "20.12.14"
47
47
  },
48
48
  "optionalDependencies": {
49
- "@remotion/compositor-darwin-arm64": "4.0.529",
50
- "@remotion/compositor-darwin-x64": "4.0.529",
51
- "@remotion/compositor-linux-arm64-gnu": "4.0.529",
52
- "@remotion/compositor-linux-arm64-musl": "4.0.529",
53
- "@remotion/compositor-linux-x64-gnu": "4.0.529",
54
- "@remotion/compositor-linux-x64-musl": "4.0.529",
55
- "@remotion/compositor-win32-x64-msvc": "4.0.529"
49
+ "@remotion/compositor-darwin-arm64": "4.0.531",
50
+ "@remotion/compositor-darwin-x64": "4.0.531",
51
+ "@remotion/compositor-linux-arm64-gnu": "4.0.531",
52
+ "@remotion/compositor-linux-arm64-musl": "4.0.531",
53
+ "@remotion/compositor-linux-x64-gnu": "4.0.531",
54
+ "@remotion/compositor-linux-x64-musl": "4.0.531",
55
+ "@remotion/compositor-win32-x64-msvc": "4.0.531"
56
56
  },
57
57
  "keywords": [
58
58
  "remotion",