@remotion/renderer 4.0.120 → 4.0.122

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 (50) hide show
  1. package/dist/browser/BrowserPage.js +29 -10
  2. package/dist/call-ffmpeg.js +3 -7
  3. package/dist/check-apple-silicon.d.ts +1 -3
  4. package/dist/check-apple-silicon.js +2 -32
  5. package/dist/combine-audio.d.ts +6 -1
  6. package/dist/combine-audio.js +33 -12
  7. package/dist/combine-video-streams-seamlessly.d.ts +3 -0
  8. package/dist/combine-video-streams-seamlessly.js +8 -0
  9. package/dist/combine-video-streams.d.ts +4 -1
  10. package/dist/combine-video-streams.js +9 -3
  11. package/dist/compositor/compose.js +2 -4
  12. package/dist/compositor/compositor.js +2 -4
  13. package/dist/compositor/make-file-executable.d.ts +1 -0
  14. package/dist/compositor/make-file-executable.js +14 -0
  15. package/dist/compress-audio.d.ts +13 -0
  16. package/dist/compress-audio.js +28 -0
  17. package/dist/create-audio.d.ts +22 -0
  18. package/dist/create-audio.js +85 -0
  19. package/dist/create-combined-video.d.ts +18 -0
  20. package/dist/create-combined-video.js +27 -0
  21. package/dist/create-ffmpeg-complex-filter.d.ts +1 -4
  22. package/dist/does-have-m2-bug.d.ts +3 -0
  23. package/dist/does-have-m2-bug.js +12 -0
  24. package/dist/get-extension-from-audio-codec.d.ts +2 -0
  25. package/dist/get-extension-from-audio-codec.js +19 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +2 -0
  28. package/dist/mux-video-and-audio.d.ts +5 -1
  29. package/dist/mux-video-and-audio.js +12 -3
  30. package/dist/options/for-seamless-aac-concatenation.d.ts +19 -0
  31. package/dist/options/for-seamless-aac-concatenation.js +40 -0
  32. package/dist/options/prefer-lossless.d.ts +12 -2
  33. package/dist/options/prefer-lossless.js +19 -4
  34. package/dist/options/prores-profile.d.ts +0 -0
  35. package/dist/options/prores-profile.js +1 -0
  36. package/dist/options/webhook-custom-data.d.ts +1 -1
  37. package/dist/provide-screenshot.d.ts +2 -1
  38. package/dist/provide-screenshot.js +2 -1
  39. package/dist/puppeteer-screenshot.d.ts +1 -0
  40. package/dist/puppeteer-screenshot.js +1 -0
  41. package/dist/render-frames.js +2 -1
  42. package/dist/screenshot-dom-element.d.ts +2 -1
  43. package/dist/screenshot-dom-element.js +2 -1
  44. package/dist/screenshot-task.d.ts +2 -1
  45. package/dist/screenshot-task.js +11 -9
  46. package/dist/take-frame-and-compose.d.ts +1 -0
  47. package/dist/take-frame-and-compose.js +1 -0
  48. package/dist/x264-preset.d.ts +15 -0
  49. package/dist/x264-preset.js +26 -1
  50. package/package.json +9 -9
@@ -195,16 +195,35 @@ class Page extends EventEmitter_1.EventEmitter {
195
195
  return __classPrivateFieldGet(this, _Page_frameManager, "f").mainFrame();
196
196
  }
197
197
  async setViewport(viewport) {
198
- const { value } = await __classPrivateFieldGet(this, _Page_client, "f").send('Emulation.setDeviceMetricsOverride', {
199
- mobile: false,
200
- width: viewport.width,
201
- height: viewport.height,
202
- deviceScaleFactor: viewport.deviceScaleFactor,
203
- screenOrientation: {
204
- angle: 0,
205
- type: 'portraitPrimary',
206
- },
207
- });
198
+ const fromSurface = !process.env.DISABLE_FROM_SURFACE;
199
+ const request = fromSurface
200
+ ? {
201
+ mobile: false,
202
+ width: viewport.width,
203
+ height: viewport.height,
204
+ deviceScaleFactor: viewport.deviceScaleFactor,
205
+ screenOrientation: {
206
+ angle: 0,
207
+ type: 'portraitPrimary',
208
+ },
209
+ }
210
+ : {
211
+ mobile: false,
212
+ width: viewport.width,
213
+ height: viewport.height,
214
+ deviceScaleFactor: 1,
215
+ screenHeight: viewport.height,
216
+ screenWidth: viewport.width,
217
+ scale: viewport.deviceScaleFactor,
218
+ viewport: {
219
+ height: viewport.height * viewport.deviceScaleFactor,
220
+ width: viewport.width * viewport.deviceScaleFactor,
221
+ scale: 1,
222
+ x: 0,
223
+ y: 0,
224
+ },
225
+ };
226
+ const { value } = await __classPrivateFieldGet(this, _Page_client, "f").send('Emulation.setDeviceMetricsOverride', request);
208
227
  return value;
209
228
  }
210
229
  setDefaultNavigationTimeout(timeout) {
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.callFfNative = exports.callFf = void 0;
7
7
  const execa_1 = __importDefault(require("execa"));
8
8
  const node_child_process_1 = require("node:child_process");
9
- const node_fs_1 = require("node:fs");
10
9
  const path_1 = __importDefault(require("path"));
11
10
  const get_executable_path_1 = require("./compositor/get-executable-path");
11
+ const make_file_executable_1 = require("./compositor/make-file-executable");
12
12
  const truthy_1 = require("./truthy");
13
13
  const callFf = ({ args, bin, indent, logLevel, options, binariesDirectory, }) => {
14
14
  const executablePath = (0, get_executable_path_1.getExecutablePath)({
@@ -17,9 +17,7 @@ const callFf = ({ args, bin, indent, logLevel, options, binariesDirectory, }) =>
17
17
  logLevel,
18
18
  binariesDirectory,
19
19
  });
20
- if (!process.env.READ_ONLY_FS) {
21
- (0, node_fs_1.chmodSync)(executablePath, 0o755);
22
- }
20
+ (0, make_file_executable_1.makeFileExecutableIfItIsNot)(executablePath);
23
21
  return (0, execa_1.default)(executablePath, args.filter(truthy_1.truthy), {
24
22
  cwd: path_1.default.dirname(executablePath),
25
23
  ...options,
@@ -33,9 +31,7 @@ const callFfNative = ({ args, bin, indent, logLevel, options, binariesDirectory,
33
31
  logLevel,
34
32
  binariesDirectory,
35
33
  });
36
- if (!process.env.READ_ONLY_FS) {
37
- (0, node_fs_1.chmodSync)(executablePath, 0o755);
38
- }
34
+ (0, make_file_executable_1.makeFileExecutableIfItIsNot)(executablePath);
39
35
  return (0, node_child_process_1.spawn)(executablePath, args.filter(truthy_1.truthy), {
40
36
  cwd: path_1.default.dirname(executablePath),
41
37
  ...options,
@@ -1,3 +1 @@
1
- import type { LogLevel } from './log-level';
2
- export declare const gLibCErrorMessage: (libCString: string) => string | null;
3
- export declare const checkNodeVersionAndWarnAboutRosetta: (logLevel: LogLevel, indent: boolean) => void;
1
+ export declare const checkNodeVersionAndWarnAboutRosetta: () => void;
@@ -1,42 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkNodeVersionAndWarnAboutRosetta = exports.gLibCErrorMessage = void 0;
4
- const logger_1 = require("./logger");
5
- const gLibCErrorMessage = (libCString) => {
6
- const split = libCString.split('.');
7
- if (split.length !== 2) {
8
- return null;
9
- }
10
- if (split[0] === '2' && Number(split[1]) >= 35) {
11
- return null;
12
- }
13
- if (Number(split[0]) > 2) {
14
- return null;
15
- }
16
- return `Rendering videos requires glibc 2.35 or higher. Your system has glibc ${libCString}.`;
17
- };
18
- exports.gLibCErrorMessage = gLibCErrorMessage;
19
- const checkLibCRequirement = (logLevel, indent) => {
20
- const { report } = process;
21
- if (report) {
22
- // @ts-expect-error no types
23
- const { glibcVersionRuntime } = report.getReport().header;
24
- if (!glibcVersionRuntime) {
25
- return;
26
- }
27
- const error = (0, exports.gLibCErrorMessage)(glibcVersionRuntime);
28
- if (error) {
29
- logger_1.Log.warn({ logLevel, indent }, error);
30
- }
31
- }
32
- };
33
- const checkNodeVersionAndWarnAboutRosetta = (logLevel, indent) => {
3
+ exports.checkNodeVersionAndWarnAboutRosetta = void 0;
4
+ const checkNodeVersionAndWarnAboutRosetta = () => {
34
5
  const version = process.version.replace('v', '').split('.');
35
6
  const majorVersion = Number(version[0]);
36
7
  const requiredNodeVersion = 16;
37
8
  if (majorVersion < 16) {
38
9
  throw new Error(`Remotion requires at least Node ${requiredNodeVersion}. You currently have ${process.version}. Update your node version to ${requiredNodeVersion} to use Remotion.`);
39
10
  }
40
- checkLibCRequirement(logLevel, indent);
41
11
  };
42
12
  exports.checkNodeVersionAndWarnAboutRosetta = checkNodeVersionAndWarnAboutRosetta;
@@ -1,8 +1,9 @@
1
1
  import type { AudioCodec } from './audio-codec';
2
2
  import type { LogLevel } from './log-level';
3
+ import type { CancelSignal } from './make-cancel-signal';
3
4
  export declare const durationOf1Frame: number;
4
5
  export declare const getClosestAlignedTime: (targetTime: number) => number;
5
- export declare const createCombinedAudio: ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, }: {
6
+ export declare const createCombinedAudio: ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, binariesDirectory, fps, cancelSignal, onProgress, }: {
6
7
  seamless: boolean;
7
8
  filelistDir: string;
8
9
  files: string[];
@@ -13,4 +14,8 @@ export declare const createCombinedAudio: ({ seamless, filelistDir, files, inden
13
14
  output: string;
14
15
  chunkDurationInSeconds: number;
15
16
  addRemotionMetadata: boolean;
17
+ binariesDirectory: string | null;
18
+ fps: number;
19
+ cancelSignal: CancelSignal | undefined;
20
+ onProgress: (frames: number) => void;
16
21
  }) => Promise<string>;
@@ -17,12 +17,14 @@ const getClosestAlignedTime = (targetTime) => {
17
17
  return nearestFrameIndexForTargetTime * exports.durationOf1Frame;
18
18
  };
19
19
  exports.getClosestAlignedTime = getClosestAlignedTime;
20
- const encodeAudio = async ({ files, resolvedAudioCodec, audioBitrate, filelistDir, output, indent, logLevel, addRemotionMetadata, }) => {
20
+ const encodeAudio = async ({ files, resolvedAudioCodec, audioBitrate, filelistDir, output, indent, logLevel, addRemotionMetadata, fps, binariesDirectory, cancelSignal, onProgress, }) => {
21
21
  var _a;
22
22
  const fileList = files.map((p) => `file '${p}'`).join('\n');
23
23
  const fileListTxt = (0, path_1.join)(filelistDir, 'audio-files.txt');
24
24
  (0, fs_1.writeFileSync)(fileListTxt, fileList);
25
+ const startCombining = Date.now();
25
26
  const command = [
27
+ '-hide_banner',
26
28
  '-f',
27
29
  'concat',
28
30
  '-safe',
@@ -41,32 +43,39 @@ const encodeAudio = async ({ files, resolvedAudioCodec, audioBitrate, filelistDi
41
43
  '-y',
42
44
  output,
43
45
  ];
46
+ logger_1.Log.verbose({ indent, logLevel }, `Combining audio with re-encoding, command: ${command.join(' ')}`);
44
47
  try {
45
48
  const task = (0, call_ffmpeg_1.callFf)({
46
49
  args: command,
47
50
  bin: 'ffmpeg',
48
51
  indent,
49
52
  logLevel,
53
+ binariesDirectory,
54
+ cancelSignal,
50
55
  });
51
56
  (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
52
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'));
57
+ const utf8 = data.toString('utf8');
58
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
53
59
  if (parsed === undefined) {
54
- logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
60
+ logger_1.Log.verbose({ indent, logLevel }, utf8);
55
61
  }
56
62
  else {
63
+ onProgress(parsed);
57
64
  logger_1.Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
58
65
  }
59
66
  });
60
67
  await task;
68
+ logger_1.Log.verbose({ indent, logLevel }, `Encoded audio in ${Date.now() - startCombining}ms`);
61
69
  return output;
62
70
  }
63
71
  catch (e) {
64
- (0, fs_1.rmSync)(filelistDir, { recursive: true });
72
+ (0, fs_1.rmSync)(fileListTxt, { recursive: true });
65
73
  throw e;
66
74
  }
67
75
  };
68
- const combineAudioSeamlessly = async ({ files, filelistDir, indent, logLevel, output, chunkDurationInSeconds, addRemotionMetadata, }) => {
76
+ const combineAudioSeamlessly = async ({ files, filelistDir, indent, logLevel, output, chunkDurationInSeconds, addRemotionMetadata, fps, binariesDirectory, cancelSignal, onProgress, }) => {
69
77
  var _a;
78
+ const startConcatenating = Date.now();
70
79
  const fileList = files
71
80
  .map((p, i) => {
72
81
  const isLast = i === files.length - 1;
@@ -97,6 +106,7 @@ const combineAudioSeamlessly = async ({ files, filelistDir, indent, logLevel, ou
97
106
  const fileListTxt = (0, path_1.join)(filelistDir, 'audio-files.txt');
98
107
  (0, fs_1.writeFileSync)(fileListTxt, fileList);
99
108
  const command = [
109
+ '-hide_banner',
100
110
  '-f',
101
111
  'concat',
102
112
  '-safe',
@@ -111,32 +121,35 @@ const combineAudioSeamlessly = async ({ files, filelistDir, indent, logLevel, ou
111
121
  '-y',
112
122
  output,
113
123
  ];
124
+ logger_1.Log.verbose({ indent, logLevel }, `Combining AAC audio seamlessly, command: ${command.join(' ')}`);
114
125
  try {
115
126
  const task = (0, call_ffmpeg_1.callFf)({
116
127
  args: command,
117
128
  bin: 'ffmpeg',
118
129
  indent,
119
130
  logLevel,
131
+ binariesDirectory,
132
+ cancelSignal,
120
133
  });
121
134
  (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
122
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'));
123
- if (parsed === undefined) {
124
- logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
125
- }
126
- else {
135
+ const utf8 = data.toString('utf8');
136
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
137
+ if (parsed !== undefined) {
138
+ onProgress(parsed);
127
139
  logger_1.Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
128
140
  }
129
141
  });
130
142
  await task;
143
+ logger_1.Log.verbose({ indent, logLevel }, `Combined audio seamlessly in ${Date.now() - startConcatenating}ms`);
131
144
  return output;
132
145
  }
133
146
  catch (e) {
134
- (0, fs_1.rmSync)(filelistDir, { recursive: true });
147
+ (0, fs_1.rmSync)(fileListTxt, { recursive: true });
135
148
  console.log(e);
136
149
  throw e;
137
150
  }
138
151
  };
139
- const createCombinedAudio = ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, }) => {
152
+ const createCombinedAudio = ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, binariesDirectory, fps, cancelSignal, onProgress, }) => {
140
153
  if (seamless) {
141
154
  return combineAudioSeamlessly({
142
155
  filelistDir,
@@ -146,6 +159,10 @@ const createCombinedAudio = ({ seamless, filelistDir, files, indent, logLevel, a
146
159
  output,
147
160
  chunkDurationInSeconds,
148
161
  addRemotionMetadata,
162
+ binariesDirectory,
163
+ fps,
164
+ cancelSignal,
165
+ onProgress,
149
166
  });
150
167
  }
151
168
  return encodeAudio({
@@ -157,6 +174,10 @@ const createCombinedAudio = ({ seamless, filelistDir, files, indent, logLevel, a
157
174
  indent,
158
175
  logLevel,
159
176
  addRemotionMetadata,
177
+ binariesDirectory,
178
+ fps,
179
+ cancelSignal,
180
+ onProgress,
160
181
  });
161
182
  };
162
183
  exports.createCombinedAudio = createCombinedAudio;
@@ -0,0 +1,3 @@
1
+ export declare const combineVideoStreamsSeamlessly: ({ files }: {
2
+ files: string[];
3
+ }) => string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.combineVideoStreamsSeamlessly = void 0;
4
+ const combineVideoStreamsSeamlessly = ({ files }) => {
5
+ const fileList = `concat:${files.join('|')}`;
6
+ return fileList;
7
+ };
8
+ exports.combineVideoStreamsSeamlessly = combineVideoStreamsSeamlessly;
@@ -1,6 +1,7 @@
1
1
  import type { Codec } from './codec';
2
2
  import type { LogLevel } from './log-level';
3
- export declare const combineVideoStreams: ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, }: {
3
+ import type { CancelSignal } from './make-cancel-signal';
4
+ export declare const combineVideoStreams: ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, binariesDirectory, cancelSignal, }: {
4
5
  fps: number;
5
6
  codec: Codec;
6
7
  filelistDir: string;
@@ -11,4 +12,6 @@ export declare const combineVideoStreams: ({ fps, codec, filelistDir, numberOfGi
11
12
  onProgress: (p: number) => void;
12
13
  files: string[];
13
14
  addRemotionMetadata: boolean;
15
+ binariesDirectory: string | null;
16
+ cancelSignal: CancelSignal | undefined;
14
17
  }) => Promise<string>;
@@ -9,12 +9,13 @@ const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif
9
9
  const logger_1 = require("./logger");
10
10
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
11
11
  const truthy_1 = require("./truthy");
12
- const combineVideoStreams = async ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, }) => {
12
+ const combineVideoStreams = async ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, binariesDirectory, cancelSignal, }) => {
13
13
  var _a;
14
14
  const fileList = files.map((p) => `file '${p}'`).join('\n');
15
15
  const fileListTxt = (0, path_1.join)(filelistDir, 'video-files.txt');
16
16
  (0, fs_1.writeFileSync)(fileListTxt, fileList);
17
17
  const command = [
18
+ '-hide_banner',
18
19
  '-r',
19
20
  String(fps),
20
21
  '-f',
@@ -37,15 +38,19 @@ const combineVideoStreams = async ({ fps, codec, filelistDir, numberOfGifLoops,
37
38
  '-y',
38
39
  output,
39
40
  ].filter(truthy_1.truthy);
41
+ const startTime = Date.now();
42
+ logger_1.Log.verbose({ indent, logLevel }, `Combining video without re-encoding, command: ${command.join(' ')}`);
40
43
  try {
41
44
  const task = (0, call_ffmpeg_1.callFf)({
42
45
  args: command,
43
46
  bin: 'ffmpeg',
44
47
  indent,
45
48
  logLevel,
49
+ binariesDirectory,
50
+ cancelSignal,
46
51
  });
47
52
  (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
48
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'));
53
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'), fps);
49
54
  if (parsed === undefined) {
50
55
  logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
51
56
  }
@@ -55,10 +60,11 @@ const combineVideoStreams = async ({ fps, codec, filelistDir, numberOfGifLoops,
55
60
  }
56
61
  });
57
62
  await task;
63
+ logger_1.Log.verbose({ indent, logLevel }, `Finished combining video in ${Date.now() - startTime}ms`);
58
64
  return output;
59
65
  }
60
66
  catch (e) {
61
- (0, fs_1.rmSync)(filelistDir, { recursive: true });
67
+ (0, fs_1.rmSync)(fileListTxt, { recursive: true });
62
68
  throw e;
63
69
  }
64
70
  };
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.callCompositor = exports.compose = exports.composeWithoutCache = exports.serializeCommand = void 0;
7
7
  const node_child_process_1 = require("node:child_process");
8
8
  const node_crypto_1 = require("node:crypto");
9
- const node_fs_1 = require("node:fs");
10
9
  const promises_1 = require("node:fs/promises");
11
10
  const node_path_1 = __importDefault(require("node:path"));
12
11
  const get_executable_path_1 = require("./get-executable-path");
12
+ const make_file_executable_1 = require("./make-file-executable");
13
13
  const make_nonce_1 = require("./make-nonce");
14
14
  const getCompositorHash = ({ ...input }) => {
15
15
  return (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(input)).digest('base64');
@@ -59,9 +59,7 @@ const callCompositor = (payload, indent, logLevel, binariesDirectory) => {
59
59
  logLevel,
60
60
  binariesDirectory,
61
61
  });
62
- if (!process.env.READ_ONLY_FS) {
63
- (0, node_fs_1.chmodSync)(execPath, 0o755);
64
- }
62
+ (0, make_file_executable_1.makeFileExecutableIfItIsNot)(execPath);
65
63
  const child = (0, node_child_process_1.spawn)(execPath, [payload], {
66
64
  cwd: node_path_1.default.dirname(execPath),
67
65
  });
@@ -5,13 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.startCompositor = exports.startLongRunningCompositor = void 0;
7
7
  const node_child_process_1 = require("node:child_process");
8
- const node_fs_1 = require("node:fs");
9
8
  const node_path_1 = __importDefault(require("node:path"));
10
9
  const get_concurrency_1 = require("../get-concurrency");
11
10
  const log_level_1 = require("../log-level");
12
11
  const logger_1 = require("../logger");
13
12
  const compose_1 = require("./compose");
14
13
  const get_executable_path_1 = require("./get-executable-path");
14
+ const make_file_executable_1 = require("./make-file-executable");
15
15
  const make_nonce_1 = require("./make-nonce");
16
16
  const startLongRunningCompositor = ({ maximumFrameCacheItemsInBytes, logLevel, indent, binariesDirectory, }) => {
17
17
  return (0, exports.startCompositor)({
@@ -35,9 +35,7 @@ const startCompositor = ({ type, payload, logLevel, indent, binariesDirectory =
35
35
  logLevel,
36
36
  binariesDirectory,
37
37
  });
38
- if (!process.env.READ_ONLY_FS) {
39
- (0, node_fs_1.chmodSync)(bin, 0o755);
40
- }
38
+ (0, make_file_executable_1.makeFileExecutableIfItIsNot)(bin);
41
39
  const fullCommand = (0, compose_1.serializeCommand)(type, payload);
42
40
  const child = (0, node_child_process_1.spawn)(bin, [JSON.stringify(fullCommand)], {
43
41
  cwd: node_path_1.default.dirname(bin),
@@ -0,0 +1 @@
1
+ export declare const makeFileExecutableIfItIsNot: (path: string) => void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeFileExecutableIfItIsNot = void 0;
4
+ /* eslint-disable no-bitwise */
5
+ const node_fs_1 = require("node:fs");
6
+ const makeFileExecutableIfItIsNot = (path) => {
7
+ try {
8
+ (0, node_fs_1.accessSync)(path, node_fs_1.constants.X_OK);
9
+ }
10
+ catch (err) {
11
+ (0, node_fs_1.chmodSync)(path, 0o755);
12
+ }
13
+ };
14
+ exports.makeFileExecutableIfItIsNot = makeFileExecutableIfItIsNot;
@@ -0,0 +1,13 @@
1
+ import type { AudioCodec } from './audio-codec';
2
+ import type { LogLevel } from './log-level';
3
+ import type { CancelSignal } from './make-cancel-signal';
4
+ export declare const compressAudio: ({ audioCodec, outName, binariesDirectory, indent, logLevel, audioBitrate, cancelSignal, inName, }: {
5
+ audioCodec: AudioCodec;
6
+ outName: string;
7
+ indent: boolean;
8
+ logLevel: LogLevel;
9
+ binariesDirectory: string | null;
10
+ audioBitrate: string | null;
11
+ cancelSignal: CancelSignal | undefined;
12
+ inName: string;
13
+ }) => Promise<void>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compressAudio = void 0;
4
+ const audio_codec_1 = require("./audio-codec");
5
+ const call_ffmpeg_1 = require("./call-ffmpeg");
6
+ const truthy_1 = require("./truthy");
7
+ const compressAudio = async ({ audioCodec, outName, binariesDirectory, indent, logLevel, audioBitrate, cancelSignal, inName, }) => {
8
+ const args = [
9
+ ['-i', inName],
10
+ ['-c:a', (0, audio_codec_1.mapAudioCodecToFfmpegAudioCodecName)(audioCodec)],
11
+ audioCodec === 'aac' ? ['-f', 'adts'] : null,
12
+ audioCodec ? ['-b:a', audioBitrate || '320k'] : null,
13
+ audioCodec === 'aac' ? '-cutoff' : null,
14
+ audioCodec === 'aac' ? '18000' : null,
15
+ ['-y', outName],
16
+ ]
17
+ .filter(truthy_1.truthy)
18
+ .flat(2);
19
+ await (0, call_ffmpeg_1.callFf)({
20
+ bin: 'ffmpeg',
21
+ args,
22
+ indent,
23
+ logLevel,
24
+ binariesDirectory,
25
+ cancelSignal,
26
+ });
27
+ };
28
+ exports.compressAudio = compressAudio;
@@ -0,0 +1,22 @@
1
+ import type { TRenderAsset } from 'remotion/no-react';
2
+ import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
3
+ import type { DownloadMap } from './assets/download-map';
4
+ import type { AudioCodec } from './audio-codec';
5
+ import type { LogLevel } from './log-level';
6
+ import type { CancelSignal } from './make-cancel-signal';
7
+ export declare const createAudio: ({ assets, onDownload, fps, expectedFrames, logLevel, onProgress, downloadMap, remotionRoot, indent, binariesDirectory, audioBitrate, audioCodec, cancelSignal, forSeamlessAacConcatenation, }: {
8
+ assets: TRenderAsset[][];
9
+ onDownload: RenderMediaOnDownload | undefined;
10
+ fps: number;
11
+ expectedFrames: number;
12
+ logLevel: LogLevel;
13
+ onProgress: (progress: number) => void;
14
+ downloadMap: DownloadMap;
15
+ remotionRoot: string;
16
+ indent: boolean;
17
+ binariesDirectory: string | null;
18
+ audioBitrate: string | null;
19
+ audioCodec: AudioCodec;
20
+ cancelSignal: CancelSignal | undefined;
21
+ forSeamlessAacConcatenation: boolean;
22
+ }) => Promise<string>;
@@ -0,0 +1,85 @@
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.createAudio = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const calculate_asset_positions_1 = require("./assets/calculate-asset-positions");
9
+ const convert_assets_to_file_urls_1 = require("./assets/convert-assets-to-file-urls");
10
+ const download_and_map_assets_to_file_1 = require("./assets/download-and-map-assets-to-file");
11
+ const compress_audio_1 = require("./compress-audio");
12
+ const delete_directory_1 = require("./delete-directory");
13
+ const get_extension_from_audio_codec_1 = require("./get-extension-from-audio-codec");
14
+ const logger_1 = require("./logger");
15
+ const merge_audio_track_1 = require("./merge-audio-track");
16
+ const preprocess_audio_track_1 = require("./preprocess-audio-track");
17
+ const truthy_1 = require("./truthy");
18
+ const createAudio = async ({ assets, onDownload, fps, expectedFrames, logLevel, onProgress, downloadMap, remotionRoot, indent, binariesDirectory, audioBitrate, audioCodec, cancelSignal, forSeamlessAacConcatenation, }) => {
19
+ const fileUrlAssets = await (0, convert_assets_to_file_urls_1.convertAssetsToFileUrls)({
20
+ assets,
21
+ onDownload: onDownload !== null && onDownload !== void 0 ? onDownload : (() => () => undefined),
22
+ downloadMap,
23
+ indent,
24
+ logLevel,
25
+ });
26
+ (0, download_and_map_assets_to_file_1.markAllAssetsAsDownloaded)(downloadMap);
27
+ const assetPositions = (0, calculate_asset_positions_1.calculateAssetPositions)(fileUrlAssets);
28
+ logger_1.Log.verbose({ indent, logLevel, tag: 'audio' }, 'asset positions', JSON.stringify(assetPositions));
29
+ const preprocessProgress = new Array(assetPositions.length).fill(0);
30
+ const updateProgress = () => {
31
+ onProgress(preprocessProgress.reduce((a, b) => a + b, 0) / assetPositions.length);
32
+ };
33
+ const audioTracks = await Promise.all(assetPositions.map(async (asset, index) => {
34
+ const filterFile = path_1.default.join(downloadMap.audioMixing, `${index}.wav`);
35
+ const result = await (0, preprocess_audio_track_1.preprocessAudioTrack)({
36
+ outName: filterFile,
37
+ asset,
38
+ expectedFrames,
39
+ fps,
40
+ downloadMap,
41
+ indent,
42
+ logLevel,
43
+ binariesDirectory,
44
+ cancelSignal,
45
+ forSeamlessAacConcatenation,
46
+ });
47
+ preprocessProgress[index] = 1;
48
+ updateProgress();
49
+ return result;
50
+ }));
51
+ const preprocessed = audioTracks.filter(truthy_1.truthy);
52
+ const merged = path_1.default.join(downloadMap.audioPreprocessing, 'merged.wav');
53
+ const extension = (0, get_extension_from_audio_codec_1.getExtensionFromAudioCodec)(audioCodec);
54
+ const outName = path_1.default.join(downloadMap.audioPreprocessing, `audio.${extension}`);
55
+ await (0, merge_audio_track_1.mergeAudioTrack)({
56
+ files: preprocessed,
57
+ outName: merged,
58
+ numberOfSeconds: Number((expectedFrames / fps).toFixed(3)),
59
+ downloadMap,
60
+ remotionRoot,
61
+ indent,
62
+ logLevel,
63
+ binariesDirectory,
64
+ cancelSignal,
65
+ });
66
+ await (0, compress_audio_1.compressAudio)({
67
+ audioBitrate,
68
+ audioCodec,
69
+ binariesDirectory,
70
+ indent,
71
+ logLevel,
72
+ inName: merged,
73
+ outName,
74
+ cancelSignal,
75
+ });
76
+ // TODO: Handle new progress
77
+ onProgress(1);
78
+ (0, delete_directory_1.deleteDirectory)(merged);
79
+ (0, delete_directory_1.deleteDirectory)(downloadMap.audioMixing);
80
+ preprocessed.forEach((p) => {
81
+ (0, delete_directory_1.deleteDirectory)(p.outName);
82
+ });
83
+ return outName;
84
+ };
85
+ exports.createAudio = createAudio;
@@ -0,0 +1,18 @@
1
+ import type { Codec } from './codec';
2
+ import type { LogLevel } from './log-level';
3
+ import type { CancelSignal } from './make-cancel-signal';
4
+ export declare const createCombinedVideo: ({ addRemotionMetadata, binariesDirectory, cancelSignal, codec, filelistDir, files, fps, indent, logLevel, numberOfGifLoops, onProgress, output, seamless, }: {
5
+ fps: number;
6
+ codec: Codec;
7
+ filelistDir: string;
8
+ numberOfGifLoops: number | null;
9
+ output: string;
10
+ indent: boolean;
11
+ logLevel: LogLevel;
12
+ onProgress: (p: number) => void;
13
+ files: string[];
14
+ addRemotionMetadata: boolean;
15
+ binariesDirectory: string | null;
16
+ cancelSignal: CancelSignal | undefined;
17
+ seamless: boolean;
18
+ }) => Promise<string | undefined>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCombinedVideo = void 0;
4
+ const combine_video_streams_1 = require("./combine-video-streams");
5
+ const combine_video_streams_seamlessly_1 = require("./combine-video-streams-seamlessly");
6
+ const createCombinedVideo = async ({ addRemotionMetadata, binariesDirectory, cancelSignal, codec, filelistDir, files, fps, indent, logLevel, numberOfGifLoops, onProgress, output, seamless, }) => {
7
+ if (seamless) {
8
+ return (0, combine_video_streams_seamlessly_1.combineVideoStreamsSeamlessly)({
9
+ files,
10
+ });
11
+ }
12
+ await (0, combine_video_streams_1.combineVideoStreams)({
13
+ addRemotionMetadata,
14
+ binariesDirectory,
15
+ cancelSignal,
16
+ codec,
17
+ filelistDir,
18
+ files,
19
+ fps,
20
+ indent,
21
+ logLevel,
22
+ numberOfGifLoops,
23
+ onProgress,
24
+ output,
25
+ });
26
+ };
27
+ exports.createCombinedVideo = createCombinedVideo;
@@ -4,9 +4,6 @@ export declare const createFfmpegComplexFilter: ({ filters, downloadMap, }: {
4
4
  filters: PreprocessedAudioTrack[];
5
5
  downloadMap: DownloadMap;
6
6
  }) => Promise<{
7
- complexFilterFlag: [
8
- string,
9
- string
10
- ] | null;
7
+ complexFilterFlag: [string, string] | null;
11
8
  cleanup: () => void;
12
9
  }>;
@@ -0,0 +1,3 @@
1
+ import type { Codec } from './codec';
2
+ import type { PixelFormat } from './pixel-format';
3
+ export declare const warnAboutM2Bug: (codec: Codec | null, pixelFormat: PixelFormat | null) => void;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.warnAboutM2Bug = void 0;
4
+ const node_os_1 = require("node:os");
5
+ const warnAboutM2Bug = (codec, pixelFormat) => {
6
+ const isM2 = (0, node_os_1.cpus)().find((c) => c.model.includes('Apple M2'));
7
+ if (codec === 'prores' && pixelFormat === 'yuv422p10le' && isM2) {
8
+ console.warn();
9
+ console.warn('⚠️ Known issue: Apple M2 CPUs currently suffer from a bug where transparent ProRes videos have flickering. https://github.com/remotion-dev/remotion/issues/1929');
10
+ }
11
+ };
12
+ exports.warnAboutM2Bug = warnAboutM2Bug;
@@ -0,0 +1,2 @@
1
+ import type { AudioCodec } from './audio-codec';
2
+ export declare const getExtensionFromAudioCodec: (audioCodec: AudioCodec) => "mp3" | "aac" | "wav" | "opus";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getExtensionFromAudioCodec = void 0;
4
+ const getExtensionFromAudioCodec = (audioCodec) => {
5
+ if (audioCodec === 'aac') {
6
+ return 'aac';
7
+ }
8
+ if (audioCodec === 'opus') {
9
+ return 'opus';
10
+ }
11
+ if (audioCodec === 'mp3') {
12
+ return 'mp3';
13
+ }
14
+ if (audioCodec === 'pcm-16') {
15
+ return 'wav';
16
+ }
17
+ throw new Error(`Unsupported audio codec: ${audioCodec}`);
18
+ };
19
+ exports.getExtensionFromAudioCodec = getExtensionFromAudioCodec;
package/dist/index.d.ts CHANGED
@@ -715,4 +715,5 @@ export declare const RenderInternals: {
715
715
  hostsToTry: string[];
716
716
  };
717
717
  makeDownloadMap: () => import("./assets/download-map").DownloadMap;
718
+ makeFileExecutableIfItIsNot: (path: string) => void;
718
719
  };
package/dist/index.js CHANGED
@@ -111,6 +111,7 @@ Object.defineProperty(exports, "stitchFramesToVideo", { enumerable: true, get: f
111
111
  var validate_output_filename_1 = require("./validate-output-filename");
112
112
  Object.defineProperty(exports, "validateOutputFilename", { enumerable: true, get: function () { return validate_output_filename_1.validateOutputFilename; } });
113
113
  const download_map_1 = require("./assets/download-map");
114
+ const make_file_executable_1 = require("./compositor/make-file-executable");
114
115
  const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
115
116
  const validate_videobitrate_1 = require("./validate-videobitrate");
116
117
  const wait_for_symbolication_error_to_be_done_1 = require("./wait-for-symbolication-error-to-be-done");
@@ -195,6 +196,7 @@ exports.RenderInternals = {
195
196
  getChromiumGpuInformation: test_gpu_1.getChromiumGpuInformation,
196
197
  getPortConfig: port_config_1.getPortConfig,
197
198
  makeDownloadMap: download_map_1.makeDownloadMap,
199
+ makeFileExecutableIfItIsNot: make_file_executable_1.makeFileExecutableIfItIsNot,
198
200
  };
199
201
  // Warn of potential performance issues with Apple Silicon (M1 chip under Rosetta)
200
202
  (0, check_version_requirements_1.checkNodeVersionAndWarnAboutRosetta)('info', false);
@@ -1,9 +1,13 @@
1
1
  import type { LogLevel } from './log-level';
2
- export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, }: {
2
+ import type { CancelSignal } from './make-cancel-signal';
3
+ export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, }: {
3
4
  videoOutput: string;
4
5
  audioOutput: string;
5
6
  output: string;
6
7
  indent: boolean;
7
8
  logLevel: LogLevel;
9
+ binariesDirectory: string | null;
10
+ fps: number;
8
11
  onProgress: (p: number) => void;
12
+ cancelSignal: CancelSignal | undefined;
9
13
  }) => Promise<void>;
@@ -6,9 +6,12 @@ const call_ffmpeg_1 = require("./call-ffmpeg");
6
6
  const logger_1 = require("./logger");
7
7
  const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
8
8
  const truthy_1 = require("./truthy");
9
- const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, }) => {
9
+ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, }) => {
10
10
  var _a;
11
+ const startTime = Date.now();
12
+ logger_1.Log.verbose({ indent, logLevel }, 'Muxing video and audio together');
11
13
  const command = [
14
+ '-hide_banner',
12
15
  '-i',
13
16
  videoOutput,
14
17
  '-i',
@@ -28,11 +31,16 @@ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logL
28
31
  args: command,
29
32
  indent,
30
33
  logLevel,
34
+ binariesDirectory,
35
+ cancelSignal,
31
36
  });
32
37
  (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
33
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'));
38
+ const utf8 = data.toString('utf8');
39
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
34
40
  if (parsed === undefined) {
35
- logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
41
+ if (!utf8.includes('Estimating duration from bitrate, this may be inaccurate')) {
42
+ logger_1.Log.verbose({ indent, logLevel }, utf8);
43
+ }
36
44
  }
37
45
  else {
38
46
  logger_1.Log.verbose({ indent, logLevel }, `Combined ${parsed} frames`);
@@ -40,5 +48,6 @@ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logL
40
48
  }
41
49
  });
42
50
  await task;
51
+ logger_1.Log.verbose({ indent, logLevel }, `Muxing done in ${Date.now() - startTime}ms`);
43
52
  };
44
53
  exports.muxVideoAndAudio = muxVideoAndAudio;
@@ -0,0 +1,19 @@
1
+ export declare const getForSeamlessAacConcatenation: () => boolean;
2
+ export declare const forSeamlessAacConcatenationOption: {
3
+ name: string;
4
+ cliFlag: "for-seamless-aac-concatenation";
5
+ description: () => import("react/jsx-runtime").JSX.Element;
6
+ docLink: string;
7
+ getValue: ({ commandLine }: {
8
+ commandLine: Record<string, unknown>;
9
+ }) => {
10
+ source: string;
11
+ value: true;
12
+ } | {
13
+ source: string;
14
+ value: false;
15
+ };
16
+ setConfig: (value: boolean) => void;
17
+ ssrName: string;
18
+ type: boolean;
19
+ };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forSeamlessAacConcatenationOption = exports.getForSeamlessAacConcatenation = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const DEFAULT = false;
6
+ let forSeamlessAacConcatenation = DEFAULT;
7
+ const getForSeamlessAacConcatenation = () => {
8
+ return forSeamlessAacConcatenation;
9
+ };
10
+ exports.getForSeamlessAacConcatenation = getForSeamlessAacConcatenation;
11
+ const cliFlag = 'for-seamless-aac-concatenation';
12
+ exports.forSeamlessAacConcatenationOption = {
13
+ name: 'For seamless AAC concatenation',
14
+ cliFlag,
15
+ description: () => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: "If enabled, the audio is trimmed to the nearest AAC frame, which is required for seamless concatenation of AAC files. This is a requirement if you later want to combine multiple video snippets seamlessly." })),
16
+ docLink: 'https://remotion.dev/docs/renderer',
17
+ getValue: ({ commandLine }) => {
18
+ if (commandLine[cliFlag]) {
19
+ return {
20
+ source: 'cli',
21
+ value: true,
22
+ };
23
+ }
24
+ if (forSeamlessAacConcatenation !== DEFAULT) {
25
+ return {
26
+ source: 'config',
27
+ value: forSeamlessAacConcatenation,
28
+ };
29
+ }
30
+ return {
31
+ source: 'default',
32
+ value: DEFAULT,
33
+ };
34
+ },
35
+ setConfig: (value) => {
36
+ forSeamlessAacConcatenation = value;
37
+ },
38
+ ssrName: 'forSeamlessAacConcatenation',
39
+ type: false,
40
+ };
@@ -1,8 +1,18 @@
1
- export declare const preferLossless: {
1
+ export declare const preferLosslessAudioOption: {
2
2
  name: string;
3
3
  cliFlag: "prefer-lossless";
4
- description: () => string;
4
+ description: () => import("react/jsx-runtime").JSX.Element;
5
5
  docLink: string;
6
6
  type: boolean;
7
7
  ssrName: "preferLossless";
8
+ getValue: ({ commandLine }: {
9
+ commandLine: Record<string, unknown>;
10
+ }) => {
11
+ value: true;
12
+ source: string;
13
+ } | {
14
+ value: false;
15
+ source: string;
16
+ };
17
+ setConfig: (val: boolean) => void;
8
18
  };
@@ -1,11 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.preferLossless = void 0;
4
- exports.preferLossless = {
3
+ exports.preferLosslessAudioOption = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const cliFlag = 'prefer-lossless';
6
+ let input = false;
7
+ exports.preferLosslessAudioOption = {
5
8
  name: 'Prefer lossless',
6
- cliFlag: 'prefer-lossless',
7
- description: () => 'Uses a lossless audio codec, if one is available for the codec. If you set audioCodec, it takes priority over preferLossless.',
9
+ cliFlag,
10
+ description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Uses a lossless audio codec, if one is available for the codec. If you set", (0, jsx_runtime_1.jsx)("code", { children: "audioCodec" }), ", it takes priority over", ' ', (0, jsx_runtime_1.jsx)("code", { children: "preferLossless" }), "."] })),
8
11
  docLink: 'https://www.remotion.dev/docs/encoding',
9
12
  type: false,
10
13
  ssrName: 'preferLossless',
14
+ getValue: ({ commandLine }) => {
15
+ if (commandLine[cliFlag]) {
16
+ return { value: true, source: 'cli' };
17
+ }
18
+ if (input === true) {
19
+ return { value: true, source: 'config' };
20
+ }
21
+ return { value: false, source: 'default' };
22
+ },
23
+ setConfig: (val) => {
24
+ input = val;
25
+ },
11
26
  };
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,7 +1,7 @@
1
1
  export declare const webhookCustomDataOption: {
2
2
  name: string;
3
3
  cliFlag: "webhook-custom-data";
4
- description: (type: "ssr" | "cli") => import("react/jsx-runtime").JSX.Element;
4
+ description: (type: "cli" | "ssr") => import("react/jsx-runtime").JSX.Element;
5
5
  ssrName: "customData";
6
6
  docLink: string;
7
7
  type: Record<string, unknown> | null;
@@ -2,7 +2,7 @@
2
2
  import type { ClipRegion } from 'remotion/no-react';
3
3
  import type { Page } from './browser/BrowserPage';
4
4
  import type { StillImageFormat } from './image-format';
5
- export declare const provideScreenshot: ({ page, imageFormat, options, jpegQuality, height, width, clipRegion, timeoutInMilliseconds, }: {
5
+ export declare const provideScreenshot: ({ page, imageFormat, options, jpegQuality, height, width, clipRegion, timeoutInMilliseconds, scale, }: {
6
6
  page: Page;
7
7
  imageFormat: StillImageFormat;
8
8
  jpegQuality: number | undefined;
@@ -14,4 +14,5 @@ export declare const provideScreenshot: ({ page, imageFormat, options, jpegQuali
14
14
  width: number;
15
15
  clipRegion: ClipRegion | null;
16
16
  timeoutInMilliseconds: number;
17
+ scale: number;
17
18
  }) => Promise<Buffer>;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.provideScreenshot = void 0;
4
4
  const screenshot_dom_element_1 = require("./screenshot-dom-element");
5
- const provideScreenshot = ({ page, imageFormat, options, jpegQuality, height, width, clipRegion, timeoutInMilliseconds, }) => {
5
+ const provideScreenshot = ({ page, imageFormat, options, jpegQuality, height, width, clipRegion, timeoutInMilliseconds, scale, }) => {
6
6
  return (0, screenshot_dom_element_1.screenshotDOMElement)({
7
7
  page,
8
8
  opts: {
@@ -14,6 +14,7 @@ const provideScreenshot = ({ page, imageFormat, options, jpegQuality, height, wi
14
14
  width,
15
15
  clipRegion,
16
16
  timeoutInMilliseconds,
17
+ scale,
17
18
  });
18
19
  };
19
20
  exports.provideScreenshot = provideScreenshot;
@@ -11,4 +11,5 @@ export declare const screenshot: (options: {
11
11
  width: number;
12
12
  height: number;
13
13
  clipRegion: ClipRegion | null;
14
+ scale: number;
14
15
  }) => Promise<Buffer | string>;
@@ -43,6 +43,7 @@ const screenshot = (options) => {
43
43
  path: options.path,
44
44
  jpegQuality: options.type === 'jpeg' ? options.jpegQuality : undefined,
45
45
  clipRegion: options.clipRegion,
46
+ scale: options.scale,
46
47
  }));
47
48
  };
48
49
  exports.screenshot = screenshot;
@@ -361,8 +361,9 @@ const internalRenderFramesRaw = ({ browserExecutable, cancelSignal, chromiumOpti
361
361
  ]).then(([{ server: openedServer, cleanupServer }, pInstance]) => {
362
362
  const { serveUrl, offthreadPort, compositor, sourceMap, downloadMap } = openedServer;
363
363
  const browserReplacer = (0, replace_browser_1.handleBrowserCrash)(pInstance, logLevel, indent);
364
+ const cycle = (0, cycle_browser_tabs_1.cycleBrowserTabs)(browserReplacer, actualConcurrency, logLevel, indent);
364
365
  cleanup.push(() => {
365
- (0, cycle_browser_tabs_1.cycleBrowserTabs)(browserReplacer, actualConcurrency, logLevel, indent).stopCycling();
366
+ cycle.stopCycling();
366
367
  return Promise.resolve();
367
368
  });
368
369
  cleanup.push(() => cleanupServer(false));
@@ -2,7 +2,7 @@
2
2
  import type { ClipRegion } from 'remotion/no-react';
3
3
  import type { Page } from './browser/BrowserPage';
4
4
  import type { StillImageFormat } from './image-format';
5
- export declare const screenshotDOMElement: ({ page, imageFormat, jpegQuality, opts, height, width, clipRegion, timeoutInMilliseconds, }: {
5
+ export declare const screenshotDOMElement: ({ page, imageFormat, jpegQuality, opts, height, width, clipRegion, timeoutInMilliseconds, scale, }: {
6
6
  page: Page;
7
7
  imageFormat: StillImageFormat;
8
8
  jpegQuality: number | undefined;
@@ -13,4 +13,5 @@ export declare const screenshotDOMElement: ({ page, imageFormat, jpegQuality, op
13
13
  width: number;
14
14
  clipRegion: ClipRegion | null;
15
15
  timeoutInMilliseconds: number;
16
+ scale: number;
16
17
  }) => Promise<Buffer>;
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.screenshotDOMElement = void 0;
4
4
  const puppeteer_evaluate_1 = require("./puppeteer-evaluate");
5
5
  const puppeteer_screenshot_1 = require("./puppeteer-screenshot");
6
- const screenshotDOMElement = async ({ page, imageFormat, jpegQuality, opts, height, width, clipRegion, timeoutInMilliseconds, }) => {
6
+ const screenshotDOMElement = async ({ page, imageFormat, jpegQuality, opts, height, width, clipRegion, timeoutInMilliseconds, scale, }) => {
7
7
  const { path } = opts;
8
8
  if (imageFormat === 'png' ||
9
9
  imageFormat === 'pdf' ||
@@ -42,6 +42,7 @@ const screenshotDOMElement = async ({ page, imageFormat, jpegQuality, opts, heig
42
42
  width,
43
43
  height,
44
44
  clipRegion,
45
+ scale,
45
46
  });
46
47
  if (typeof buf === 'string') {
47
48
  throw new TypeError('Expected a buffer');
@@ -2,7 +2,7 @@
2
2
  import type { ClipRegion } from 'remotion/no-react';
3
3
  import type { Page } from './browser/BrowserPage';
4
4
  import type { StillImageFormat } from './image-format';
5
- export declare const screenshotTask: ({ format, height, omitBackground, page, width, path, jpegQuality, clipRegion, }: {
5
+ export declare const screenshotTask: ({ format, height, omitBackground, page, width, path, jpegQuality, clipRegion, scale, }: {
6
6
  page: Page;
7
7
  format: StillImageFormat;
8
8
  path?: string | undefined;
@@ -11,4 +11,5 @@ export declare const screenshotTask: ({ format, height, omitBackground, page, wi
11
11
  width: number;
12
12
  height: number;
13
13
  clipRegion: ClipRegion | null;
14
+ scale: number;
14
15
  }) => Promise<Buffer | string>;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.screenshotTask = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const perf_1 = require("./perf");
9
- const screenshotTask = async ({ format, height, omitBackground, page, width, path, jpegQuality, clipRegion, }) => {
9
+ const screenshotTask = async ({ format, height, omitBackground, page, width, path, jpegQuality, clipRegion, scale, }) => {
10
10
  var _a;
11
11
  const client = page._client();
12
12
  const target = page.target();
@@ -37,6 +37,13 @@ const screenshotTask = async ({ format, height, omitBackground, page, width, pat
37
37
  result = res.value;
38
38
  }
39
39
  else {
40
+ // We find that there is a 0.1% framedrop when rendering under memory pressure
41
+ // which can be circumvented by disabling this option on Lambda.
42
+ // To be determined: Is this a problem with Lambda, or the Chrome version
43
+ // we are using on Lambda?
44
+ // We already found out that the problem is not a general Linux problem.
45
+ const fromSurface = !process.env.DISABLE_FROM_SURFACE;
46
+ const scaleFactor = fromSurface ? 1 : scale;
40
47
  const { value } = await client.send('Page.captureScreenshot', {
41
48
  format,
42
49
  quality: jpegQuality,
@@ -51,18 +58,13 @@ const screenshotTask = async ({ format, height, omitBackground, page, width, pat
51
58
  : {
52
59
  x: 0,
53
60
  y: 0,
54
- height,
61
+ height: height * scaleFactor,
55
62
  scale: 1,
56
- width,
63
+ width: width * scaleFactor,
57
64
  },
58
65
  captureBeyondViewport: true,
59
66
  optimizeForSpeed: true,
60
- // We find that there is a 0.1% framedrop when rendering under memory pressure
61
- // which can be circumvented by disabling this option on Lambda.
62
- // To be determined: Is this a problem with Lambda, or the Chrome version
63
- // we are using on Lambda?
64
- // We already found out that the problem is not a general Linux problem.
65
- fromSurface: !process.env.DISABLE_FROM_SURFACE,
67
+ fromSurface,
66
68
  });
67
69
  result = value;
68
70
  }
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { TRenderAsset } from 'remotion/no-react';
2
3
  import type { DownloadMap } from './assets/download-map';
3
4
  import type { Page } from './browser/BrowserPage';
@@ -59,6 +59,7 @@ const takeFrameAndCompose = async ({ freePage, imageFormat, jpegQuality, frame,
59
59
  width,
60
60
  clipRegion,
61
61
  timeoutInMilliseconds,
62
+ scale,
62
63
  });
63
64
  if (shouldMakeBuffer) {
64
65
  return { buffer: buf, collectedAssets };
@@ -5,3 +5,18 @@ export declare const validateSelectedCodecAndPresetCombination: ({ codec, x264Pr
5
5
  codec: Codec;
6
6
  x264Preset: X264Preset | undefined;
7
7
  }) => void;
8
+ export declare const x264Option: {
9
+ name: string;
10
+ cliFlag: "x264-preset";
11
+ description: () => import("react/jsx-runtime").JSX.Element;
12
+ ssrName: "x264Preset";
13
+ docLink: string;
14
+ type: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow" | "placebo";
15
+ getValue: ({ commandLine }: {
16
+ commandLine: Record<string, unknown>;
17
+ }) => {
18
+ value: "ultrafast" | "superfast" | "veryfast" | "faster" | "fast" | "medium" | "slow" | "slower" | "veryslow" | "placebo";
19
+ source: string;
20
+ };
21
+ setConfig: (profile: X264Preset | undefined) => void;
22
+ };
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateSelectedCodecAndPresetCombination = exports.x264PresetOptions = void 0;
3
+ exports.x264Option = exports.validateSelectedCodecAndPresetCombination = exports.x264PresetOptions = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
5
  exports.x264PresetOptions = [
5
6
  'ultrafast',
6
7
  'superfast',
@@ -13,6 +14,7 @@ exports.x264PresetOptions = [
13
14
  'veryslow',
14
15
  'placebo',
15
16
  ];
17
+ let preset;
16
18
  const validateSelectedCodecAndPresetCombination = ({ codec, x264Preset, }) => {
17
19
  if (typeof x264Preset !== 'undefined' &&
18
20
  codec !== 'h264' &&
@@ -27,3 +29,26 @@ const validateSelectedCodecAndPresetCombination = ({ codec, x264Preset, }) => {
27
29
  }
28
30
  };
29
31
  exports.validateSelectedCodecAndPresetCombination = validateSelectedCodecAndPresetCombination;
32
+ const cliFlag = 'x264-preset';
33
+ const DEFAULT_PRESET = 'medium';
34
+ exports.x264Option = {
35
+ name: 'x264 Preset',
36
+ cliFlag,
37
+ description: () => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Sets a x264 preset profile. Only applies to videos rendered with", ' ', (0, jsx_runtime_1.jsx)("code", { children: "h264" }), " codec.", (0, jsx_runtime_1.jsx)("br", {}), "Possible values: ", (0, jsx_runtime_1.jsx)("code", { children: "superfast" }), ", ", (0, jsx_runtime_1.jsx)("code", { children: "veryfast" }), ",", ' ', (0, jsx_runtime_1.jsx)("code", { children: "faster" }), ", ", (0, jsx_runtime_1.jsx)("code", { children: "fast" }), ", ", (0, jsx_runtime_1.jsx)("code", { children: "medium" }), ",", ' ', (0, jsx_runtime_1.jsx)("code", { children: "slow" }), ", ", (0, jsx_runtime_1.jsx)("code", { children: "slower" }), ", ", (0, jsx_runtime_1.jsx)("code", { children: "veryslow" }), ",", ' ', (0, jsx_runtime_1.jsx)("code", { children: "placebo" }), ".", (0, jsx_runtime_1.jsx)("br", {}), "Default: ", (0, jsx_runtime_1.jsx)("code", { children: DEFAULT_PRESET })] })),
38
+ ssrName: 'x264Preset',
39
+ docLink: 'https://www.remotion.dev/docs/renderer/render-media',
40
+ type: 'fast',
41
+ getValue: ({ commandLine }) => {
42
+ const value = commandLine[cliFlag];
43
+ if (typeof value !== 'undefined') {
44
+ return { value: value, source: 'cli' };
45
+ }
46
+ if (typeof preset !== 'undefined') {
47
+ return { value: preset, source: 'config' };
48
+ }
49
+ return { value: DEFAULT_PRESET, source: 'default' };
50
+ },
51
+ setConfig: (profile) => {
52
+ preset = profile;
53
+ },
54
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "4.0.120",
3
+ "version": "4.0.122",
4
4
  "description": "Renderer for Remotion",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,7 +18,7 @@
18
18
  "extract-zip": "2.0.1",
19
19
  "source-map": "^0.8.0-beta.0",
20
20
  "ws": "8.7.0",
21
- "remotion": "4.0.120"
21
+ "remotion": "4.0.122"
22
22
  },
23
23
  "peerDependencies": {
24
24
  "react": ">=16.8.0",
@@ -40,13 +40,13 @@
40
40
  "vitest": "0.31.1"
41
41
  },
42
42
  "optionalDependencies": {
43
- "@remotion/compositor-darwin-arm64": "4.0.120",
44
- "@remotion/compositor-darwin-x64": "4.0.120",
45
- "@remotion/compositor-linux-arm64-musl": "4.0.120",
46
- "@remotion/compositor-linux-arm64-gnu": "4.0.120",
47
- "@remotion/compositor-linux-x64-gnu": "4.0.120",
48
- "@remotion/compositor-linux-x64-musl": "4.0.120",
49
- "@remotion/compositor-win32-x64-msvc": "4.0.120"
43
+ "@remotion/compositor-darwin-x64": "4.0.122",
44
+ "@remotion/compositor-darwin-arm64": "4.0.122",
45
+ "@remotion/compositor-linux-x64-gnu": "4.0.122",
46
+ "@remotion/compositor-linux-x64-musl": "4.0.122",
47
+ "@remotion/compositor-win32-x64-msvc": "4.0.122",
48
+ "@remotion/compositor-linux-arm64-gnu": "4.0.122",
49
+ "@remotion/compositor-linux-arm64-musl": "4.0.122"
50
50
  },
51
51
  "keywords": [
52
52
  "remotion",