@remotion/renderer
Version:
Render Remotion videos using Node.js or Bun
134 lines (133 loc) • 5.28 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeAudioTrack = void 0;
const node_path_1 = __importDefault(require("node:path"));
const call_ffmpeg_1 = require("./call-ffmpeg");
const chunk_1 = require("./chunk");
const create_ffmpeg_complex_filter_1 = require("./create-ffmpeg-complex-filter");
const create_ffmpeg_merge_filter_1 = require("./create-ffmpeg-merge-filter");
const create_silent_audio_1 = require("./create-silent-audio");
const delete_directory_1 = require("./delete-directory");
const logger_1 = require("./logger");
const p_limit_1 = require("./p-limit");
const parse_ffmpeg_progress_1 = require("./parse-ffmpeg-progress");
const tmp_dir_1 = require("./tmp-dir");
const truthy_1 = require("./truthy");
const mergeAudioTrackUnlimited = async ({ outName, files, downloadMap, remotionRoot, indent, logLevel, binariesDirectory, cancelSignal, onProgress, fps, chunkLengthInSeconds, }) => {
var _a;
if (files.length === 0) {
await (0, create_silent_audio_1.createSilentAudio)({
outName,
chunkLengthInSeconds,
indent,
logLevel,
binariesDirectory,
cancelSignal,
});
onProgress(1);
return;
}
// Previously a bug: We cannot optimize for files.length === 1 because we need to pad the audio
// In FFMPEG, the total number of left and right tracks that can be merged at one time is limited to 64
if (files.length >= 32) {
const chunked = (0, chunk_1.chunk)(files, 10);
const tempPath = (0, tmp_dir_1.tmpDir)('remotion-large-audio-mixing');
try {
const partialProgress = new Array(chunked.length).fill(0);
let finalProgress = 0;
const callProgress = () => {
const totalProgress = partialProgress.reduce((a, b) => a + b, 0) / chunked.length;
const combinedProgress = totalProgress * 0.8 + finalProgress * 0.2;
onProgress(combinedProgress);
};
const chunkNames = await Promise.all(chunked.map(async (chunkFiles, i) => {
const chunkOutname = node_path_1.default.join(tempPath, `chunk-${i}.wav`);
await (0, exports.mergeAudioTrack)({
files: chunkFiles,
chunkLengthInSeconds,
outName: chunkOutname,
downloadMap,
remotionRoot,
indent,
logLevel,
binariesDirectory,
cancelSignal,
onProgress: (progress) => {
partialProgress[i] = progress;
callProgress();
},
fps,
});
return chunkOutname;
}));
await (0, exports.mergeAudioTrack)({
files: chunkNames.map((c) => ({
filter: {
pad_end: null,
pad_start: null,
},
outName: c,
})),
outName,
downloadMap,
remotionRoot,
indent,
logLevel,
binariesDirectory,
cancelSignal,
onProgress: (progress) => {
finalProgress = progress;
callProgress();
},
fps,
chunkLengthInSeconds,
});
return;
}
finally {
(0, delete_directory_1.deleteDirectory)(tempPath);
}
}
const { complexFilterFlag: mergeFilter, cleanup, complexFilter, } = await (0, create_ffmpeg_complex_filter_1.createFfmpegComplexFilter)({
filters: files,
downloadMap,
});
const args = [
['-hide_banner'],
...files.map((f) => ['-i', f.outName]),
mergeFilter,
['-c:a', 'pcm_s16le'],
['-map', `[${create_ffmpeg_merge_filter_1.OUTPUT_FILTER_NAME}]`],
['-y', outName],
]
.filter(truthy_1.truthy)
.flat(2);
logger_1.Log.verbose({ indent, logLevel }, `Merging audio tracks`, 'Command:', `ffmpeg ${args.join(' ')}`, 'Complex filter script:', complexFilter);
const task = (0, call_ffmpeg_1.callFf)({
bin: 'ffmpeg',
args,
indent,
logLevel,
binariesDirectory,
cancelSignal,
});
(_a = task.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (data) => {
const utf8 = data.toString('utf8');
const parsed = (0, parse_ffmpeg_progress_1.parseFfmpegProgress)(utf8, fps);
if (parsed !== undefined) {
onProgress(parsed / (chunkLengthInSeconds * fps));
}
});
await task;
onProgress(1);
cleanup();
};
// Must be at least 3 because recursively called twice in mergeAudioTrack
const limit = (0, p_limit_1.pLimit)(3);
const mergeAudioTrack = (options) => {
return limit(mergeAudioTrackUnlimited, options);
};
exports.mergeAudioTrack = mergeAudioTrack;