@remotion/renderer 3.2.11 → 3.2.12-crf.5
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/codec-supports-media.d.ts +1 -0
- package/dist/codec-supports-media.js +8 -1
- package/dist/combine-videos.d.ts +2 -1
- package/dist/combine-videos.js +5 -1
- package/dist/extract-frame-from-video.d.ts +0 -1
- package/dist/extract-frame-from-video.js +30 -7
- package/dist/guess-extension-for-media.d.ts +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/render-media.js +6 -3
- package/dist/stitch-frames-to-video.js +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.codecSupportsMedia = void 0;
|
|
3
|
+
exports.codecSupportsCrf = exports.codecSupportsMedia = void 0;
|
|
4
|
+
const get_codec_name_1 = require("./get-codec-name");
|
|
4
5
|
const support = {
|
|
5
6
|
'h264-mkv': {
|
|
6
7
|
audio: true,
|
|
@@ -47,3 +48,9 @@ const codecSupportsMedia = (codec) => {
|
|
|
47
48
|
return support[codec];
|
|
48
49
|
};
|
|
49
50
|
exports.codecSupportsMedia = codecSupportsMedia;
|
|
51
|
+
const codecSupportsCrf = (codec) => {
|
|
52
|
+
const encoderName = (0, get_codec_name_1.getCodecName)(codec);
|
|
53
|
+
const supportsCrf = encoderName && codec !== 'prores';
|
|
54
|
+
return supportsCrf;
|
|
55
|
+
};
|
|
56
|
+
exports.codecSupportsCrf = codecSupportsCrf;
|
package/dist/combine-videos.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Codec } from './codec';
|
|
2
|
-
export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, }: {
|
|
2
|
+
export declare const combineVideos: ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, crf, }: {
|
|
3
3
|
files: string[];
|
|
4
4
|
filelistDir: string;
|
|
5
5
|
output: string;
|
|
@@ -8,4 +8,5 @@ export declare const combineVideos: ({ files, filelistDir, output, onProgress, n
|
|
|
8
8
|
codec: Codec;
|
|
9
9
|
fps: number;
|
|
10
10
|
numberOfGifLoops: number | null;
|
|
11
|
+
crf: number | null;
|
|
11
12
|
}) => Promise<void>;
|
package/dist/combine-videos.js
CHANGED
|
@@ -8,15 +8,17 @@ exports.combineVideos = void 0;
|
|
|
8
8
|
const execa_1 = __importDefault(require("execa"));
|
|
9
9
|
const fs_1 = require("fs");
|
|
10
10
|
const path_1 = require("path");
|
|
11
|
+
const codec_supports_media_1 = require("./codec-supports-media");
|
|
11
12
|
const get_audio_codec_name_1 = require("./get-audio-codec-name");
|
|
12
13
|
const is_audio_codec_1 = require("./is-audio-codec");
|
|
13
14
|
const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
|
|
14
15
|
const truthy_1 = require("./truthy");
|
|
15
|
-
const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, }) => {
|
|
16
|
+
const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfFrames, codec, fps, numberOfGifLoops, crf, }) => {
|
|
16
17
|
var _a;
|
|
17
18
|
const fileList = files.map((p) => `file '${p}'`).join('\n');
|
|
18
19
|
const fileListTxt = (0, path_1.join)(filelistDir, 'files.txt');
|
|
19
20
|
(0, fs_1.writeFileSync)(fileListTxt, fileList);
|
|
21
|
+
const supportsCrf = (0, codec_supports_media_1.codecSupportsCrf)(codec);
|
|
20
22
|
try {
|
|
21
23
|
const task = (0, execa_1.default)('ffmpeg', [
|
|
22
24
|
(0, is_audio_codec_1.isAudioCodec)(codec) ? null : '-r',
|
|
@@ -40,6 +42,8 @@ const combineVideos = async ({ files, filelistDir, output, onProgress, numberOfF
|
|
|
40
42
|
// Set max bitrate up to 1024kbps, will choose lower if that's too much
|
|
41
43
|
'-b:a',
|
|
42
44
|
'512K',
|
|
45
|
+
supportsCrf ? '-crf' : null,
|
|
46
|
+
supportsCrf ? String(crf) : null,
|
|
43
47
|
codec === 'h264' ? '-movflags' : null,
|
|
44
48
|
codec === 'h264' ? 'faststart' : null,
|
|
45
49
|
'-shortest',
|
|
@@ -32,9 +32,9 @@ const determineResizeParams = (needsResize) => {
|
|
|
32
32
|
};
|
|
33
33
|
// Uses no seeking, therefore the whole video has to be decoded. This is a last resort and should only happen
|
|
34
34
|
// if the video is corrupted
|
|
35
|
-
const getFrameOfVideoSlow = async ({ src,
|
|
36
|
-
console.warn(`\nUsing a slow method to extract the frame at ${
|
|
37
|
-
const actualOffset = `-${
|
|
35
|
+
const getFrameOfVideoSlow = async ({ src, duration, ffmpegExecutable, imageFormat, specialVCodecForTransparency, needsResize, offset, fps, }) => {
|
|
36
|
+
console.warn(`\nUsing a slow method to extract the frame at ${duration}ms of ${src}. See https://remotion.dev/docs/slow-method-to-extract-frame for advice`);
|
|
37
|
+
const actualOffset = `-${duration * 1000 - offset}ms`;
|
|
38
38
|
const command = [
|
|
39
39
|
'-itsoffset',
|
|
40
40
|
actualOffset,
|
|
@@ -72,7 +72,20 @@ const getFrameOfVideoSlow = async ({ src, timestamp, ffmpegExecutable, imageForm
|
|
|
72
72
|
const [stdErr, stdoutBuffer] = await Promise.all([stdErrString, stdoutChunk]);
|
|
73
73
|
const isEmpty = stdErr.includes('Output file is empty');
|
|
74
74
|
if (isEmpty) {
|
|
75
|
-
|
|
75
|
+
if (offset > 70) {
|
|
76
|
+
throw new Error(`Could not get last frame of ${src}. Tried to seek to the end using the command "ffmpeg ${command.join(' ')}" but got no frame. Most likely this video is corrupted.`);
|
|
77
|
+
}
|
|
78
|
+
return getFrameOfVideoSlow({
|
|
79
|
+
ffmpegExecutable,
|
|
80
|
+
duration,
|
|
81
|
+
// Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
|
|
82
|
+
offset: offset + (fps === null ? 10 : 1000 / fps),
|
|
83
|
+
src,
|
|
84
|
+
imageFormat,
|
|
85
|
+
specialVCodecForTransparency,
|
|
86
|
+
needsResize,
|
|
87
|
+
fps,
|
|
88
|
+
});
|
|
76
89
|
}
|
|
77
90
|
return stdoutBuffer;
|
|
78
91
|
};
|
|
@@ -88,12 +101,14 @@ const getLastFrameOfVideoFastUnlimited = async (options) => {
|
|
|
88
101
|
}
|
|
89
102
|
if (options.specialVCodecForTransparency === 'vp8' || offset > 40) {
|
|
90
103
|
const last = await getFrameOfVideoSlow({
|
|
91
|
-
|
|
104
|
+
duration,
|
|
92
105
|
ffmpegExecutable,
|
|
93
106
|
src,
|
|
94
107
|
imageFormat: options.imageFormat,
|
|
95
108
|
specialVCodecForTransparency: options.specialVCodecForTransparency,
|
|
96
109
|
needsResize: options.needsResize,
|
|
110
|
+
offset: offset - 1000 / (fps === null ? 10 : fps),
|
|
111
|
+
fps,
|
|
97
112
|
});
|
|
98
113
|
return last;
|
|
99
114
|
}
|
|
@@ -143,7 +158,7 @@ const getLastFrameOfVideoFastUnlimited = async (options) => {
|
|
|
143
158
|
const unlimited = await getLastFrameOfVideoFastUnlimited({
|
|
144
159
|
ffmpegExecutable,
|
|
145
160
|
// Decrement in 10ms increments, or 1 frame (e.g. fps = 25 --> 40ms)
|
|
146
|
-
offset: offset +
|
|
161
|
+
offset: offset + (fps === null ? 10 : 1000 / fps),
|
|
147
162
|
src,
|
|
148
163
|
ffprobeExecutable,
|
|
149
164
|
imageFormat: options.imageFormat,
|
|
@@ -167,13 +182,16 @@ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutab
|
|
|
167
182
|
const src = await (0, ensure_presentation_timestamp_1.ensurePresentationTimestamps)(downloadMap, options.src);
|
|
168
183
|
const { specialVcodec, needsResize } = await (0, get_video_info_1.getVideoInfo)(downloadMap, src, ffprobeExecutable);
|
|
169
184
|
if (specialVcodec === 'vp8') {
|
|
185
|
+
const { fps } = await (0, get_video_stream_duration_1.getVideoStreamDuration)(downloadMap, src, ffprobeExecutable);
|
|
170
186
|
return getFrameOfVideoSlow({
|
|
171
187
|
ffmpegExecutable,
|
|
172
188
|
imageFormat,
|
|
173
189
|
specialVCodecForTransparency: specialVcodec,
|
|
174
190
|
src,
|
|
175
|
-
|
|
191
|
+
duration: time,
|
|
176
192
|
needsResize,
|
|
193
|
+
offset: 0,
|
|
194
|
+
fps,
|
|
177
195
|
});
|
|
178
196
|
}
|
|
179
197
|
if ((0, is_beyond_last_frame_1.isBeyondLastFrame)(downloadMap, src, time)) {
|
|
@@ -243,6 +261,11 @@ const extractFrameFromVideoFn = async ({ time, ffmpegExecutable, ffprobeExecutab
|
|
|
243
261
|
});
|
|
244
262
|
return last;
|
|
245
263
|
}
|
|
264
|
+
if (stdOut.length === 0) {
|
|
265
|
+
console.log('FFMPEG Logs:');
|
|
266
|
+
console.log(stderrStr);
|
|
267
|
+
throw new Error("Couldn't extract frame from video - FFMPEG did not return any data. Check logs to see more information");
|
|
268
|
+
}
|
|
246
269
|
return stdOut;
|
|
247
270
|
};
|
|
248
271
|
const extractFrameFromVideo = async (options) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "
|
|
1
|
+
export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
|
package/dist/index.d.ts
CHANGED
|
@@ -60,14 +60,14 @@ export declare const RenderInternals: {
|
|
|
60
60
|
width: number;
|
|
61
61
|
height: number;
|
|
62
62
|
scale: number;
|
|
63
|
-
codec: "
|
|
63
|
+
codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
64
64
|
}) => void;
|
|
65
65
|
normalizeServeUrl: (unnormalized: string) => string;
|
|
66
66
|
spawnFfmpeg: (options: import("./stitch-frames-to-video").StitcherOptions) => Promise<{
|
|
67
67
|
task: Promise<Buffer | null>;
|
|
68
68
|
getLogs: () => string;
|
|
69
69
|
}>;
|
|
70
|
-
getFileExtensionFromCodec: (codec: "
|
|
70
|
+
getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "mp4" | "mkv" | "mov" | "webm";
|
|
71
71
|
tmpDir: (str: string) => string;
|
|
72
72
|
deleteDirectory: (directory: string) => Promise<void>;
|
|
73
73
|
isServeUrl: (potentialUrl: string) => boolean;
|
|
@@ -115,7 +115,7 @@ export declare const RenderInternals: {
|
|
|
115
115
|
};
|
|
116
116
|
registerErrorSymbolicationLock: () => number;
|
|
117
117
|
unlockErrorSymbolicationLock: (id: number) => void;
|
|
118
|
-
canUseParallelEncoding: (codec: "
|
|
118
|
+
canUseParallelEncoding: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => boolean;
|
|
119
119
|
mimeContentType: typeof mimeContentType;
|
|
120
120
|
mimeLookup: typeof mimeLookup;
|
|
121
121
|
validateConcurrency: (value: unknown, setting: string) => void;
|
|
@@ -124,8 +124,8 @@ export declare const RenderInternals: {
|
|
|
124
124
|
validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
|
|
125
125
|
DEFAULT_OPENGL_RENDERER: "swangle" | "angle" | "egl" | "swiftshader" | null;
|
|
126
126
|
validateOpenGlRenderer: (option: "swangle" | "angle" | "egl" | "swiftshader" | null) => "swangle" | "angle" | "egl" | "swiftshader" | null;
|
|
127
|
-
getDefaultCrfForCodec: (codec: "
|
|
128
|
-
validateSelectedCrfAndCodecCombination: (crf: unknown, codec: "
|
|
127
|
+
getDefaultCrfForCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => number;
|
|
128
|
+
validateSelectedCrfAndCodecCombination: (crf: unknown, codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
|
|
129
129
|
validImageFormats: readonly ["png", "jpeg", "none"];
|
|
130
130
|
validCodecs: readonly ["h264", "h265", "vp8", "vp9", "mp3", "aac", "wav", "prores", "h264-mkv", "gif"];
|
|
131
131
|
DEFAULT_OVERWRITE: boolean;
|
|
@@ -133,14 +133,14 @@ export declare const RenderInternals: {
|
|
|
133
133
|
validateQuality: (q: number | undefined) => void;
|
|
134
134
|
validateFrame: (frame: number, durationInFrames: number) => void;
|
|
135
135
|
DEFAULT_TIMEOUT: number;
|
|
136
|
-
getValidCrfRanges: (codec: "
|
|
137
|
-
validateSelectedPixelFormatAndCodecCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", codec: "
|
|
138
|
-
validateSelectedCodecAndProResCombination: (actualCodec: "
|
|
136
|
+
getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
|
|
137
|
+
validateSelectedPixelFormatAndCodecCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
|
|
138
|
+
validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "4444-xq" | "4444" | "hq" | "standard" | "light" | "proxy" | undefined) => void;
|
|
139
139
|
validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "png" | "jpeg" | "none") => "none" | "valid";
|
|
140
|
-
DEFAULT_CODEC: "
|
|
141
|
-
isAudioCodec: (codec: "
|
|
140
|
+
DEFAULT_CODEC: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
141
|
+
isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
|
|
142
142
|
logLevels: readonly ["verbose", "info", "warn", "error"];
|
|
143
|
-
isEqualOrBelowLogLevel: (currentLevel: "verbose" | "
|
|
143
|
+
isEqualOrBelowLogLevel: (currentLevel: "verbose" | "error" | "info" | "warn", level: "verbose" | "error" | "info" | "warn") => boolean;
|
|
144
144
|
isValidLogLevel: (level: string) => boolean;
|
|
145
145
|
validateEveryNthFrame: (everyNthFrame: unknown) => void;
|
|
146
146
|
perf: typeof perf;
|
package/dist/render-media.js
CHANGED
|
@@ -46,6 +46,9 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, ffmpegExecu
|
|
|
46
46
|
if (outputLocation) {
|
|
47
47
|
(0, validate_output_filename_1.validateOutputFilename)(codec, (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation));
|
|
48
48
|
}
|
|
49
|
+
const absoluteOutputLocation = outputLocation
|
|
50
|
+
? path_1.default.resolve(process.cwd(), outputLocation)
|
|
51
|
+
: null;
|
|
49
52
|
(0, validate_scale_1.validateScale)(scale);
|
|
50
53
|
const everyNthFrame = (_a = options.everyNthFrame) !== null && _a !== void 0 ? _a : 1;
|
|
51
54
|
const numberOfGifLoops = (_b = options.numberOfGifLoops) !== null && _b !== void 0 ? _b : null;
|
|
@@ -207,8 +210,8 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, ffmpegExecu
|
|
|
207
210
|
.then(([{ assetsInfo }]) => {
|
|
208
211
|
renderedDoneIn = Date.now() - renderStart;
|
|
209
212
|
callUpdate();
|
|
210
|
-
if (
|
|
211
|
-
(0, ensure_output_directory_1.ensureOutputDirectory)(
|
|
213
|
+
if (absoluteOutputLocation) {
|
|
214
|
+
(0, ensure_output_directory_1.ensureOutputDirectory)(absoluteOutputLocation);
|
|
212
215
|
}
|
|
213
216
|
const stitchStart = Date.now();
|
|
214
217
|
return Promise.all([
|
|
@@ -216,7 +219,7 @@ const renderMedia = ({ parallelism, proResProfile, crf, composition, ffmpegExecu
|
|
|
216
219
|
width: composition.width * (scale !== null && scale !== void 0 ? scale : 1),
|
|
217
220
|
height: composition.height * (scale !== null && scale !== void 0 ? scale : 1),
|
|
218
221
|
fps,
|
|
219
|
-
outputLocation,
|
|
222
|
+
outputLocation: absoluteOutputLocation,
|
|
220
223
|
internalOptions: {
|
|
221
224
|
preEncodedFileLocation,
|
|
222
225
|
imageFormat,
|
|
@@ -96,7 +96,7 @@ const spawnFfmpeg = async (options) => {
|
|
|
96
96
|
const audioCodecName = (0, get_audio_codec_name_1.getAudioCodecName)(codec);
|
|
97
97
|
const proResProfileName = (0, get_prores_profile_name_1.getProResProfileName)(codec, options.proResProfile);
|
|
98
98
|
const mediaSupport = (0, codec_supports_media_1.codecSupportsMedia)(codec);
|
|
99
|
-
const supportsCrf =
|
|
99
|
+
const supportsCrf = (0, codec_supports_media_1.codecSupportsCrf)(codec);
|
|
100
100
|
const tempFile = options.outputLocation
|
|
101
101
|
? null
|
|
102
102
|
: path_1.default.join(options.assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, 'final')}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remotion/renderer",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.12-crf.5+14dc380ad",
|
|
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.12-crf.5+14dc380ad",
|
|
26
26
|
"source-map": "^0.8.0-beta.0",
|
|
27
27
|
"ws": "8.7.0"
|
|
28
28
|
},
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "14dc380ad148868c35c5612f48eaf8e78ab42d9c"
|
|
61
61
|
}
|