@remotion/renderer 4.0.516 → 4.0.518
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/inline-audio-mixing.d.ts +6 -1
- package/dist/assets/inline-audio-mixing.js +42 -39
- package/dist/combine-chunks.js +0 -15
- package/dist/create-audio.js +22 -8
- package/dist/esm/index.mjs +295 -99
- package/dist/ffmpeg-args.js +1 -0
- package/dist/finalize-fast-start.d.ts +12 -0
- package/dist/finalize-fast-start.js +70 -0
- package/dist/get-codec-name.d.ts +3 -1
- package/dist/get-codec-name.js +11 -2
- package/dist/get-fast-start-muxer.d.ts +2 -0
- package/dist/get-fast-start-muxer.js +11 -0
- package/dist/merge-inline-audio-tracks.d.ts +21 -0
- package/dist/merge-inline-audio-tracks.js +74 -0
- package/dist/mux-video-and-audio.d.ts +1 -2
- package/dist/mux-video-and-audio.js +9 -3
- package/dist/prespawn-ffmpeg.d.ts +2 -0
- package/dist/prespawn-ffmpeg.js +1 -0
- package/dist/probe-encoder.d.ts +3 -1
- package/dist/probe-encoder.js +10 -1
- package/dist/render-media.js +2 -0
- package/dist/stitch-frames-to-video.d.ts +2 -0
- package/dist/stitch-frames-to-video.js +51 -30
- package/package.json +13 -13
package/dist/ffmpeg-args.js
CHANGED
|
@@ -43,6 +43,7 @@ const generateFfmpegArgs = ({ hasPreencoded, proResProfileName, pixelFormat, x26
|
|
|
43
43
|
hardwareAcceleration,
|
|
44
44
|
indent,
|
|
45
45
|
logLevel,
|
|
46
|
+
onLog: null,
|
|
46
47
|
});
|
|
47
48
|
if (encoderSettings === null) {
|
|
48
49
|
throw new TypeError(`encoderSettings is null: ${JSON.stringify(codec)} (hwaccel = ${hardwareAcceleration})`);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastStartMuxer } from './get-fast-start-muxer';
|
|
2
|
+
import type { CancelSignal } from './make-cancel-signal';
|
|
3
|
+
export declare const finalizeFastStart: ({ input, output, muxer, force, indent, logLevel, binariesDirectory, cancelSignal, }: {
|
|
4
|
+
input: string;
|
|
5
|
+
output: string;
|
|
6
|
+
muxer: FastStartMuxer;
|
|
7
|
+
force: boolean;
|
|
8
|
+
indent: boolean;
|
|
9
|
+
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
10
|
+
binariesDirectory: string | null;
|
|
11
|
+
cancelSignal: CancelSignal | null;
|
|
12
|
+
}) => Promise<void>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.finalizeFastStart = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const node_fs_1 = require("node:fs");
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const call_ffmpeg_1 = require("./call-ffmpeg");
|
|
11
|
+
const logger_1 = require("./logger");
|
|
12
|
+
const finalizeFastStart = async ({ input, output, muxer, force, indent, logLevel, binariesDirectory, cancelSignal, }) => {
|
|
13
|
+
var _a;
|
|
14
|
+
let fastStartFile = null;
|
|
15
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
16
|
+
const candidate = node_path_1.default.join(node_path_1.default.dirname(output), `${node_path_1.default.basename(output)}.${(0, node_crypto_1.randomUUID)()}.remotion-in-progress`);
|
|
17
|
+
const task = (0, call_ffmpeg_1.callFf)({
|
|
18
|
+
bin: 'ffmpeg',
|
|
19
|
+
args: [
|
|
20
|
+
'-hide_banner',
|
|
21
|
+
'-i',
|
|
22
|
+
input,
|
|
23
|
+
'-c',
|
|
24
|
+
'copy',
|
|
25
|
+
'-movflags',
|
|
26
|
+
'faststart',
|
|
27
|
+
'-y',
|
|
28
|
+
'-f',
|
|
29
|
+
muxer,
|
|
30
|
+
candidate,
|
|
31
|
+
],
|
|
32
|
+
indent,
|
|
33
|
+
logLevel,
|
|
34
|
+
binariesDirectory,
|
|
35
|
+
cancelSignal: cancelSignal !== null && cancelSignal !== void 0 ? cancelSignal : undefined,
|
|
36
|
+
});
|
|
37
|
+
let stderr = '';
|
|
38
|
+
(_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
|
|
39
|
+
stderr += data.toString();
|
|
40
|
+
});
|
|
41
|
+
try {
|
|
42
|
+
await task;
|
|
43
|
+
fastStartFile = candidate;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
await node_fs_1.promises.rm(candidate, { force: true }).catch(() => undefined);
|
|
48
|
+
const isReopenFailure = stderr.includes('Unable to re-open') &&
|
|
49
|
+
stderr.includes('output file for shifting data');
|
|
50
|
+
if (!isReopenFailure || attempt === 2) {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
logger_1.Log.verbose({ indent, logLevel, tag: 'stitchFramesToVideo()' }, `Retrying Fast Start finalization (attempt ${attempt + 2} of 3)`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (fastStartFile === null) {
|
|
57
|
+
throw new Error('Fast Start finalization did not produce an output file');
|
|
58
|
+
}
|
|
59
|
+
if (force) {
|
|
60
|
+
await node_fs_1.promises.rename(fastStartFile, output);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
await node_fs_1.promises.link(fastStartFile, output);
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
await node_fs_1.promises.rm(fastStartFile, { force: true });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
exports.finalizeFastStart = finalizeFastStart;
|
package/dist/get-codec-name.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OnLog } from './browser/BrowserPage';
|
|
1
2
|
export type CodecSettings = {
|
|
2
3
|
encoderName: string;
|
|
3
4
|
hardwareAccelerated: boolean;
|
|
@@ -7,7 +8,7 @@ export declare const hasSpecifiedUnsupportedHardwareQualifySettings: ({ encoding
|
|
|
7
8
|
encodingBufferSize: string | null;
|
|
8
9
|
crf: unknown;
|
|
9
10
|
}) => "crf" | "encodingBufferSize" | "encodingMaxRate" | null;
|
|
10
|
-
export declare const getCodecName: ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwareAcceleration, logLevel, indent, }: {
|
|
11
|
+
export declare const getCodecName: ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwareAcceleration, logLevel, indent, onLog, }: {
|
|
11
12
|
codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav";
|
|
12
13
|
hardwareAcceleration: "disable" | "if-possible" | "required";
|
|
13
14
|
encodingMaxRate: string | null;
|
|
@@ -15,4 +16,5 @@ export declare const getCodecName: ({ codec, encodingMaxRate, encodingBufferSize
|
|
|
15
16
|
crf: unknown;
|
|
16
17
|
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
17
18
|
indent: boolean;
|
|
19
|
+
onLog: OnLog | null;
|
|
18
20
|
}) => CodecSettings | null;
|
package/dist/get-codec-name.js
CHANGED
|
@@ -15,7 +15,7 @@ const hasSpecifiedUnsupportedHardwareQualifySettings = ({ encodingMaxRate, encod
|
|
|
15
15
|
return null;
|
|
16
16
|
};
|
|
17
17
|
exports.hasSpecifiedUnsupportedHardwareQualifySettings = hasSpecifiedUnsupportedHardwareQualifySettings;
|
|
18
|
-
const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwareAcceleration, logLevel, indent, }) => {
|
|
18
|
+
const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwareAcceleration, logLevel, indent, onLog, }) => {
|
|
19
19
|
const preferredHwAcceleration = hardwareAcceleration === 'required' ||
|
|
20
20
|
hardwareAcceleration === 'if-possible';
|
|
21
21
|
const unsupportedQualityOption = (0, exports.hasSpecifiedUnsupportedHardwareQualifySettings)({
|
|
@@ -28,7 +28,16 @@ const getCodecName = ({ codec, encodingMaxRate, encodingBufferSize, crf, hardwar
|
|
|
28
28
|
}
|
|
29
29
|
const warnAboutDisabledHardwareAcceleration = () => {
|
|
30
30
|
if (hardwareAcceleration === 'if-possible' && unsupportedQualityOption) {
|
|
31
|
-
|
|
31
|
+
const message = `Hardware accelerated encoding disabled - "${unsupportedQualityOption}" option is not supported with hardware acceleration`;
|
|
32
|
+
if (onLog !== null) {
|
|
33
|
+
onLog({
|
|
34
|
+
logLevel: 'warn',
|
|
35
|
+
previewString: message,
|
|
36
|
+
tag: '',
|
|
37
|
+
});
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
logger_1.Log.warn({ indent, logLevel }, `${indent ? '' : '\n'}${message}`);
|
|
32
41
|
}
|
|
33
42
|
};
|
|
34
43
|
if (codec === 'prores') {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getFastStartMuxer = void 0;
|
|
4
|
+
const getFastStartMuxer = (outputExtension) => {
|
|
5
|
+
const normalizedExtension = outputExtension.toLowerCase();
|
|
6
|
+
if (normalizedExtension === 'mp4' || normalizedExtension === 'mov') {
|
|
7
|
+
return normalizedExtension;
|
|
8
|
+
}
|
|
9
|
+
return null;
|
|
10
|
+
};
|
|
11
|
+
exports.getFastStartMuxer = getFastStartMuxer;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DownloadMap } from './assets/download-map';
|
|
2
|
+
import type { InlineAudioTrack } from './assets/inline-audio-mixing';
|
|
3
|
+
import type { CancelSignal } from './make-cancel-signal';
|
|
4
|
+
import type { PreprocessedAudioTrack } from './preprocess-audio-track';
|
|
5
|
+
export declare const inlineAudioTrackToPreprocessedAudioTrack: ({ track, relativeToInSamples, padToDurationInSamples, }: {
|
|
6
|
+
track: InlineAudioTrack;
|
|
7
|
+
relativeToInSamples: number;
|
|
8
|
+
padToDurationInSamples: number | null;
|
|
9
|
+
}) => PreprocessedAudioTrack;
|
|
10
|
+
export declare const mergeInlineAudioTracks: ({ tracks, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, fps, chunkLengthInSeconds, sampleRate, }: {
|
|
11
|
+
tracks: InlineAudioTrack[];
|
|
12
|
+
downloadMap: DownloadMap;
|
|
13
|
+
remotionRoot: string;
|
|
14
|
+
indent: boolean;
|
|
15
|
+
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
|
|
16
|
+
binariesDirectory: string | null;
|
|
17
|
+
cancelSignal: CancelSignal | undefined;
|
|
18
|
+
fps: number;
|
|
19
|
+
chunkLengthInSeconds: number;
|
|
20
|
+
sampleRate: number;
|
|
21
|
+
}) => Promise<InlineAudioTrack | null>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mergeInlineAudioTracks = exports.inlineAudioTrackToPreprocessedAudioTrack = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const chunk_1 = require("./chunk");
|
|
9
|
+
const delete_directory_1 = require("./delete-directory");
|
|
10
|
+
const merge_audio_track_1 = require("./merge-audio-track");
|
|
11
|
+
const MAX_INLINE_AUDIO_INPUTS = 10;
|
|
12
|
+
const inlineAudioTrackToPreprocessedAudioTrack = ({ track, relativeToInSamples, padToDurationInSamples, }) => {
|
|
13
|
+
const delayInSamples = Math.max(0, track.startInSamples - relativeToInSamples);
|
|
14
|
+
const padAtEndInSamples = padToDurationInSamples === null
|
|
15
|
+
? 0
|
|
16
|
+
: Math.max(0, padToDurationInSamples - delayInSamples - track.durationInSamples);
|
|
17
|
+
return {
|
|
18
|
+
outName: track.outName,
|
|
19
|
+
filter: {
|
|
20
|
+
pad_start: delayInSamples === 0
|
|
21
|
+
? null
|
|
22
|
+
: `adelay=${new Array(3).fill(`${delayInSamples}S`).join('|')}`,
|
|
23
|
+
pad_end: padAtEndInSamples === 0 ? null : `apad=pad_len=${padAtEndInSamples}`,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.inlineAudioTrackToPreprocessedAudioTrack = inlineAudioTrackToPreprocessedAudioTrack;
|
|
28
|
+
const mergeInlineAudioTracks = async ({ tracks, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, fps, chunkLengthInSeconds, sampleRate, }) => {
|
|
29
|
+
var _a;
|
|
30
|
+
let currentTracks = tracks.sort((a, b) => a.startInSamples - b.startInSamples);
|
|
31
|
+
let level = 0;
|
|
32
|
+
while (currentTracks.length > 1) {
|
|
33
|
+
const groups = (0, chunk_1.chunk)(currentTracks, MAX_INLINE_AUDIO_INPUTS);
|
|
34
|
+
currentTracks = await Promise.all(groups.map(async (group, index) => {
|
|
35
|
+
if (group.length === 1) {
|
|
36
|
+
return group[0];
|
|
37
|
+
}
|
|
38
|
+
const [{ startInSamples }] = group;
|
|
39
|
+
const durationInSamples = Math.max(...group.map((track) => {
|
|
40
|
+
return (track.startInSamples - startInSamples + track.durationInSamples);
|
|
41
|
+
}));
|
|
42
|
+
const outName = node_path_1.default.join(downloadMap.audioMixing, `inline-${level}-${index}.wav`);
|
|
43
|
+
await (0, merge_audio_track_1.mergeAudioTrack)({
|
|
44
|
+
files: group.map((track) => (0, exports.inlineAudioTrackToPreprocessedAudioTrack)({
|
|
45
|
+
track,
|
|
46
|
+
relativeToInSamples: startInSamples,
|
|
47
|
+
padToDurationInSamples: null,
|
|
48
|
+
})),
|
|
49
|
+
outName,
|
|
50
|
+
downloadMap,
|
|
51
|
+
remotionRoot,
|
|
52
|
+
indent,
|
|
53
|
+
logLevel,
|
|
54
|
+
binariesDirectory,
|
|
55
|
+
cancelSignal,
|
|
56
|
+
onProgress: () => undefined,
|
|
57
|
+
fps,
|
|
58
|
+
chunkLengthInSeconds,
|
|
59
|
+
sampleRate,
|
|
60
|
+
});
|
|
61
|
+
for (const track of group) {
|
|
62
|
+
(0, delete_directory_1.deleteDirectory)(track.outName);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
outName,
|
|
66
|
+
startInSamples,
|
|
67
|
+
durationInSamples,
|
|
68
|
+
};
|
|
69
|
+
}));
|
|
70
|
+
level++;
|
|
71
|
+
}
|
|
72
|
+
return (_a = currentTracks[0]) !== null && _a !== void 0 ? _a : null;
|
|
73
|
+
};
|
|
74
|
+
exports.mergeInlineAudioTracks = mergeInlineAudioTracks;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CancelSignal } from './make-cancel-signal';
|
|
2
|
-
export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal,
|
|
2
|
+
export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, }: {
|
|
3
3
|
videoOutput: string | null;
|
|
4
4
|
audioOutput: string | null;
|
|
5
5
|
output: string;
|
|
@@ -9,7 +9,6 @@ export declare const muxVideoAndAudio: ({ videoOutput, audioOutput, output, inde
|
|
|
9
9
|
fps: number;
|
|
10
10
|
onProgress: (p: number) => void;
|
|
11
11
|
cancelSignal: CancelSignal | undefined;
|
|
12
|
-
addFaststart: boolean;
|
|
13
12
|
metadata?: Record<string, string> | null | undefined;
|
|
14
13
|
numberOfGifLoops: number | null;
|
|
15
14
|
}) => Promise<void>;
|
|
@@ -3,14 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.muxVideoAndAudio = void 0;
|
|
4
4
|
const call_ffmpeg_1 = require("./call-ffmpeg");
|
|
5
5
|
const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif-loops-to-ffmpeg");
|
|
6
|
+
const get_extension_of_filename_1 = require("./get-extension-of-filename");
|
|
7
|
+
const get_fast_start_muxer_1 = require("./get-fast-start-muxer");
|
|
6
8
|
const logger_1 = require("./logger");
|
|
7
9
|
const make_metadata_args_1 = require("./make-metadata-args");
|
|
8
10
|
const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
|
|
9
11
|
const truthy_1 = require("./truthy");
|
|
10
|
-
const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal,
|
|
12
|
+
const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logLevel, onProgress, binariesDirectory, fps, cancelSignal, metadata, numberOfGifLoops, }) => {
|
|
11
13
|
var _a;
|
|
12
14
|
const startTime = Date.now();
|
|
13
15
|
logger_1.Log.verbose({ indent, logLevel }, 'Muxing video and audio together');
|
|
16
|
+
const outputExtension = (0, get_extension_of_filename_1.getExtensionOfFilename)(output);
|
|
17
|
+
const fastStartMuxer = outputExtension
|
|
18
|
+
? (0, get_fast_start_muxer_1.getFastStartMuxer)(outputExtension)
|
|
19
|
+
: null;
|
|
14
20
|
const command = [
|
|
15
21
|
'-hide_banner',
|
|
16
22
|
videoOutput ? '-i' : null,
|
|
@@ -30,8 +36,8 @@ const muxVideoAndAudio = async ({ videoOutput, audioOutput, output, indent, logL
|
|
|
30
36
|
numberOfGifLoops === null
|
|
31
37
|
? null
|
|
32
38
|
: (0, convert_number_of_gif_loops_to_ffmpeg_1.convertNumberOfGifLoopsToFfmpegSyntax)(numberOfGifLoops),
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
fastStartMuxer ? '-movflags' : null,
|
|
40
|
+
fastStartMuxer ? 'faststart' : null,
|
|
35
41
|
...(0, make_metadata_args_1.makeMetadataArgs)(metadata !== null && metadata !== void 0 ? metadata : {}),
|
|
36
42
|
'-y',
|
|
37
43
|
output,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { _InternalTypes } from 'remotion';
|
|
2
|
+
import type { OnLog } from './browser/BrowserPage';
|
|
2
3
|
import type { HardwareAccelerationOption } from './client';
|
|
3
4
|
import type { Codec } from './codec';
|
|
4
5
|
import type { FfmpegOverrideFn } from './ffmpeg-override';
|
|
@@ -42,6 +43,7 @@ type PreStitcherOptions = {
|
|
|
42
43
|
colorSpace: ColorSpace | null;
|
|
43
44
|
binariesDirectory: string | null;
|
|
44
45
|
hardwareAcceleration: HardwareAccelerationOption;
|
|
46
|
+
onLog: OnLog;
|
|
45
47
|
};
|
|
46
48
|
export declare const prespawnFfmpeg: (options: PreStitcherOptions) => {
|
|
47
49
|
task: import("execa").ExecaChildProcess<string>;
|
package/dist/prespawn-ffmpeg.js
CHANGED
package/dist/probe-encoder.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OnLog } from './browser/BrowserPage';
|
|
1
2
|
/**
|
|
2
3
|
* Probes FFmpeg to check if a specific encoder is available.
|
|
3
4
|
* Returns true if the encoder is supported, false otherwise.
|
|
@@ -14,7 +15,7 @@ export declare const probeEncoderAvailability: ({ encoderName, binariesDirectory
|
|
|
14
15
|
* - `if-possible` mode: falls back to software encoding
|
|
15
16
|
* - `required` mode: throws a clear error
|
|
16
17
|
*/
|
|
17
|
-
export declare const resolveHardwareAcceleration: ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, }: {
|
|
18
|
+
export declare const resolveHardwareAcceleration: ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, onLog, }: {
|
|
18
19
|
codec: "aac" | "av1" | "gif" | "h264" | "h264-mkv" | "h264-ts" | "h265" | "mp3" | "prores" | "vp8" | "vp9" | "wav";
|
|
19
20
|
hardwareAcceleration: "disable" | "if-possible" | "required";
|
|
20
21
|
binariesDirectory: string | null;
|
|
@@ -23,4 +24,5 @@ export declare const resolveHardwareAcceleration: ({ codec, hardwareAcceleration
|
|
|
23
24
|
crf: unknown;
|
|
24
25
|
encodingMaxRate: string | null;
|
|
25
26
|
encodingBufferSize: string | null;
|
|
27
|
+
onLog: OnLog | null;
|
|
26
28
|
}) => "disable" | "if-possible" | "required";
|
package/dist/probe-encoder.js
CHANGED
|
@@ -53,7 +53,7 @@ exports.probeEncoderAvailability = probeEncoderAvailability;
|
|
|
53
53
|
* - `if-possible` mode: falls back to software encoding
|
|
54
54
|
* - `required` mode: throws a clear error
|
|
55
55
|
*/
|
|
56
|
-
const resolveHardwareAcceleration = ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, }) => {
|
|
56
|
+
const resolveHardwareAcceleration = ({ codec, hardwareAcceleration, binariesDirectory, indent, logLevel, crf, encodingMaxRate, encodingBufferSize, onLog, }) => {
|
|
57
57
|
if (hardwareAcceleration === 'disable') {
|
|
58
58
|
return 'disable';
|
|
59
59
|
}
|
|
@@ -65,6 +65,7 @@ const resolveHardwareAcceleration = ({ codec, hardwareAcceleration, binariesDire
|
|
|
65
65
|
encodingBufferSize,
|
|
66
66
|
logLevel,
|
|
67
67
|
indent,
|
|
68
|
+
onLog,
|
|
68
69
|
});
|
|
69
70
|
// Audio codecs return null, no probing needed
|
|
70
71
|
if (preferred === null) {
|
|
@@ -72,6 +73,14 @@ const resolveHardwareAcceleration = ({ codec, hardwareAcceleration, binariesDire
|
|
|
72
73
|
}
|
|
73
74
|
// Only probe if getCodecName selected a hardware-accelerated encoder
|
|
74
75
|
if (!preferred.hardwareAccelerated) {
|
|
76
|
+
if (hardwareAcceleration === 'if-possible' &&
|
|
77
|
+
(0, get_codec_name_1.hasSpecifiedUnsupportedHardwareQualifySettings)({
|
|
78
|
+
crf,
|
|
79
|
+
encodingMaxRate,
|
|
80
|
+
encodingBufferSize,
|
|
81
|
+
})) {
|
|
82
|
+
return 'disable';
|
|
83
|
+
}
|
|
75
84
|
return hardwareAcceleration;
|
|
76
85
|
}
|
|
77
86
|
const encoderAvailable = (0, exports.probeEncoderAvailability)({
|
package/dist/render-media.js
CHANGED
|
@@ -255,6 +255,7 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
|
|
|
255
255
|
colorSpace,
|
|
256
256
|
binariesDirectory,
|
|
257
257
|
hardwareAcceleration,
|
|
258
|
+
onLog,
|
|
258
259
|
});
|
|
259
260
|
stitcherFfmpeg = preStitcher.task;
|
|
260
261
|
}
|
|
@@ -478,6 +479,7 @@ const internalRenderMediaRaw = ({ proResProfile, x264Preset, gopSize, crf, compo
|
|
|
478
479
|
separateAudioTo,
|
|
479
480
|
metadata,
|
|
480
481
|
hardwareAcceleration,
|
|
482
|
+
onLog,
|
|
481
483
|
sampleRate,
|
|
482
484
|
});
|
|
483
485
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { _InternalTypes } from 'remotion';
|
|
2
2
|
import type { RenderMediaOnDownload } from './assets/download-and-map-assets-to-file';
|
|
3
3
|
import type { RenderAssetInfo } from './assets/download-map';
|
|
4
|
+
import type { OnLog } from './browser/BrowserPage';
|
|
4
5
|
import type { Codec } from './codec';
|
|
5
6
|
import type { FfmpegOverrideFn } from './ffmpeg-override';
|
|
6
7
|
import type { LogLevel } from './log-level';
|
|
@@ -44,6 +45,7 @@ type InternalStitchFramesToVideoOptions = {
|
|
|
44
45
|
binariesDirectory: string | null;
|
|
45
46
|
metadata: Record<string, string> | null;
|
|
46
47
|
sampleRate: number;
|
|
48
|
+
onLog: OnLog;
|
|
47
49
|
} & ToOptions<typeof optionsMap.stitchFramesToVideo>;
|
|
48
50
|
export type StitchFramesToVideoOptions = {
|
|
49
51
|
fps: number;
|
|
@@ -13,10 +13,14 @@ const codec_supports_media_1 = require("./codec-supports-media");
|
|
|
13
13
|
const convert_number_of_gif_loops_to_ffmpeg_1 = require("./convert-number-of-gif-loops-to-ffmpeg");
|
|
14
14
|
const create_audio_1 = require("./create-audio");
|
|
15
15
|
const crf_1 = require("./crf");
|
|
16
|
+
const default_on_log_1 = require("./default-on-log");
|
|
16
17
|
const delete_directory_1 = require("./delete-directory");
|
|
17
18
|
const ffmpeg_args_1 = require("./ffmpeg-args");
|
|
19
|
+
const finalize_fast_start_1 = require("./finalize-fast-start");
|
|
18
20
|
const find_closest_package_json_1 = require("./find-closest-package-json");
|
|
19
21
|
const get_extension_from_codec_1 = require("./get-extension-from-codec");
|
|
22
|
+
const get_extension_of_filename_1 = require("./get-extension-of-filename");
|
|
23
|
+
const get_fast_start_muxer_1 = require("./get-fast-start-muxer");
|
|
20
24
|
const get_prores_profile_name_1 = require("./get-prores-profile-name");
|
|
21
25
|
const logger_1 = require("./logger");
|
|
22
26
|
const make_cancel_signal_1 = require("./make-cancel-signal");
|
|
@@ -32,8 +36,9 @@ const render_has_audio_1 = require("./render-has-audio");
|
|
|
32
36
|
const validate_1 = require("./validate");
|
|
33
37
|
const validate_even_dimensions_with_codec_1 = require("./validate-even-dimensions-with-codec");
|
|
34
38
|
const validate_videobitrate_1 = require("./validate-videobitrate");
|
|
35
|
-
const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec: audioCodecSetting, cancelSignal, codec, crf, gopSize, enforceAudioTrack, ffmpegOverride, force, fps, height, indent, muted, onDownload, outputLocation, pixelFormat, preEncodedFileLocation, preferLossless, proResProfile, logLevel, videoBitrate, maxRate, bufferSize, width, numberOfGifLoops, onProgress, x264Preset, colorSpace, binariesDirectory, separateAudioTo, metadata, hardwareAcceleration, sampleRate, }, remotionRoot) => {
|
|
39
|
+
const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec: audioCodecSetting, cancelSignal, codec, crf, gopSize, enforceAudioTrack, ffmpegOverride, force, fps, height, indent, muted, onDownload, outputLocation, pixelFormat, preEncodedFileLocation, preferLossless, proResProfile, logLevel, videoBitrate, maxRate, bufferSize, width, numberOfGifLoops, onProgress, x264Preset, colorSpace, binariesDirectory, separateAudioTo, metadata, hardwareAcceleration, sampleRate, onLog, }, remotionRoot) => {
|
|
36
40
|
var _a;
|
|
41
|
+
var _b, _c;
|
|
37
42
|
(0, validate_1.validateDimension)(height, 'height', 'passed to `stitchFramesToVideo()`');
|
|
38
43
|
(0, validate_1.validateDimension)(width, 'width', 'passed to `stitchFramesToVideo()`');
|
|
39
44
|
(0, validate_even_dimensions_with_codec_1.validateEvenDimensionsWithCodec)({
|
|
@@ -82,6 +87,13 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
|
|
|
82
87
|
const tempFile = outputLocation
|
|
83
88
|
? null
|
|
84
89
|
: node_path_1.default.join(assetsInfo.downloadMap.stitchFrames, `out.${(0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, resolvedAudioCodec)}`);
|
|
90
|
+
const outputExtension = ((_b = (0, get_extension_of_filename_1.getExtensionOfFilename)(outputLocation)) !== null && _b !== void 0 ? _b : (0, get_extension_from_codec_1.getFileExtensionFromCodec)(codec, resolvedAudioCodec)).toLowerCase();
|
|
91
|
+
const fastStartMuxer = (0, get_fast_start_muxer_1.getFastStartMuxer)(outputExtension);
|
|
92
|
+
// Fast Start reopens its output for an in-place second pass. Keep that work
|
|
93
|
+
// away from the public output path so Windows file observers cannot lock it.
|
|
94
|
+
const fastStartIntermediate = fastStartMuxer === null
|
|
95
|
+
? null
|
|
96
|
+
: node_path_1.default.join(assetsInfo.downloadMap.stitchFrames, 'fast-start-intermediate.remotion-in-progress');
|
|
85
97
|
logger_1.Log.verbose({
|
|
86
98
|
indent,
|
|
87
99
|
logLevel,
|
|
@@ -178,16 +190,20 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
|
|
|
178
190
|
assetsInfo.downloadMap.allowCleanup();
|
|
179
191
|
return Promise.resolve(file);
|
|
180
192
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
// Parallel encoding already resolved the encoder in the pre-stitcher.
|
|
194
|
+
const resolvedHardwareAcceleration = preEncodedFileLocation
|
|
195
|
+
? 'disable'
|
|
196
|
+
: (0, probe_encoder_1.resolveHardwareAcceleration)({
|
|
197
|
+
codec,
|
|
198
|
+
hardwareAcceleration,
|
|
199
|
+
binariesDirectory,
|
|
200
|
+
indent: indent !== null && indent !== void 0 ? indent : false,
|
|
201
|
+
logLevel,
|
|
202
|
+
crf,
|
|
203
|
+
encodingMaxRate: maxRate,
|
|
204
|
+
encodingBufferSize: bufferSize,
|
|
205
|
+
onLog,
|
|
206
|
+
});
|
|
191
207
|
const ffmpegArgs = [
|
|
192
208
|
...(preEncodedFileLocation
|
|
193
209
|
? [['-i', preEncodedFileLocation]]
|
|
@@ -221,12 +237,12 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
|
|
|
221
237
|
indent,
|
|
222
238
|
logLevel,
|
|
223
239
|
}),
|
|
224
|
-
codec === 'h264' ? ['-movflags', 'faststart'] : null,
|
|
225
240
|
// Ignore metadata that may come from remote media
|
|
226
241
|
['-map_metadata', '-1'],
|
|
227
242
|
...(0, make_metadata_args_1.makeMetadataArgs)(metadata !== null && metadata !== void 0 ? metadata : {}),
|
|
228
|
-
force ? '-y' : null,
|
|
229
|
-
|
|
243
|
+
force || fastStartIntermediate ? '-y' : null,
|
|
244
|
+
fastStartIntermediate ? ['-f', fastStartMuxer] : null,
|
|
245
|
+
(_c = fastStartIntermediate !== null && fastStartIntermediate !== void 0 ? fastStartIntermediate : outputLocation) !== null && _c !== void 0 ? _c : tempFile,
|
|
230
246
|
];
|
|
231
247
|
const ffmpegString = ffmpegArgs.flat(2).filter(Boolean);
|
|
232
248
|
const finalFfmpegString = ffmpegOverride
|
|
@@ -282,30 +298,34 @@ const innerStitchFramesToVideo = async ({ assetsInfo, audioBitrate, audioCodec:
|
|
|
282
298
|
(0, node_fs_1.cpSync)(audio, finalDestination);
|
|
283
299
|
(0, node_fs_1.rmSync)(audio);
|
|
284
300
|
}
|
|
285
|
-
|
|
301
|
+
await new Promise((resolve, reject) => {
|
|
286
302
|
task.once('close', (code, signal) => {
|
|
287
303
|
if (code === 0) {
|
|
288
|
-
|
|
289
|
-
(0, download_map_1.cleanDownloadMap)(assetsInfo.downloadMap);
|
|
290
|
-
return resolve(null);
|
|
291
|
-
}
|
|
292
|
-
node_fs_1.promises
|
|
293
|
-
.readFile(tempFile)
|
|
294
|
-
.then((f) => {
|
|
295
|
-
resolve(f);
|
|
296
|
-
})
|
|
297
|
-
.catch((e) => {
|
|
298
|
-
reject(e);
|
|
299
|
-
})
|
|
300
|
-
.finally(() => {
|
|
301
|
-
(0, download_map_1.cleanDownloadMap)(assetsInfo.downloadMap);
|
|
302
|
-
});
|
|
304
|
+
resolve();
|
|
303
305
|
}
|
|
304
306
|
else {
|
|
305
307
|
reject(new Error(`FFmpeg quit with code ${code} ${signal ? `(${signal})` : ''} The FFmpeg output was ${ffmpegStderr}`));
|
|
306
308
|
}
|
|
307
309
|
});
|
|
308
310
|
});
|
|
311
|
+
if (fastStartIntermediate && fastStartMuxer) {
|
|
312
|
+
const destination = outputLocation !== null && outputLocation !== void 0 ? outputLocation : tempFile;
|
|
313
|
+
if (destination === null) {
|
|
314
|
+
throw new Error('Expected a Fast Start output destination');
|
|
315
|
+
}
|
|
316
|
+
await (0, finalize_fast_start_1.finalizeFastStart)({
|
|
317
|
+
input: fastStartIntermediate,
|
|
318
|
+
output: node_path_1.default.resolve(remotionRoot, destination),
|
|
319
|
+
muxer: fastStartMuxer,
|
|
320
|
+
force,
|
|
321
|
+
indent,
|
|
322
|
+
logLevel,
|
|
323
|
+
binariesDirectory,
|
|
324
|
+
cancelSignal,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
const result = tempFile === null ? null : await node_fs_1.promises.readFile(tempFile);
|
|
328
|
+
(0, download_map_1.cleanDownloadMap)(assetsInfo.downloadMap);
|
|
309
329
|
assetsInfo.downloadMap.allowCleanup();
|
|
310
330
|
return result;
|
|
311
331
|
};
|
|
@@ -363,6 +383,7 @@ const stitchFramesToVideo = ({ assetsInfo, force, fps, height, width, audioBitra
|
|
|
363
383
|
separateAudioTo: separateAudioTo !== null && separateAudioTo !== void 0 ? separateAudioTo : null,
|
|
364
384
|
hardwareAcceleration: hardwareAcceleration !== null && hardwareAcceleration !== void 0 ? hardwareAcceleration : 'disable',
|
|
365
385
|
sampleRate: sampleRate !== null && sampleRate !== void 0 ? sampleRate : 48000,
|
|
386
|
+
onLog: default_on_log_1.defaultOnLog,
|
|
366
387
|
});
|
|
367
388
|
};
|
|
368
389
|
exports.stitchFramesToVideo = stitchFramesToVideo;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/renderer"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/renderer",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.518",
|
|
7
7
|
"description": "Render Remotion videos using Node.js or Bun",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"execa": "5.1.1",
|
|
25
|
-
"remotion": "4.0.
|
|
26
|
-
"@remotion/streaming": "4.0.
|
|
25
|
+
"remotion": "4.0.518",
|
|
26
|
+
"@remotion/streaming": "4.0.518",
|
|
27
27
|
"source-map": "0.8.0",
|
|
28
28
|
"ws": "8.21.0",
|
|
29
|
-
"@remotion/licensing": "4.0.
|
|
29
|
+
"@remotion/licensing": "4.0.518"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"react": ">=16.8.0",
|
|
@@ -40,19 +40,19 @@
|
|
|
40
40
|
"react-dom": "19.2.3",
|
|
41
41
|
"@typescript/native-preview": "7.0.0-dev.20260217.1",
|
|
42
42
|
"@types/ws": "8.5.10",
|
|
43
|
-
"@remotion/example-videos": "4.0.
|
|
44
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
43
|
+
"@remotion/example-videos": "4.0.518",
|
|
44
|
+
"@remotion/eslint-config-internal": "4.0.518",
|
|
45
45
|
"eslint": "9.19.0",
|
|
46
46
|
"@types/node": "20.12.14"
|
|
47
47
|
},
|
|
48
48
|
"optionalDependencies": {
|
|
49
|
-
"@remotion/compositor-darwin-arm64": "4.0.
|
|
50
|
-
"@remotion/compositor-darwin-x64": "4.0.
|
|
51
|
-
"@remotion/compositor-linux-arm64-gnu": "4.0.
|
|
52
|
-
"@remotion/compositor-linux-arm64-musl": "4.0.
|
|
53
|
-
"@remotion/compositor-linux-x64-gnu": "4.0.
|
|
54
|
-
"@remotion/compositor-linux-x64-musl": "4.0.
|
|
55
|
-
"@remotion/compositor-win32-x64-msvc": "4.0.
|
|
49
|
+
"@remotion/compositor-darwin-arm64": "4.0.518",
|
|
50
|
+
"@remotion/compositor-darwin-x64": "4.0.518",
|
|
51
|
+
"@remotion/compositor-linux-arm64-gnu": "4.0.518",
|
|
52
|
+
"@remotion/compositor-linux-arm64-musl": "4.0.518",
|
|
53
|
+
"@remotion/compositor-linux-x64-gnu": "4.0.518",
|
|
54
|
+
"@remotion/compositor-linux-x64-musl": "4.0.518",
|
|
55
|
+
"@remotion/compositor-win32-x64-msvc": "4.0.518"
|
|
56
56
|
},
|
|
57
57
|
"keywords": [
|
|
58
58
|
"remotion",
|