@remotion/renderer 3.2.30 → 3.2.32
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.
- package/dist/crf.d.ts +5 -1
- package/dist/crf.js +33 -6
- package/dist/ffmpeg-args-hook.d.ts +4 -0
- package/dist/ffmpeg-args-hook.js +2 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +3 -2
- package/dist/make-assets-download-dir.d.ts +1 -0
- package/dist/make-assets-download-dir.js +13 -0
- package/dist/prespawn-ffmpeg.d.ts +2 -1
- package/dist/prespawn-ffmpeg.js +12 -11
- package/dist/prores-profile.d.ts +4 -1
- package/dist/prores-profile.js +6 -6
- package/dist/render-media.d.ts +3 -1
- package/dist/render-media.js +14 -5
- package/dist/stitch-frames-to-video.d.ts +2 -0
- package/dist/stitch-frames-to-video.js +26 -16
- package/dist/validate-ffmpeg-args-hook.d.ts +2 -0
- package/dist/validate-ffmpeg-args-hook.js +12 -0
- package/dist/validate-ffmpeg-override-fn.d.ts +2 -0
- package/dist/validate-ffmpeg-override-fn.js +12 -0
- package/dist/validate-videobitrate.d.ts +1 -0
- package/dist/validate-videobitrate.js +20 -0
- package/package.json +4 -4
- package/dist/assets/dl-browser.d.ts +0 -1
- package/dist/assets/dl-browser.js +0 -38
package/dist/crf.d.ts
CHANGED
|
@@ -2,4 +2,8 @@ import type { Codec } from './codec';
|
|
|
2
2
|
export declare type Crf = number | undefined;
|
|
3
3
|
export declare const getDefaultCrfForCodec: (codec: Codec) => number;
|
|
4
4
|
export declare const getValidCrfRanges: (codec: Codec) => [number, number];
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const validateQualitySettings: ({ codec, crf, videoBitrate, }: {
|
|
6
|
+
crf: unknown;
|
|
7
|
+
codec: Codec;
|
|
8
|
+
videoBitrate: string | null | undefined;
|
|
9
|
+
}) => string[];
|
package/dist/crf.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.validateQualitySettings = exports.getValidCrfRanges = exports.getDefaultCrfForCodec = void 0;
|
|
4
4
|
const is_audio_codec_1 = require("./is-audio-codec");
|
|
5
5
|
const getDefaultCrfForCodec = (codec) => {
|
|
6
6
|
if ((0, is_audio_codec_1.isAudioCodec)(codec)) {
|
|
@@ -46,19 +46,46 @@ const getValidCrfRanges = (codec) => {
|
|
|
46
46
|
throw new TypeError(`Got unexpected codec "${codec}"`);
|
|
47
47
|
};
|
|
48
48
|
exports.getValidCrfRanges = getValidCrfRanges;
|
|
49
|
-
const
|
|
50
|
-
if (crf
|
|
51
|
-
|
|
49
|
+
const validateQualitySettings = ({ codec, crf, videoBitrate, }) => {
|
|
50
|
+
if (crf && videoBitrate) {
|
|
51
|
+
throw new Error('"crf" and "videoBitrate" can not both be set. Choose one of either.');
|
|
52
|
+
}
|
|
53
|
+
if (videoBitrate) {
|
|
54
|
+
if (codec === 'prores') {
|
|
55
|
+
console.warn('ProRes does not support videoBitrate. Ignoring.');
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
if ((0, is_audio_codec_1.isAudioCodec)(codec)) {
|
|
59
|
+
console.warn(`${codec} does not support videoBitrate. Ignoring.`);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return ['-b:v', videoBitrate];
|
|
63
|
+
}
|
|
64
|
+
if (crf === null || typeof crf === 'undefined') {
|
|
65
|
+
const actualCrf = (0, exports.getDefaultCrfForCodec)(codec);
|
|
66
|
+
return ['-crf', String(actualCrf)];
|
|
52
67
|
}
|
|
53
68
|
if (typeof crf !== 'number') {
|
|
54
69
|
throw new TypeError('Expected CRF to be a number, but is ' + JSON.stringify(crf));
|
|
55
70
|
}
|
|
56
71
|
const range = (0, exports.getValidCrfRanges)(codec);
|
|
57
72
|
if (crf === 0 && (codec === 'h264' || codec === 'h264-mkv')) {
|
|
58
|
-
throw new TypeError("Setting the CRF to 0 with a H264 codec is not supported anymore because of it's inconsistencies between platforms. Videos with CRF 0 cannot be played on iOS/macOS. 0 is a extreme value with inefficient settings which you probably want. Set CRF to a higher value to fix this error.");
|
|
73
|
+
throw new TypeError("Setting the CRF to 0 with a H264 codec is not supported anymore because of it's inconsistencies between platforms. Videos with CRF 0 cannot be played on iOS/macOS. 0 is a extreme value with inefficient settings which you probably do not want. Set CRF to a higher value to fix this error.");
|
|
59
74
|
}
|
|
60
75
|
if (crf < range[0] || crf > range[1]) {
|
|
76
|
+
if (range[0] === 0 && range[1] === 0) {
|
|
77
|
+
throw new TypeError(`The "${codec}" codec does not support the --crf option.`);
|
|
78
|
+
}
|
|
61
79
|
throw new TypeError(`CRF must be between ${range[0]} and ${range[1]} for codec ${codec}. Passed: ${crf}`);
|
|
62
80
|
}
|
|
81
|
+
if (codec === 'prores') {
|
|
82
|
+
console.warn('ProRes does not support the "crf" option. Ignoring.');
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
if ((0, is_audio_codec_1.isAudioCodec)(codec)) {
|
|
86
|
+
console.warn(`${codec} does not support the "crf" option. Ignoring.`);
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return ['-crf', String(crf)];
|
|
63
90
|
};
|
|
64
|
-
exports.
|
|
91
|
+
exports.validateQualitySettings = validateQualitySettings;
|
package/dist/index.d.ts
CHANGED
|
@@ -126,8 +126,11 @@ export declare const RenderInternals: {
|
|
|
126
126
|
validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
|
|
127
127
|
DEFAULT_OPENGL_RENDERER: "angle" | "swangle" | "egl" | "swiftshader" | null;
|
|
128
128
|
validateOpenGlRenderer: (option: "angle" | "swangle" | "egl" | "swiftshader" | null) => "angle" | "swangle" | "egl" | "swiftshader" | null;
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
validateQualitySettings: ({ codec, crf, videoBitrate, }: {
|
|
130
|
+
crf: unknown;
|
|
131
|
+
codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
132
|
+
videoBitrate: string | null | undefined;
|
|
133
|
+
}) => string[];
|
|
131
134
|
validImageFormats: readonly ["png", "jpeg", "none"];
|
|
132
135
|
validCodecs: readonly ["h264", "h265", "vp8", "vp9", "mp3", "aac", "wav", "prores", "h264-mkv", "gif"];
|
|
133
136
|
DEFAULT_PIXEL_FORMAT: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le";
|
|
@@ -136,7 +139,10 @@ export declare const RenderInternals: {
|
|
|
136
139
|
DEFAULT_TIMEOUT: number;
|
|
137
140
|
getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
|
|
138
141
|
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: (
|
|
142
|
+
validateSelectedCodecAndProResCombination: ({ codec, proResProfile, }: {
|
|
143
|
+
codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
144
|
+
proResProfile: "proxy" | "4444-xq" | "4444" | "hq" | "standard" | "light" | undefined;
|
|
145
|
+
}) => void;
|
|
140
146
|
validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "jpeg" | "png" | "none") => "none" | "valid";
|
|
141
147
|
DEFAULT_CODEC: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
142
148
|
isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
|
|
@@ -151,4 +157,5 @@ export declare const RenderInternals: {
|
|
|
151
157
|
frame: number;
|
|
152
158
|
durationInFrames: number;
|
|
153
159
|
}) => number;
|
|
160
|
+
validateBitrate: (bitrate: unknown, name: string) => void;
|
|
154
161
|
};
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,7 @@ const validate_frame_1 = require("./validate-frame");
|
|
|
74
74
|
const validate_opengl_renderer_1 = require("./validate-opengl-renderer");
|
|
75
75
|
const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
|
|
76
76
|
const validate_scale_1 = require("./validate-scale");
|
|
77
|
+
const validate_videobitrate_1 = require("./validate-videobitrate");
|
|
77
78
|
const wait_for_symbolication_error_to_be_done_1 = require("./wait-for-symbolication-error-to-be-done");
|
|
78
79
|
var combine_videos_1 = require("./combine-videos");
|
|
79
80
|
Object.defineProperty(exports, "combineVideos", { enumerable: true, get: function () { return combine_videos_1.combineVideos; } });
|
|
@@ -139,8 +140,7 @@ exports.RenderInternals = {
|
|
|
139
140
|
validateFrameRange: frame_range_1.validateFrameRange,
|
|
140
141
|
DEFAULT_OPENGL_RENDERER: validate_opengl_renderer_1.DEFAULT_OPENGL_RENDERER,
|
|
141
142
|
validateOpenGlRenderer: validate_opengl_renderer_1.validateOpenGlRenderer,
|
|
142
|
-
|
|
143
|
-
validateSelectedCrfAndCodecCombination: crf_1.validateSelectedCrfAndCodecCombination,
|
|
143
|
+
validateQualitySettings: crf_1.validateQualitySettings,
|
|
144
144
|
validImageFormats: image_format_1.validImageFormats,
|
|
145
145
|
validCodecs: codec_1.validCodecs,
|
|
146
146
|
DEFAULT_PIXEL_FORMAT: pixel_format_1.DEFAULT_PIXEL_FORMAT,
|
|
@@ -161,6 +161,7 @@ exports.RenderInternals = {
|
|
|
161
161
|
makeDownloadMap: download_map_1.makeDownloadMap,
|
|
162
162
|
cleanDownloadMap: download_map_1.cleanDownloadMap,
|
|
163
163
|
convertToPositiveFrameIndex: convert_to_positive_frame_index_1.convertToPositiveFrameIndex,
|
|
164
|
+
validateBitrate: validate_videobitrate_1.validateBitrate,
|
|
164
165
|
};
|
|
165
166
|
// Warn of potential performance issues with Apple Silicon (M1 chip under Rosetta)
|
|
166
167
|
(0, check_apple_silicon_1.warnIfAppleSiliconIsNotUsingArm64Architecture)();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const makeAssetsDownloadTmpDir: () => string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeAssetsDownloadTmpDir = void 0;
|
|
4
|
+
const tmp_dir_1 = require("./tmp-dir");
|
|
5
|
+
let dir = null;
|
|
6
|
+
const makeAssetsDownloadTmpDir = () => {
|
|
7
|
+
if (dir) {
|
|
8
|
+
return dir;
|
|
9
|
+
}
|
|
10
|
+
dir = (0, tmp_dir_1.tmpDir)('remotion-assets-dir');
|
|
11
|
+
return dir;
|
|
12
|
+
};
|
|
13
|
+
exports.makeAssetsDownloadTmpDir = makeAssetsDownloadTmpDir;
|
|
@@ -19,8 +19,9 @@ declare type PreSticherOptions = {
|
|
|
19
19
|
verbose: boolean;
|
|
20
20
|
ffmpegExecutable: FfmpegExecutable | undefined;
|
|
21
21
|
imageFormat: ImageFormat;
|
|
22
|
-
ffmpegOverride
|
|
22
|
+
ffmpegOverride: FfmpegOverrideFn;
|
|
23
23
|
signal: CancelSignal;
|
|
24
|
+
videoBitrate: string | null;
|
|
24
25
|
};
|
|
25
26
|
export declare const prespawnFfmpeg: (options: PreSticherOptions) => Promise<{
|
|
26
27
|
task: execa.ExecaChildProcess<string>;
|
package/dist/prespawn-ffmpeg.js
CHANGED
|
@@ -15,7 +15,7 @@ const pixel_format_1 = require("./pixel-format");
|
|
|
15
15
|
const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
|
|
16
16
|
const validate_ffmpeg_1 = require("./validate-ffmpeg");
|
|
17
17
|
const prespawnFfmpeg = async (options) => {
|
|
18
|
-
var _a, _b, _c, _d, _e, _f
|
|
18
|
+
var _a, _b, _c, _d, _e, _f;
|
|
19
19
|
remotion_1.Internals.validateDimension(options.height, 'height', 'passed to `stitchFramesToVideo()`');
|
|
20
20
|
remotion_1.Internals.validateDimension(options.width, 'width', 'passed to `stitchFramesToVideo()`');
|
|
21
21
|
const codec = (_a = options.codec) !== null && _a !== void 0 ? _a : codec_1.DEFAULT_CODEC;
|
|
@@ -26,9 +26,8 @@ const prespawnFfmpeg = async (options) => {
|
|
|
26
26
|
codec,
|
|
27
27
|
scale: 1,
|
|
28
28
|
});
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
await (0, validate_ffmpeg_1.validateFfmpeg)((_d = options.ffmpegExecutable) !== null && _d !== void 0 ? _d : null);
|
|
29
|
+
const pixelFormat = (_b = options.pixelFormat) !== null && _b !== void 0 ? _b : pixel_format_1.DEFAULT_PIXEL_FORMAT;
|
|
30
|
+
await (0, validate_ffmpeg_1.validateFfmpeg)((_c = options.ffmpegExecutable) !== null && _c !== void 0 ? _c : null);
|
|
32
31
|
const encoderName = (0, get_codec_name_1.getCodecName)(codec);
|
|
33
32
|
const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
|
|
34
33
|
if (encoderName === null) {
|
|
@@ -36,16 +35,15 @@ const prespawnFfmpeg = async (options) => {
|
|
|
36
35
|
}
|
|
37
36
|
const supportsCrf = codec !== 'prores';
|
|
38
37
|
if (options.verbose) {
|
|
39
|
-
console.log('[verbose] ffmpeg', (
|
|
38
|
+
console.log('[verbose] ffmpeg', (_d = options.ffmpegExecutable) !== null && _d !== void 0 ? _d : 'ffmpeg in PATH');
|
|
40
39
|
console.log('[verbose] encoder', encoderName);
|
|
41
40
|
console.log('[verbose] pixelFormat', pixelFormat);
|
|
42
41
|
if (supportsCrf) {
|
|
43
|
-
console.log('[verbose] crf', crf);
|
|
42
|
+
console.log('[verbose] crf', options.crf);
|
|
44
43
|
}
|
|
45
44
|
console.log('[verbose] codec', codec);
|
|
46
45
|
console.log('[verbose] proResProfileName', proResProfileName);
|
|
47
46
|
}
|
|
48
|
-
(0, crf_1.validateSelectedCrfAndCodecCombination)(crf, codec);
|
|
49
47
|
(0, pixel_format_1.validateSelectedPixelFormatAndCodecCombination)(pixelFormat, codec);
|
|
50
48
|
const ffmpegArgs = [
|
|
51
49
|
['-r', options.fps.toFixed(2)],
|
|
@@ -61,12 +59,15 @@ const prespawnFfmpeg = async (options) => {
|
|
|
61
59
|
// and specified the video codec.
|
|
62
60
|
['-c:v', encoderName],
|
|
63
61
|
proResProfileName ? ['-profile:v', proResProfileName] : null,
|
|
64
|
-
supportsCrf ? ['-crf', String(crf)] : null,
|
|
65
62
|
['-pix_fmt', pixelFormat],
|
|
66
63
|
// Without explicitly disabling auto-alt-ref,
|
|
67
64
|
// transparent WebM generation doesn't work
|
|
68
65
|
pixelFormat === 'yuva420p' ? ['-auto-alt-ref', '0'] : null,
|
|
69
|
-
|
|
66
|
+
...(0, crf_1.validateQualitySettings)({
|
|
67
|
+
crf: options.crf,
|
|
68
|
+
videoBitrate: options.videoBitrate,
|
|
69
|
+
codec,
|
|
70
|
+
}),
|
|
70
71
|
'-y',
|
|
71
72
|
options.outputLocation,
|
|
72
73
|
];
|
|
@@ -78,12 +79,12 @@ const prespawnFfmpeg = async (options) => {
|
|
|
78
79
|
const finalFfmpegString = options.ffmpegOverride
|
|
79
80
|
? options.ffmpegOverride({ type: 'pre-stitcher', args: ffmpegString })
|
|
80
81
|
: ffmpegString;
|
|
81
|
-
const task = (0, execa_1.default)((
|
|
82
|
+
const task = (0, execa_1.default)((_e = options.ffmpegExecutable) !== null && _e !== void 0 ? _e : 'ffmpeg', finalFfmpegString);
|
|
82
83
|
options.signal(() => {
|
|
83
84
|
task.kill();
|
|
84
85
|
});
|
|
85
86
|
let ffmpegOutput = '';
|
|
86
|
-
(
|
|
87
|
+
(_f = task.stderr) === null || _f === void 0 ? void 0 : _f.on('data', (data) => {
|
|
87
88
|
const str = data.toString();
|
|
88
89
|
ffmpegOutput += str;
|
|
89
90
|
if (options.onProgress) {
|
package/dist/prores-profile.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Codec } from './codec';
|
|
2
2
|
declare const proResProfileOptions: readonly ["4444-xq", "4444", "hq", "standard", "light", "proxy"];
|
|
3
3
|
export declare type ProResProfile = typeof proResProfileOptions[number];
|
|
4
|
-
export declare const validateSelectedCodecAndProResCombination: (
|
|
4
|
+
export declare const validateSelectedCodecAndProResCombination: ({ codec, proResProfile, }: {
|
|
5
|
+
codec: Codec;
|
|
6
|
+
proResProfile: ProResProfile | undefined;
|
|
7
|
+
}) => void;
|
|
5
8
|
export {};
|
package/dist/prores-profile.js
CHANGED
|
@@ -9,13 +9,13 @@ const proResProfileOptions = [
|
|
|
9
9
|
'light',
|
|
10
10
|
'proxy',
|
|
11
11
|
];
|
|
12
|
-
const validateSelectedCodecAndProResCombination = (
|
|
13
|
-
if (typeof
|
|
14
|
-
throw new TypeError(
|
|
12
|
+
const validateSelectedCodecAndProResCombination = ({ codec, proResProfile, }) => {
|
|
13
|
+
if (typeof proResProfile !== 'undefined' && codec !== 'prores') {
|
|
14
|
+
throw new TypeError(`You have set a ProRes profile but the codec is "${codec}". Set the codec to "prores" or remove the ProRes profile.`);
|
|
15
15
|
}
|
|
16
|
-
if (
|
|
17
|
-
!proResProfileOptions.includes(
|
|
18
|
-
throw new TypeError(`The ProRes profile "${
|
|
16
|
+
if (proResProfile !== undefined &&
|
|
17
|
+
!proResProfileOptions.includes(proResProfile)) {
|
|
18
|
+
throw new TypeError(`The ProRes profile "${proResProfile}" is not valid. Valid options are ${proResProfileOptions
|
|
19
19
|
.map((p) => `"${p}"`)
|
|
20
20
|
.join(', ')}`);
|
|
21
21
|
}
|
package/dist/render-media.d.ts
CHANGED
|
@@ -66,6 +66,8 @@ export declare type RenderMediaOptions = {
|
|
|
66
66
|
muted?: boolean;
|
|
67
67
|
enforceAudioTrack?: boolean;
|
|
68
68
|
ffmpegOverride?: FfmpegOverrideFn;
|
|
69
|
+
audioBitrate?: string | null;
|
|
70
|
+
videoBitrate?: string | null;
|
|
69
71
|
onSlowestFrames?: OnSlowestFrames;
|
|
70
72
|
disallowParallelEncoding?: boolean;
|
|
71
73
|
} & ServeUrlOrWebpackBundle & ConcurrencyOrParallelism;
|
|
@@ -82,5 +84,5 @@ declare type ConcurrencyOrParallelism = {
|
|
|
82
84
|
* @description Render a video from a composition
|
|
83
85
|
* @link https://www.remotion.dev/docs/renderer/render-media
|
|
84
86
|
*/
|
|
85
|
-
export declare const renderMedia: ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, onSlowestFrames, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
|
|
87
|
+
export declare const renderMedia: ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, ...options }: RenderMediaOptions) => Promise<Buffer | null>;
|
|
86
88
|
export {};
|
package/dist/render-media.js
CHANGED
|
@@ -26,6 +26,7 @@ const overwrite_1 = require("./overwrite");
|
|
|
26
26
|
const perf_1 = require("./perf");
|
|
27
27
|
const prespawn_ffmpeg_1 = require("./prespawn-ffmpeg");
|
|
28
28
|
const prestitcher_memory_usage_1 = require("./prestitcher-memory-usage");
|
|
29
|
+
const prores_profile_1 = require("./prores-profile");
|
|
29
30
|
const quality_1 = require("./quality");
|
|
30
31
|
const render_frames_1 = require("./render-frames");
|
|
31
32
|
const stitch_frames_to_video_1 = require("./stitch-frames-to-video");
|
|
@@ -33,6 +34,7 @@ const validate_even_dimensions_with_codec_1 = require("./validate-even-dimension
|
|
|
33
34
|
const validate_ffmpeg_override_1 = require("./validate-ffmpeg-override");
|
|
34
35
|
const validate_output_filename_1 = require("./validate-output-filename");
|
|
35
36
|
const validate_scale_1 = require("./validate-scale");
|
|
37
|
+
const validate_videobitrate_1 = require("./validate-videobitrate");
|
|
36
38
|
const SLOWEST_FRAME_COUNT = 10;
|
|
37
39
|
const getConcurrency = (others) => {
|
|
38
40
|
if ('concurrency' in others) {
|
|
@@ -48,12 +50,16 @@ const getConcurrency = (others) => {
|
|
|
48
50
|
* @description Render a video from a composition
|
|
49
51
|
* @link https://www.remotion.dev/docs/renderer/render-media
|
|
50
52
|
*/
|
|
51
|
-
const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, onSlowestFrames, ...options }) => {
|
|
53
|
+
const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprobeExecutable, inputProps, pixelFormat, codec, envVariables, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, muted, enforceAudioTrack, ffmpegOverride, audioBitrate, videoBitrate, onSlowestFrames, ...options }) => {
|
|
52
54
|
var _a, _b, _c, _d;
|
|
53
55
|
(0, quality_1.validateQuality)(options.quality);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
(0, crf_1.validateQualitySettings)({ crf, codec, videoBitrate });
|
|
57
|
+
(0, validate_videobitrate_1.validateBitrate)(audioBitrate, 'audioBitrate');
|
|
58
|
+
(0, validate_videobitrate_1.validateBitrate)(videoBitrate, 'videoBitrate');
|
|
59
|
+
(0, prores_profile_1.validateSelectedCodecAndProResCombination)({
|
|
60
|
+
codec,
|
|
61
|
+
proResProfile,
|
|
62
|
+
});
|
|
57
63
|
if (outputLocation) {
|
|
58
64
|
(0, validate_output_filename_1.validateOutputFilename)(codec, (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation));
|
|
59
65
|
}
|
|
@@ -152,7 +158,8 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
|
|
|
152
158
|
ffmpegExecutable,
|
|
153
159
|
imageFormat,
|
|
154
160
|
signal: cancelPrestitcher.cancelSignal,
|
|
155
|
-
ffmpegOverride,
|
|
161
|
+
ffmpegOverride: ffmpegOverride !== null && ffmpegOverride !== void 0 ? ffmpegOverride : (({ args }) => args),
|
|
162
|
+
videoBitrate: videoBitrate !== null && videoBitrate !== void 0 ? videoBitrate : null,
|
|
156
163
|
});
|
|
157
164
|
stitcherFfmpeg = preStitcher.task;
|
|
158
165
|
}
|
|
@@ -290,6 +297,8 @@ const renderMedia = ({ proResProfile, crf, composition, ffmpegExecutable, ffprob
|
|
|
290
297
|
muted: disableAudio,
|
|
291
298
|
enforceAudioTrack,
|
|
292
299
|
ffmpegOverride,
|
|
300
|
+
audioBitrate,
|
|
301
|
+
videoBitrate,
|
|
293
302
|
}),
|
|
294
303
|
stitchStart,
|
|
295
304
|
]);
|
|
@@ -9,6 +9,8 @@ import type { CancelSignal } from './make-cancel-signal';
|
|
|
9
9
|
import type { PixelFormat } from './pixel-format';
|
|
10
10
|
import type { ProResProfile } from './prores-profile';
|
|
11
11
|
export declare type StitcherOptions = {
|
|
12
|
+
audioBitrate?: string | null;
|
|
13
|
+
videoBitrate?: string | null;
|
|
12
14
|
fps: number;
|
|
13
15
|
width: number;
|
|
14
16
|
height: number;
|
|
@@ -25,9 +25,11 @@ const merge_audio_track_1 = require("./merge-audio-track");
|
|
|
25
25
|
const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
|
|
26
26
|
const pixel_format_1 = require("./pixel-format");
|
|
27
27
|
const preprocess_audio_track_1 = require("./preprocess-audio-track");
|
|
28
|
+
const prores_profile_1 = require("./prores-profile");
|
|
28
29
|
const truthy_1 = require("./truthy");
|
|
29
30
|
const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
|
|
30
31
|
const validate_ffmpeg_1 = require("./validate-ffmpeg");
|
|
32
|
+
const validate_videobitrate_1 = require("./validate-videobitrate");
|
|
31
33
|
const packageJsonPath = path_1.default.join(__dirname, '..', 'package.json');
|
|
32
34
|
const packageJson = fs_1.default.existsSync(packageJsonPath)
|
|
33
35
|
? JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf-8'))
|
|
@@ -88,15 +90,19 @@ const spawnFfmpeg = async (options) => {
|
|
|
88
90
|
codec,
|
|
89
91
|
scale: 1,
|
|
90
92
|
});
|
|
93
|
+
(0, prores_profile_1.validateSelectedCodecAndProResCombination)({
|
|
94
|
+
codec,
|
|
95
|
+
proResProfile: options.proResProfile,
|
|
96
|
+
});
|
|
97
|
+
(0, validate_videobitrate_1.validateBitrate)(options.audioBitrate, 'audioBitrate');
|
|
98
|
+
(0, validate_videobitrate_1.validateBitrate)(options.videoBitrate, 'videoBitrate');
|
|
91
99
|
remotion_1.Internals.validateFps(options.fps, 'in `stitchFramesToVideo()`', false);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
await (0, validate_ffmpeg_1.validateFfmpeg)((_d = options.ffmpegExecutable) !== null && _d !== void 0 ? _d : null);
|
|
100
|
+
const pixelFormat = (_b = options.pixelFormat) !== null && _b !== void 0 ? _b : pixel_format_1.DEFAULT_PIXEL_FORMAT;
|
|
101
|
+
await (0, validate_ffmpeg_1.validateFfmpeg)((_c = options.ffmpegExecutable) !== null && _c !== void 0 ? _c : null);
|
|
95
102
|
const encoderName = (0, get_codec_name_1.getCodecName)(codec);
|
|
96
103
|
const audioCodecName = (0, get_audio_codec_name_1.getAudioCodecName)(codec);
|
|
97
104
|
const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
|
|
98
105
|
const mediaSupport = (0, codec_supports_media_1.codecSupportsMedia)(codec);
|
|
99
|
-
const supportsCrf = (0, codec_supports_media_1.codecSupportsCrf)(codec);
|
|
100
106
|
const tempFile = options.outputLocation
|
|
101
107
|
? null
|
|
102
108
|
: path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'final')}`);
|
|
@@ -109,13 +115,10 @@ const spawnFfmpeg = async (options) => {
|
|
|
109
115
|
throw new Error('The output format has neither audio nor video. This can happen if you are rendering an audio codec and the output file has no audio or the muted flag was passed.');
|
|
110
116
|
}
|
|
111
117
|
if (options.verbose) {
|
|
112
|
-
console.log('[verbose] ffmpeg', (
|
|
118
|
+
console.log('[verbose] ffmpeg', (_d = options.ffmpegExecutable) !== null && _d !== void 0 ? _d : 'ffmpeg in PATH');
|
|
113
119
|
console.log('[verbose] encoder', encoderName);
|
|
114
120
|
console.log('[verbose] audioCodec', audioCodecName);
|
|
115
121
|
console.log('[verbose] pixelFormat', pixelFormat);
|
|
116
|
-
if (supportsCrf) {
|
|
117
|
-
console.log('[verbose] crf', crf);
|
|
118
|
-
}
|
|
119
122
|
if (options.ffmpegOverride) {
|
|
120
123
|
console.log('[verbose] ffmpegOverride', options.ffmpegOverride);
|
|
121
124
|
}
|
|
@@ -124,7 +127,11 @@ const spawnFfmpeg = async (options) => {
|
|
|
124
127
|
console.log('[verbose] shouldRenderVideo', shouldRenderVideo);
|
|
125
128
|
console.log('[verbose] proResProfileName', proResProfileName);
|
|
126
129
|
}
|
|
127
|
-
(0, crf_1.
|
|
130
|
+
(0, crf_1.validateQualitySettings)({
|
|
131
|
+
crf: options.crf,
|
|
132
|
+
codec,
|
|
133
|
+
videoBitrate: options.videoBitrate,
|
|
134
|
+
});
|
|
128
135
|
(0, pixel_format_1.validateSelectedPixelFormatAndCodecCombination)(pixelFormat, codec);
|
|
129
136
|
const expectedFrames = options.assetsInfo.assets.length;
|
|
130
137
|
const updateProgress = (preStitchProgress, muxProgress) => {
|
|
@@ -138,9 +145,9 @@ const spawnFfmpeg = async (options) => {
|
|
|
138
145
|
onDownload: options.onDownload,
|
|
139
146
|
fps: options.fps,
|
|
140
147
|
expectedFrames,
|
|
141
|
-
verbose: (
|
|
142
|
-
ffmpegExecutable: (
|
|
143
|
-
ffprobeExecutable: (
|
|
148
|
+
verbose: (_e = options.verbose) !== null && _e !== void 0 ? _e : false,
|
|
149
|
+
ffmpegExecutable: (_f = options.ffmpegExecutable) !== null && _f !== void 0 ? _f : null,
|
|
150
|
+
ffprobeExecutable: (_g = options.ffprobeExecutable) !== null && _g !== void 0 ? _g : null,
|
|
144
151
|
onProgress: (prog) => updateProgress(prog, 0),
|
|
145
152
|
downloadMap: options.assetsInfo.downloadMap,
|
|
146
153
|
})
|
|
@@ -156,7 +163,7 @@ const spawnFfmpeg = async (options) => {
|
|
|
156
163
|
audioCodecName,
|
|
157
164
|
// Set bitrate up to 320k, for aac it might effectively be lower
|
|
158
165
|
'-b:a',
|
|
159
|
-
'320k',
|
|
166
|
+
(_h = options.audioBitrate) !== null && _h !== void 0 ? _h : '320k',
|
|
160
167
|
options.force ? '-y' : null,
|
|
161
168
|
(_j = options.outputLocation) !== null && _j !== void 0 ? _j : tempFile,
|
|
162
169
|
].filter(remotion_1.Internals.truthy));
|
|
@@ -210,17 +217,20 @@ const spawnFfmpeg = async (options) => {
|
|
|
210
217
|
? []
|
|
211
218
|
: [
|
|
212
219
|
proResProfileName ? ['-profile:v', proResProfileName] : null,
|
|
213
|
-
supportsCrf ? ['-crf', String(crf)] : null,
|
|
214
220
|
['-pix_fmt', pixelFormat],
|
|
215
221
|
// Without explicitly disabling auto-alt-ref,
|
|
216
222
|
// transparent WebM generation doesn't work
|
|
217
223
|
pixelFormat === 'yuva420p' ? ['-auto-alt-ref', '0'] : null,
|
|
218
|
-
|
|
224
|
+
...(0, crf_1.validateQualitySettings)({
|
|
225
|
+
crf: options.crf,
|
|
226
|
+
videoBitrate: options.videoBitrate,
|
|
227
|
+
codec,
|
|
228
|
+
}),
|
|
219
229
|
]),
|
|
220
230
|
codec === 'h264' ? ['-movflags', 'faststart'] : null,
|
|
221
231
|
audioCodecName ? ['-c:a', audioCodecName] : null,
|
|
222
232
|
// Set max bitrate up to 1024kbps, will choose lower if that's too much
|
|
223
|
-
audioCodecName ? ['-b:a', '512K'] : null,
|
|
233
|
+
audioCodecName ? ['-b:a', options.audioBitrate || '512K'] : null,
|
|
224
234
|
// Ignore metadata that may come from remote media
|
|
225
235
|
['-map_metadata', '-1'],
|
|
226
236
|
[
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateFfmpegArgsHook = void 0;
|
|
4
|
+
const validateFfmpegArgsHook = (ffmpegArgsHook) => {
|
|
5
|
+
if (typeof ffmpegArgsHook === 'undefined') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (ffmpegArgsHook && typeof ffmpegArgsHook !== 'function') {
|
|
9
|
+
throw new TypeError(`Argument passed for "ffmpegArgsHook" is not a function: ${ffmpegArgsHook}`);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
exports.validateFfmpegArgsHook = validateFfmpegArgsHook;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateFfmpegOverride = void 0;
|
|
4
|
+
const validateFfmpegOverride = (ffmpegArgsHook) => {
|
|
5
|
+
if (typeof ffmpegArgsHook === 'undefined') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (ffmpegArgsHook && typeof ffmpegArgsHook !== 'function') {
|
|
9
|
+
throw new TypeError(`Argument passed for "ffmpegArgsHook" is not a function: ${ffmpegArgsHook}`);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
exports.validateFfmpegOverride = validateFfmpegOverride;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const validateBitrate: (bitrate: unknown, name: string) => void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateBitrate = void 0;
|
|
4
|
+
const validateBitrate = (bitrate, name) => {
|
|
5
|
+
if (bitrate === null || typeof bitrate === 'undefined') {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (typeof bitrate === 'number') {
|
|
9
|
+
throw new TypeError(`"${name}" must be a string ending in "K" or "M". Got a number: ${bitrate}`);
|
|
10
|
+
}
|
|
11
|
+
if (typeof bitrate !== 'string') {
|
|
12
|
+
throw new TypeError(`"${name}" must be a string or null, but got ${JSON.stringify(bitrate)}`);
|
|
13
|
+
}
|
|
14
|
+
if (!bitrate.endsWith('K') &&
|
|
15
|
+
!bitrate.endsWith('k') &&
|
|
16
|
+
!bitrate.endsWith('M')) {
|
|
17
|
+
throw new TypeError(`"${name}" must end in "K", "k" or "M", but got ${JSON.stringify(bitrate)}`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
exports.validateBitrate = validateBitrate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/renderer",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.32",
|
|
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.2.
|
|
25
|
+
"remotion": "3.2.32",
|
|
26
26
|
"source-map": "^0.8.0-beta.0",
|
|
27
27
|
"ws": "8.7.0"
|
|
28
28
|
},
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"react": "18.0.0",
|
|
45
45
|
"react-dom": "18.0.0",
|
|
46
46
|
"typescript": "^4.7.0",
|
|
47
|
-
"vitest": "
|
|
47
|
+
"vitest": "0.18.0"
|
|
48
48
|
},
|
|
49
49
|
"keywords": [
|
|
50
50
|
"remotion",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "4f7ab3637405d140041f898f95f78c99943d1b40"
|
|
61
61
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const downloadBrowser: (url: string) => Promise<void>;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.downloadBrowser = void 0;
|
|
4
|
-
const listeners = {};
|
|
5
|
-
const isDownloading = {};
|
|
6
|
-
const waitForFfmpegToBeDownloaded = (url) => {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
if (!listeners[url]) {
|
|
9
|
-
listeners[url] = [];
|
|
10
|
-
}
|
|
11
|
-
listeners[url].push(resolve);
|
|
12
|
-
});
|
|
13
|
-
};
|
|
14
|
-
const downloadBrowser = async (url) => {
|
|
15
|
-
if (isDownloading[url]) {
|
|
16
|
-
console.log('WAITING');
|
|
17
|
-
return waitForFfmpegToBeDownloaded(url);
|
|
18
|
-
}
|
|
19
|
-
isDownloading[url] = true;
|
|
20
|
-
await new Promise((resolve) => {
|
|
21
|
-
console.log('DOWNLOADING BROWSER');
|
|
22
|
-
setTimeout(resolve, 20000);
|
|
23
|
-
});
|
|
24
|
-
isDownloading[url] = false;
|
|
25
|
-
if (!listeners[url]) {
|
|
26
|
-
listeners[url] = [];
|
|
27
|
-
}
|
|
28
|
-
listeners[url].forEach((listener) => listener());
|
|
29
|
-
listeners[url] = [];
|
|
30
|
-
};
|
|
31
|
-
exports.downloadBrowser = downloadBrowser;
|
|
32
|
-
(0, exports.downloadBrowser)('https://remotion.dev').then(() => {
|
|
33
|
-
(0, exports.downloadBrowser)('https://remotion.dev').then(() => {
|
|
34
|
-
(0, exports.downloadBrowser)('https://remotion.dev').then(() => console.log('FINISHED DOWNLOADING'));
|
|
35
|
-
(0, exports.downloadBrowser)('https://remotion.dev').then(() => console.log('FINISHED DOWNLOADING'));
|
|
36
|
-
(0, exports.downloadBrowser)('https://remotion.dev').then(() => console.log('FINISHED DOWNLOADING'));
|
|
37
|
-
});
|
|
38
|
-
});
|