@remotion/renderer 3.1.7 → 3.1.11

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.
@@ -109,7 +109,6 @@ const downloadAsset = async ({ src, onDownload, downloadMap, }) => {
109
109
  downloadMap.isDownloadingMap[src][downloadDir] = false;
110
110
  }
111
111
  if ((_c = downloadMap.isDownloadingMap[src]) === null || _c === void 0 ? void 0 : _c[downloadDir]) {
112
- console.log('TRIGGER WAIT', { src, downloadDir });
113
112
  return waitForAssetToBeDownloaded({ downloadMap, src, downloadDir });
114
113
  }
115
114
  if (!downloadMap.isDownloadingMap[src]) {
@@ -46,6 +46,12 @@ export declare type DownloadMap = {
46
46
  videoDurationResultCache: Record<string, VideoDurationResult>;
47
47
  durationOfAssetCache: Record<string, AudioChannelsAndDurationResultCache>;
48
48
  downloadDir: string;
49
+ preEncode: string;
50
+ audioMixing: string;
51
+ complexFilter: string;
52
+ audioPreprocessing: string;
53
+ stitchFrames: string;
54
+ assetDir: string;
49
55
  };
50
56
  export declare type RenderAssetInfo = {
51
57
  assets: TAsset[][];
@@ -54,4 +60,5 @@ export declare type RenderAssetInfo = {
54
60
  downloadMap: DownloadMap;
55
61
  };
56
62
  export declare const makeDownloadMap: () => DownloadMap;
63
+ export declare const cleanDownloadMap: (downloadMap: DownloadMap) => Promise<void>;
57
64
  export {};
@@ -1,8 +1,49 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
2
28
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeDownloadMap = void 0;
29
+ exports.cleanDownloadMap = exports.makeDownloadMap = void 0;
30
+ const fs_1 = __importStar(require("fs"));
31
+ const path_1 = __importDefault(require("path"));
32
+ const delete_directory_1 = require("../delete-directory");
4
33
  const tmp_dir_1 = require("../tmp-dir");
34
+ const makeAndReturn = (dir, name) => {
35
+ const p = path_1.default.join(dir, name);
36
+ (0, fs_1.mkdirSync)(p);
37
+ return p;
38
+ };
39
+ const packageJsonPath = path_1.default.join(__dirname, '..', '..', 'package.json');
40
+ const packageJson = fs_1.default.existsSync(packageJsonPath)
41
+ ? JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'))
42
+ : null;
5
43
  const makeDownloadMap = () => {
44
+ const dir = (0, tmp_dir_1.tmpDir)(packageJson
45
+ ? `remotion-v${packageJson.version.replace(/\./g, '-')}-assets`
46
+ : 'remotion-assets');
6
47
  return {
7
48
  isDownloadingMap: {},
8
49
  hasBeenDownloadedMap: {},
@@ -14,7 +55,19 @@ const makeDownloadMap = () => {
14
55
  videoDurationResultCache: {},
15
56
  durationOfAssetCache: {},
16
57
  id: String(Math.random()),
17
- downloadDir: (0, tmp_dir_1.tmpDir)('remotion-assets-dir'),
58
+ assetDir: dir,
59
+ downloadDir: makeAndReturn(dir, 'remotion-assets-dir'),
60
+ complexFilter: makeAndReturn(dir, 'remotion-complex-filter'),
61
+ preEncode: makeAndReturn(dir, 'pre-encode'),
62
+ audioMixing: makeAndReturn(dir, 'remotion-audio-mixing'),
63
+ audioPreprocessing: makeAndReturn(dir, 'remotion-audio-preprocessing'),
64
+ stitchFrames: makeAndReturn(dir, 'remotion-stitch-temp-dir'),
18
65
  };
19
66
  };
20
67
  exports.makeDownloadMap = makeDownloadMap;
68
+ const cleanDownloadMap = async (downloadMap) => {
69
+ await (0, delete_directory_1.deleteDirectory)(downloadMap.downloadDir);
70
+ await (0, delete_directory_1.deleteDirectory)(downloadMap.complexFilter);
71
+ await (0, delete_directory_1.deleteDirectory)(downloadMap.assetDir);
72
+ };
73
+ exports.cleanDownloadMap = cleanDownloadMap;
@@ -1,4 +1,5 @@
1
- export declare const createFfmpegComplexFilter: (filters: number) => Promise<{
1
+ import type { DownloadMap } from './assets/download-map';
2
+ export declare const createFfmpegComplexFilter: (filters: number, downloadMap: DownloadMap) => Promise<{
2
3
  complexFilterFlag: [string, string] | null;
3
4
  cleanup: () => void;
4
5
  }>;
@@ -1,23 +1,14 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.createFfmpegComplexFilter = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
4
  const create_ffmpeg_merge_filter_1 = require("./create-ffmpeg-merge-filter");
10
5
  const ffmpeg_filter_file_1 = require("./ffmpeg-filter-file");
11
- const tmp_dir_1 = require("./tmp-dir");
12
- const createFfmpegComplexFilter = async (filters) => {
6
+ const createFfmpegComplexFilter = async (filters, downloadMap) => {
13
7
  if (filters === 0) {
14
8
  return { complexFilterFlag: null, cleanup: () => undefined };
15
9
  }
16
10
  const complexFilter = (0, create_ffmpeg_merge_filter_1.createFfmpegMergeFilter)(filters);
17
- const { file, cleanup } = await (0, ffmpeg_filter_file_1.makeFfmpegFilterFile)(complexFilter);
18
- const tempPath = (0, tmp_dir_1.tmpDir)('remotion-complex-filter-script');
19
- const filterFile = path_1.default.join(tempPath, 'complex-filter.txt');
20
- await fs_1.default.promises.writeFile(filterFile, complexFilter);
11
+ const { file, cleanup } = await (0, ffmpeg_filter_file_1.makeFfmpegFilterFile)(complexFilter, downloadMap);
21
12
  return {
22
13
  complexFilterFlag: ['-filter_complex_script', file],
23
14
  cleanup,
@@ -1,4 +1,5 @@
1
- export declare const makeFfmpegFilterFile: (complexFilter: string) => Promise<{
1
+ import type { DownloadMap } from './assets/download-map';
2
+ export declare const makeFfmpegFilterFile: (complexFilter: string, downloadMap: DownloadMap) => Promise<{
2
3
  file: string;
3
4
  cleanup: () => void;
4
5
  }>;
@@ -8,16 +8,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.makeFfmpegFilterFile = void 0;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
- const delete_directory_1 = require("./delete-directory");
12
- const tmp_dir_1 = require("./tmp-dir");
13
- const makeFfmpegFilterFile = async (complexFilter) => {
14
- const tempPath = (0, tmp_dir_1.tmpDir)('remotion-complex-filter');
15
- const filterFile = path_1.default.join(tempPath, 'complex-filter.txt');
11
+ const makeFfmpegFilterFile = async (complexFilter, downloadMap) => {
12
+ const random = Math.random().toString().replace('.', '');
13
+ const filterFile = path_1.default.join(downloadMap.complexFilter, 'complex-filter-' + random + '.txt');
16
14
  await fs_1.default.promises.writeFile(filterFile, complexFilter);
17
15
  return {
18
16
  file: filterFile,
19
17
  cleanup: () => {
20
- (0, delete_directory_1.deleteDirectory)(filterFile);
18
+ fs_1.default.unlinkSync(filterFile);
21
19
  },
22
20
  };
23
21
  };
@@ -89,6 +89,10 @@ const getCompositions = async (serveUrlOrWebpackUrl, config) => {
89
89
  cleanup();
90
90
  close === null || close === void 0 ? void 0 : close();
91
91
  cleanupPageError();
92
+ // Clean download map if it was not passed in
93
+ if (!(config === null || config === void 0 ? void 0 : config.downloadMap)) {
94
+ (0, download_map_1.cleanDownloadMap)(downloadMap);
95
+ }
92
96
  });
93
97
  });
94
98
  };
@@ -1,2 +1,2 @@
1
1
  import type { Codec } from './codec';
2
- export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
2
+ export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
@@ -0,0 +1 @@
1
+ export declare const getIdealVideoThreadsFlag: () => number;
@@ -0,0 +1,18 @@
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.getIdealVideoThreadsFlag = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const MEMORY_USAGE_PER_THREAD = 400000000; // 400MB
9
+ const RESERVED_MEMORY = 2000000000;
10
+ const getIdealVideoThreadsFlag = () => {
11
+ const freeMemory = os_1.default.freemem();
12
+ const cpus = os_1.default.cpus().length;
13
+ const maxRecommendedBasedOnCpus = (cpus * 2) / 3;
14
+ const maxRecommendedBasedOnMemory = (freeMemory - RESERVED_MEMORY) / MEMORY_USAGE_PER_THREAD;
15
+ const maxRecommended = Math.min(maxRecommendedBasedOnCpus, maxRecommendedBasedOnMemory);
16
+ return Math.max(1, Math.round(maxRecommended));
17
+ };
18
+ exports.getIdealVideoThreadsFlag = getIdealVideoThreadsFlag;
@@ -1 +1 @@
1
- export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
1
+ export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "mp4" | "webm">;
package/dist/index.d.ts CHANGED
@@ -68,7 +68,7 @@ export declare const RenderInternals: {
68
68
  task: Promise<Buffer | null>;
69
69
  getLogs: () => string;
70
70
  }>;
71
- getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
71
+ getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
72
72
  tmpDir: (str: string) => string;
73
73
  deleteDirectory: (directory: string) => Promise<void>;
74
74
  isServeUrl: (potentialUrl: string) => boolean;
@@ -123,8 +123,8 @@ export declare const RenderInternals: {
123
123
  validPixelFormats: readonly ["yuv420p", "yuva420p", "yuv422p", "yuv444p", "yuv420p10le", "yuv422p10le", "yuv444p10le", "yuva444p10le"];
124
124
  DEFAULT_BROWSER: import("./browser").Browser;
125
125
  validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
126
- DEFAULT_OPENGL_RENDERER: "angle" | "swangle" | "egl" | "swiftshader" | null;
127
- validateOpenGlRenderer: (option: "angle" | "swangle" | "egl" | "swiftshader" | null) => "angle" | "swangle" | "egl" | "swiftshader" | null;
126
+ DEFAULT_OPENGL_RENDERER: "swangle" | "angle" | "egl" | "swiftshader" | null;
127
+ validateOpenGlRenderer: (option: "swangle" | "angle" | "egl" | "swiftshader" | null) => "swangle" | "angle" | "egl" | "swiftshader" | null;
128
128
  getDefaultCrfForCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => number;
129
129
  validateSelectedCrfAndCodecCombination: (crf: unknown, codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
130
130
  validImageFormats: readonly ["png", "jpeg", "none"];
@@ -136,14 +136,15 @@ export declare const RenderInternals: {
136
136
  DEFAULT_TIMEOUT: number;
137
137
  getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
138
138
  validateSelectedPixelFormatAndCodecCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
139
- validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "proxy" | "4444-xq" | "4444" | "hq" | "standard" | "light" | undefined) => void;
140
- validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "jpeg" | "png" | "none") => "none" | "valid";
139
+ validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "4444-xq" | "4444" | "hq" | "standard" | "light" | "proxy" | undefined) => void;
140
+ validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "png" | "jpeg" | "none") => "none" | "valid";
141
141
  DEFAULT_CODEC: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
142
142
  isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
143
143
  logLevels: readonly ["verbose", "info", "warn", "error"];
144
- isEqualOrBelowLogLevel: (currentLevel: "error" | "verbose" | "info" | "warn", level: "error" | "verbose" | "info" | "warn") => boolean;
144
+ isEqualOrBelowLogLevel: (currentLevel: "verbose" | "error" | "info" | "warn", level: "verbose" | "error" | "info" | "warn") => boolean;
145
145
  isValidLogLevel: (level: string) => boolean;
146
146
  validateEveryNthFrame: (everyNthFrame: unknown) => void;
147
147
  perf: typeof perf;
148
148
  makeDownloadMap: () => import("./assets/download-map").DownloadMap;
149
+ cleanDownloadMap: (downloadMap: import("./assets/download-map").DownloadMap) => Promise<void>;
149
150
  };
package/dist/index.js CHANGED
@@ -157,4 +157,5 @@ exports.RenderInternals = {
157
157
  validateEveryNthFrame: validate_every_nth_frame_1.validateEveryNthFrame,
158
158
  perf,
159
159
  makeDownloadMap: download_map_1.makeDownloadMap,
160
+ cleanDownloadMap: download_map_1.cleanDownloadMap,
160
161
  };
@@ -1,9 +1,11 @@
1
+ import type { DownloadMap } from './assets/download-map';
1
2
  import type { FfmpegExecutable } from './ffmpeg-executable';
2
3
  declare type Options = {
3
4
  ffmpegExecutable: FfmpegExecutable;
4
5
  files: string[];
5
6
  outName: string;
6
7
  numberOfSeconds: number;
8
+ downloadMap: DownloadMap;
7
9
  };
8
10
  export declare const mergeAudioTrack: (options: Options) => Promise<void>;
9
11
  export {};
@@ -14,7 +14,7 @@ const delete_directory_1 = require("./delete-directory");
14
14
  const p_limit_1 = require("./p-limit");
15
15
  const tmp_dir_1 = require("./tmp-dir");
16
16
  const truthy_1 = require("./truthy");
17
- const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numberOfSeconds, }) => {
17
+ const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numberOfSeconds, downloadMap, }) => {
18
18
  if (files.length === 0) {
19
19
  await (0, create_silent_audio_1.createSilentAudio)({
20
20
  outName,
@@ -42,6 +42,7 @@ const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numb
42
42
  files: chunkFiles,
43
43
  numberOfSeconds,
44
44
  outName: chunkOutname,
45
+ downloadMap,
45
46
  });
46
47
  return chunkOutname;
47
48
  }));
@@ -50,11 +51,12 @@ const mergeAudioTrackUnlimited = async ({ ffmpegExecutable, outName, files, numb
50
51
  files: chunkNames,
51
52
  numberOfSeconds,
52
53
  outName,
54
+ downloadMap,
53
55
  });
54
56
  await (0, delete_directory_1.deleteDirectory)(tempPath);
55
57
  return;
56
58
  }
57
- const { complexFilterFlag: mergeFilter, cleanup } = await (0, create_ffmpeg_complex_filter_1.createFfmpegComplexFilter)(files.length);
59
+ const { complexFilterFlag: mergeFilter, cleanup } = await (0, create_ffmpeg_complex_filter_1.createFfmpegComplexFilter)(files.length, downloadMap);
58
60
  const args = [
59
61
  ...files.map((f) => ['-i', f]),
60
62
  mergeFilter,
@@ -9,6 +9,7 @@ const os_1 = __importDefault(require("os"));
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const node_1 = require("./browser/node");
11
11
  const get_local_browser_executable_1 = require("./get-local-browser-executable");
12
+ const get_video_threads_flag_1 = require("./get-video-threads-flag");
12
13
  const validate_opengl_renderer_1 = require("./validate-opengl-renderer");
13
14
  const validRenderers = ['swangle', 'angle', 'egl', 'swiftshader'];
14
15
  const getOpenGlRenderer = (option) => {
@@ -69,7 +70,7 @@ const openBrowser = async (browser, options) => {
69
70
  '--force-color-profile=srgb',
70
71
  '--metrics-recording-only',
71
72
  '--no-first-run',
72
- '--video-threads=16',
73
+ '--video-threads=' + (0, get_video_threads_flag_1.getIdealVideoThreadsFlag)(),
73
74
  '--enable-automation',
74
75
  '--password-store=basic',
75
76
  '--use-mock-keychain',
@@ -22,7 +22,7 @@ const preprocessAudioTrackUnlimited = async ({ ffmpegExecutable, ffprobeExecutab
22
22
  if (filter === null) {
23
23
  return null;
24
24
  }
25
- const { cleanup, file } = await (0, ffmpeg_filter_file_1.makeFfmpegFilterFile)(filter);
25
+ const { cleanup, file } = await (0, ffmpeg_filter_file_1.makeFfmpegFilterFile)(filter, downloadMap);
26
26
  const args = [
27
27
  ['-i', (0, resolve_asset_src_1.resolveAssetSrc)(asset.src)],
28
28
  ['-ac', '2'],
@@ -0,0 +1,12 @@
1
+ export declare const estimateMemoryUsageForPrestitcher: ({ width, height, }: {
2
+ width: number;
3
+ height: number;
4
+ }) => number;
5
+ export declare const shouldUseParallelEncoding: ({ width, height, }: {
6
+ width: number;
7
+ height: number;
8
+ }) => {
9
+ hasEnoughMemory: boolean;
10
+ freeMemory: number;
11
+ estimatedUsage: number;
12
+ };
@@ -0,0 +1,30 @@
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.shouldUseParallelEncoding = exports.estimateMemoryUsageForPrestitcher = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const estimateMemoryUsageForPrestitcher = ({ width, height, }) => {
9
+ // Empirically we detected that per 1 million pixels, FFMPEG uses around 1GB of memory, relatively independent of
10
+ // the duration of the video.
11
+ const memoryUsageFor4K = 1000000000;
12
+ const memoryUsageOfPixel = memoryUsageFor4K / 1000000;
13
+ return memoryUsageOfPixel * width * height;
14
+ };
15
+ exports.estimateMemoryUsageForPrestitcher = estimateMemoryUsageForPrestitcher;
16
+ const shouldUseParallelEncoding = ({ width, height, }) => {
17
+ const freeMemory = os_1.default.freemem();
18
+ const estimatedUsage = (0, exports.estimateMemoryUsageForPrestitcher)({
19
+ height,
20
+ width,
21
+ });
22
+ const hasEnoughMemory = freeMemory - estimatedUsage > 2000000000 &&
23
+ estimatedUsage / freeMemory < 0.5;
24
+ return {
25
+ hasEnoughMemory,
26
+ freeMemory,
27
+ estimatedUsage,
28
+ };
29
+ };
30
+ exports.shouldUseParallelEncoding = shouldUseParallelEncoding;
@@ -311,6 +311,7 @@ const renderFrames = (options) => {
311
311
  cleanup.forEach((c) => {
312
312
  c();
313
313
  });
314
+ // Don't clear download dir because it might be used by stitchFramesToVideo
314
315
  });
315
316
  });
316
317
  };
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import type { SmallTCompMetadata } from 'remotion';
3
2
  import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
4
3
  import type { DownloadMap } from './assets/download-map';
@@ -63,4 +62,4 @@ export declare type RenderMediaOptions = {
63
62
  * @description Render a video from a composition
64
63
  * @link https://www.remotion.dev/docs/renderer/render-media
65
64
  */
66
- export declare const renderMedia: ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, downloadMap, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
65
+ export declare const renderMedia: ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
@@ -11,6 +11,7 @@ const remotion_1 = require("remotion");
11
11
  const download_map_1 = require("./assets/download-map");
12
12
  const can_use_parallel_encoding_1 = require("./can-use-parallel-encoding");
13
13
  const crf_1 = require("./crf");
14
+ const delete_directory_1 = require("./delete-directory");
14
15
  const ensure_frames_in_order_1 = require("./ensure-frames-in-order");
15
16
  const ensure_output_directory_1 = require("./ensure-output-directory");
16
17
  const get_duration_from_frame_range_1 = require("./get-duration-from-frame-range");
@@ -22,10 +23,10 @@ const make_cancel_signal_1 = require("./make-cancel-signal");
22
23
  const overwrite_1 = require("./overwrite");
23
24
  const perf_1 = require("./perf");
24
25
  const prespawn_ffmpeg_1 = require("./prespawn-ffmpeg");
26
+ const prestitcher_memory_usage_1 = require("./prestitcher-memory-usage");
25
27
  const quality_1 = require("./quality");
26
28
  const render_frames_1 = require("./render-frames");
27
29
  const stitch_frames_to_video_1 = require("./stitch-frames-to-video");
28
- const tmp_dir_1 = require("./tmp-dir");
29
30
  const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
30
31
  const validate_output_filename_1 = require("./validate-output-filename");
31
32
  const validate_scale_1 = require("./validate-scale");
@@ -34,8 +35,8 @@ const validate_scale_1 = require("./validate-scale");
34
35
  * @description Render a video from a composition
35
36
  * @link https://www.remotion.dev/docs/renderer/render-media
36
37
  */
37
- const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, downloadMap, ...options }) => {
38
- var _a, _b;
38
+ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, ...options }) => {
39
+ var _a, _b, _c;
39
40
  (0, quality_1.validateQuality)(quality);
40
41
  if (typeof crf !== 'undefined' && crf !== null) {
41
42
  (0, crf_1.validateSelectedCrfAndCodecCombination)(crf, codec);
@@ -56,11 +57,25 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
56
57
  let encodedDoneIn = null;
57
58
  let cancelled = false;
58
59
  const renderStart = Date.now();
59
- const tmpdir = (0, tmp_dir_1.tmpDir)('pre-encode');
60
- const parallelEncoding = (0, can_use_parallel_encoding_1.canUseParallelEncoding)(codec);
60
+ const downloadMap = (_c = options.downloadMap) !== null && _c !== void 0 ? _c : (0, download_map_1.makeDownloadMap)();
61
+ const { estimatedUsage, freeMemory, hasEnoughMemory } = (0, prestitcher_memory_usage_1.shouldUseParallelEncoding)({
62
+ height: composition.height,
63
+ width: composition.width,
64
+ });
65
+ const parallelEncoding = hasEnoughMemory && (0, can_use_parallel_encoding_1.canUseParallelEncoding)(codec);
66
+ if (options.verbose) {
67
+ console.log('[PRESTITCHER] Free memory:', freeMemory, 'Estimated usage parallel encoding', estimatedUsage);
68
+ console.log('[PRESTICHER]: Codec supports parallel rendering:', (0, can_use_parallel_encoding_1.canUseParallelEncoding)(codec));
69
+ if (parallelEncoding) {
70
+ console.log('[PRESTICHER] Parallel encoding is enabled.');
71
+ }
72
+ else {
73
+ console.log('[PRESTITCHER] Parallel encoding is disabled.');
74
+ }
75
+ }
61
76
  const actualImageFormat = imageFormat !== null && imageFormat !== void 0 ? imageFormat : 'jpeg';
62
77
  const preEncodedFileLocation = parallelEncoding
63
- ? path_1.default.join(tmpdir, 'pre-encode.' + (0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'chunk'))
78
+ ? path_1.default.join(downloadMap.preEncode, 'pre-encode.' + (0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'chunk'))
64
79
  : null;
65
80
  const outputDir = parallelEncoding
66
81
  ? null
@@ -174,7 +189,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
174
189
  browserExecutable,
175
190
  port,
176
191
  cancelSignal: cancelRenderFrames.cancelSignal,
177
- downloadMap: downloadMap !== null && downloadMap !== void 0 ? downloadMap : (0, download_map_1.makeDownloadMap)(),
192
+ downloadMap,
178
193
  });
179
194
  return renderFramesProc;
180
195
  })
@@ -252,7 +267,11 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat
252
267
  .finally(() => {
253
268
  if (preEncodedFileLocation !== null &&
254
269
  fs_1.default.existsSync(preEncodedFileLocation)) {
255
- fs_1.default.unlinkSync(preEncodedFileLocation);
270
+ (0, delete_directory_1.deleteDirectory)(path_1.default.dirname(preEncodedFileLocation));
271
+ }
272
+ // Clean download map if it was not passed in
273
+ if (!(options === null || options === void 0 ? void 0 : options.downloadMap)) {
274
+ (0, download_map_1.cleanDownloadMap)(downloadMap);
256
275
  }
257
276
  });
258
277
  return Promise.race([
@@ -175,7 +175,13 @@ const renderStill = (options) => {
175
175
  })
176
176
  .then((res) => resolve(res))
177
177
  .catch((err) => reject(err))
178
- .finally(() => close === null || close === void 0 ? void 0 : close());
178
+ .finally(() => {
179
+ // Clean download map if it was not passed in
180
+ if (!(options === null || options === void 0 ? void 0 : options.downloadMap)) {
181
+ (0, download_map_1.cleanDownloadMap)(downloadMap);
182
+ }
183
+ return close === null || close === void 0 ? void 0 : close();
184
+ });
179
185
  });
180
186
  return Promise.race([
181
187
  happyPath,
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import type { Page } from './browser/BrowserPage';
3
2
  import type { ScreenshotOptions } from './browser/ScreenshotOptions';
4
3
  import type { StillImageFormat } from './image-format';
@@ -7,6 +7,7 @@ exports._screenshotTask = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const perf_1 = require("./perf");
9
9
  const _screenshotTask = async (page, format, options) => {
10
+ var _a;
10
11
  const client = page._client();
11
12
  const target = page.target();
12
13
  const perfTarget = (0, perf_1.startPerfMeasure)('activate-target');
@@ -20,20 +21,34 @@ const _screenshotTask = async (page, format, options) => {
20
21
  color: { r: 0, g: 0, b: 0, a: 0 },
21
22
  });
22
23
  const cap = (0, perf_1.startPerfMeasure)('capture');
23
- const result = await client.send('Page.captureScreenshot', {
24
- format,
25
- quality: options.quality,
26
- clip: undefined,
27
- captureBeyondViewport: true,
28
- });
29
- (0, perf_1.stopPerfMeasure)(cap);
30
- if (shouldSetDefaultBackground)
31
- await client.send('Emulation.setDefaultBackgroundColorOverride');
32
- const saveMarker = (0, perf_1.startPerfMeasure)('save');
33
- const buffer = Buffer.from(result.data, 'base64');
34
- if (options.path)
35
- await fs_1.default.promises.writeFile(options.path, buffer);
36
- (0, perf_1.stopPerfMeasure)(saveMarker);
37
- return buffer;
24
+ try {
25
+ const result = await client.send('Page.captureScreenshot', {
26
+ format,
27
+ quality: options.quality,
28
+ clip: undefined,
29
+ captureBeyondViewport: true,
30
+ });
31
+ (0, perf_1.stopPerfMeasure)(cap);
32
+ if (shouldSetDefaultBackground)
33
+ await client.send('Emulation.setDefaultBackgroundColorOverride');
34
+ const saveMarker = (0, perf_1.startPerfMeasure)('save');
35
+ const buffer = Buffer.from(result.data, 'base64');
36
+ if (options.path)
37
+ await fs_1.default.promises.writeFile(options.path, buffer);
38
+ (0, perf_1.stopPerfMeasure)(saveMarker);
39
+ return buffer;
40
+ }
41
+ catch (err) {
42
+ if (err.message.includes('Unable to capture screenshot')) {
43
+ const errMessage = [
44
+ 'Could not take a screenshot because Google Chrome ran out of memory or disk space.',
45
+ ((_a = process === null || process === void 0 ? void 0 : process.env) === null || _a === void 0 ? void 0 : _a.REMOTION_LAMBDA)
46
+ ? 'Deploy a new Lambda function with more memory or disk space.'
47
+ : 'Decrease the concurrency to use less RAM.',
48
+ ].join(' ');
49
+ throw new Error(errMessage);
50
+ }
51
+ throw err;
52
+ }
38
53
  };
39
54
  exports._screenshotTask = _screenshotTask;
@@ -1,34 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.stitchFramesToVideo = exports.spawnFfmpeg = void 0;
30
7
  const execa_1 = __importDefault(require("execa"));
31
- const fs_1 = __importStar(require("fs"));
8
+ const fs_1 = __importDefault(require("fs"));
32
9
  const promises_1 = require("fs/promises");
33
10
  const path_1 = __importDefault(require("path"));
34
11
  const remotion_1 = require("remotion");
@@ -48,7 +25,6 @@ const merge_audio_track_1 = require("./merge-audio-track");
48
25
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
49
26
  const pixel_format_1 = require("./pixel-format");
50
27
  const preprocess_audio_track_1 = require("./preprocess-audio-track");
51
- const tmp_dir_1 = require("./tmp-dir");
52
28
  const truthy_1 = require("./truthy");
53
29
  const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
54
30
  const validate_ffmpeg_1 = require("./validate-ffmpeg");
@@ -67,13 +43,12 @@ const getAssetsData = async ({ assets, onDownload, fps, expectedFrames, verbose,
67
43
  if (verbose) {
68
44
  console.log('asset positions', assetPositions);
69
45
  }
70
- const tempPath = (0, tmp_dir_1.tmpDir)('remotion-audio-mixing');
71
46
  const preprocessProgress = new Array(assetPositions.length).fill(0);
72
47
  const updateProgress = () => {
73
48
  onProgress(preprocessProgress.reduce((a, b) => a + b, 0) / assetPositions.length);
74
49
  };
75
50
  const preprocessed = (await Promise.all(assetPositions.map(async (asset, index) => {
76
- const filterFile = path_1.default.join(tempPath, `${index}.wav`);
51
+ const filterFile = path_1.default.join(downloadMap.audioMixing, `${index}.wav`);
77
52
  const result = await (0, preprocess_audio_track_1.preprocessAudioTrack)({
78
53
  ffmpegExecutable: ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : null,
79
54
  ffprobeExecutable: ffprobeExecutable !== null && ffprobeExecutable !== void 0 ? ffprobeExecutable : null,
@@ -87,14 +62,15 @@ const getAssetsData = async ({ assets, onDownload, fps, expectedFrames, verbose,
87
62
  updateProgress();
88
63
  return result;
89
64
  }))).filter(truthy_1.truthy);
90
- const outName = path_1.default.join((0, tmp_dir_1.tmpDir)('remotion-audio-preprocessing'), `audio.wav`);
65
+ const outName = path_1.default.join(downloadMap.audioPreprocessing, `audio.wav`);
91
66
  await (0, merge_audio_track_1.mergeAudioTrack)({
92
67
  ffmpegExecutable: ffmpegExecutable !== null && ffmpegExecutable !== void 0 ? ffmpegExecutable : null,
93
68
  files: preprocessed,
94
69
  outName,
95
70
  numberOfSeconds: Number((expectedFrames / fps).toFixed(3)),
71
+ downloadMap,
96
72
  });
97
- (0, delete_directory_1.deleteDirectory)(tempPath);
73
+ (0, delete_directory_1.deleteDirectory)(downloadMap.audioMixing);
98
74
  onProgress(1);
99
75
  preprocessed.forEach((p) => {
100
76
  (0, delete_directory_1.deleteDirectory)(p);
@@ -123,7 +99,7 @@ const spawnFfmpeg = async (options) => {
123
99
  const supportsCrf = encoderName && codec !== 'prores';
124
100
  const tempFile = options.outputLocation
125
101
  ? null
126
- : path_1.default.join((0, tmp_dir_1.tmpDir)('remotion-stitch-temp-dir'), `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'final')}`);
102
+ : path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'final')}`);
127
103
  if (options.verbose) {
128
104
  console.log('[verbose] ffmpeg', (_e = options.ffmpegExecutable) !== null && _e !== void 0 ? _e : 'ffmpeg in PATH');
129
105
  console.log('[verbose] encoder', encoderName);
@@ -184,7 +160,6 @@ const spawnFfmpeg = async (options) => {
184
160
  if (tempFile) {
185
161
  (0, promises_1.readFile)(tempFile)
186
162
  .then((f) => {
187
- (0, fs_1.unlinkSync)(tempFile);
188
163
  return resolve(f);
189
164
  })
190
165
  .catch((e) => reject(e));
@@ -193,6 +168,7 @@ const spawnFfmpeg = async (options) => {
193
168
  resolve(null);
194
169
  }
195
170
  });
171
+ await (0, delete_directory_1.deleteDirectory)(options.assetsInfo.downloadMap.stitchFrames);
196
172
  return {
197
173
  getLogs: () => '',
198
174
  task: Promise.resolve(file),
@@ -280,12 +256,18 @@ const spawnFfmpeg = async (options) => {
280
256
  });
281
257
  return {
282
258
  task: task.then(() => {
259
+ (0, delete_directory_1.deleteDirectory)(options.assetsInfo.downloadMap.audioPreprocessing);
283
260
  if (tempFile === null) {
261
+ (0, delete_directory_1.deleteDirectory)(options.assetsInfo.downloadMap.stitchFrames);
284
262
  return null;
285
263
  }
286
264
  return (0, promises_1.readFile)(tempFile)
287
265
  .then((file) => {
288
- return Promise.all([file, (0, delete_directory_1.deleteDirectory)(path_1.default.dirname(tempFile))]);
266
+ return Promise.all([
267
+ file,
268
+ (0, delete_directory_1.deleteDirectory)(path_1.default.dirname(tempFile)),
269
+ (0, delete_directory_1.deleteDirectory)(options.assetsInfo.downloadMap.stitchFrames),
270
+ ]);
289
271
  })
290
272
  .then(([file]) => file);
291
273
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "3.1.7",
3
+ "version": "3.1.11",
4
4
  "description": "Renderer for Remotion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "execa": "5.1.1",
24
24
  "extract-zip": "2.0.1",
25
- "remotion": "3.1.7",
25
+ "remotion": "3.1.11",
26
26
  "source-map": "^0.8.0-beta.0",
27
27
  "ws": "8.7.0"
28
28
  },
@@ -57,5 +57,5 @@
57
57
  "publishConfig": {
58
58
  "access": "public"
59
59
  },
60
- "gitHead": "599379fef6043a7125d1979766f915580ec1cf77"
60
+ "gitHead": "bc4184c9faf944a3bac2fd56bdf990cc74e3ec94"
61
61
  }
@@ -1,9 +0,0 @@
1
- export declare type FileNameAndSize = {
2
- filename: string;
3
- size: number;
4
- };
5
- export declare function getFolderFiles(folder: string): FileNameAndSize[];
6
- export declare const getTmpDirStateIfENoSp: () => {
7
- files: FileNameAndSize[];
8
- total: number;
9
- };
@@ -1,50 +0,0 @@
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.getTmpDirStateIfENoSp = exports.getFolderFiles = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- function getFolderFiles(folder) {
10
- const files = fs_1.default.readdirSync(folder);
11
- const paths = [];
12
- files.forEach((file) => {
13
- const full = path_1.default.join(folder, file);
14
- try {
15
- const stat = fs_1.default.statSync(full);
16
- if (stat.isDirectory()) {
17
- paths.push(...getFolderFiles(full));
18
- }
19
- else {
20
- paths.push({
21
- filename: full,
22
- size: stat.size,
23
- });
24
- }
25
- }
26
- catch (err) {
27
- if (err.message.includes('ENOENT')) {
28
- // Race condition: File was deleted in the meanwhile.
29
- // Do nothing
30
- }
31
- else {
32
- throw err;
33
- }
34
- }
35
- });
36
- return paths;
37
- }
38
- exports.getFolderFiles = getFolderFiles;
39
- const getTmpDirStateIfENoSp = () => {
40
- const files = getFolderFiles('/tmp');
41
- return {
42
- files: files
43
- .slice(0)
44
- .sort((a, b) => a.size - b.size)
45
- .reverse()
46
- .slice(0, 100),
47
- total: files.reduce((a, b) => a + b.size, 0),
48
- };
49
- };
50
- exports.getTmpDirStateIfENoSp = getTmpDirStateIfENoSp;