@remotion/renderer 4.0.22 → 4.0.23

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 (109) hide show
  1. package/.prettierignore +2 -0
  2. package/dist/assets/get-video-stream-duration.d.ts +9 -0
  3. package/dist/assets/get-video-stream-duration.js +71 -0
  4. package/dist/audio-codec.d.ts +3 -3
  5. package/dist/calculate-sar-dar-pixels.d.ts +9 -0
  6. package/dist/calculate-sar-dar-pixels.js +19 -0
  7. package/dist/client.d.ts +154 -8
  8. package/dist/client.js +4 -0
  9. package/dist/codec.d.ts +1 -1
  10. package/dist/compositor/compositor.d.ts +2 -2
  11. package/dist/compositor/compositor.js +22 -19
  12. package/dist/compositor/payloads.d.ts +2 -2
  13. package/dist/copy-to-clipboard.js +1 -1
  14. package/dist/create-ffmpeg-complex-filter.d.ts +4 -1
  15. package/dist/determine-resize-params.d.ts +4 -0
  16. package/dist/determine-resize-params.js +10 -0
  17. package/dist/determine-vcodec-ffmpeg-flags.d.ts +2 -0
  18. package/dist/determine-vcodec-ffmpeg-flags.js +13 -0
  19. package/dist/ensure-ffmpeg.d.ts +18 -0
  20. package/dist/ensure-ffmpeg.js +58 -0
  21. package/dist/ensure-presentation-timestamp.d.ts +15 -0
  22. package/dist/ensure-presentation-timestamp.js +88 -0
  23. package/dist/extract-frame-from-video.d.ts +16 -0
  24. package/dist/extract-frame-from-video.js +191 -0
  25. package/dist/ffmpeg-executable.d.ts +1 -0
  26. package/dist/ffmpeg-executable.js +2 -0
  27. package/dist/ffmpeg-flags.d.ts +31 -0
  28. package/dist/ffmpeg-flags.js +245 -0
  29. package/dist/file-extensions.d.ts +1 -1
  30. package/dist/format-logs.js +3 -1
  31. package/dist/frame-to-ffmpeg-timestamp.d.ts +1 -0
  32. package/dist/frame-to-ffmpeg-timestamp.js +8 -0
  33. package/dist/get-can-extract-frames-fast.d.ts +14 -0
  34. package/dist/get-can-extract-frames-fast.js +71 -0
  35. package/dist/get-compositions.d.ts +5 -2
  36. package/dist/get-compositions.js +4 -1
  37. package/dist/get-extension-from-codec.js +1 -2
  38. package/dist/get-frame-of-video-slow.d.ts +17 -0
  39. package/dist/get-frame-of-video-slow.js +72 -0
  40. package/dist/get-silent-parts.js +1 -1
  41. package/dist/get-video-info.d.ts +8 -0
  42. package/dist/get-video-info.js +59 -0
  43. package/dist/get-video-metadata.js +1 -1
  44. package/dist/image-format.d.ts +2 -2
  45. package/dist/index.d.ts +38 -14
  46. package/dist/is-beyond-last-frame.d.ts +3 -0
  47. package/dist/is-beyond-last-frame.js +12 -0
  48. package/dist/last-frame-from-video-cache.d.ts +17 -0
  49. package/dist/last-frame-from-video-cache.js +55 -0
  50. package/dist/legacy-webpack-config.d.ts +9 -0
  51. package/dist/legacy-webpack-config.js +13 -0
  52. package/dist/log-level.d.ts +1 -1
  53. package/dist/offthread-video-server.d.ts +2 -1
  54. package/dist/offthread-video-server.js +4 -2
  55. package/dist/open-browser.d.ts +1 -1
  56. package/dist/options/audio-bitrate.d.ts +8 -2
  57. package/dist/options/audio-bitrate.js +1 -0
  58. package/dist/options/crf.d.ts +8 -2
  59. package/dist/options/crf.js +1 -0
  60. package/dist/options/enforce-audio.d.ts +8 -2
  61. package/dist/options/enforce-audio.js +1 -0
  62. package/dist/options/jpeg-quality.d.ts +8 -2
  63. package/dist/options/jpeg-quality.js +1 -0
  64. package/dist/options/mute.d.ts +8 -2
  65. package/dist/options/mute.js +1 -0
  66. package/dist/options/offthreadvideo-cache-size.d.ts +9 -0
  67. package/dist/options/offthreadvideo-cache-size.js +33 -0
  68. package/dist/options/option.d.ts +7 -2
  69. package/dist/options/options-map.d.ts +82 -0
  70. package/dist/options/options-map.js +16 -0
  71. package/dist/options/scale.d.ts +8 -2
  72. package/dist/options/scale.js +1 -0
  73. package/dist/options/video-bitrate.d.ts +8 -2
  74. package/dist/options/video-bitrate.js +1 -0
  75. package/dist/options/video-codec.d.ts +8 -2
  76. package/dist/options/video-codec.js +1 -0
  77. package/dist/pixel-format.d.ts +1 -1
  78. package/dist/prepare-server.d.ts +2 -1
  79. package/dist/prepare-server.js +3 -1
  80. package/dist/prores-profile.d.ts +1 -1
  81. package/dist/provide-screenshot.d.ts +0 -1
  82. package/dist/puppeteer-screenshot.d.ts +0 -1
  83. package/dist/quality.d.ts +1 -0
  84. package/dist/quality.js +21 -0
  85. package/dist/render-frames.d.ts +5 -2
  86. package/dist/render-frames.js +4 -2
  87. package/dist/render-media.d.ts +6 -3
  88. package/dist/render-media.js +5 -2
  89. package/dist/render-still.d.ts +5 -1
  90. package/dist/render-still.js +3 -1
  91. package/dist/screenshot-dom-element.d.ts +0 -1
  92. package/dist/screenshot-task.d.ts +0 -1
  93. package/dist/select-composition.d.ts +4 -1
  94. package/dist/select-composition.js +5 -1
  95. package/dist/serve-static.d.ts +1 -0
  96. package/dist/serve-static.js +1 -0
  97. package/dist/stitch-frames-to-video.d.ts +0 -1
  98. package/dist/take-frame-and-compose.d.ts +0 -1
  99. package/dist/try-to-extract-frame-of-video-fast.d.ts +12 -0
  100. package/dist/try-to-extract-frame-of-video-fast.js +55 -0
  101. package/dist/validate-ffmpeg.d.ts +7 -0
  102. package/dist/validate-ffmpeg.js +77 -0
  103. package/dist/validate-opengl-renderer.d.ts +1 -1
  104. package/dist/warn-about-ffmpeg-version.d.ts +5 -0
  105. package/dist/warn-about-ffmpeg-version.js +37 -0
  106. package/dist/x264-preset.d.ts +1 -1
  107. package/package.json +11 -12
  108. package/dist/presets-profile.d.ts +0 -7
  109. package/dist/presets-profile.js +0 -27
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCanExtractFramesFast = exports.ACCEPTABLE_OFFSET_THRESHOLD = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const get_video_stream_duration_1 = require("./assets/get-video-stream-duration");
9
+ const ensure_presentation_timestamp_1 = require("./ensure-presentation-timestamp");
10
+ const find_closest_package_json_1 = require("./find-closest-package-json");
11
+ const get_video_info_1 = require("./get-video-info");
12
+ const try_to_extract_frame_of_video_fast_1 = require("./try-to-extract-frame-of-video-fast");
13
+ exports.ACCEPTABLE_OFFSET_THRESHOLD = 50;
14
+ /**
15
+ * @description Probes whether frames of a video can be efficiently extracted when using <OffthreadVideo>.
16
+ * @see [Documentation](https://www.remotion.dev/docs/renderer/get-can-extract-frames-fast)
17
+ */
18
+ const getCanExtractFramesFast = async ({ src, ffmpegExecutable, ffprobeExecutable, }) => {
19
+ const remotionRoot = (0, find_closest_package_json_1.findRemotionRoot)();
20
+ const out = await (0, ensure_presentation_timestamp_1.ensurePresentationTimestampWithoutCache)({
21
+ ffmpegExecutable: ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : null,
22
+ ffprobeExecutable: ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : null,
23
+ remotionRoot,
24
+ src,
25
+ });
26
+ const { specialVcodecForTransparency: specialVcodec } = await (0, get_video_info_1.getVideoInfoUncached)({
27
+ src: out,
28
+ ffprobeExecutable: ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : null,
29
+ remotionRoot,
30
+ });
31
+ if (specialVcodec === 'vp8') {
32
+ fs_1.default.unlinkSync(out);
33
+ return {
34
+ canExtractFramesFast: false,
35
+ shouldReencode: false,
36
+ };
37
+ }
38
+ const { duration } = await (0, get_video_stream_duration_1.getVideoStreamDurationwithoutCache)({
39
+ ffprobeExecutable: ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : null,
40
+ remotionRoot,
41
+ src: out,
42
+ });
43
+ if (duration === null) {
44
+ fs_1.default.unlinkSync(out);
45
+ throw new Error(`Could not determine the duration of ${src} using FFMPEG. The file is not supported.`);
46
+ }
47
+ const actualOffset = `${duration * 1000 - exports.ACCEPTABLE_OFFSET_THRESHOLD}ms`;
48
+ const [stdErr] = await (0, try_to_extract_frame_of_video_fast_1.tryToExtractFrameOfVideoFast)({
49
+ actualOffset,
50
+ ffmpegExecutable: ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : null,
51
+ imageFormat: 'jpeg',
52
+ // Intentionally leaving needsResize as null, because we don't need to resize
53
+ needsResize: null,
54
+ remotionRoot,
55
+ specialVCodecForTransparency: specialVcodec,
56
+ src: out,
57
+ });
58
+ fs_1.default.unlinkSync(out);
59
+ const isEmpty = stdErr.includes('Output file is empty');
60
+ if (isEmpty) {
61
+ return {
62
+ canExtractFramesFast: false,
63
+ shouldReencode: true,
64
+ };
65
+ }
66
+ return {
67
+ canExtractFramesFast: true,
68
+ shouldReencode: false,
69
+ };
70
+ };
71
+ exports.getCanExtractFramesFast = getCanExtractFramesFast;
@@ -4,6 +4,8 @@ import type { BrowserLog } from './browser-log';
4
4
  import type { HeadlessBrowser } from './browser/Browser';
5
5
  import { type LogLevel } from './log-level';
6
6
  import type { ChromiumOptions } from './open-browser';
7
+ import type { ToOptions } from './options/option';
8
+ import type { optionsMap } from './options/options-map';
7
9
  import type { RemotionServer } from './prepare-server';
8
10
  type InternalGetCompositionsOptions = {
9
11
  serializedInputPropsWithCustomSchema: string;
@@ -18,7 +20,7 @@ type InternalGetCompositionsOptions = {
18
20
  indent: boolean;
19
21
  logLevel: LogLevel;
20
22
  serveUrlOrWebpackUrl: string;
21
- };
23
+ } & ToOptions<typeof optionsMap.getCompositions>;
22
24
  export type GetCompositionsOptions = {
23
25
  inputProps?: Record<string, unknown> | null;
24
26
  envVariables?: Record<string, string>;
@@ -29,8 +31,9 @@ export type GetCompositionsOptions = {
29
31
  chromiumOptions?: ChromiumOptions;
30
32
  port?: number | null;
31
33
  logLevel?: LogLevel;
34
+ offthreadVideoCacheSizeInBytes?: number | null;
32
35
  };
33
- export declare const internalGetCompositions: ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, }: InternalGetCompositionsOptions) => Promise<VideoConfig[]>;
36
+ export declare const internalGetCompositions: ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, offthreadVideoCacheSizeInBytes, }: InternalGetCompositionsOptions) => Promise<VideoConfig[]>;
34
37
  /**
35
38
  * @description Gets the compositions defined in a Remotion project based on a Webpack bundle.
36
39
  * @see [Documentation](https://www.remotion.dev/docs/renderer/get-compositions)
@@ -70,7 +70,7 @@ const innerGetCompositions = async ({ envVariables, serializedInputPropsWithCust
70
70
  };
71
71
  });
72
72
  };
73
- const internalGetCompositions = async ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, }) => {
73
+ const internalGetCompositions = async ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, offthreadVideoCacheSizeInBytes, }) => {
74
74
  const { page, cleanup: cleanupPage } = await (0, get_browser_instance_1.getPageAndCleanupFn)({
75
75
  passedInInstance: puppeteerInstance,
76
76
  browserExecutable,
@@ -95,6 +95,7 @@ const internalGetCompositions = async ({ browserExecutable, chromiumOptions, env
95
95
  concurrency: 1,
96
96
  logLevel,
97
97
  indent,
98
+ offthreadVideoCacheSizeInBytes,
98
99
  }, {
99
100
  onDownload: () => undefined,
100
101
  onError,
@@ -133,6 +134,7 @@ exports.internalGetCompositions = internalGetCompositions;
133
134
  * @see [Documentation](https://www.remotion.dev/docs/renderer/get-compositions)
134
135
  */
135
136
  const getCompositions = (serveUrlOrWebpackUrl, config) => {
137
+ var _a;
136
138
  const { browserExecutable, chromiumOptions, envVariables, inputProps, onBrowserLog, port, puppeteerInstance, timeoutInMilliseconds, logLevel, } = config !== null && config !== void 0 ? config : {};
137
139
  return (0, exports.internalGetCompositions)({
138
140
  browserExecutable: browserExecutable !== null && browserExecutable !== void 0 ? browserExecutable : null,
@@ -151,6 +153,7 @@ const getCompositions = (serveUrlOrWebpackUrl, config) => {
151
153
  server: undefined,
152
154
  timeoutInMilliseconds: timeoutInMilliseconds !== null && timeoutInMilliseconds !== void 0 ? timeoutInMilliseconds : TimeoutSettings_1.DEFAULT_TIMEOUT,
153
155
  logLevel: logLevel !== null && logLevel !== void 0 ? logLevel : (0, logger_1.getLogLevel)(),
156
+ offthreadVideoCacheSizeInBytes: (_a = config === null || config === void 0 ? void 0 : config.offthreadVideoCacheSizeInBytes) !== null && _a !== void 0 ? _a : null,
154
157
  });
155
158
  };
156
159
  exports.getCompositions = getCompositions;
@@ -15,8 +15,7 @@ const getFileExtensionFromCodec = (codec, audioCodec) => {
15
15
  if (!(typedAudioCodec in map.forAudioCodec)) {
16
16
  throw new Error(`Audio codec ${typedAudioCodec} is not supported for codec ${codec}`);
17
17
  }
18
- return map.forAudioCodec[audioCodec]
19
- .default;
18
+ return map.forAudioCodec[audioCodec].default;
20
19
  };
21
20
  exports.getFileExtensionFromCodec = getFileExtensionFromCodec;
22
21
  const makeFileExtensionMap = () => {
@@ -0,0 +1,17 @@
1
+ import type { OffthreadVideoImageFormat } from 'remotion';
2
+ import type { SpecialVCodecForTransparency } from './assets/download-map';
3
+ import type { FfmpegExecutable } from './ffmpeg-executable';
4
+ export declare const getFrameOfVideoSlow: ({ src, duration, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, offset, fps, remotionRoot, }: {
5
+ ffmpegExecutable: FfmpegExecutable;
6
+ src: string;
7
+ duration: number;
8
+ imageFormat: OffthreadVideoImageFormat;
9
+ specialVCodecForTransparency: SpecialVCodecForTransparency;
10
+ needsResize: [
11
+ number,
12
+ number
13
+ ] | null;
14
+ offset: number;
15
+ fps: number | null;
16
+ remotionRoot: string;
17
+ }) => Promise<Buffer>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getFrameOfVideoSlow = void 0;
7
+ // Uses no seeking, therefore the whole video has to be decoded. This is a last resort and should only happen
8
+ // if the video is corrupted
9
+ const execa_1 = __importDefault(require("execa"));
10
+ const determine_resize_params_1 = require("./determine-resize-params");
11
+ const determine_vcodec_ffmpeg_flags_1 = require("./determine-vcodec-ffmpeg-flags");
12
+ const ffmpeg_flags_1 = require("./ffmpeg-flags");
13
+ const truthy_1 = require("./truthy");
14
+ const getFrameOfVideoSlow = async ({ src, duration, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, offset, fps, remotionRoot, }) => {
15
+ console.warn(`\nUsing a slow method to extract the frame at ${duration}ms of ${src}. See https://remotion.dev/docs/slow-method-to-extract-frame for advice`);
16
+ const actualOffset = `-${duration * 1000 - offset}ms`;
17
+ const command = [
18
+ '-itsoffset',
19
+ actualOffset,
20
+ ...(0, determine_vcodec_ffmpeg_flags_1.determineVcodecFfmpegFlags)(specialVCodecForTransparency),
21
+ '-i',
22
+ src,
23
+ '-frames:v',
24
+ '1',
25
+ '-c:v',
26
+ imageFormat === 'jpeg' ? 'mjpeg' : 'png',
27
+ '-f',
28
+ 'image2pipe',
29
+ ...(0, determine_resize_params_1.determineResizeParams)(needsResize),
30
+ '-',
31
+ ].filter(truthy_1.truthy);
32
+ const { stdout, stderr } = (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)(ffmpegExecutable, remotionRoot, 'ffmpeg'), command);
33
+ if (!stderr) {
34
+ throw new Error('unexpectedly did not get stderr');
35
+ }
36
+ if (!stdout) {
37
+ throw new Error('unexpectedly did not get stdout');
38
+ }
39
+ const stderrChunks = [];
40
+ const stdoutChunks = [];
41
+ const stdErrString = new Promise((resolve, reject) => {
42
+ stderr.on('data', (d) => stderrChunks.push(d));
43
+ stderr.on('error', (err) => reject(err));
44
+ stderr.on('end', () => resolve(Buffer.concat(stderrChunks).toString('utf-8')));
45
+ });
46
+ const stdoutChunk = new Promise((resolve, reject) => {
47
+ stdout.on('data', (d) => stdoutChunks.push(d));
48
+ stdout.on('error', (err) => reject(err));
49
+ stdout.on('end', () => resolve(Buffer.concat(stdoutChunks)));
50
+ });
51
+ const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
52
+ const isEmpty = stdErr.includes('Output file is empty');
53
+ if (isEmpty) {
54
+ if (offset > 70) {
55
+ throw new Error(`Could not get last frame of ${src}. Tried to seek to the end using the command "ffmpeg ${command.join(' ')}" but got no frame. Most likely this video is corrupted.`);
56
+ }
57
+ return (0, exports.getFrameOfVideoSlow)({
58
+ ffmpegExecutable,
59
+ duration,
60
+ // Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
61
+ offset: offset + (fps === null ? 10 : 1000 / fps),
62
+ src,
63
+ imageFormat,
64
+ specialVCodecForTransparency,
65
+ needsResize,
66
+ fps,
67
+ remotionRoot,
68
+ });
69
+ }
70
+ return stdoutBuffer;
71
+ };
72
+ exports.getFrameOfVideoSlow = getFrameOfVideoSlow;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getSilentParts = void 0;
4
4
  const compositor_1 = require("./compositor/compositor");
5
5
  const getSilentParts = async ({ src, noiseThresholdInDecibels: passedNoiseThresholdInDecibels, minDurationInSeconds: passedMinDuration, logLevel, }) => {
6
- const compositor = (0, compositor_1.startLongRunningCompositor)((0, compositor_1.getIdealMaximumFrameCacheItems)(), logLevel !== null && logLevel !== void 0 ? logLevel : 'info', false);
6
+ const compositor = (0, compositor_1.startLongRunningCompositor)((0, compositor_1.getIdealMaximumFrameCacheSizeInBytes)(), logLevel !== null && logLevel !== void 0 ? logLevel : 'info', false);
7
7
  const minDurationInSeconds = passedMinDuration !== null && passedMinDuration !== void 0 ? passedMinDuration : 1;
8
8
  if (typeof minDurationInSeconds !== 'number') {
9
9
  throw new Error(`minDurationInSeconds must be a number, but was ${minDurationInSeconds}`);
@@ -0,0 +1,8 @@
1
+ import type { DownloadMap, Vp9Result } from './assets/download-map';
2
+ import type { FfmpegExecutable } from './ffmpeg-executable';
3
+ export declare function getVideoInfoUncached({ src, ffprobeExecutable, remotionRoot, }: {
4
+ src: string;
5
+ ffprobeExecutable: FfmpegExecutable;
6
+ remotionRoot: string;
7
+ }): Promise<Vp9Result>;
8
+ export declare const getVideoInfo: (downloadMap: DownloadMap, src: string, ffprobeExecutable: FfmpegExecutable, remotionRoot: string) => Promise<Vp9Result>;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getVideoInfo = exports.getVideoInfoUncached = void 0;
7
+ const execa_1 = __importDefault(require("execa"));
8
+ const calculate_sar_dar_pixels_1 = require("./calculate-sar-dar-pixels");
9
+ const ffmpeg_flags_1 = require("./ffmpeg-flags");
10
+ const p_limit_1 = require("./p-limit");
11
+ const limit = (0, p_limit_1.pLimit)(1);
12
+ async function getVideoInfoUncached({ src, ffprobeExecutable, remotionRoot, }) {
13
+ var _a;
14
+ const task = await (0, execa_1.default)(await (0, ffmpeg_flags_1.getExecutableBinary)(ffprobeExecutable, remotionRoot, 'ffprobe'), [src]);
15
+ const isVp9 = task.stderr.includes('Video: vp9');
16
+ const isVp8 = task.stderr.includes('Video: vp8');
17
+ const dimensions = (_a = task.stderr
18
+ .split('\n')
19
+ .find((n) => n.trim().startsWith('Stream #'))) === null || _a === void 0 ? void 0 : _a.match(/([0-9]{2,6})x([0-9]{2,6})/);
20
+ const dar = task.stderr.match(/DAR\s([0-9]+):([0-9]+)/);
21
+ let needsResize = null;
22
+ if (dimensions && dar) {
23
+ const width = parseInt(dimensions[1], 10);
24
+ const height = parseInt(dimensions[2], 10);
25
+ const darWidth = parseInt(dar[1], 10);
26
+ const darHeight = parseInt(dar[2], 10);
27
+ const { width: actualWidth, height: actualHeight } = (0, calculate_sar_dar_pixels_1.calculateDisplayVideoSize)({
28
+ darX: darWidth,
29
+ darY: darHeight,
30
+ x: width,
31
+ y: height,
32
+ });
33
+ if (actualWidth !== width || actualHeight !== height) {
34
+ needsResize = [actualWidth, actualHeight];
35
+ }
36
+ }
37
+ const result = {
38
+ specialVcodecForTransparency: isVp9 ? 'vp9' : isVp8 ? 'vp8' : 'none',
39
+ needsResize,
40
+ };
41
+ return result;
42
+ }
43
+ exports.getVideoInfoUncached = getVideoInfoUncached;
44
+ async function getVideoInfoUnlimited(downloadMap, src, ffprobeExecutable, remotionRoot) {
45
+ if (typeof downloadMap.isVp9VideoCache[src] !== 'undefined') {
46
+ return downloadMap.isVp9VideoCache[src];
47
+ }
48
+ const result = await getVideoInfoUncached({
49
+ ffprobeExecutable,
50
+ remotionRoot,
51
+ src,
52
+ });
53
+ downloadMap.isVp9VideoCache[src] = result;
54
+ return downloadMap.isVp9VideoCache[src];
55
+ }
56
+ const getVideoInfo = (downloadMap, src, ffprobeExecutable, remotionRoot) => {
57
+ return limit(() => getVideoInfoUnlimited(downloadMap, src, ffprobeExecutable, remotionRoot));
58
+ };
59
+ exports.getVideoInfo = getVideoInfo;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getVideoMetadata = void 0;
4
4
  const compositor_1 = require("./compositor/compositor");
5
5
  const getVideoMetadata = async (videoSource) => {
6
- const compositor = (0, compositor_1.startLongRunningCompositor)((0, compositor_1.getIdealMaximumFrameCacheItems)(), 'info', false);
6
+ const compositor = (0, compositor_1.startLongRunningCompositor)((0, compositor_1.getIdealMaximumFrameCacheSizeInBytes)(), 'info', false);
7
7
  const metadataResponse = await compositor.executeCommand('GetVideoMetadata', {
8
8
  src: videoSource,
9
9
  });
@@ -1,8 +1,8 @@
1
1
  import type { PixelFormat } from './pixel-format';
2
2
  export declare const validVideoImageFormats: readonly ["png", "jpeg", "none"];
3
3
  export declare const validStillImageFormats: readonly ["png", "jpeg", "pdf", "webp"];
4
- export type VideoImageFormat = typeof validVideoImageFormats[number];
5
- export type StillImageFormat = typeof validStillImageFormats[number];
4
+ export type VideoImageFormat = (typeof validVideoImageFormats)[number];
5
+ export type StillImageFormat = (typeof validStillImageFormats)[number];
6
6
  /**
7
7
  * @deprecated Use VideoImageFormat or StillImageFormat instead
8
8
  */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import execa from 'execa';
3
2
  import { HeadlessBrowser } from './browser/Browser';
4
3
  import { SymbolicateableError } from './error-handling/symbolicateable-error';
@@ -24,7 +23,7 @@ export type { LogLevel } from './log-level';
24
23
  export { CancelSignal, makeCancelSignal } from './make-cancel-signal';
25
24
  export { openBrowser } from './open-browser';
26
25
  export type { ChromiumOptions } from './open-browser';
27
- export { RemotionOption } from './options/option';
26
+ export { AnyRemotionOption, RemotionOption, ToOptions } from './options/option';
28
27
  export { PixelFormat } from './pixel-format';
29
28
  export { RemotionServer } from './prepare-server';
30
29
  export { ProResProfile } from './prores-profile';
@@ -48,6 +47,7 @@ export declare const RenderInternals: {
48
47
  concurrency: number;
49
48
  logLevel: "verbose" | "info" | "warn" | "error";
50
49
  indent: boolean;
50
+ offthreadVideoCacheSizeInBytes: number | null;
51
51
  }) => Promise<{
52
52
  port: number;
53
53
  close: () => Promise<void>;
@@ -116,8 +116,8 @@ export declare const RenderInternals: {
116
116
  validPixelFormats: readonly ["yuv420p", "yuva420p", "yuv422p", "yuv444p", "yuv420p10le", "yuv422p10le", "yuv444p10le", "yuva444p10le"];
117
117
  DEFAULT_BROWSER: "chrome";
118
118
  validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
119
- DEFAULT_OPENGL_RENDERER: "angle" | "swangle" | "egl" | "swiftshader" | null;
120
- validateOpenGlRenderer: (option: "angle" | "swangle" | "egl" | "swiftshader" | null) => "angle" | "swangle" | "egl" | "swiftshader" | null;
119
+ DEFAULT_OPENGL_RENDERER: "swangle" | "angle" | "egl" | "swiftshader" | null;
120
+ validateOpenGlRenderer: (option: "swangle" | "angle" | "egl" | "swiftshader" | null) => "swangle" | "angle" | "egl" | "swiftshader" | null;
121
121
  validCodecs: readonly ["h264", "h265", "vp8", "vp9", "mp3", "aac", "wav", "prores", "h264-mkv", "gif"];
122
122
  DEFAULT_PIXEL_FORMAT: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le";
123
123
  validateJpegQuality: (q: number | undefined) => void;
@@ -305,8 +305,8 @@ export declare const RenderInternals: {
305
305
  };
306
306
  validStillImageFormats: readonly ["png", "jpeg", "pdf", "webp"];
307
307
  validVideoImageFormats: readonly ["png", "jpeg", "none"];
308
- DEFAULT_STILL_IMAGE_FORMAT: "jpeg" | "png" | "webp" | "pdf";
309
- DEFAULT_VIDEO_IMAGE_FORMAT: "jpeg" | "png" | "none";
308
+ DEFAULT_STILL_IMAGE_FORMAT: "png" | "jpeg" | "pdf" | "webp";
309
+ DEFAULT_VIDEO_IMAGE_FORMAT: "png" | "jpeg" | "none";
310
310
  DEFAULT_JPEG_QUALITY: number;
311
311
  chalk: {
312
312
  enabled: () => boolean;
@@ -389,13 +389,14 @@ export declare const RenderInternals: {
389
389
  INDENT_TOKEN: string;
390
390
  isColorSupported: () => boolean;
391
391
  HeadlessBrowser: typeof HeadlessBrowser;
392
- prepareServer: ({ webpackConfigOrServeUrl, port, remotionRoot, concurrency, logLevel, indent, }: {
392
+ prepareServer: ({ webpackConfigOrServeUrl, port, remotionRoot, concurrency, logLevel, indent, offthreadVideoCacheSizeInBytes, }: {
393
393
  webpackConfigOrServeUrl: string;
394
394
  port: number | null;
395
395
  remotionRoot: string;
396
396
  concurrency: number;
397
397
  logLevel: "verbose" | "info" | "warn" | "error";
398
398
  indent: boolean;
399
+ offthreadVideoCacheSizeInBytes: number | null;
399
400
  }) => Promise<import("./prepare-server").RemotionServer>;
400
401
  makeOrReuseServer: (server: import("./prepare-server").RemotionServer | undefined, config: {
401
402
  webpackConfigOrServeUrl: string;
@@ -404,6 +405,7 @@ export declare const RenderInternals: {
404
405
  concurrency: number;
405
406
  logLevel: "verbose" | "info" | "warn" | "error";
406
407
  indent: boolean;
408
+ offthreadVideoCacheSizeInBytes: number | null;
407
409
  }, { onDownload, onError, }: {
408
410
  onError: (err: Error) => void;
409
411
  onDownload: import("./assets/download-and-map-assets-to-file").RenderMediaOnDownload | null;
@@ -417,7 +419,7 @@ export declare const RenderInternals: {
417
419
  frame: number;
418
420
  serializedInputPropsWithCustomSchema: string;
419
421
  serializedResolvedPropsWithCustomSchema: string;
420
- imageFormat: "jpeg" | "png" | "webp" | "pdf";
422
+ imageFormat: "png" | "jpeg" | "pdf" | "webp";
421
423
  jpegQuality: number;
422
424
  puppeteerInstance: HeadlessBrowser | null;
423
425
  envVariables: Record<string, string>;
@@ -434,7 +436,15 @@ export declare const RenderInternals: {
434
436
  logLevel: "verbose" | "info" | "warn" | "error";
435
437
  serveUrl: string;
436
438
  port: number | null;
437
- }) => Promise<{
439
+ offthreadVideoCacheSizeInBytes: number | null;
440
+ } & import("./options/option").ToOptions<readonly [{
441
+ name: string;
442
+ cliFlag: "offthreadvideo-cache-size-in-bytes";
443
+ description: JSX.Element;
444
+ ssrName: "offthreadVideoCacheSizeInBytes";
445
+ docLink: string;
446
+ type: number | null;
447
+ }]>) => Promise<{
438
448
  buffer: Buffer | null;
439
449
  }>;
440
450
  internalOpenBrowser: ({ browser, browserExecutable, chromiumOptions, forceDeviceScaleFactor, indent, viewport, logLevel, }: {
@@ -460,11 +470,18 @@ export declare const RenderInternals: {
460
470
  logLevel: "verbose" | "info" | "warn" | "error";
461
471
  serveUrl: string;
462
472
  id: string;
463
- }) => Promise<{
473
+ } & import("./options/option").ToOptions<readonly [{
474
+ name: string;
475
+ cliFlag: "offthreadvideo-cache-size-in-bytes";
476
+ description: JSX.Element;
477
+ ssrName: "offthreadVideoCacheSizeInBytes";
478
+ docLink: string;
479
+ type: number | null;
480
+ }]>) => Promise<{
464
481
  metadata: import("remotion").VideoConfig;
465
482
  propsSize: number;
466
483
  }>;
467
- internalGetCompositions: ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, }: {
484
+ internalGetCompositions: ({ browserExecutable, chromiumOptions, envVariables, indent, serializedInputPropsWithCustomSchema, onBrowserLog, port, puppeteerInstance, serveUrlOrWebpackUrl, server, timeoutInMilliseconds, logLevel, offthreadVideoCacheSizeInBytes, }: {
468
485
  serializedInputPropsWithCustomSchema: string;
469
486
  envVariables: Record<string, string>;
470
487
  puppeteerInstance: HeadlessBrowser | undefined;
@@ -477,9 +494,16 @@ export declare const RenderInternals: {
477
494
  indent: boolean;
478
495
  logLevel: "verbose" | "info" | "warn" | "error";
479
496
  serveUrlOrWebpackUrl: string;
480
- }) => Promise<import("remotion").VideoConfig[]>;
481
- internalRenderFrames: ({ browserExecutable, cancelSignal, chromiumOptions, composition, concurrency, envVariables, everyNthFrame, frameRange, imageFormat, indent, jpegQuality, muted, onBrowserLog, onDownload, onFrameBuffer, onFrameUpdate, onStart, outputDir, port, puppeteerInstance, scale, server, timeoutInMilliseconds, logLevel, webpackBundleOrServeUrl, serializedInputPropsWithCustomSchema, serializedResolvedPropsWithCustomSchema, }: import("./render-frames").InternalRenderFramesOptions) => Promise<import("./types").RenderFramesOutput>;
482
- internalRenderMedia: ({ proResProfile, x264Preset, crf, composition, serializedInputPropsWithCustomSchema, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, audioCodec, concurrency, disallowParallelEncoding, everyNthFrame, imageFormat: provisionalImageFormat, indent, jpegQuality, numberOfGifLoops, onCtrlCExit, preferLossless, serveUrl, server: reusedServer, logLevel, serializedResolvedPropsWithCustomSchema, }: import("./render-media").InternalRenderMediaOptions) => Promise<{
497
+ } & import("./options/option").ToOptions<readonly [{
498
+ name: string;
499
+ cliFlag: "offthreadvideo-cache-size-in-bytes";
500
+ description: JSX.Element;
501
+ ssrName: "offthreadVideoCacheSizeInBytes";
502
+ docLink: string;
503
+ type: number | null;
504
+ }]>) => Promise<import("remotion").VideoConfig[]>;
505
+ internalRenderFrames: ({ browserExecutable, cancelSignal, chromiumOptions, composition, concurrency, envVariables, everyNthFrame, frameRange, imageFormat, indent, jpegQuality, muted, onBrowserLog, onDownload, onFrameBuffer, onFrameUpdate, onStart, outputDir, port, puppeteerInstance, scale, server, timeoutInMilliseconds, logLevel, webpackBundleOrServeUrl, serializedInputPropsWithCustomSchema, serializedResolvedPropsWithCustomSchema, offthreadVideoCacheSizeInBytes, }: import("./render-frames").InternalRenderFramesOptions) => Promise<import("./types").RenderFramesOutput>;
506
+ internalRenderMedia: ({ proResProfile, x264Preset, crf, composition, serializedInputPropsWithCustomSchema, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, audioCodec, concurrency, disallowParallelEncoding, everyNthFrame, imageFormat: provisionalImageFormat, indent, jpegQuality, numberOfGifLoops, onCtrlCExit, preferLossless, serveUrl, server: reusedServer, logLevel, serializedResolvedPropsWithCustomSchema, offthreadVideoCacheSizeInBytes, }: import("./render-media").InternalRenderMediaOptions) => Promise<{
483
507
  buffer: Buffer | null;
484
508
  slowestFrames: import("./render-media").SlowFrame[];
485
509
  }>;
@@ -0,0 +1,3 @@
1
+ import type { DownloadMap } from './assets/download-map';
2
+ export declare const isBeyondLastFrame: (downloadMap: DownloadMap, src: string, time: number) => boolean | 0;
3
+ export declare const markAsBeyondLastFrame: (downloadMap: DownloadMap, src: string, time: number) => void;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.markAsBeyondLastFrame = exports.isBeyondLastFrame = void 0;
4
+ const isBeyondLastFrame = (downloadMap, src, time) => {
5
+ return (downloadMap.isBeyondLastFrameMap[src] &&
6
+ time >= downloadMap.isBeyondLastFrameMap[src]);
7
+ };
8
+ exports.isBeyondLastFrame = isBeyondLastFrame;
9
+ const markAsBeyondLastFrame = (downloadMap, src, time) => {
10
+ downloadMap.isBeyondLastFrameMap[src] = time;
11
+ };
12
+ exports.markAsBeyondLastFrame = markAsBeyondLastFrame;
@@ -0,0 +1,17 @@
1
+ import type { OffthreadVideoImageFormat } from 'remotion';
2
+ import type { DownloadMap, SpecialVCodecForTransparency } from './assets/download-map';
3
+ import type { FfmpegExecutable } from './ffmpeg-executable';
4
+ export type LastFrameOptions = {
5
+ ffmpegExecutable: FfmpegExecutable;
6
+ ffprobeExecutable: FfmpegExecutable;
7
+ offset: number;
8
+ src: string;
9
+ specialVCodecForTransparency: SpecialVCodecForTransparency;
10
+ imageFormat: OffthreadVideoImageFormat;
11
+ needsResize: [number, number] | null;
12
+ downloadMap: DownloadMap;
13
+ remotionRoot: string;
14
+ };
15
+ export declare const setLastFrameInCache: (options: LastFrameOptions, data: Buffer) => void;
16
+ export declare const getLastFrameFromCache: (options: LastFrameOptions) => Buffer | null;
17
+ export declare const clearLastFileCache: (downloadMap: DownloadMap) => void;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ // OffthreadVideo requires sometimes that the last frame of a video gets extracted, however, this can be slow. We allocate a cache for it but that can be garbage collected
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.clearLastFileCache = exports.getLastFrameFromCache = exports.setLastFrameInCache = void 0;
5
+ const MAX_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
6
+ let bufferSize = 0;
7
+ const makeLastFrameCacheKey = (options) => {
8
+ return [
9
+ options.ffmpegExecutable,
10
+ options.offset,
11
+ options.src,
12
+ options.imageFormat,
13
+ options.downloadMap.id,
14
+ ].join('-');
15
+ };
16
+ const setLastFrameInCache = (options, data) => {
17
+ const key = makeLastFrameCacheKey(options);
18
+ if (options.downloadMap.lastFrameMap[key]) {
19
+ bufferSize -= options.downloadMap.lastFrameMap[key].data.byteLength;
20
+ }
21
+ options.downloadMap.lastFrameMap[key] = { data, lastAccessed: Date.now() };
22
+ bufferSize += data.byteLength;
23
+ ensureMaxSize(options.downloadMap);
24
+ };
25
+ exports.setLastFrameInCache = setLastFrameInCache;
26
+ const getLastFrameFromCache = (options) => {
27
+ var _a;
28
+ const key = makeLastFrameCacheKey(options);
29
+ if (!options.downloadMap.lastFrameMap[key]) {
30
+ return null;
31
+ }
32
+ options.downloadMap.lastFrameMap[key].lastAccessed = Date.now();
33
+ return (_a = options.downloadMap.lastFrameMap[key].data) !== null && _a !== void 0 ? _a : null;
34
+ };
35
+ exports.getLastFrameFromCache = getLastFrameFromCache;
36
+ const removedLastFrameFromCache = (key, downloadMap) => {
37
+ if (!downloadMap.lastFrameMap[key]) {
38
+ return;
39
+ }
40
+ bufferSize -= downloadMap.lastFrameMap[key].data.byteLength;
41
+ delete downloadMap.lastFrameMap[key];
42
+ };
43
+ const ensureMaxSize = (downloadMap) => {
44
+ // eslint-disable-next-line no-unmodified-loop-condition
45
+ while (bufferSize > MAX_CACHE_SIZE) {
46
+ const earliest = Object.entries(downloadMap.lastFrameMap).sort((a, b) => {
47
+ return a[1].lastAccessed - b[1].lastAccessed;
48
+ })[0];
49
+ removedLastFrameFromCache(earliest[0], downloadMap);
50
+ }
51
+ };
52
+ const clearLastFileCache = (downloadMap) => {
53
+ downloadMap.lastFrameMap = {};
54
+ };
55
+ exports.clearLastFileCache = clearLastFileCache;
@@ -0,0 +1,9 @@
1
+ export type ServeUrlOrWebpackBundle = {
2
+ serveUrl: string;
3
+ } | {
4
+ /**
5
+ * @deprecated Renamed to `serveUrl`
6
+ */
7
+ webpackBundle: string;
8
+ };
9
+ export declare const getServeUrlWithFallback: (serve: ServeUrlOrWebpackBundle) => string;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getServeUrlWithFallback = void 0;
4
+ const getServeUrlWithFallback = (serve) => {
5
+ if ('webpackBundle' in serve) {
6
+ return serve.webpackBundle;
7
+ }
8
+ if ('serveUrl' in serve) {
9
+ return serve.serveUrl;
10
+ }
11
+ throw new Error('You must pass the `serveUrl` parameter');
12
+ };
13
+ exports.getServeUrlWithFallback = getServeUrlWithFallback;
@@ -1,4 +1,4 @@
1
1
  export declare const logLevels: readonly ["verbose", "info", "warn", "error"];
2
- export type LogLevel = typeof logLevels[number];
2
+ export type LogLevel = (typeof logLevels)[number];
3
3
  export declare const isValidLogLevel: (level: string) => boolean;
4
4
  export declare const isEqualOrBelowLogLevel: (currentLevel: LogLevel, level: LogLevel) => boolean;
@@ -7,8 +7,9 @@ export declare const extractUrlAndSourceFromUrl: (url: string) => {
7
7
  time: number;
8
8
  transparent: boolean;
9
9
  };
10
- export declare const startOffthreadVideoServer: ({ downloadMap, concurrency, logLevel, indent, }: {
10
+ export declare const startOffthreadVideoServer: ({ downloadMap, concurrency, logLevel, indent, offthreadVideoCacheSizeInBytes, }: {
11
11
  downloadMap: DownloadMap;
12
+ offthreadVideoCacheSizeInBytes: number | null;
12
13
  concurrency: number;
13
14
  logLevel: LogLevel;
14
15
  indent: boolean;
@@ -6,6 +6,7 @@ const download_and_map_assets_to_file_1 = require("./assets/download-and-map-ass
6
6
  const compositor_1 = require("./compositor/compositor");
7
7
  const log_level_1 = require("./log-level");
8
8
  const logger_1 = require("./logger");
9
+ const offthreadvideo_cache_size_1 = require("./options/offthreadvideo-cache-size");
9
10
  const extractUrlAndSourceFromUrl = (url) => {
10
11
  const parsed = new URL(url, 'http://localhost');
11
12
  const query = parsed.search;
@@ -30,10 +31,11 @@ const extractUrlAndSourceFromUrl = (url) => {
30
31
  };
31
32
  exports.extractUrlAndSourceFromUrl = extractUrlAndSourceFromUrl;
32
33
  const REQUEST_CLOSED_TOKEN = 'Request closed';
33
- const startOffthreadVideoServer = ({ downloadMap, concurrency, logLevel, indent, }) => {
34
+ const startOffthreadVideoServer = ({ downloadMap, concurrency, logLevel, indent, offthreadVideoCacheSizeInBytes, }) => {
35
+ (0, offthreadvideo_cache_size_1.validateOffthreadVideoCacheSizeInBytes)(offthreadVideoCacheSizeInBytes);
34
36
  const compositor = (0, compositor_1.startCompositor)('StartLongRunningProcess', {
35
37
  concurrency,
36
- maximum_frame_cache_items: (0, compositor_1.getIdealMaximumFrameCacheItems)(),
38
+ maximum_frame_cache_size_in_bytes: offthreadVideoCacheSizeInBytes !== null && offthreadVideoCacheSizeInBytes !== void 0 ? offthreadVideoCacheSizeInBytes : (0, compositor_1.getIdealMaximumFrameCacheSizeInBytes)(),
37
39
  verbose: (0, log_level_1.isEqualOrBelowLogLevel)(logLevel, 'verbose'),
38
40
  }, logLevel, indent);
39
41
  return {