@remotion/renderer 4.0.108 → 4.0.110

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.
@@ -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;
@@ -0,0 +1,16 @@
1
+ import type { AudioCodec } from './audio-codec';
2
+ import type { LogLevel } from './log-level';
3
+ export declare const durationOf1Frame: number;
4
+ export declare const getClosestAlignedTime: (targetTime: number) => number;
5
+ export declare const createCombinedAudio: ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, }: {
6
+ seamless: boolean;
7
+ filelistDir: string;
8
+ files: string[];
9
+ indent: boolean;
10
+ logLevel: LogLevel;
11
+ audioBitrate: string | null;
12
+ resolvedAudioCodec: AudioCodec;
13
+ output: string;
14
+ chunkDurationInSeconds: number;
15
+ addRemotionMetadata: boolean;
16
+ }) => Promise<string>;
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCombinedAudio = exports.getClosestAlignedTime = exports.durationOf1Frame = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const version_1 = require("remotion/version");
7
+ const audio_codec_1 = require("./audio-codec");
8
+ const call_ffmpeg_1 = require("./call-ffmpeg");
9
+ const logger_1 = require("./logger");
10
+ const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
11
+ const sample_rate_1 = require("./sample-rate");
12
+ const truthy_1 = require("./truthy");
13
+ exports.durationOf1Frame = (1024 / sample_rate_1.DEFAULT_SAMPLE_RATE) * 1000000;
14
+ const getClosestAlignedTime = (targetTime) => {
15
+ const decimalFramesToTargetTime = targetTime / exports.durationOf1Frame;
16
+ const nearestFrameIndexForTargetTime = Math.round(decimalFramesToTargetTime);
17
+ return nearestFrameIndexForTargetTime * exports.durationOf1Frame;
18
+ };
19
+ exports.getClosestAlignedTime = getClosestAlignedTime;
20
+ const encodeAudio = async ({ files, resolvedAudioCodec, audioBitrate, filelistDir, output, indent, logLevel, addRemotionMetadata, }) => {
21
+ var _a;
22
+ const fileList = files.map((p) => `file '${p}'`).join('\n');
23
+ const fileListTxt = (0, path_1.join)(filelistDir, 'audio-files.txt');
24
+ (0, fs_1.writeFileSync)(fileListTxt, fileList);
25
+ const command = [
26
+ '-f',
27
+ 'concat',
28
+ '-safe',
29
+ '0',
30
+ '-i',
31
+ fileListTxt,
32
+ '-c:a',
33
+ (0, audio_codec_1.mapAudioCodecToFfmpegAudioCodecName)(resolvedAudioCodec),
34
+ resolvedAudioCodec === 'aac' ? '-cutoff' : null,
35
+ resolvedAudioCodec === 'aac' ? '18000' : null,
36
+ '-b:a',
37
+ audioBitrate ? audioBitrate : '320k',
38
+ '-vn',
39
+ addRemotionMetadata ? `-metadata` : null,
40
+ addRemotionMetadata ? `comment=Made with Remotion ${version_1.VERSION}` : null,
41
+ '-y',
42
+ output,
43
+ ];
44
+ try {
45
+ const task = (0, call_ffmpeg_1.callFf)({
46
+ args: command,
47
+ bin: 'ffmpeg',
48
+ indent,
49
+ logLevel,
50
+ });
51
+ (_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'));
53
+ if (parsed === undefined) {
54
+ logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
55
+ }
56
+ else {
57
+ logger_1.Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
58
+ }
59
+ });
60
+ await task;
61
+ return output;
62
+ }
63
+ catch (e) {
64
+ (0, fs_1.rmSync)(filelistDir, { recursive: true });
65
+ throw e;
66
+ }
67
+ };
68
+ const combineAudioSeamlessly = async ({ files, filelistDir, indent, logLevel, output, chunkDurationInSeconds, addRemotionMetadata, }) => {
69
+ var _a;
70
+ const fileList = files
71
+ .map((p, i) => {
72
+ const isLast = i === files.length - 1;
73
+ const targetStart = i * chunkDurationInSeconds * 1000000;
74
+ const endStart = (i + 1) * chunkDurationInSeconds * 1000000;
75
+ const startTime = (0, exports.getClosestAlignedTime)(targetStart);
76
+ const endTime = (0, exports.getClosestAlignedTime)(endStart);
77
+ const realDuration = endTime - startTime;
78
+ let inpoint = 0;
79
+ if (i > 0) {
80
+ // Although we only asked for two frames of padding, ffmpeg will add an
81
+ // additional 2 frames of silence at the start of the segment. When we slice out
82
+ // our real data with inpoint and outpoint, we'll want remove both the silence
83
+ // and the extra frames we asked for.
84
+ inpoint = exports.durationOf1Frame * 4;
85
+ }
86
+ // inpoint is inclusive and outpoint is exclusive. To avoid overlap, we subtract
87
+ // the duration of one frame from the outpoint.
88
+ // we don't have to subtract a frame if this is the last segment.
89
+ const outpoint = (i === 0 ? exports.durationOf1Frame * 2 : inpoint) +
90
+ realDuration -
91
+ (isLast ? 0 : exports.durationOf1Frame);
92
+ return [`file '${p}'`, `inpoint ${inpoint}us`, `outpoint ${outpoint}us`]
93
+ .filter(truthy_1.truthy)
94
+ .join('\n');
95
+ })
96
+ .join('\n');
97
+ const fileListTxt = (0, path_1.join)(filelistDir, 'audio-files.txt');
98
+ (0, fs_1.writeFileSync)(fileListTxt, fileList);
99
+ const command = [
100
+ '-f',
101
+ 'concat',
102
+ '-safe',
103
+ '0',
104
+ '-i',
105
+ fileListTxt,
106
+ '-c:a',
107
+ 'copy',
108
+ '-vn',
109
+ addRemotionMetadata ? `-metadata` : null,
110
+ addRemotionMetadata ? `comment=Made with Remotion ${version_1.VERSION}` : null,
111
+ '-y',
112
+ output,
113
+ ];
114
+ try {
115
+ const task = (0, call_ffmpeg_1.callFf)({
116
+ args: command,
117
+ bin: 'ffmpeg',
118
+ indent,
119
+ logLevel,
120
+ });
121
+ (_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 {
127
+ logger_1.Log.verbose({ indent, logLevel }, `Encoded ${parsed} audio frames`);
128
+ }
129
+ });
130
+ await task;
131
+ return output;
132
+ }
133
+ catch (e) {
134
+ (0, fs_1.rmSync)(filelistDir, { recursive: true });
135
+ console.log(e);
136
+ throw e;
137
+ }
138
+ };
139
+ const createCombinedAudio = ({ seamless, filelistDir, files, indent, logLevel, audioBitrate, resolvedAudioCodec, output, chunkDurationInSeconds, addRemotionMetadata, }) => {
140
+ if (seamless) {
141
+ return combineAudioSeamlessly({
142
+ filelistDir,
143
+ files,
144
+ indent,
145
+ logLevel,
146
+ output,
147
+ chunkDurationInSeconds,
148
+ addRemotionMetadata,
149
+ });
150
+ }
151
+ return encodeAudio({
152
+ filelistDir,
153
+ files,
154
+ resolvedAudioCodec,
155
+ audioBitrate,
156
+ output,
157
+ indent,
158
+ logLevel,
159
+ addRemotionMetadata,
160
+ });
161
+ };
162
+ exports.createCombinedAudio = createCombinedAudio;
@@ -0,0 +1,14 @@
1
+ import type { Codec } from './codec';
2
+ import type { LogLevel } from './log-level';
3
+ export declare const combineVideoStreams: ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, }: {
4
+ fps: number;
5
+ codec: Codec;
6
+ filelistDir: string;
7
+ numberOfGifLoops: number | null;
8
+ output: string;
9
+ indent: boolean;
10
+ logLevel: LogLevel;
11
+ onProgress: (p: number) => void;
12
+ files: string[];
13
+ addRemotionMetadata: boolean;
14
+ }) => Promise<string>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.combineVideoStreams = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const version_1 = require("remotion/version");
7
+ const call_ffmpeg_1 = require("./call-ffmpeg");
8
+ const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif-loops-to-ffmpeg");
9
+ const logger_1 = require("./logger");
10
+ const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
11
+ const truthy_1 = require("./truthy");
12
+ const combineVideoStreams = async ({ fps, codec, filelistDir, numberOfGifLoops, output, indent, logLevel, onProgress, files, addRemotionMetadata, }) => {
13
+ var _a;
14
+ const fileList = files.map((p) => `file '${p}'`).join('\n');
15
+ const fileListTxt = (0, path_1.join)(filelistDir, 'video-files.txt');
16
+ (0, fs_1.writeFileSync)(fileListTxt, fileList);
17
+ const command = [
18
+ '-r',
19
+ String(fps),
20
+ '-f',
21
+ 'concat',
22
+ '-safe',
23
+ '0',
24
+ '-i',
25
+ fileListTxt,
26
+ numberOfGifLoops === null ? null : '-loop',
27
+ numberOfGifLoops === null
28
+ ? null
29
+ : (0, convert_number_of_gif_loops_to_ffmpeg_1.convertNumberOfGifLoopsToFfmpegSyntax)(numberOfGifLoops),
30
+ '-an',
31
+ '-c:v',
32
+ codec === 'gif' ? 'gif' : 'copy',
33
+ codec === 'h264' ? '-movflags' : null,
34
+ codec === 'h264' ? 'faststart' : null,
35
+ addRemotionMetadata ? `-metadata` : null,
36
+ addRemotionMetadata ? `comment=Made with Remotion ${version_1.VERSION}` : null,
37
+ '-y',
38
+ output,
39
+ ].filter(truthy_1.truthy);
40
+ try {
41
+ const task = (0, call_ffmpeg_1.callFf)({
42
+ args: command,
43
+ bin: 'ffmpeg',
44
+ indent,
45
+ logLevel,
46
+ });
47
+ (_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'));
49
+ if (parsed === undefined) {
50
+ logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
51
+ }
52
+ else {
53
+ logger_1.Log.verbose({ indent, logLevel }, `Encoded ${parsed} video frames`);
54
+ onProgress(parsed);
55
+ }
56
+ });
57
+ await task;
58
+ return output;
59
+ }
60
+ catch (e) {
61
+ (0, fs_1.rmSync)(filelistDir, { recursive: true });
62
+ throw e;
63
+ }
64
+ };
65
+ exports.combineVideoStreams = combineVideoStreams;
@@ -59,7 +59,7 @@ const combineVideos = async (options) => {
59
59
  });
60
60
  (_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
61
61
  if (onProgress) {
62
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'));
62
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(data.toString('utf8'), fps);
63
63
  if (parsed === undefined) {
64
64
  logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
65
65
  }
@@ -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,9 @@
1
+ import type { LogLevel } from './log-level';
2
+ export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, }: {
3
+ videoOutput: string;
4
+ audioOutput: string;
5
+ output: string;
6
+ indent: boolean;
7
+ logLevel: LogLevel;
8
+ onProgress: (p: number) => void;
9
+ }) => Promise<void>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.muxVideoAndAudio = void 0;
4
+ const version_1 = require("remotion/version");
5
+ const call_ffmpeg_1 = require("./call-ffmpeg");
6
+ const logger_1 = require("./logger");
7
+ const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
8
+ const truthy_1 = require("./truthy");
9
+ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, }) => {
10
+ var _a;
11
+ const command = [
12
+ '-i',
13
+ videoOutput,
14
+ '-i',
15
+ audioOutput,
16
+ '-c:v',
17
+ 'copy',
18
+ '-c:a',
19
+ 'copy',
20
+ `-metadata`,
21
+ `comment=Made with Remotion ${version_1.VERSION}`,
22
+ '-y',
23
+ output,
24
+ ].filter(truthy_1.truthy);
25
+ logger_1.Log.verbose({ indent, logLevel }, 'Combining command: ', command);
26
+ const task = (0, call_ffmpeg_1.callFf)({
27
+ bin: 'ffmpeg',
28
+ args: command,
29
+ indent,
30
+ logLevel,
31
+ });
32
+ (_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'));
34
+ if (parsed === undefined) {
35
+ logger_1.Log.verbose({ indent, logLevel }, data.toString('utf8'));
36
+ }
37
+ else {
38
+ logger_1.Log.verbose({ indent, logLevel }, `Combined ${parsed} frames`);
39
+ onProgress(parsed);
40
+ }
41
+ });
42
+ await task;
43
+ };
44
+ exports.muxVideoAndAudio = muxVideoAndAudio;
@@ -0,0 +1,8 @@
1
+ export declare const preferLossless: {
2
+ name: string;
3
+ cliFlag: "prefer-lossless";
4
+ description: () => string;
5
+ docLink: string;
6
+ type: boolean;
7
+ ssrName: "preferLossless";
8
+ };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preferLossless = void 0;
4
+ exports.preferLossless = {
5
+ 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.',
8
+ docLink: 'https://www.remotion.dev/docs/encoding',
9
+ type: false,
10
+ ssrName: 'preferLossless',
11
+ };
@@ -1 +1 @@
1
- export declare const parseFfmpegProgress: (input: string) => number | undefined;
1
+ export declare const parseFfmpegProgress: (input: string, fps: number) => number | undefined;
@@ -1,11 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseFfmpegProgress = void 0;
4
- const parseFfmpegProgress = (input) => {
4
+ const parseFfmpegProgress = (input, fps) => {
5
5
  const match = input.match(/frame=(\s+)?([0-9]+)\s/);
6
- if (!match) {
7
- return undefined;
6
+ if (match) {
7
+ return Number(match[2]);
8
+ }
9
+ const match2 = input.match(/time=(\d+):(\d+):(\d+).(\d+)\s/);
10
+ if (match2) {
11
+ const [, hours, minutes, seconds, hundreds] = match2;
12
+ return ((Number(hundreds) / 100) * fps +
13
+ Number(seconds) * fps +
14
+ Number(minutes) * fps * 60 +
15
+ Number(hours) * fps * 60 * 60);
8
16
  }
9
- return Number(match[2]);
10
17
  };
11
18
  exports.parseFfmpegProgress = parseFfmpegProgress;
@@ -79,7 +79,7 @@ const prespawnFfmpeg = (options) => {
79
79
  const str = data.toString();
80
80
  ffmpegOutput += str;
81
81
  if (options.onProgress) {
82
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(str);
82
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(str, options.fps);
83
83
  if (parsed !== undefined) {
84
84
  options.onProgress(parsed);
85
85
  }
@@ -284,7 +284,7 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec,
284
284
  const str = data.toString();
285
285
  ffmpegStderr += str;
286
286
  if (onProgress) {
287
- const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(str);
287
+ const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(str, fps);
288
288
  // FFMPEG bug: In some cases, FFMPEG does hang after it is finished with it's job
289
289
  // Example repo: https://github.com/JonnyBurger/ffmpeg-repro (access can be given upon request)
290
290
  if (parsed !== undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/renderer",
3
- "version": "4.0.108",
3
+ "version": "4.0.110",
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.108"
21
+ "remotion": "4.0.110"
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.108",
44
- "@remotion/compositor-darwin-x64": "4.0.108",
45
- "@remotion/compositor-linux-arm64-gnu": "4.0.108",
46
- "@remotion/compositor-linux-x64-musl": "4.0.108",
47
- "@remotion/compositor-win32-x64-msvc": "4.0.108",
48
- "@remotion/compositor-linux-x64-gnu": "4.0.108",
49
- "@remotion/compositor-linux-arm64-musl": "4.0.108"
43
+ "@remotion/compositor-darwin-arm64": "4.0.110",
44
+ "@remotion/compositor-linux-arm64-gnu": "4.0.110",
45
+ "@remotion/compositor-darwin-x64": "4.0.110",
46
+ "@remotion/compositor-linux-x64-gnu": "4.0.110",
47
+ "@remotion/compositor-linux-x64-musl": "4.0.110",
48
+ "@remotion/compositor-win32-x64-msvc": "4.0.110",
49
+ "@remotion/compositor-linux-arm64-musl": "4.0.110"
50
50
  },
51
51
  "keywords": [
52
52
  "remotion",