@remotion/renderer 4.0.514 → 4.0.516

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.
@@ -20605,7 +20605,7 @@ var renderFrameWithOptionToReject = async ({
20605
20605
  if (!buffer2) {
20606
20606
  throw new Error("unexpected null buffer");
20607
20607
  }
20608
- onFrameBuffer(buffer2, frame);
20608
+ await onFrameBuffer(buffer2, frame);
20609
20609
  }
20610
20610
  const onlyAvailableAssets = assets.filter(truthy);
20611
20611
  const previousAudioRenderAssets = onlyAvailableAssets.map((a) => a.audioAndVideoAssets).flat(2);
@@ -22291,6 +22291,13 @@ var prespawnFfmpeg = (options2) => {
22291
22291
  };
22292
22292
 
22293
22293
  // src/prestitcher-memory-usage.ts
22294
+ var MINIMUM_MEMORY_LEFT = 2000000000;
22295
+ var hasEnoughMemoryForParallelEncoding = ({
22296
+ freeMemory,
22297
+ estimatedUsage
22298
+ }) => {
22299
+ return freeMemory - estimatedUsage > MINIMUM_MEMORY_LEFT;
22300
+ };
22294
22301
  var estimateMemoryUsageForPrestitcher = ({
22295
22302
  width,
22296
22303
  height
@@ -22309,7 +22316,10 @@ var shouldUseParallelEncoding = ({
22309
22316
  height,
22310
22317
  width
22311
22318
  });
22312
- const hasEnoughMemory = freeMemory - estimatedUsage > 2000000000 && estimatedUsage / freeMemory < 0.5;
22319
+ const hasEnoughMemory = hasEnoughMemoryForParallelEncoding({
22320
+ estimatedUsage,
22321
+ freeMemory
22322
+ });
22313
22323
  return {
22314
22324
  hasEnoughMemory,
22315
22325
  freeMemory,
@@ -23819,6 +23829,41 @@ var validateOutputFilename = ({
23819
23829
  }
23820
23830
  };
23821
23831
 
23832
+ // src/write-with-backpressure.ts
23833
+ var writeWithBackpressure = ({
23834
+ data,
23835
+ writable
23836
+ }) => {
23837
+ if (writable.write(data)) {
23838
+ return Promise.resolve();
23839
+ }
23840
+ return new Promise((resolve2, reject) => {
23841
+ const cleanup = () => {
23842
+ writable.off("close", onClose);
23843
+ writable.off("drain", onDrain);
23844
+ writable.off("error", onError);
23845
+ };
23846
+ const onClose = () => {
23847
+ cleanup();
23848
+ reject(new Error("The writable stream closed before it accepted all data."));
23849
+ };
23850
+ const onDrain = () => {
23851
+ cleanup();
23852
+ resolve2();
23853
+ };
23854
+ const onError = (error) => {
23855
+ cleanup();
23856
+ reject(error);
23857
+ };
23858
+ writable.once("close", onClose);
23859
+ writable.once("drain", onDrain);
23860
+ writable.once("error", onError);
23861
+ if (writable.destroyed) {
23862
+ onClose();
23863
+ }
23864
+ });
23865
+ };
23866
+
23822
23867
  // src/render-media.ts
23823
23868
  var SLOWEST_FRAME_COUNT = 10;
23824
23869
  var MAX_RECENT_FRAME_TIMINGS = 150;
@@ -24176,7 +24221,11 @@ var internalRenderMediaRaw = ({
24176
24221
  if (exitStatus?.type === "quit-with-error") {
24177
24222
  throw new Error(`FFmpeg quit with code ${exitStatus.exitCode}${exitStatus.signal ? ` (signal ${exitStatus.signal})` : ""} while piping frame ${frame}. Stderr: ${exitStatus.stderr}`);
24178
24223
  }
24179
- stitcherFfmpeg?.stdin?.write(buffer2);
24224
+ const stdin = stitcherFfmpeg?.stdin;
24225
+ if (!stdin) {
24226
+ throw new Error(`FFmpeg stdin is not available while trying to pipe frame ${frame} to it.`);
24227
+ }
24228
+ await writeWithBackpressure({ data: buffer2, writable: stdin });
24180
24229
  stopPerfMeasure(id);
24181
24230
  const frameIndex = framesToRender.indexOf(frame);
24182
24231
  setFrameToStitch(framesToRender[frameIndex + 1] ?? lastFrameToRender + 1);
package/dist/index.d.ts CHANGED
@@ -1056,7 +1056,7 @@ export declare const RenderInternals: {
1056
1056
  puppeteerInstance: HeadlessBrowser | undefined;
1057
1057
  browserExecutable: import("./browser-executable").BrowserExecutable;
1058
1058
  onBrowserLog: ((log: import("./browser-log").BrowserLog) => void) | null;
1059
- onFrameBuffer: ((buffer: Buffer, frame: number) => void) | null;
1059
+ onFrameBuffer: ((buffer: Buffer, frame: number) => void | Promise<void>) | null;
1060
1060
  onDownload: import(".").RenderMediaOnDownload | null;
1061
1061
  chromiumOptions: import("./open-browser").ChromiumOptions;
1062
1062
  scale: number;
@@ -0,0 +1,16 @@
1
+ export declare const enableCancellationOption: {
2
+ name: string;
3
+ cliFlag: "enable-cancellation";
4
+ description: () => import("react/jsx-runtime").JSX.Element;
5
+ ssrName: null;
6
+ docLink: string;
7
+ type: boolean;
8
+ setConfig: (value: boolean) => void;
9
+ getValue: ({ commandLine }: {
10
+ commandLine: Record<string, unknown>;
11
+ }) => {
12
+ value: boolean;
13
+ source: string;
14
+ };
15
+ id: "enable-cancellation";
16
+ };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enableCancellationOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const cliFlag = 'enable-cancellation';
6
+ let enableCancellation = false;
7
+ exports.enableCancellationOption = {
8
+ name: 'Enable cancellation',
9
+ cliFlag,
10
+ description: () => (jsx_runtime_1.jsxs(jsx_runtime_1.Fragment, { children: ["Allows a Lambda render to be cancelled by pressing Ctrl+C while running", ' ', jsx_runtime_1.jsx("code", { children: "npx remotion lambda render" }),
11
+ ". Enabling cancellation causes each renderer function to poll S3 once per second while it is running."] })),
12
+ ssrName: null,
13
+ docLink: 'https://www.remotion.dev/docs/lambda/cli/render#--enable-cancellation',
14
+ type: false,
15
+ setConfig: (value) => {
16
+ enableCancellation = value;
17
+ },
18
+ getValue: ({ commandLine }) => {
19
+ if (typeof commandLine[cliFlag] === 'boolean') {
20
+ return {
21
+ value: commandLine[cliFlag],
22
+ source: 'cli',
23
+ };
24
+ }
25
+ return {
26
+ value: enableCancellation,
27
+ source: enableCancellation ? 'config' : 'default',
28
+ };
29
+ },
30
+ id: cliFlag,
31
+ };
@@ -376,6 +376,22 @@ export declare const allOptions: {
376
376
  setConfig: (value: boolean | null) => void;
377
377
  id: "enable-folder-expiry";
378
378
  };
379
+ enableCancellationOption: {
380
+ name: string;
381
+ cliFlag: "enable-cancellation";
382
+ description: () => import("react/jsx-runtime").JSX.Element;
383
+ ssrName: null;
384
+ docLink: string;
385
+ type: boolean;
386
+ setConfig: (value: boolean) => void;
387
+ getValue: ({ commandLine }: {
388
+ commandLine: Record<string, unknown>;
389
+ }) => {
390
+ value: boolean;
391
+ source: string;
392
+ };
393
+ id: "enable-cancellation";
394
+ };
379
395
  enableMultiprocessOnLinuxOption: {
380
396
  name: string;
381
397
  cliFlag: "enable-multiprocess-on-linux";
@@ -25,6 +25,7 @@ const delete_after_1 = require("./delete-after");
25
25
  const disable_git_source_1 = require("./disable-git-source");
26
26
  const disable_web_security_1 = require("./disable-web-security");
27
27
  const disallow_parallel_encoding_1 = require("./disallow-parallel-encoding");
28
+ const enable_cancellation_1 = require("./enable-cancellation");
28
29
  const enable_lambda_insights_1 = require("./enable-lambda-insights");
29
30
  const enable_multiprocess_on_linux_1 = require("./enable-multiprocess-on-linux");
30
31
  const encoding_buffer_size_1 = require("./encoding-buffer-size");
@@ -119,6 +120,7 @@ exports.allOptions = {
119
120
  disableWebSecurityOption: disable_web_security_1.disableWebSecurityOption,
120
121
  disallowParallelEncodingOption: disallow_parallel_encoding_1.disallowParallelEncodingOption,
121
122
  folderExpiryOption: folder_expiry_1.folderExpiryOption,
123
+ enableCancellationOption: enable_cancellation_1.enableCancellationOption,
122
124
  enableMultiprocessOnLinuxOption: enable_multiprocess_on_linux_1.enableMultiprocessOnLinuxOption,
123
125
  glOption: gl_1.glOption,
124
126
  gopSizeOption: gop_size_1.gopSizeOption,
@@ -1,3 +1,7 @@
1
+ export declare const hasEnoughMemoryForParallelEncoding: ({ freeMemory, estimatedUsage, }: {
2
+ freeMemory: number;
3
+ estimatedUsage: number;
4
+ }) => boolean;
1
5
  export declare const shouldUseParallelEncoding: ({ width, height, logLevel, }: {
2
6
  width: number;
3
7
  height: number;
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.shouldUseParallelEncoding = void 0;
3
+ exports.shouldUseParallelEncoding = exports.hasEnoughMemoryForParallelEncoding = void 0;
4
4
  const get_available_memory_1 = require("./memory/get-available-memory");
5
+ const MINIMUM_MEMORY_LEFT = 2000000000;
6
+ const hasEnoughMemoryForParallelEncoding = ({ freeMemory, estimatedUsage, }) => {
7
+ return freeMemory - estimatedUsage > MINIMUM_MEMORY_LEFT;
8
+ };
9
+ exports.hasEnoughMemoryForParallelEncoding = hasEnoughMemoryForParallelEncoding;
5
10
  const estimateMemoryUsageForPrestitcher = ({ width, height, }) => {
6
11
  // Empirically we detected that per 1 million pixels, FFMPEG uses around 1GB of memory, relatively independent of
7
12
  // the duration of the video.
@@ -15,8 +20,10 @@ const shouldUseParallelEncoding = ({ width, height, logLevel, }) => {
15
20
  height,
16
21
  width,
17
22
  });
18
- const hasEnoughMemory = freeMemory - estimatedUsage > 2000000000 &&
19
- estimatedUsage / freeMemory < 0.5;
23
+ const hasEnoughMemory = (0, exports.hasEnoughMemoryForParallelEncoding)({
24
+ estimatedUsage,
25
+ freeMemory,
26
+ });
20
27
  return {
21
28
  hasEnoughMemory,
22
29
  freeMemory,
@@ -41,7 +41,7 @@ export declare const renderFrameAndRetryTargetClose: ({ retriesLeft, attempt, as
41
41
  framesRenderedObj: {
42
42
  count: number;
43
43
  };
44
- onFrameBuffer: ((buffer: Buffer, frame: number) => void) | null | undefined;
44
+ onFrameBuffer: ((buffer: Buffer, frame: number) => void | Promise<void>) | null | undefined;
45
45
  onFrameUpdate: ((framesRendered: number, frameIndex: number, timeToRenderInMilliseconds: number) => void) | null;
46
46
  nextFrameToRender: {
47
47
  getNextFrame: (_pageIndex: number) => number;
@@ -17,7 +17,7 @@ export declare const renderFrameWithOptionToReject: ({ reject, width, height, co
17
17
  indent: boolean;
18
18
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
19
19
  outputDir: string | null;
20
- onFrameBuffer: ((buffer: Buffer, frame: number) => void) | null | undefined;
20
+ onFrameBuffer: ((buffer: Buffer, frame: number) => void | Promise<void>) | null | undefined;
21
21
  imageFormat: "jpeg" | "none" | "png";
22
22
  onError: (err: Error) => void;
23
23
  lastFrame: number;
@@ -83,7 +83,7 @@ const renderFrameWithOptionToReject = async ({ reject, width, height, compId, at
83
83
  if (!buffer) {
84
84
  throw new Error('unexpected null buffer');
85
85
  }
86
- onFrameBuffer(buffer, frame);
86
+ await onFrameBuffer(buffer, frame);
87
87
  }
88
88
  const onlyAvailableAssets = assets.filter(truthy_1.truthy);
89
89
  const previousAudioRenderAssets = onlyAvailableAssets
@@ -28,7 +28,7 @@ export declare const renderFrame: ({ attempt, binariesDirectory, cancelSignal, i
28
28
  };
29
29
  timeoutInMilliseconds: number;
30
30
  outputDir: string | null;
31
- onFrameBuffer: ((buffer: Buffer, frame: number) => void) | null | undefined;
31
+ onFrameBuffer: ((buffer: Buffer, frame: number) => void | Promise<void>) | null | undefined;
32
32
  lastFrame: number;
33
33
  onFrameUpdate: ((framesRendered: number, frameIndex: number, timeToRenderInMilliseconds: number) => void) | null;
34
34
  framesRenderedObj: {
@@ -28,7 +28,7 @@ type InternalRenderFramesOptions = {
28
28
  puppeteerInstance: HeadlessBrowser | undefined;
29
29
  browserExecutable: BrowserExecutable | null;
30
30
  onBrowserLog: null | ((log: BrowserLog) => void);
31
- onFrameBuffer: null | ((buffer: Buffer, frame: number) => void);
31
+ onFrameBuffer: null | ((buffer: Buffer, frame: number) => void | Promise<void>);
32
32
  onDownload: RenderMediaOnDownload | null;
33
33
  chromiumOptions: ChromiumOptions;
34
34
  scale: number;
@@ -85,7 +85,7 @@ export type RenderFramesOptions = Prettify<{
85
85
  puppeteerInstance?: HeadlessBrowser;
86
86
  browserExecutable?: BrowserExecutable;
87
87
  onBrowserLog?: (log: BrowserLog) => void;
88
- onFrameBuffer?: (buffer: Buffer, frame: number) => void;
88
+ onFrameBuffer?: (buffer: Buffer, frame: number) => void | Promise<void>;
89
89
  onDownload?: RenderMediaOnDownload;
90
90
  timeoutInMilliseconds?: number;
91
91
  chromiumOptions?: ChromiumOptions;
@@ -126,7 +126,7 @@ export declare const renderFrames: (options: {
126
126
  puppeteerInstance?: HeadlessBrowser | undefined;
127
127
  browserExecutable?: BrowserExecutable | undefined;
128
128
  onBrowserLog?: ((log: BrowserLog) => void) | undefined;
129
- onFrameBuffer?: ((buffer: Buffer, frame: number) => void) | undefined;
129
+ onFrameBuffer?: ((buffer: Buffer, frame: number) => void | Promise<void>) | undefined;
130
130
  onDownload?: RenderMediaOnDownload | undefined;
131
131
  timeoutInMilliseconds?: number | undefined;
132
132
  chromiumOptions?: ChromiumOptions | undefined;
@@ -54,6 +54,7 @@ const validate_output_filename_1 = require("./validate-output-filename");
54
54
  const validate_scale_1 = require("./validate-scale");
55
55
  const validate_videobitrate_1 = require("./validate-videobitrate");
56
56
  const wrap_with_error_handling_1 = require("./wrap-with-error-handling");
57
+ const write_with_backpressure_1 = require("./write-with-backpressure");
57
58
  const SLOWEST_FRAME_COUNT = 10;
58
59
  const MAX_RECENT_FRAME_TIMINGS = 150;
59
60
  const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, composition: compositionWithPossibleUnevenDimensions, serializedInputPropsWithCustomSchema, pixelFormat: userPixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, encodingMaxRate, encodingBufferSize, audioCodec, concurrency, disallowParallelEncoding, everyNthFrame, imageFormat: provisionalImageFormat, indent, jpegQuality, numberOfGifLoops, onCtrlCExit, preferLossless, serveUrl, server: reusedServer, logLevel, serializedResolvedPropsWithCustomSchema, offthreadVideoCacheSizeInBytes, colorSpace, repro, binariesDirectory, separateAudioTo, forSeamlessAacConcatenation, compositionStart, onBrowserDownload, onArtifact, metadata, hardwareAcceleration, chromeMode, offthreadVideoThreads, mediaCacheSizeInBytes, onLog, licenseKey, isProduction, sampleRate, }) => {
@@ -361,7 +362,6 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
361
362
  onFrameBuffer: parallelEncoding
362
363
  ? async (buffer, frame) => {
363
364
  var _a;
364
- var _b;
365
365
  await waitForRightTimeOfFrameToBeInserted(frame);
366
366
  if (cancelled) {
367
367
  return;
@@ -374,10 +374,14 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
374
374
  if ((exitStatus === null || exitStatus === void 0 ? void 0 : exitStatus.type) === 'quit-with-error') {
375
375
  throw new Error(`FFmpeg quit with code ${exitStatus.exitCode}${exitStatus.signal ? ` (signal ${exitStatus.signal})` : ''} while piping frame ${frame}. Stderr: ${exitStatus.stderr}`);
376
376
  }
377
- (_a = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin) === null || _a === void 0 ? void 0 : _a.write(buffer);
377
+ const stdin = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin;
378
+ if (!stdin) {
379
+ throw new Error(`FFmpeg stdin is not available while trying to pipe frame ${frame} to it.`);
380
+ }
381
+ await (0, write_with_backpressure_1.writeWithBackpressure)({ data: buffer, writable: stdin });
378
382
  (0, perf_1.stopPerfMeasure)(id);
379
383
  const frameIndex = framesToRender.indexOf(frame);
380
- setFrameToStitch((_b = framesToRender[frameIndex + 1]) !== null && _b !== void 0 ? _b : lastFrameToRender + 1);
384
+ setFrameToStitch((_a = framesToRender[frameIndex + 1]) !== null && _a !== void 0 ? _a : lastFrameToRender + 1);
381
385
  }
382
386
  : null,
383
387
  webpackBundleOrServeUrl: serveUrl,
@@ -0,0 +1,5 @@
1
+ import type { Writable } from 'node:stream';
2
+ export declare const writeWithBackpressure: ({ data, writable, }: {
3
+ data: Buffer;
4
+ writable: Writable;
5
+ }) => Promise<void>;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.writeWithBackpressure = void 0;
4
+ const writeWithBackpressure = ({ data, writable, }) => {
5
+ if (writable.write(data)) {
6
+ return Promise.resolve();
7
+ }
8
+ return new Promise((resolve, reject) => {
9
+ const cleanup = () => {
10
+ writable.off('close', onClose);
11
+ writable.off('drain', onDrain);
12
+ writable.off('error', onError);
13
+ };
14
+ const onClose = () => {
15
+ cleanup();
16
+ reject(new Error('The writable stream closed before it accepted all data.'));
17
+ };
18
+ const onDrain = () => {
19
+ cleanup();
20
+ resolve();
21
+ };
22
+ const onError = (error) => {
23
+ cleanup();
24
+ reject(error);
25
+ };
26
+ writable.once('close', onClose);
27
+ writable.once('drain', onDrain);
28
+ writable.once('error', onError);
29
+ if (writable.destroyed) {
30
+ onClose();
31
+ }
32
+ });
33
+ };
34
+ exports.writeWithBackpressure = writeWithBackpressure;
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.514",
6
+ "version": "4.0.516",
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.514",
26
- "@remotion/streaming": "4.0.514",
25
+ "remotion": "4.0.516",
26
+ "@remotion/streaming": "4.0.516",
27
27
  "source-map": "0.8.0",
28
28
  "ws": "8.21.0",
29
- "@remotion/licensing": "4.0.514"
29
+ "@remotion/licensing": "4.0.516"
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.514",
44
- "@remotion/eslint-config-internal": "4.0.514",
43
+ "@remotion/example-videos": "4.0.516",
44
+ "@remotion/eslint-config-internal": "4.0.516",
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.514",
50
- "@remotion/compositor-darwin-x64": "4.0.514",
51
- "@remotion/compositor-linux-arm64-gnu": "4.0.514",
52
- "@remotion/compositor-linux-arm64-musl": "4.0.514",
53
- "@remotion/compositor-linux-x64-gnu": "4.0.514",
54
- "@remotion/compositor-linux-x64-musl": "4.0.514",
55
- "@remotion/compositor-win32-x64-msvc": "4.0.514"
49
+ "@remotion/compositor-darwin-arm64": "4.0.516",
50
+ "@remotion/compositor-darwin-x64": "4.0.516",
51
+ "@remotion/compositor-linux-arm64-gnu": "4.0.516",
52
+ "@remotion/compositor-linux-arm64-musl": "4.0.516",
53
+ "@remotion/compositor-linux-x64-gnu": "4.0.516",
54
+ "@remotion/compositor-linux-x64-musl": "4.0.516",
55
+ "@remotion/compositor-win32-x64-msvc": "4.0.516"
56
56
  },
57
57
  "keywords": [
58
58
  "remotion",