@remotion/renderer 3.2.12 → 3.2.13
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/assets/download-file.js +1 -0
- package/dist/codec-supports-media.d.ts +1 -0
- package/dist/codec-supports-media.js +8 -1
- package/dist/create-ffmpeg-complex-filter.d.ts +1 -4
- package/dist/extract-frame-from-video.d.ts +1 -0
- package/dist/extract-frame-from-video.js +30 -7
- package/dist/get-extension-from-codec.d.ts +1 -1
- package/dist/guess-extension-for-media.d.ts +1 -1
- package/dist/index.d.ts +7 -6
- package/dist/last-frame-from-video-cache.d.ts +1 -0
- package/dist/provide-screenshot.d.ts +1 -0
- package/dist/puppeteer-screenshot.d.ts +1 -0
- package/dist/render-media.d.ts +1 -0
- package/dist/screenshot-dom-element.d.ts +1 -0
- package/dist/screenshot-task.d.ts +1 -0
- package/dist/stitch-frames-to-video.js +1 -1
- package/package.json +3 -3
|
@@ -29,6 +29,7 @@ const downloadFile = ({ onProgress, url, to: toFn, }) => {
|
|
|
29
29
|
return resolve({ sizeInBytes: downloaded, to });
|
|
30
30
|
});
|
|
31
31
|
writeStream.on('error', (err) => reject(err));
|
|
32
|
+
res.on('error', (err) => reject(err));
|
|
32
33
|
res.pipe(writeStream).on('error', (err) => reject(err));
|
|
33
34
|
res.on('data', (d) => {
|
|
34
35
|
downloaded += d.length;
|
|
@@ -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;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { DownloadMap } from './assets/download-map';
|
|
2
2
|
export declare const createFfmpegComplexFilter: (filters: number, downloadMap: DownloadMap) => Promise<{
|
|
3
|
-
complexFilterFlag: [
|
|
4
|
-
string,
|
|
5
|
-
string
|
|
6
|
-
] | null;
|
|
3
|
+
complexFilterFlag: [string, string] | null;
|
|
7
4
|
cleanup: () => void;
|
|
8
5
|
}>;
|
|
@@ -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,2 +1,2 @@
|
|
|
1
1
|
import type { Codec } from './codec';
|
|
2
|
-
export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "
|
|
2
|
+
export declare const getFileExtensionFromCodec: (codec: Codec, type: 'chunk' | 'final') => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "
|
|
1
|
+
export declare const guessExtensionForVideo: (src: string) => Promise<"mp3" | "wav" | "webm" | "mp4">;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import execa from 'execa';
|
|
2
3
|
import { SymbolicateableError } from './error-handling/symbolicateable-error';
|
|
3
4
|
import { mimeContentType, mimeLookup } from './mime-types';
|
|
@@ -67,7 +68,7 @@ export declare const RenderInternals: {
|
|
|
67
68
|
task: Promise<Buffer | null>;
|
|
68
69
|
getLogs: () => string;
|
|
69
70
|
}>;
|
|
70
|
-
getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "
|
|
71
|
+
getFileExtensionFromCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", type: "chunk" | "final") => "mp3" | "aac" | "wav" | "gif" | "webm" | "mp4" | "mov" | "mkv";
|
|
71
72
|
tmpDir: (str: string) => string;
|
|
72
73
|
deleteDirectory: (directory: string) => Promise<void>;
|
|
73
74
|
isServeUrl: (potentialUrl: string) => boolean;
|
|
@@ -122,8 +123,8 @@ export declare const RenderInternals: {
|
|
|
122
123
|
validPixelFormats: readonly ["yuv420p", "yuva420p", "yuv422p", "yuv444p", "yuv420p10le", "yuv422p10le", "yuv444p10le", "yuva444p10le"];
|
|
123
124
|
DEFAULT_BROWSER: import("./browser").Browser;
|
|
124
125
|
validateFrameRange: (frameRange: import("./frame-range").FrameRange | null) => void;
|
|
125
|
-
DEFAULT_OPENGL_RENDERER: "
|
|
126
|
-
validateOpenGlRenderer: (option: "
|
|
126
|
+
DEFAULT_OPENGL_RENDERER: "angle" | "swangle" | "egl" | "swiftshader" | null;
|
|
127
|
+
validateOpenGlRenderer: (option: "angle" | "swangle" | "egl" | "swiftshader" | null) => "angle" | "swangle" | "egl" | "swiftshader" | null;
|
|
127
128
|
getDefaultCrfForCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => number;
|
|
128
129
|
validateSelectedCrfAndCodecCombination: (crf: unknown, codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => void;
|
|
129
130
|
validImageFormats: readonly ["png", "jpeg", "none"];
|
|
@@ -135,12 +136,12 @@ export declare const RenderInternals: {
|
|
|
135
136
|
DEFAULT_TIMEOUT: number;
|
|
136
137
|
getValidCrfRanges: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif") => [number, number];
|
|
137
138
|
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" |
|
|
139
|
-
validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "
|
|
139
|
+
validateSelectedCodecAndProResCombination: (actualCodec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif", actualProResProfile: "proxy" | "4444-xq" | "4444" | "hq" | "standard" | "light" | undefined) => void;
|
|
140
|
+
validateSelectedPixelFormatAndImageFormatCombination: (pixelFormat: "yuv420p" | "yuva420p" | "yuv422p" | "yuv444p" | "yuv420p10le" | "yuv422p10le" | "yuv444p10le" | "yuva444p10le", imageFormat: "jpeg" | "png" | "none") => "none" | "valid";
|
|
140
141
|
DEFAULT_CODEC: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif";
|
|
141
142
|
isAudioCodec: (codec: "h264" | "h265" | "vp8" | "vp9" | "mp3" | "aac" | "wav" | "prores" | "h264-mkv" | "gif" | undefined) => boolean;
|
|
142
143
|
logLevels: readonly ["verbose", "info", "warn", "error"];
|
|
143
|
-
isEqualOrBelowLogLevel: (currentLevel: "
|
|
144
|
+
isEqualOrBelowLogLevel: (currentLevel: "error" | "verbose" | "info" | "warn", level: "error" | "verbose" | "info" | "warn") => boolean;
|
|
144
145
|
isValidLogLevel: (level: string) => boolean;
|
|
145
146
|
validateEveryNthFrame: (everyNthFrame: unknown) => void;
|
|
146
147
|
perf: typeof perf;
|
package/dist/render-media.d.ts
CHANGED
|
@@ -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.13",
|
|
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.13",
|
|
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": "989d20fc0ace36685c71396847e1b7b7a06d00d7"
|
|
61
61
|
}
|