@remotion/serverless 4.0.514 → 4.0.515

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.
@@ -0,0 +1,11 @@
1
+ import type { CloudProvider, ProviderSpecifics } from '@remotion/serverless-client';
2
+ export declare const startCancellationPolling: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>>({ bucketName, renderId, region, providerSpecifics, forcePathStyle, intervalInMilliseconds, logLevel, onCancelled, }: {
3
+ bucketName: string;
4
+ renderId: string;
5
+ region: Provider["region"];
6
+ providerSpecifics: ProviderSpecifics<Provider>;
7
+ forcePathStyle: boolean;
8
+ intervalInMilliseconds: number;
9
+ logLevel: "error" | "info" | "trace" | "verbose" | "warn";
10
+ onCancelled: () => void;
11
+ }) => () => void;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startCancellationPolling = void 0;
4
+ const renderer_1 = require("@remotion/renderer");
5
+ const serverless_client_1 = require("@remotion/serverless-client");
6
+ const isMissingCancellationSignalError = (err) => {
7
+ const error = err;
8
+ return (error.name === 'NoSuchKey' ||
9
+ error.name === 'NotFound' ||
10
+ error.Code === 'NoSuchKey' ||
11
+ error.code === 'NoSuchKey');
12
+ };
13
+ const startCancellationPolling = ({ bucketName, renderId, region, providerSpecifics, forcePathStyle, intervalInMilliseconds, logLevel, onCancelled, }) => {
14
+ let stopped = false;
15
+ let timeout = null;
16
+ const poll = async () => {
17
+ try {
18
+ await providerSpecifics.headFile({
19
+ bucketName,
20
+ key: (0, serverless_client_1.cancellationKey)(renderId),
21
+ region,
22
+ customCredentials: null,
23
+ forcePathStyle,
24
+ requestHandler: null,
25
+ });
26
+ if (!stopped) {
27
+ stopped = true;
28
+ renderer_1.RenderInternals.Log.info({ indent: false, logLevel }, 'Render cancellation signal received.');
29
+ onCancelled();
30
+ }
31
+ }
32
+ catch (err) {
33
+ if (!isMissingCancellationSignalError(err)) {
34
+ renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, 'Could not check for a render cancellation signal. Retrying.', err);
35
+ }
36
+ }
37
+ if (!stopped) {
38
+ timeout = setTimeout(poll, intervalInMilliseconds);
39
+ }
40
+ };
41
+ poll().catch((err) => {
42
+ renderer_1.RenderInternals.Log.warn({ indent: false, logLevel }, 'Cancellation polling stopped unexpectedly.', err);
43
+ });
44
+ return () => {
45
+ stopped = true;
46
+ if (timeout !== null) {
47
+ clearTimeout(timeout);
48
+ }
49
+ };
50
+ };
51
+ exports.startCancellationPolling = startCancellationPolling;
@@ -164,8 +164,9 @@ const innerLaunchHandler = async ({ params, options, overallProgress, registerCl
164
164
  // Throttling a bit, allowing more progress if there is lower concurrency.
165
165
  const progressEveryNthFrame = Math.ceil(chunks.length / 15);
166
166
  const lambdaPayloads = chunks.map((chunkPayload) => {
167
- var _a, _b, _c;
167
+ var _a, _b, _c, _d;
168
168
  const payload = {
169
+ enableCancellation: (_a = params.enableCancellation) !== null && _a !== void 0 ? _a : false,
169
170
  type: serverless_client_2.ServerlessRoutines.renderer,
170
171
  frameRange: chunkPayload,
171
172
  serveUrl: params.serveUrl,
@@ -186,13 +187,13 @@ const innerLaunchHandler = async ({ params, options, overallProgress, registerCl
186
187
  pixelFormat: params.pixelFormat,
187
188
  proResProfile: params.proResProfile,
188
189
  x264Preset: params.x264Preset,
189
- gopSize: (_a = params.gopSize) !== null && _a !== void 0 ? _a : null,
190
+ gopSize: (_b = params.gopSize) !== null && _b !== void 0 ? _b : null,
190
191
  jpegQuality: params.jpegQuality,
191
192
  privacy: params.privacy,
192
- logLevel: (_b = params.logLevel) !== null && _b !== void 0 ? _b : 'info',
193
+ logLevel: (_c = params.logLevel) !== null && _c !== void 0 ? _c : 'info',
193
194
  attempt: 1,
194
195
  timeoutInMilliseconds: params.timeoutInMilliseconds,
195
- chromiumOptions: (_c = params.chromiumOptions) !== null && _c !== void 0 ? _c : {},
196
+ chromiumOptions: (_d = params.chromiumOptions) !== null && _d !== void 0 ? _d : {},
196
197
  scale: params.scale,
197
198
  everyNthFrame: params.everyNthFrame,
198
199
  concurrencyPerLambda: params.concurrencyPerFunction,
@@ -428,7 +429,7 @@ const innerLaunchHandler = async ({ params, options, overallProgress, registerCl
428
429
  return postRenderData;
429
430
  };
430
431
  const launchHandler = async ({ params, options, providerSpecifics, insideFunctionSpecifics, }) => {
431
- var _a, _b, _c, _d;
432
+ var _a, _b, _c, _d, _e;
432
433
  if (params.type !== serverless_client_2.ServerlessRoutines.launch) {
433
434
  throw new Error('Expected launch type');
434
435
  }
@@ -452,6 +453,25 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
452
453
  cleanupTasks.length = 0;
453
454
  return prom;
454
455
  };
456
+ const cancelOtherRenderers = async () => {
457
+ if (!params.enableCancellation) {
458
+ return;
459
+ }
460
+ try {
461
+ await (0, serverless_client_2.writeCancellationSignal)({
462
+ bucketName: params.bucketName,
463
+ renderId: params.renderId,
464
+ region: insideFunctionSpecifics.getCurrentRegionInFunction(),
465
+ expectedBucketOwner: options.expectedBucketOwner,
466
+ providerSpecifics,
467
+ forcePathStyle: params.forcePathStyle,
468
+ requestHandler: null,
469
+ });
470
+ }
471
+ catch (err) {
472
+ renderer_1.RenderInternals.Log.warn({ indent: false, logLevel: params.logLevel }, 'Could not signal other renderers to stop.', err);
473
+ }
474
+ };
455
475
  const onTimeout = async () => {
456
476
  var _a;
457
477
  renderer_1.RenderInternals.Log.error({ indent: false, logLevel: params.logLevel }, 'Function is about to time out. Can not finish render.');
@@ -461,6 +481,7 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
461
481
  globalThis._dumpUnreleasedBuffers.emit('dump-unreleased-buffers');
462
482
  }
463
483
  runCleanupTasks();
484
+ await cancelOtherRenderers();
464
485
  if (!params.webhook) {
465
486
  renderer_1.RenderInternals.Log.verbose({
466
487
  indent: false,
@@ -522,6 +543,7 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
522
543
  const webhookDueToTimeout = setTimeout(onTimeout, Math.max(options.getRemainingTimeInMillis() - 1000, 1000));
523
544
  renderer_1.RenderInternals.Log.info(logOptions, `Function has ${Math.max(options.getRemainingTimeInMillis() - 1000, 1000)} before it times out`);
524
545
  const overallProgress = (0, overall_render_progress_1.makeOverallRenderProgress)({
546
+ cancellationEnabled: (_a = params.enableCancellation) !== null && _a !== void 0 ? _a : false,
525
547
  renderId: params.renderId,
526
548
  bucketName: params.bucketName,
527
549
  expectedBucketOwner: options.expectedBucketOwner,
@@ -545,10 +567,10 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
545
567
  });
546
568
  clearTimeout(webhookDueToTimeout);
547
569
  (0, send_telemetry_event_1.sendTelemetryEvent)({
548
- licenseKey: (_a = params.licenseKey) !== null && _a !== void 0 ? _a : null,
570
+ licenseKey: (_b = params.licenseKey) !== null && _b !== void 0 ? _b : null,
549
571
  logLevel: params.logLevel,
550
572
  isStill: false,
551
- isProduction: (_b = params.isProduction) !== null && _b !== void 0 ? _b : true,
573
+ isProduction: (_c = params.isProduction) !== null && _c !== void 0 ? _c : true,
552
574
  });
553
575
  if (!params.webhook || webhookInvoked) {
554
576
  return;
@@ -563,7 +585,7 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
563
585
  renderId: params.renderId,
564
586
  expectedBucketOwner: options.expectedBucketOwner,
565
587
  bucketName: params.bucketName,
566
- customData: (_c = params.webhook.customData) !== null && _c !== void 0 ? _c : null,
588
+ customData: (_d = params.webhook.customData) !== null && _d !== void 0 ? _d : null,
567
589
  outputUrl: postRenderData.outputFile,
568
590
  lambdaErrors: postRenderData.errors,
569
591
  outputFile: postRenderData.outputFile,
@@ -602,6 +624,7 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
602
624
  if (process.env.NODE_ENV === 'test') {
603
625
  throw err;
604
626
  }
627
+ await cancelOtherRenderers();
605
628
  renderer_1.RenderInternals.Log.error({ indent: false, logLevel: params.logLevel }, 'Error occurred', err);
606
629
  overallProgress.addErrorWithoutUpload({
607
630
  chunk: null,
@@ -631,7 +654,7 @@ const launchHandler = async ({ params, options, providerSpecifics, insideFunctio
631
654
  renderId: params.renderId,
632
655
  expectedBucketOwner: options.expectedBucketOwner,
633
656
  bucketName: params.bucketName,
634
- customData: (_d = params.webhook.customData) !== null && _d !== void 0 ? _d : null,
657
+ customData: (_e = params.webhook.customData) !== null && _e !== void 0 ? _e : null,
635
658
  errors: [err].map((e) => ({
636
659
  message: e.message,
637
660
  name: e.name,
@@ -9,11 +9,12 @@ const node_path_1 = __importDefault(require("node:path"));
9
9
  const renderer_1 = require("@remotion/renderer");
10
10
  const pure_1 = require("@remotion/renderer/pure");
11
11
  const serverless_client_1 = require("@remotion/serverless-client");
12
+ const cancellation_polling_1 = require("../cancellation-polling");
12
13
  const get_tmp_dir_1 = require("../get-tmp-dir");
13
14
  const leak_detection_1 = require("../leak-detection");
14
15
  const on_downloads_helpers_1 = require("../on-downloads-helpers");
15
16
  const why_is_node_running_1 = require("../why-is-node-running");
16
- const renderHandler = async ({ params, options, logs, onStream, providerSpecifics, insideFunctionSpecifics, onBrowserInstance, onMediaFiles, }) => {
17
+ const renderHandler = async ({ params, options, logs, onStream, providerSpecifics, insideFunctionSpecifics, onBrowserInstance, onMediaFiles, cancelSignal, }) => {
17
18
  if (params.type !== serverless_client_1.ServerlessRoutines.renderer) {
18
19
  throw new Error('Params must be renderer');
19
20
  }
@@ -209,7 +210,7 @@ const renderHandler = async ({ params, options, logs, onStream, providerSpecific
209
210
  audioCodec,
210
211
  preferLossless: params.preferLossless,
211
212
  browserExecutable: providerSpecifics.getChromiumPath(),
212
- cancelSignal: undefined,
213
+ cancelSignal: cancelSignal !== null && cancelSignal !== void 0 ? cancelSignal : undefined,
213
214
  disallowParallelEncoding: false,
214
215
  ffmpegOverride: ({ args }) => args,
215
216
  indent: false,
@@ -302,6 +303,23 @@ const rendererHandler = async ({ onStream, options, params, providerSpecifics, r
302
303
  const leakDetection = (0, why_is_node_running_1.enableNodeIntrospection)(ENABLE_SLOW_LEAK_DETECTION);
303
304
  let shouldKeepBrowserOpen = true;
304
305
  let instance;
306
+ let cancellationRequested = false;
307
+ const { cancel, cancelSignal } = (0, renderer_1.makeCancelSignal)();
308
+ const stopCancellationPolling = params.enableCancellation
309
+ ? (0, cancellation_polling_1.startCancellationPolling)({
310
+ bucketName: params.bucketName,
311
+ renderId: params.renderId,
312
+ region: insideFunctionSpecifics.getCurrentRegionInFunction(),
313
+ providerSpecifics,
314
+ forcePathStyle: params.forcePathStyle,
315
+ intervalInMilliseconds: 1000,
316
+ logLevel: params.logLevel,
317
+ onCancelled: () => {
318
+ cancellationRequested = true;
319
+ cancel();
320
+ },
321
+ })
322
+ : () => undefined;
305
323
  try {
306
324
  await renderHandler({
307
325
  params,
@@ -314,6 +332,7 @@ const rendererHandler = async ({ onStream, options, params, providerSpecifics, r
314
332
  instance = browserInstance;
315
333
  },
316
334
  onMediaFiles,
335
+ cancelSignal: params.enableCancellation ? cancelSignal : null,
317
336
  });
318
337
  }
319
338
  catch (err) {
@@ -328,7 +347,7 @@ const rendererHandler = async ({ onStream, options, params, providerSpecifics, r
328
347
  if (isRetryableError) {
329
348
  shouldKeepBrowserOpen = false;
330
349
  }
331
- const shouldNotRetry = err.name === 'CancelledError';
350
+ const shouldNotRetry = cancellationRequested || err.name === 'CancelledError';
332
351
  const shouldRetry = isRetryableError && params.retriesLeft > 0 && !shouldNotRetry;
333
352
  renderer_1.RenderInternals.Log.error({ indent: false, logLevel: params.logLevel }, `Error occurred (will retry = ${String(shouldRetry)})`);
334
353
  renderer_1.RenderInternals.Log.error({ indent: false, logLevel: params.logLevel }, err.stack);
@@ -354,6 +373,7 @@ const rendererHandler = async ({ onStream, options, params, providerSpecifics, r
354
373
  });
355
374
  }
356
375
  finally {
376
+ stopCancellationPolling();
357
377
  if (shouldKeepBrowserOpen && instance) {
358
378
  insideFunctionSpecifics.forgetBrowserEventLoop({
359
379
  logLevel: params.logLevel,
@@ -5,7 +5,7 @@ const serverless_client_1 = require("@remotion/serverless-client");
5
5
  const overall_render_progress_1 = require("../overall-render-progress");
6
6
  const check_version_mismatch_1 = require("./check-version-mismatch");
7
7
  const startHandler = async ({ params, options, providerSpecifics, insideFunctionSpecifics, }) => {
8
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
8
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
9
9
  if (params.type !== serverless_client_1.ServerlessRoutines.start) {
10
10
  throw new TypeError('Expected type start');
11
11
  }
@@ -35,7 +35,7 @@ const startHandler = async ({ params, options, providerSpecifics, insideFunction
35
35
  bucketName,
36
36
  downloadBehavior: null,
37
37
  region,
38
- body: JSON.stringify((0, overall_render_progress_1.makeInitialOverallRenderProgress)(options.timeoutInMilliseconds + Date.now())),
38
+ body: JSON.stringify((0, overall_render_progress_1.makeInitialOverallRenderProgress)(options.timeoutInMilliseconds + Date.now(), (_b = params.enableCancellation) !== null && _b !== void 0 ? _b : false)),
39
39
  expectedBucketOwner: options.expectedBucketOwner,
40
40
  key: (0, serverless_client_1.overallProgressKey)(options.renderId),
41
41
  privacy: 'private',
@@ -45,6 +45,7 @@ const startHandler = async ({ params, options, providerSpecifics, insideFunction
45
45
  requestHandler: null,
46
46
  });
47
47
  const payload = {
48
+ enableCancellation: (_c = params.enableCancellation) !== null && _c !== void 0 ? _c : false,
48
49
  type: serverless_client_1.ServerlessRoutines.launch,
49
50
  framesPerFunction: params.framesPerLambda,
50
51
  concurrency: params.concurrency,
@@ -55,12 +56,14 @@ const startHandler = async ({ params, options, providerSpecifics, insideFunction
55
56
  renderId: options.renderId,
56
57
  codec: params.codec,
57
58
  imageFormat: params.imageFormat,
58
- crf: (_b = params.crf) !== null && _b !== void 0 ? _b : null,
59
+ crf: (_d = params.crf) !== null && _d !== void 0 ? _d : null,
59
60
  envVariables: params.envVariables,
60
- pixelFormat: (_c = params.pixelFormat) !== null && _c !== void 0 ? _c : null,
61
- proResProfile: (_d = params.proResProfile) !== null && _d !== void 0 ? _d : null,
62
- x264Preset: params.x264Preset,
63
- gopSize: (_e = params.gopSize) !== null && _e !== void 0 ? _e : null,
61
+ pixelFormat: (_e = params.pixelFormat) !== null && _e !== void 0 ? _e : null,
62
+ proResProfile: (_f = params.proResProfile) !== null && _f !== void 0 ? _f : null,
63
+ x264Preset: (_g = params.x264Preset) !== null && _g !== void 0 ? _g : (params.codec === 'h264'
64
+ ? insideFunctionSpecifics.defaultX264Preset
65
+ : null),
66
+ gopSize: (_h = params.gopSize) !== null && _h !== void 0 ? _h : null,
64
67
  jpegQuality: params.jpegQuality,
65
68
  maxRetries: params.maxRetries,
66
69
  privacy: params.privacy,
@@ -83,8 +86,8 @@ const startHandler = async ({ params, options, providerSpecifics, insideFunction
83
86
  encodingMaxRate: params.encodingMaxRate,
84
87
  forceHeight: params.forceHeight,
85
88
  forceWidth: params.forceWidth,
86
- forceFps: (_f = params.forceFps) !== null && _f !== void 0 ? _f : null,
87
- forceDurationInFrames: (_g = params.forceDurationInFrames) !== null && _g !== void 0 ? _g : null,
89
+ forceFps: (_j = params.forceFps) !== null && _j !== void 0 ? _j : null,
90
+ forceDurationInFrames: (_k = params.forceDurationInFrames) !== null && _k !== void 0 ? _k : null,
88
91
  rendererFunctionName: params.rendererFunctionName,
89
92
  audioCodec: params.audioCodec,
90
93
  offthreadVideoCacheSizeInBytes: params.offthreadVideoCacheSizeInBytes,
@@ -97,8 +100,8 @@ const startHandler = async ({ params, options, providerSpecifics, insideFunction
97
100
  offthreadVideoThreads: params.offthreadVideoThreads,
98
101
  storageClass: params.storageClass,
99
102
  mediaCacheSizeInBytes: params.mediaCacheSizeInBytes,
100
- isProduction: (_h = params.isProduction) !== null && _h !== void 0 ? _h : true,
101
- sampleRate: (_j = params.sampleRate) !== null && _j !== void 0 ? _j : 48000,
103
+ isProduction: (_l = params.isProduction) !== null && _l !== void 0 ? _l : true,
104
+ sampleRate: (_m = params.sampleRate) !== null && _m !== void 0 ? _m : 48000,
102
105
  };
103
106
  await providerSpecifics.callFunctionAsync({
104
107
  functionName: insideFunctionSpecifics.getCurrentFunctionName(),
@@ -153,7 +153,7 @@ const innerStillHandler = async ({ params, expectedBucketOwner, renderId, onStre
153
153
  },
154
154
  scale: params.scale,
155
155
  };
156
- const still = (0, overall_render_progress_1.makeInitialOverallRenderProgress)(timeoutInMilliseconds);
156
+ const still = (0, overall_render_progress_1.makeInitialOverallRenderProgress)(timeoutInMilliseconds, false);
157
157
  still.renderMetadata = renderMetadata;
158
158
  await providerSpecifics.writeFile({
159
159
  bucketName,
@@ -21,8 +21,8 @@ export type OverallProgressHelper<Provider extends CloudProvider> = {
21
21
  addReceivedArtifact: (asset: ReceivedArtifact<Provider>) => void;
22
22
  getReceivedArtifacts: () => ReceivedArtifact<Provider>[];
23
23
  };
24
- export declare const makeInitialOverallRenderProgress: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>>(timeoutTimestamp: number) => OverallRenderProgress<Provider>;
25
- export declare const makeOverallRenderProgress: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>>({ renderId, bucketName, expectedBucketOwner, region, timeoutTimestamp, logLevel, providerSpecifics, forcePathStyle, }: {
24
+ export declare const makeInitialOverallRenderProgress: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>>(timeoutTimestamp: number, cancellationEnabled: boolean) => OverallRenderProgress<Provider>;
25
+ export declare const makeOverallRenderProgress: <Provider extends CloudProvider<string, Record<string, unknown>, Record<string, unknown>, string, object>>({ renderId, bucketName, expectedBucketOwner, region, timeoutTimestamp, logLevel, providerSpecifics, forcePathStyle, cancellationEnabled, }: {
26
26
  renderId: string;
27
27
  bucketName: string;
28
28
  expectedBucketOwner: string;
@@ -31,4 +31,5 @@ export declare const makeOverallRenderProgress: <Provider extends CloudProvider<
31
31
  logLevel: "error" | "info" | "trace" | "verbose" | "warn";
32
32
  providerSpecifics: ProviderSpecifics<Provider>;
33
33
  forcePathStyle: boolean;
34
+ cancellationEnabled: boolean;
34
35
  }) => OverallProgressHelper<Provider>;
@@ -9,8 +9,9 @@ const wait = (duration) => {
9
9
  setTimeout(resolve, duration);
10
10
  });
11
11
  };
12
- const makeInitialOverallRenderProgress = (timeoutTimestamp) => {
12
+ const makeInitialOverallRenderProgress = (timeoutTimestamp, cancellationEnabled) => {
13
13
  return {
14
+ cancellationEnabled,
14
15
  chunks: [],
15
16
  framesRendered: 0,
16
17
  framesEncoded: 0,
@@ -33,11 +34,11 @@ const makeInitialOverallRenderProgress = (timeoutTimestamp) => {
33
34
  };
34
35
  };
35
36
  exports.makeInitialOverallRenderProgress = makeInitialOverallRenderProgress;
36
- const makeOverallRenderProgress = ({ renderId, bucketName, expectedBucketOwner, region, timeoutTimestamp, logLevel, providerSpecifics, forcePathStyle, }) => {
37
+ const makeOverallRenderProgress = ({ renderId, bucketName, expectedBucketOwner, region, timeoutTimestamp, logLevel, providerSpecifics, forcePathStyle, cancellationEnabled, }) => {
37
38
  let framesRendered = [];
38
39
  let framesEncoded = [];
39
40
  let lambdasInvoked = [];
40
- const renderProgress = (0, exports.makeInitialOverallRenderProgress)(timeoutTimestamp);
41
+ const renderProgress = (0, exports.makeInitialOverallRenderProgress)(timeoutTimestamp, cancellationEnabled);
41
42
  let dirty = false;
42
43
  let dirtyReasons = [];
43
44
  let uploadLoopPromise = null;
@@ -1,5 +1,5 @@
1
1
  import type { BundlerInternals } from '@remotion/bundler';
2
- import type { ChromiumOptions, EmittedArtifact, LogLevel } from '@remotion/renderer';
2
+ import type { ChromiumOptions, EmittedArtifact, LogLevel, X264Preset } from '@remotion/renderer';
3
3
  import type { CloudProvider, DeleteAfter, GetFolderFiles, Privacy, ProviderSpecifics, ReceivedArtifact, WebhookPayload } from '@remotion/serverless-client';
4
4
  import type { LaunchedBrowser } from './get-browser-instance';
5
5
  export type MakeArtifactWithDetails<Provider extends CloudProvider> = (params: {
@@ -79,6 +79,7 @@ export type InvokeWebhookParams = {
79
79
  };
80
80
  export type InvokeWebhook = (params: InvokeWebhookParams) => Promise<void>;
81
81
  export type InsideFunctionSpecifics<Provider extends CloudProvider> = {
82
+ defaultX264Preset: X264Preset | null;
82
83
  getBrowserInstance: GetBrowserInstance;
83
84
  forgetBrowserEventLoop: ForgetBrowserEventLoop;
84
85
  timer: DebuggingTimer;
@@ -12,6 +12,29 @@ const streamRenderer = ({ payload, functionName, outdir, overallProgress, files,
12
12
  throw new Error('Expected renderer type');
13
13
  }
14
14
  return new Promise((resolve) => {
15
+ const chunkFilename = (type) => (0, path_1.join)(outdir, `chunk:${String(payload.chunk).padStart(8, '0')}:${type}`);
16
+ // Stream the chunks to disk as they arrive instead of buffering them
17
+ // in memory. With many chunks arriving concurrently, buffering can
18
+ // exhaust the memory of the main function.
19
+ const getBinaryPayloadSink = ({ messageType }) => {
20
+ const filename = chunkFilename(messageType === 'video-chunk-rendered' ? 'video' : 'audio');
21
+ const writeStream = (0, fs_1.createWriteStream)(filename);
22
+ return {
23
+ write: (data) => new Promise((resolve_, reject) => {
24
+ writeStream.write(data, (err) => {
25
+ if (err) {
26
+ reject(err);
27
+ }
28
+ else {
29
+ resolve_();
30
+ }
31
+ });
32
+ }),
33
+ end: () => new Promise((resolve_) => {
34
+ writeStream.end(resolve_);
35
+ }),
36
+ };
37
+ };
15
38
  const receivedStreamingPayload = ({ message }) => {
16
39
  if (message.type === 'lambda-invoked') {
17
40
  overallProgress.setLambdaInvoked(payload.chunk);
@@ -26,15 +49,21 @@ const streamRenderer = ({ payload, functionName, outdir, overallProgress, files,
26
49
  return;
27
50
  }
28
51
  if (message.type === 'video-chunk-rendered') {
29
- const filename = (0, path_1.join)(outdir, `chunk:${String(payload.chunk).padStart(8, '0')}:video`);
30
- (0, fs_1.writeFileSync)(filename, new Uint8Array(message.payload));
52
+ const filename = chunkFilename('video');
53
+ // An empty payload means the chunk was already streamed to disk
54
+ if (message.payload.length > 0) {
55
+ (0, fs_1.writeFileSync)(filename, message.payload);
56
+ }
31
57
  files.push(filename);
32
58
  renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `Received video chunk for chunk ${payload.chunk}`);
33
59
  return;
34
60
  }
35
61
  if (message.type === 'audio-chunk-rendered') {
36
- const filename = (0, path_1.join)(outdir, `chunk:${String(payload.chunk).padStart(8, '0')}:audio`);
37
- (0, fs_1.writeFileSync)(filename, new Uint8Array(message.payload));
62
+ const filename = chunkFilename('audio');
63
+ // An empty payload means the chunk was already streamed to disk
64
+ if (message.payload.length > 0) {
65
+ (0, fs_1.writeFileSync)(filename, message.payload);
66
+ }
38
67
  renderer_1.RenderInternals.Log.verbose({ indent: false, logLevel }, `Received audio chunk for chunk ${payload.chunk}`);
39
68
  files.push(filename);
40
69
  return;
@@ -91,6 +120,7 @@ const streamRenderer = ({ payload, functionName, outdir, overallProgress, files,
91
120
  type: serverless_client_1.ServerlessRoutines.renderer,
92
121
  receivedStreamingPayload,
93
122
  requestHandler,
123
+ getBinaryPayloadSink,
94
124
  })
95
125
  .then(() => {
96
126
  resolve({
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "url": "https://github.com/remotion-dev/remotion/tree/main/packages/serverless"
4
4
  },
5
5
  "name": "@remotion/serverless",
6
- "version": "4.0.514",
6
+ "version": "4.0.515",
7
7
  "description": "A runtime for distributed rendering",
8
8
  "main": "dist",
9
9
  "scripts": {
@@ -23,13 +23,13 @@
23
23
  "access": "public"
24
24
  },
25
25
  "dependencies": {
26
- "@remotion/renderer": "4.0.514",
27
- "@remotion/bundler": "4.0.514",
28
- "@remotion/licensing": "4.0.514",
29
- "@remotion/serverless-client": "4.0.514"
26
+ "@remotion/renderer": "4.0.515",
27
+ "@remotion/bundler": "4.0.515",
28
+ "@remotion/licensing": "4.0.515",
29
+ "@remotion/serverless-client": "4.0.515"
30
30
  },
31
31
  "devDependencies": {
32
- "@remotion/eslint-config-internal": "4.0.514",
32
+ "@remotion/eslint-config-internal": "4.0.515",
33
33
  "eslint": "9.19.0",
34
34
  "@typescript/native-preview": "7.0.0-dev.20260217.1"
35
35
  },