@remotion/renderer 4.0.0-offthread.34 → 4.0.0-offthread.6
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-and-map-assets-to-file.d.ts +1 -1
- package/dist/assets/download-and-map-assets-to-file.js +30 -13
- package/dist/combine-videos.d.ts +2 -1
- package/dist/combine-videos.js +3 -1
- package/dist/create-ffmpeg-complex-filter.d.ts +1 -4
- package/dist/cycle-browser-tabs.d.ts +2 -1
- package/dist/cycle-browser-tabs.js +9 -2
- package/dist/extract-frame-from-video.d.ts +0 -5
- package/dist/extract-frame-from-video.js +17 -7
- package/dist/get-compositions.d.ts +2 -1
- package/dist/get-compositions.js +3 -1
- package/dist/index.d.ts +2 -13
- package/dist/index.js +3 -5
- package/dist/last-frame-from-video-cache.d.ts +11 -0
- package/dist/last-frame-from-video-cache.js +52 -0
- package/dist/make-cancel-signal.d.ts +7 -0
- package/dist/make-cancel-signal.js +25 -0
- package/dist/merge-audio-track.js +2 -2
- package/dist/offthread-video-server.d.ts +6 -1
- package/dist/offthread-video-server.js +5 -4
- package/dist/open-browser.d.ts +5 -5
- package/dist/open-browser.js +2 -1
- package/dist/prepare-server.d.ts +2 -1
- package/dist/prepare-server.js +6 -6
- package/dist/prespawn-ffmpeg.d.ts +2 -0
- package/dist/prespawn-ffmpeg.js +3 -0
- package/dist/puppeteer-screenshot.js +5 -1
- package/dist/render-frames.d.ts +3 -0
- package/dist/render-frames.js +76 -37
- package/dist/render-media.d.ts +6 -2
- package/dist/render-media.js +117 -55
- package/dist/render-still.d.ts +7 -3
- package/dist/render-still.js +30 -12
- package/dist/serve-static.js +9 -1
- package/dist/set-props-and-env.d.ts +2 -1
- package/dist/set-props-and-env.js +22 -3
- package/dist/stitch-frames-to-video.d.ts +3 -1
- package/dist/stitch-frames-to-video.js +27 -14
- package/package.json +4 -4
package/dist/render-frames.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { BrowserExecutable, FfmpegExecutable, FrameRange, ImageFormat, SmallTCom
|
|
|
4
4
|
import { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
|
|
5
5
|
import { BrowserLog } from './browser-log';
|
|
6
6
|
import { ServeUrlOrWebpackBundle } from './legacy-webpack-config';
|
|
7
|
+
import { CancelSignal } from './make-cancel-signal';
|
|
7
8
|
import { ChromiumOptions } from './open-browser';
|
|
8
9
|
import { OnStartData, RenderFramesOutput } from './types';
|
|
9
10
|
declare type ConfigOrComposition = {
|
|
@@ -34,6 +35,8 @@ declare type RenderFramesOptions = {
|
|
|
34
35
|
chromiumOptions?: ChromiumOptions;
|
|
35
36
|
scale?: number;
|
|
36
37
|
ffmpegExecutable?: FfmpegExecutable;
|
|
38
|
+
port?: number | null;
|
|
39
|
+
cancelSignal?: CancelSignal;
|
|
37
40
|
} & ConfigOrComposition & ServeUrlOrWebpackBundle;
|
|
38
41
|
export declare const renderFrames: (options: RenderFramesOptions) => Promise<RenderFramesOutput>;
|
|
39
42
|
export {};
|
package/dist/render-frames.js
CHANGED
|
@@ -33,9 +33,14 @@ const getComposition = (others) => {
|
|
|
33
33
|
}
|
|
34
34
|
return undefined;
|
|
35
35
|
};
|
|
36
|
-
const
|
|
36
|
+
const getPool = async (pages) => {
|
|
37
|
+
const puppeteerPages = await Promise.all(pages);
|
|
38
|
+
const pool = new pool_1.Pool(puppeteerPages);
|
|
39
|
+
return pool;
|
|
40
|
+
};
|
|
41
|
+
const innerRenderFrames = ({ onFrameUpdate, outputDir, onStart, inputProps, quality, imageFormat = image_format_1.DEFAULT_IMAGE_FORMAT, frameRange, puppeteerInstance, onError, envVariables, onBrowserLog, onFrameBuffer, onDownload, pagesArray, serveUrl, composition, timeoutInMilliseconds, scale, actualParallelism, downloadDir, proxyPort, cancelSignal, }) => {
|
|
37
42
|
if (!puppeteerInstance) {
|
|
38
|
-
throw new Error('
|
|
43
|
+
throw new Error('no puppeteer instance passed to innerRenderFrames - internal error');
|
|
39
44
|
}
|
|
40
45
|
if (outputDir) {
|
|
41
46
|
if (!fs_1.default.existsSync(outputDir)) {
|
|
@@ -49,7 +54,7 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
|
|
|
49
54
|
const pages = new Array(actualParallelism).fill(true).map(async () => {
|
|
50
55
|
const page = await puppeteerInstance.newPage();
|
|
51
56
|
pagesArray.push(page);
|
|
52
|
-
page.setViewport({
|
|
57
|
+
await page.setViewport({
|
|
53
58
|
width: composition.width,
|
|
54
59
|
height: composition.height,
|
|
55
60
|
deviceScaleFactor: scale !== null && scale !== void 0 ? scale : 1,
|
|
@@ -77,6 +82,7 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
|
|
|
77
82
|
initialFrame,
|
|
78
83
|
timeoutInMilliseconds,
|
|
79
84
|
proxyPort,
|
|
85
|
+
retriesRemaining: 2,
|
|
80
86
|
});
|
|
81
87
|
await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
|
|
82
88
|
pageFunction: (id) => {
|
|
@@ -92,23 +98,30 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
|
|
|
92
98
|
page.off('console', logCallback);
|
|
93
99
|
return page;
|
|
94
100
|
});
|
|
95
|
-
const puppeteerPages = await Promise.all(pages);
|
|
96
|
-
const pool = new pool_1.Pool(puppeteerPages);
|
|
97
101
|
const [firstFrameIndex, lastFrameIndex] = realFrameRange;
|
|
98
102
|
// Substract one because 100 frames will be 00-99
|
|
99
103
|
// --> 2 digits
|
|
100
104
|
const filePadLength = String(lastFrameIndex).length;
|
|
101
105
|
let framesRendered = 0;
|
|
106
|
+
const poolPromise = getPool(pages);
|
|
102
107
|
onStart({
|
|
103
108
|
frameCount,
|
|
104
109
|
});
|
|
105
110
|
const assets = new Array(frameCount).fill(undefined);
|
|
106
|
-
|
|
111
|
+
let stopped = false;
|
|
112
|
+
cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
|
|
113
|
+
stopped = true;
|
|
114
|
+
});
|
|
115
|
+
const progress = Promise.all(new Array(frameCount)
|
|
107
116
|
.fill(Boolean)
|
|
108
|
-
.map((
|
|
117
|
+
.map((_x, i) => i)
|
|
109
118
|
.map(async (index) => {
|
|
110
119
|
const frame = realFrameRange[0] + index;
|
|
120
|
+
const pool = await poolPromise;
|
|
111
121
|
const freePage = await pool.acquire();
|
|
122
|
+
if (stopped) {
|
|
123
|
+
throw new Error('Render was stopped');
|
|
124
|
+
}
|
|
112
125
|
const paddedIndex = String(frame).padStart(filePadLength, '0');
|
|
113
126
|
const errorCallbackOnFrame = (err) => {
|
|
114
127
|
onError(err);
|
|
@@ -175,18 +188,28 @@ const innerRenderFrames = async ({ onFrameUpdate, outputDir, onStart, inputProps
|
|
|
175
188
|
freePage.off('error', errorCallbackOnFrame);
|
|
176
189
|
return compressedAssets;
|
|
177
190
|
}));
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
const happyPath = progress.then(() => {
|
|
192
|
+
const returnValue = {
|
|
193
|
+
assetsInfo: {
|
|
194
|
+
assets,
|
|
195
|
+
downloadDir,
|
|
196
|
+
firstFrameIndex,
|
|
197
|
+
imageSequenceName: `element-%0${filePadLength}d.${imageFormat}`,
|
|
198
|
+
},
|
|
199
|
+
frameCount,
|
|
200
|
+
};
|
|
201
|
+
return returnValue;
|
|
202
|
+
});
|
|
203
|
+
return Promise.race([
|
|
204
|
+
happyPath,
|
|
205
|
+
new Promise((_resolve, reject) => {
|
|
206
|
+
cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
|
|
207
|
+
reject(new Error('renderFrames() got cancelled'));
|
|
208
|
+
});
|
|
209
|
+
}),
|
|
210
|
+
]);
|
|
188
211
|
};
|
|
189
|
-
const renderFrames =
|
|
212
|
+
const renderFrames = (options) => {
|
|
190
213
|
var _a, _b, _c, _d;
|
|
191
214
|
const composition = getComposition(options);
|
|
192
215
|
if (!composition) {
|
|
@@ -202,33 +225,43 @@ const renderFrames = async (options) => {
|
|
|
202
225
|
const selectedServeUrl = (0, legacy_webpack_config_1.getServeUrlWithFallback)(options);
|
|
203
226
|
remotion_1.Internals.validateQuality(options.quality);
|
|
204
227
|
(0, validate_scale_1.validateScale)(options.scale);
|
|
205
|
-
const browserInstance = (_a = options.puppeteerInstance) !== null && _a !== void 0 ? _a : (
|
|
228
|
+
const browserInstance = (_a = options.puppeteerInstance) !== null && _a !== void 0 ? _a : (0, open_browser_1.openBrowser)(remotion_1.Internals.DEFAULT_BROWSER, {
|
|
206
229
|
shouldDumpIo: options.dumpBrowserLogs,
|
|
207
230
|
browserExecutable: options.browserExecutable,
|
|
208
231
|
chromiumOptions: options.chromiumOptions,
|
|
209
232
|
forceDeviceScaleFactor: (_b = options.scale) !== null && _b !== void 0 ? _b : 1,
|
|
210
|
-
})
|
|
233
|
+
});
|
|
211
234
|
const downloadDir = (0, make_assets_download_dir_1.makeAssetsDownloadTmpDir)();
|
|
212
235
|
const onDownload = (_c = options.onDownload) !== null && _c !== void 0 ? _c : (() => () => undefined);
|
|
213
236
|
const actualParallelism = (0, get_concurrency_1.getActualConcurrency)((_d = options.parallelism) !== null && _d !== void 0 ? _d : null);
|
|
214
|
-
const { stopCycling } = (0, cycle_browser_tabs_1.cycleBrowserTabs)(browserInstance, actualParallelism);
|
|
215
237
|
const openedPages = [];
|
|
216
238
|
return new Promise((resolve, reject) => {
|
|
217
|
-
var _a;
|
|
218
|
-
|
|
239
|
+
var _a, _b;
|
|
240
|
+
const cleanup = [];
|
|
219
241
|
const onError = (err) => reject(err);
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
242
|
+
Promise.all([
|
|
243
|
+
(0, prepare_server_1.prepareServer)({
|
|
244
|
+
webpackConfigOrServeUrl: selectedServeUrl,
|
|
245
|
+
downloadDir,
|
|
246
|
+
onDownload,
|
|
247
|
+
onError,
|
|
248
|
+
ffmpegExecutable: (_a = options.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
|
|
249
|
+
port: (_b = options.port) !== null && _b !== void 0 ? _b : null,
|
|
250
|
+
}),
|
|
251
|
+
browserInstance,
|
|
252
|
+
])
|
|
253
|
+
.then(([{ serveUrl, closeServer, offthreadPort }, puppeteerInstance]) => {
|
|
254
|
+
var _a;
|
|
255
|
+
const { stopCycling } = (0, cycle_browser_tabs_1.cycleBrowserTabs)(puppeteerInstance, actualParallelism);
|
|
256
|
+
cleanup.push(stopCycling);
|
|
257
|
+
(_a = options.cancelSignal) === null || _a === void 0 ? void 0 : _a.call(options, () => {
|
|
258
|
+
stopCycling();
|
|
259
|
+
closeServer();
|
|
260
|
+
});
|
|
261
|
+
cleanup.push(closeServer);
|
|
262
|
+
const renderFramesProm = innerRenderFrames({
|
|
230
263
|
...options,
|
|
231
|
-
puppeteerInstance
|
|
264
|
+
puppeteerInstance,
|
|
232
265
|
onError,
|
|
233
266
|
pagesArray: openedPages,
|
|
234
267
|
serveUrl,
|
|
@@ -238,6 +271,7 @@ const renderFrames = async (options) => {
|
|
|
238
271
|
downloadDir,
|
|
239
272
|
proxyPort: offthreadPort,
|
|
240
273
|
});
|
|
274
|
+
return renderFramesProm;
|
|
241
275
|
})
|
|
242
276
|
.then((res) => resolve(res))
|
|
243
277
|
.catch((err) => reject(err))
|
|
@@ -251,12 +285,17 @@ const renderFrames = async (options) => {
|
|
|
251
285
|
});
|
|
252
286
|
}
|
|
253
287
|
else {
|
|
254
|
-
|
|
288
|
+
Promise.resolve(browserInstance)
|
|
289
|
+
.then((puppeteerInstance) => {
|
|
290
|
+
return puppeteerInstance.close();
|
|
291
|
+
})
|
|
292
|
+
.catch((err) => {
|
|
255
293
|
console.log('Unable to close browser', err);
|
|
256
294
|
});
|
|
257
295
|
}
|
|
258
|
-
|
|
259
|
-
|
|
296
|
+
cleanup.forEach((c) => {
|
|
297
|
+
c();
|
|
298
|
+
});
|
|
260
299
|
});
|
|
261
300
|
});
|
|
262
301
|
};
|
package/dist/render-media.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Browser as PuppeteerBrowser } from 'puppeteer-core';
|
|
2
|
-
import { Codec, FfmpegExecutable, FrameRange, PixelFormat, ProResProfile, SmallTCompMetadata } from 'remotion';
|
|
2
|
+
import { BrowserExecutable, Codec, FfmpegExecutable, FrameRange, PixelFormat, ProResProfile, SmallTCompMetadata } from 'remotion';
|
|
3
3
|
import { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
|
|
4
4
|
import { BrowserLog } from './browser-log';
|
|
5
5
|
import { ServeUrlOrWebpackBundle } from './legacy-webpack-config';
|
|
6
|
+
import { CancelSignal } from './make-cancel-signal';
|
|
6
7
|
import { ChromiumOptions } from './open-browser';
|
|
7
8
|
import { OnStartData } from './types';
|
|
8
9
|
export declare type StitchingState = 'encoding' | 'muxing';
|
|
@@ -37,5 +38,8 @@ export declare type RenderMediaOptions = {
|
|
|
37
38
|
timeoutInMilliseconds?: number;
|
|
38
39
|
chromiumOptions?: ChromiumOptions;
|
|
39
40
|
scale?: number;
|
|
41
|
+
port?: number | null;
|
|
42
|
+
cancelSignal?: CancelSignal;
|
|
43
|
+
browserExecutable?: BrowserExecutable;
|
|
40
44
|
} & ServeUrlOrWebpackBundle;
|
|
41
|
-
export declare const renderMedia: ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, ...options }: RenderMediaOptions) => Promise<void>;
|
|
45
|
+
export declare const renderMedia: ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, ...options }: RenderMediaOptions) => Promise<void>;
|
package/dist/render-media.js
CHANGED
|
@@ -16,6 +16,7 @@ const get_extension_from_codec_1 = require("./get-extension-from-codec");
|
|
|
16
16
|
const get_extension_of_filename_1 = require("./get-extension-of-filename");
|
|
17
17
|
const get_frame_to_render_1 = require("./get-frame-to-render");
|
|
18
18
|
const legacy_webpack_config_1 = require("./legacy-webpack-config");
|
|
19
|
+
const make_cancel_signal_1 = require("./make-cancel-signal");
|
|
19
20
|
const prespawn_ffmpeg_1 = require("./prespawn-ffmpeg");
|
|
20
21
|
const render_frames_1 = require("./render-frames");
|
|
21
22
|
const stitch_frames_to_video_1 = require("./stitch-frames-to-video");
|
|
@@ -23,8 +24,7 @@ const tmp_dir_1 = require("./tmp-dir");
|
|
|
23
24
|
const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
|
|
24
25
|
const validate_output_filename_1 = require("./validate-output-filename");
|
|
25
26
|
const validate_scale_1 = require("./validate-scale");
|
|
26
|
-
const renderMedia =
|
|
27
|
-
var _a;
|
|
27
|
+
const renderMedia = ({ parallelism, proResProfile, crf, composition, imageFormat, ffmpegExecutable, inputProps, pixelFormat, codec, envVariables, quality, frameRange, puppeteerInstance, outputLocation, onProgress, overwrite, onDownload, dumpBrowserLogs, onBrowserLog, onStart, timeoutInMilliseconds, chromiumOptions, scale, browserExecutable, port, cancelSignal, ...options }) => {
|
|
28
28
|
remotion_1.Internals.validateQuality(quality);
|
|
29
29
|
if (typeof crf !== 'undefined' && crf !== null) {
|
|
30
30
|
remotion_1.Internals.validateSelectedCrfAndCodecCombination(crf, codec);
|
|
@@ -39,6 +39,7 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
|
|
|
39
39
|
let renderedFrames = 0;
|
|
40
40
|
let renderedDoneIn = null;
|
|
41
41
|
let encodedDoneIn = null;
|
|
42
|
+
let cancelled = false;
|
|
42
43
|
const renderStart = Date.now();
|
|
43
44
|
const tmpdir = (0, tmp_dir_1.tmpDir)('pre-encode');
|
|
44
45
|
const parallelEncoding = (0, can_use_parallel_encoding_1.canUseParallelEncoding)(codec);
|
|
@@ -48,23 +49,31 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
|
|
|
48
49
|
: null;
|
|
49
50
|
const outputDir = parallelEncoding
|
|
50
51
|
? null
|
|
51
|
-
:
|
|
52
|
+
: fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'react-motion-render'));
|
|
52
53
|
(0, validate_even_dimensions_with_codec_1.validateEvenDimensionsWithCodec)({
|
|
53
54
|
codec,
|
|
54
55
|
height: composition.height,
|
|
55
56
|
scale: scale !== null && scale !== void 0 ? scale : 1,
|
|
56
57
|
width: composition.width,
|
|
57
58
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
const callUpdate = () => {
|
|
60
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress({
|
|
61
|
+
encodedDoneIn,
|
|
62
|
+
encodedFrames,
|
|
63
|
+
renderedDoneIn,
|
|
64
|
+
renderedFrames,
|
|
65
|
+
stitchStage,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const realFrameRange = (0, get_frame_to_render_1.getRealFrameRange)(composition.durationInFrames, frameRange !== null && frameRange !== void 0 ? frameRange : null);
|
|
69
|
+
const cancelRenderFrames = (0, make_cancel_signal_1.makeCancelSignal)();
|
|
70
|
+
const cancelPrestitcher = (0, make_cancel_signal_1.makeCancelSignal)();
|
|
71
|
+
const cancelStitcher = (0, make_cancel_signal_1.makeCancelSignal)();
|
|
72
|
+
cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
|
|
73
|
+
cancelRenderFrames.cancel();
|
|
74
|
+
});
|
|
75
|
+
const { waitForRightTimeOfFrameToBeInserted, setFrameToStitch, waitForFinish } = (0, ensure_frames_in_order_1.ensureFramesInOrder)(realFrameRange);
|
|
76
|
+
const createPrestitcherIfNecessary = async () => {
|
|
68
77
|
if (preEncodedFileLocation) {
|
|
69
78
|
preStitcher = await (0, prespawn_ffmpeg_1.prespawnFfmpeg)({
|
|
70
79
|
width: composition.width * (scale !== null && scale !== void 0 ? scale : 1),
|
|
@@ -82,12 +91,27 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
|
|
|
82
91
|
verbose: remotion_1.Internals.Logging.isEqualOrBelowLogLevel(remotion_1.Internals.Logging.getLogLevel(), 'verbose'),
|
|
83
92
|
ffmpegExecutable,
|
|
84
93
|
imageFormat: actualImageFormat,
|
|
94
|
+
signal: cancelPrestitcher.cancelSignal,
|
|
85
95
|
});
|
|
86
96
|
stitcherFfmpeg = preStitcher.task;
|
|
87
97
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
};
|
|
99
|
+
const waitForPrestitcherIfNecessary = async () => {
|
|
100
|
+
var _a;
|
|
101
|
+
if (stitcherFfmpeg) {
|
|
102
|
+
await waitForFinish();
|
|
103
|
+
(_a = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin) === null || _a === void 0 ? void 0 : _a.end();
|
|
104
|
+
try {
|
|
105
|
+
await stitcherFfmpeg;
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
throw new Error(preStitcher === null || preStitcher === void 0 ? void 0 : preStitcher.getLogs());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const happyPath = createPrestitcherIfNecessary()
|
|
113
|
+
.then(() => {
|
|
114
|
+
const renderFramesProc = (0, render_frames_1.renderFrames)({
|
|
91
115
|
config: composition,
|
|
92
116
|
onFrameUpdate: (frame) => {
|
|
93
117
|
renderedFrames = frame;
|
|
@@ -110,6 +134,9 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
|
|
|
110
134
|
? async (buffer, frame) => {
|
|
111
135
|
var _a;
|
|
112
136
|
await waitForRightTimeOfFrameToBeInserted(frame);
|
|
137
|
+
if (cancelled) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
113
140
|
(_a = stitcherFfmpeg === null || stitcherFfmpeg === void 0 ? void 0 : stitcherFfmpeg.stdin) === null || _a === void 0 ? void 0 : _a.write(buffer);
|
|
114
141
|
setFrameToStitch(frame + 1);
|
|
115
142
|
}
|
|
@@ -121,57 +148,92 @@ const renderMedia = async ({ parallelism, proResProfile, crf, composition, image
|
|
|
121
148
|
timeoutInMilliseconds,
|
|
122
149
|
chromiumOptions,
|
|
123
150
|
scale,
|
|
124
|
-
// TODO: Document new property for FFMPEG executable
|
|
125
151
|
ffmpegExecutable,
|
|
152
|
+
browserExecutable,
|
|
153
|
+
port,
|
|
154
|
+
cancelSignal: cancelRenderFrames.cancelSignal,
|
|
126
155
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
catch (err) {
|
|
134
|
-
throw new Error(preStitcher === null || preStitcher === void 0 ? void 0 : preStitcher.getLogs());
|
|
135
|
-
}
|
|
136
|
-
}
|
|
156
|
+
return renderFramesProc;
|
|
157
|
+
})
|
|
158
|
+
.then((renderFramesReturn) => {
|
|
159
|
+
return Promise.all([renderFramesReturn, waitForPrestitcherIfNecessary()]);
|
|
160
|
+
})
|
|
161
|
+
.then(([{ assetsInfo }]) => {
|
|
137
162
|
renderedDoneIn = Date.now() - renderStart;
|
|
138
163
|
callUpdate();
|
|
139
164
|
(0, ensure_output_directory_1.ensureOutputDirectory)(outputLocation);
|
|
140
165
|
const stitchStart = Date.now();
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
+
return Promise.all([
|
|
167
|
+
(0, stitch_frames_to_video_1.stitchFramesToVideo)({
|
|
168
|
+
width: composition.width * (scale !== null && scale !== void 0 ? scale : 1),
|
|
169
|
+
height: composition.height * (scale !== null && scale !== void 0 ? scale : 1),
|
|
170
|
+
fps: composition.fps,
|
|
171
|
+
outputLocation,
|
|
172
|
+
internalOptions: {
|
|
173
|
+
preEncodedFileLocation,
|
|
174
|
+
imageFormat: actualImageFormat,
|
|
175
|
+
},
|
|
176
|
+
force: overwrite !== null && overwrite !== void 0 ? overwrite : remotion_1.Internals.DEFAULT_OVERWRITE,
|
|
177
|
+
pixelFormat,
|
|
178
|
+
codec,
|
|
179
|
+
proResProfile,
|
|
180
|
+
crf,
|
|
181
|
+
assetsInfo,
|
|
182
|
+
ffmpegExecutable,
|
|
183
|
+
onProgress: (frame) => {
|
|
184
|
+
stitchStage = 'muxing';
|
|
185
|
+
encodedFrames = frame;
|
|
186
|
+
callUpdate();
|
|
187
|
+
},
|
|
188
|
+
onDownload,
|
|
189
|
+
verbose: remotion_1.Internals.Logging.isEqualOrBelowLogLevel(remotion_1.Internals.Logging.getLogLevel(), 'verbose'),
|
|
190
|
+
dir: outputDir !== null && outputDir !== void 0 ? outputDir : undefined,
|
|
191
|
+
cancelSignal: cancelStitcher.cancelSignal,
|
|
192
|
+
}),
|
|
193
|
+
stitchStart,
|
|
194
|
+
]);
|
|
195
|
+
})
|
|
196
|
+
.then(([, stitchStart]) => {
|
|
166
197
|
encodedFrames = (0, get_duration_from_frame_range_1.getDurationFromFrameRange)(frameRange !== null && frameRange !== void 0 ? frameRange : null, composition.durationInFrames);
|
|
167
198
|
encodedDoneIn = Date.now() - stitchStart;
|
|
168
199
|
callUpdate();
|
|
169
|
-
}
|
|
170
|
-
|
|
200
|
+
})
|
|
201
|
+
.catch((err) => {
|
|
202
|
+
/**
|
|
203
|
+
* When an error is thrown in renderFrames(...) (e.g., when delayRender() is used incorrectly), fs.unlinkSync(...) throws an error that the file is locked because ffmpeg is still running, and renderMedia returns it.
|
|
204
|
+
* Therefore we first kill the FFMPEG process before deleting the file
|
|
205
|
+
*/
|
|
206
|
+
cancelled = true;
|
|
207
|
+
cancelRenderFrames.cancel();
|
|
208
|
+
cancelStitcher.cancel();
|
|
209
|
+
cancelPrestitcher.cancel();
|
|
210
|
+
if (stitcherFfmpeg !== undefined && stitcherFfmpeg.exitCode === null) {
|
|
211
|
+
const promise = new Promise((resolve) => {
|
|
212
|
+
setTimeout(() => {
|
|
213
|
+
resolve();
|
|
214
|
+
}, 2000);
|
|
215
|
+
stitcherFfmpeg.on('close', resolve);
|
|
216
|
+
});
|
|
217
|
+
stitcherFfmpeg.kill();
|
|
218
|
+
return promise.then(() => {
|
|
219
|
+
throw err;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
})
|
|
224
|
+
.finally(() => {
|
|
171
225
|
if (preEncodedFileLocation !== null &&
|
|
172
226
|
fs_1.default.existsSync(preEncodedFileLocation)) {
|
|
173
227
|
fs_1.default.unlinkSync(preEncodedFileLocation);
|
|
174
228
|
}
|
|
175
|
-
}
|
|
229
|
+
});
|
|
230
|
+
return Promise.race([
|
|
231
|
+
happyPath,
|
|
232
|
+
new Promise((_resolve, reject) => {
|
|
233
|
+
cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
|
|
234
|
+
reject(new Error('renderMedia() got cancelled'));
|
|
235
|
+
});
|
|
236
|
+
}),
|
|
237
|
+
]);
|
|
176
238
|
};
|
|
177
239
|
exports.renderMedia = renderMedia;
|
package/dist/render-still.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Browser as PuppeteerBrowser } from 'puppeteer-core';
|
|
2
|
-
import { BrowserExecutable, FfmpegExecutable,
|
|
2
|
+
import { BrowserExecutable, FfmpegExecutable, SmallTCompMetadata, StillImageFormat } from 'remotion';
|
|
3
3
|
import { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
|
|
4
4
|
import { ServeUrlOrWebpackBundle } from './legacy-webpack-config';
|
|
5
|
+
import { CancelSignal } from './make-cancel-signal';
|
|
5
6
|
import { ChromiumOptions } from './open-browser';
|
|
6
7
|
declare type InnerStillOptions = {
|
|
7
|
-
composition:
|
|
8
|
+
composition: SmallTCompMetadata;
|
|
8
9
|
output: string;
|
|
9
10
|
frame?: number;
|
|
10
11
|
inputProps?: unknown;
|
|
@@ -19,9 +20,12 @@ declare type InnerStillOptions = {
|
|
|
19
20
|
chromiumOptions?: ChromiumOptions;
|
|
20
21
|
scale?: number;
|
|
21
22
|
onDownload?: RenderMediaOnDownload;
|
|
23
|
+
cancelSignal?: CancelSignal;
|
|
22
24
|
ffmpegExecutable?: FfmpegExecutable;
|
|
23
25
|
};
|
|
24
|
-
declare type RenderStillOptions = InnerStillOptions & ServeUrlOrWebpackBundle
|
|
26
|
+
declare type RenderStillOptions = InnerStillOptions & ServeUrlOrWebpackBundle & {
|
|
27
|
+
port?: number | null;
|
|
28
|
+
};
|
|
25
29
|
/**
|
|
26
30
|
* @description Render a still frame from a composition and returns an image path
|
|
27
31
|
*/
|
package/dist/render-still.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -38,7 +42,7 @@ const seek_to_frame_1 = require("./seek-to-frame");
|
|
|
38
42
|
const set_props_and_env_1 = require("./set-props-and-env");
|
|
39
43
|
const validate_puppeteer_timeout_1 = require("./validate-puppeteer-timeout");
|
|
40
44
|
const validate_scale_1 = require("./validate-scale");
|
|
41
|
-
const innerRenderStill = async ({ composition, quality, imageFormat = 'png', serveUrl, puppeteerInstance, dumpBrowserLogs = false, onError, inputProps, envVariables, output, frame = 0, overwrite = true, browserExecutable, timeoutInMilliseconds, chromiumOptions, scale, proxyPort, }) => {
|
|
45
|
+
const innerRenderStill = async ({ composition, quality, imageFormat = 'png', serveUrl, puppeteerInstance, dumpBrowserLogs = false, onError, inputProps, envVariables, output, frame = 0, overwrite = true, browserExecutable, timeoutInMilliseconds, chromiumOptions, scale, proxyPort, cancelSignal, }) => {
|
|
42
46
|
remotion_1.Internals.validateDimension(composition.height, 'height', 'in the `config` object passed to `renderStill()`');
|
|
43
47
|
remotion_1.Internals.validateDimension(composition.width, 'width', 'in the `config` object passed to `renderStill()`');
|
|
44
48
|
remotion_1.Internals.validateFps(composition.fps, 'in the `config` object of `renderStill()`');
|
|
@@ -72,11 +76,20 @@ const innerRenderStill = async ({ composition, quality, imageFormat = 'png', ser
|
|
|
72
76
|
forceDeviceScaleFactor: scale !== null && scale !== void 0 ? scale : 1,
|
|
73
77
|
}));
|
|
74
78
|
const page = await browserInstance.newPage();
|
|
75
|
-
page.setViewport({
|
|
79
|
+
await page.setViewport({
|
|
76
80
|
width: composition.width,
|
|
77
81
|
height: composition.height,
|
|
78
82
|
deviceScaleFactor: scale !== null && scale !== void 0 ? scale : 1,
|
|
79
83
|
});
|
|
84
|
+
const errorCallback = (err) => {
|
|
85
|
+
onError(err);
|
|
86
|
+
cleanup();
|
|
87
|
+
};
|
|
88
|
+
const cleanUpJSException = (0, handle_javascript_exception_1.handleJavascriptException)({
|
|
89
|
+
page,
|
|
90
|
+
onError: errorCallback,
|
|
91
|
+
frame: null,
|
|
92
|
+
});
|
|
80
93
|
const cleanup = async () => {
|
|
81
94
|
cleanUpJSException();
|
|
82
95
|
if (puppeteerInstance) {
|
|
@@ -88,14 +101,8 @@ const innerRenderStill = async ({ composition, quality, imageFormat = 'png', ser
|
|
|
88
101
|
});
|
|
89
102
|
}
|
|
90
103
|
};
|
|
91
|
-
|
|
92
|
-
onError(err);
|
|
104
|
+
cancelSignal === null || cancelSignal === void 0 ? void 0 : cancelSignal(() => {
|
|
93
105
|
cleanup();
|
|
94
|
-
};
|
|
95
|
-
const cleanUpJSException = (0, handle_javascript_exception_1.handleJavascriptException)({
|
|
96
|
-
page,
|
|
97
|
-
onError: errorCallback,
|
|
98
|
-
frame: null,
|
|
99
106
|
});
|
|
100
107
|
await (0, set_props_and_env_1.setPropsAndEnv)({
|
|
101
108
|
inputProps,
|
|
@@ -105,6 +112,7 @@ const innerRenderStill = async ({ composition, quality, imageFormat = 'png', ser
|
|
|
105
112
|
initialFrame: frame,
|
|
106
113
|
timeoutInMilliseconds,
|
|
107
114
|
proxyPort,
|
|
115
|
+
retriesRemaining: 2,
|
|
108
116
|
});
|
|
109
117
|
await (0, puppeteer_evaluate_1.puppeteerEvaluateWithCatch)({
|
|
110
118
|
pageFunction: (id) => {
|
|
@@ -137,8 +145,8 @@ const renderStill = (options) => {
|
|
|
137
145
|
const selectedServeUrl = (0, legacy_webpack_config_1.getServeUrlWithFallback)(options);
|
|
138
146
|
const downloadDir = (0, make_assets_download_dir_1.makeAssetsDownloadTmpDir)();
|
|
139
147
|
const onDownload = (_a = options.onDownload) !== null && _a !== void 0 ? _a : (() => () => undefined);
|
|
140
|
-
|
|
141
|
-
var _a;
|
|
148
|
+
const happyPath = new Promise((resolve, reject) => {
|
|
149
|
+
var _a, _b;
|
|
142
150
|
const onError = (err) => reject(err);
|
|
143
151
|
let close = null;
|
|
144
152
|
(0, prepare_server_1.prepareServer)({
|
|
@@ -147,6 +155,7 @@ const renderStill = (options) => {
|
|
|
147
155
|
onDownload,
|
|
148
156
|
onError,
|
|
149
157
|
ffmpegExecutable: (_a = options.ffmpegExecutable) !== null && _a !== void 0 ? _a : null,
|
|
158
|
+
port: (_b = options.port) !== null && _b !== void 0 ? _b : null,
|
|
150
159
|
})
|
|
151
160
|
.then(({ serveUrl, closeServer, offthreadPort }) => {
|
|
152
161
|
close = closeServer;
|
|
@@ -161,5 +170,14 @@ const renderStill = (options) => {
|
|
|
161
170
|
.catch((err) => reject(err))
|
|
162
171
|
.finally(() => close === null || close === void 0 ? void 0 : close());
|
|
163
172
|
});
|
|
173
|
+
return Promise.race([
|
|
174
|
+
happyPath,
|
|
175
|
+
new Promise((_resolve, reject) => {
|
|
176
|
+
var _a;
|
|
177
|
+
(_a = options.cancelSignal) === null || _a === void 0 ? void 0 : _a.call(options, () => {
|
|
178
|
+
reject(new Error('renderStill() got cancelled'));
|
|
179
|
+
});
|
|
180
|
+
}),
|
|
181
|
+
]);
|
|
164
182
|
};
|
|
165
183
|
exports.renderStill = renderStill;
|
package/dist/serve-static.js
CHANGED
|
@@ -12,7 +12,12 @@ const offthread_video_server_1 = require("./offthread-video-server");
|
|
|
12
12
|
const serveStatic = async (path, options) => {
|
|
13
13
|
var _a, _b;
|
|
14
14
|
const port = await (0, get_port_1.getDesiredPort)((_b = (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : remotion_1.Internals.getServerPort()) !== null && _b !== void 0 ? _b : undefined, 3000, 3100);
|
|
15
|
-
const offthreadRequest = (0, offthread_video_server_1.startOffthreadVideoServer)(
|
|
15
|
+
const offthreadRequest = (0, offthread_video_server_1.startOffthreadVideoServer)({
|
|
16
|
+
ffmpegExecutable: options.ffmpegExecutable,
|
|
17
|
+
downloadDir: options.downloadDir,
|
|
18
|
+
onDownload: options.onDownload,
|
|
19
|
+
onError: options.onError,
|
|
20
|
+
});
|
|
16
21
|
try {
|
|
17
22
|
const server = http_1.default
|
|
18
23
|
.createServer((request, response) => {
|
|
@@ -39,6 +44,9 @@ const serveStatic = async (path, options) => {
|
|
|
39
44
|
return new Promise((resolve, reject) => {
|
|
40
45
|
server.close((err) => {
|
|
41
46
|
if (err) {
|
|
47
|
+
if (err.code === 'ERR_SERVER_NOT_RUNNING') {
|
|
48
|
+
return resolve();
|
|
49
|
+
}
|
|
42
50
|
reject(err);
|
|
43
51
|
}
|
|
44
52
|
else {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Page } from 'puppeteer-core';
|
|
2
|
-
export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, }: {
|
|
2
|
+
export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl, initialFrame, timeoutInMilliseconds, proxyPort, retriesRemaining, }: {
|
|
3
3
|
inputProps: unknown;
|
|
4
4
|
envVariables: Record<string, string> | undefined;
|
|
5
5
|
page: Page;
|
|
@@ -7,4 +7,5 @@ export declare const setPropsAndEnv: ({ inputProps, envVariables, page, serveUrl
|
|
|
7
7
|
initialFrame: number;
|
|
8
8
|
timeoutInMilliseconds: number | undefined;
|
|
9
9
|
proxyPort: number;
|
|
10
|
+
retriesRemaining: number;
|
|
10
11
|
}) => Promise<void>;
|