403 lines
18 KiB
JavaScript
403 lines
18 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.benchmarkCommand = void 0;
|
||
const renderer_1 = require("@remotion/renderer");
|
||
const client_1 = require("@remotion/renderer/client");
|
||
const no_react_1 = require("remotion/no-react");
|
||
const browser_download_bar_1 = require("./browser-download-bar");
|
||
const chalk_1 = require("./chalk");
|
||
const cleanup_before_quit_1 = require("./cleanup-before-quit");
|
||
const config_1 = require("./config");
|
||
const preview_server_1 = require("./config/preview-server");
|
||
const convert_entry_point_to_serve_url_1 = require("./convert-entry-point-to-serve-url");
|
||
const entry_point_1 = require("./entry-point");
|
||
const get_cli_options_1 = require("./get-cli-options");
|
||
const image_formats_1 = require("./image-formats");
|
||
const log_1 = require("./log");
|
||
const make_progress_bar_1 = require("./make-progress-bar");
|
||
const parsed_cli_1 = require("./parsed-cli");
|
||
const progress_bar_1 = require("./progress-bar");
|
||
const setup_cache_1 = require("./setup-cache");
|
||
const should_use_non_overlaying_logger_1 = require("./should-use-non-overlaying-logger");
|
||
const show_compositions_picker_1 = require("./show-compositions-picker");
|
||
const truthy_1 = require("./truthy");
|
||
const DEFAULT_RUNS = 3;
|
||
const { audioBitrateOption, x264Option, offthreadVideoCacheSizeInBytesOption, scaleOption, crfOption, jpegQualityOption, videoBitrateOption, enforceAudioOption, mutedOption, videoCodecOption, colorSpaceOption, disallowParallelEncodingOption, enableMultiprocessOnLinuxOption, glOption, numberOfGifLoopsOption, encodingMaxRateOption, encodingBufferSizeOption, delayRenderTimeoutInMillisecondsOption, headlessOption, overwriteOption, binariesDirectoryOption, forSeamlessAacConcatenationOption, publicPathOption, publicDirOption, metadataOption, hardwareAccelerationOption, chromeModeOption, offthreadVideoThreadsOption, mediaCacheSizeInBytesOption, darkModeOption, askAIOption, experimentalClientSideRenderingOption, keyboardShortcutsOption, } = client_1.BrowserSafeApis.options;
|
||
const getValidConcurrency = (cliConcurrency) => {
|
||
const { concurrencies } = parsed_cli_1.parsedCli;
|
||
if (!concurrencies) {
|
||
return [renderer_1.RenderInternals.resolveConcurrency(cliConcurrency)];
|
||
}
|
||
return String(concurrencies)
|
||
.split(',')
|
||
.map((c) => parseInt(c.trim(), 10));
|
||
};
|
||
const runBenchmark = async (runs, options, onProgress) => {
|
||
const timeTaken = [];
|
||
for (let run = 0; run < runs; ++run) {
|
||
const startTime = performance.now();
|
||
await renderer_1.RenderInternals.internalRenderMedia({
|
||
onProgress: ({ progress }) => onProgress === null || onProgress === void 0 ? void 0 : onProgress(run, progress),
|
||
...options,
|
||
});
|
||
const endTime = performance.now();
|
||
timeTaken.push(endTime - startTime);
|
||
}
|
||
return timeTaken;
|
||
};
|
||
const formatTime = (time) => {
|
||
let ret = '';
|
||
const hours = Math.floor(time / (60 * 60 * 1000));
|
||
if (hours) {
|
||
ret = `${hours}h`;
|
||
}
|
||
time %= 60 * 60 * 1000;
|
||
const minutes = Math.floor(time / (60 * 1000));
|
||
if (minutes) {
|
||
ret = `${ret}${minutes}m`;
|
||
}
|
||
time %= 60 * 1000;
|
||
const seconds = (time / 1000).toFixed(5);
|
||
if (seconds) {
|
||
ret = `${ret}${seconds}s`;
|
||
}
|
||
return ret;
|
||
};
|
||
const avg = (time) => time.reduce((prev, curr) => prev + curr) / time.length;
|
||
const stdDev = (time) => {
|
||
const mean = avg(time);
|
||
return Math.sqrt(time.map((x) => (x - mean) ** 2).reduce((a, b) => a + b) / time.length);
|
||
};
|
||
const getResults = (results, runs) => {
|
||
const mean = avg(results);
|
||
const dev = stdDev(results);
|
||
const max = Math.max(...results);
|
||
const min = Math.min(...results);
|
||
return ` Time (${chalk_1.chalk.green('mean')} ± ${chalk_1.chalk.green('σ')}): ${chalk_1.chalk.green(formatTime(mean))} ± ${chalk_1.chalk.green(formatTime(dev))}\n Range (${chalk_1.chalk.blue('min')} ... ${chalk_1.chalk.red('max')}): ${chalk_1.chalk.blue(formatTime(min))} ... ${chalk_1.chalk.red(formatTime(max))} \t ${chalk_1.chalk.gray(`${runs} runs`)}
|
||
`;
|
||
};
|
||
const makeBenchmarkProgressBar = ({ totalRuns, run, progress, doneIn, }) => {
|
||
const totalProgress = (run + progress) / totalRuns;
|
||
return [
|
||
`Rendering (${run + 1} out of ${totalRuns} runs)`,
|
||
(0, make_progress_bar_1.makeProgressBar)(totalProgress, false),
|
||
doneIn === null
|
||
? `${(totalProgress * 100).toFixed(2)}% `
|
||
: chalk_1.chalk.gray(doneIn),
|
||
].join(' ');
|
||
};
|
||
const benchmarkCommand = async (remotionRoot, args, logLevel) => {
|
||
var _a, _b, _c;
|
||
const runs = (_a = parsed_cli_1.parsedCli.runs) !== null && _a !== void 0 ? _a : DEFAULT_RUNS;
|
||
const { file, reason, remainingArgs } = (0, entry_point_1.findEntryPoint)({
|
||
args,
|
||
remotionRoot,
|
||
logLevel,
|
||
allowDirectory: true,
|
||
});
|
||
if (!file) {
|
||
log_1.Log.error({ indent: false, logLevel }, 'No entry file passed.');
|
||
log_1.Log.info({ indent: false, logLevel }, 'Pass an additional argument specifying the entry file');
|
||
log_1.Log.info({ indent: false, logLevel });
|
||
log_1.Log.info({ indent: false, logLevel }, `$ remotion benchmark <entry file>`);
|
||
process.exit(1);
|
||
}
|
||
const fullEntryPoint = (0, convert_entry_point_to_serve_url_1.convertEntryPointToServeUrl)(file);
|
||
const { inputProps, envVariables, browserExecutable, proResProfile, frameRange: defaultFrameRange, pixelFormat, everyNthFrame, ffmpegOverride, height, width, concurrency: unparsedConcurrency, disableWebSecurity, userAgent, ignoreCertificateErrors, } = (0, get_cli_options_1.getCliOptions)({
|
||
isStill: false,
|
||
logLevel,
|
||
indent: false,
|
||
});
|
||
log_1.Log.verbose({ indent: false, logLevel }, 'Entry point:', fullEntryPoint, 'reason:', reason);
|
||
const scale = scaleOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const enableMultiProcessOnLinux = enableMultiprocessOnLinuxOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const gl = glOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const headless = headlessOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const publicPath = publicPathOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const publicDir = publicDirOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const chromeMode = chromeModeOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const darkMode = darkModeOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const experimentalClientSideRenderingEnabled = experimentalClientSideRenderingOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const askAIEnabled = askAIOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
if (experimentalClientSideRenderingEnabled) {
|
||
log_1.Log.warn({ indent: false, logLevel }, 'Enabling WIP client-side rendering. Please see caveats on https://www.remotion.dev/docs/client-side-rendering/.');
|
||
}
|
||
const chromiumOptions = {
|
||
disableWebSecurity,
|
||
enableMultiProcessOnLinux,
|
||
gl,
|
||
headless,
|
||
ignoreCertificateErrors,
|
||
userAgent,
|
||
darkMode,
|
||
};
|
||
const onBrowserDownload = (0, browser_download_bar_1.defaultBrowserDownloadProgress)({
|
||
indent: false,
|
||
logLevel,
|
||
quiet: (0, parsed_cli_1.quietFlagProvided)(),
|
||
onProgress: () => undefined,
|
||
});
|
||
const indent = false;
|
||
await renderer_1.RenderInternals.internalEnsureBrowser({
|
||
browserExecutable,
|
||
indent,
|
||
logLevel,
|
||
onBrowserDownload,
|
||
chromeMode,
|
||
});
|
||
const browserInstance = renderer_1.RenderInternals.internalOpenBrowser({
|
||
browser: 'chrome',
|
||
browserExecutable,
|
||
chromiumOptions,
|
||
forceDeviceScaleFactor: scale,
|
||
indent,
|
||
viewport: null,
|
||
logLevel,
|
||
onBrowserDownload,
|
||
chromeMode,
|
||
});
|
||
const { urlOrBundle: bundleLocation, cleanup: cleanupBundle } = await (0, setup_cache_1.bundleOnCliOrTakeServeUrl)({
|
||
fullPath: fullEntryPoint,
|
||
publicDir,
|
||
remotionRoot,
|
||
onProgress: () => undefined,
|
||
indentOutput: false,
|
||
logLevel,
|
||
onDirectoryCreated: (dir) => {
|
||
(0, cleanup_before_quit_1.registerCleanupJob)(`Delete ${dir}`, () => renderer_1.RenderInternals.deleteDirectory(dir));
|
||
},
|
||
quietProgress: false,
|
||
quietFlag: (0, parsed_cli_1.quietFlagProvided)(),
|
||
outDir: null,
|
||
// Not needed for benchmark
|
||
gitSource: null,
|
||
bufferStateDelayInMilliseconds: null,
|
||
maxTimelineTracks: null,
|
||
publicPath,
|
||
audioLatencyHint: null,
|
||
experimentalClientSideRenderingEnabled,
|
||
askAIEnabled,
|
||
keyboardShortcutsEnabled,
|
||
});
|
||
(0, cleanup_before_quit_1.registerCleanupJob)(`Deleting bundle`, () => cleanupBundle());
|
||
const puppeteerInstance = await browserInstance;
|
||
const serializedInputPropsWithCustomSchema = no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
|
||
data: inputProps !== null && inputProps !== void 0 ? inputProps : {},
|
||
indent: undefined,
|
||
staticBase: null,
|
||
}).serializedString;
|
||
const comps = await renderer_1.RenderInternals.internalGetCompositions({
|
||
serveUrlOrWebpackUrl: bundleLocation,
|
||
serializedInputPropsWithCustomSchema,
|
||
envVariables,
|
||
chromiumOptions,
|
||
timeoutInMilliseconds: delayRenderTimeoutInMillisecondsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
port: (0, preview_server_1.getRendererPortFromConfigFileAndCliFlag)(),
|
||
puppeteerInstance,
|
||
browserExecutable,
|
||
indent: false,
|
||
onBrowserLog: null,
|
||
// Intentionally disabling server to not cache results
|
||
server: undefined,
|
||
logLevel,
|
||
offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytesOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
offthreadVideoThreads: offthreadVideoThreadsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
binariesDirectory: binariesDirectoryOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
onBrowserDownload,
|
||
chromeMode,
|
||
mediaCacheSizeInBytes: mediaCacheSizeInBytesOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
onLog: renderer_1.RenderInternals.defaultOnLog,
|
||
});
|
||
const ids = (remainingArgs[0]
|
||
? String(remainingArgs[0])
|
||
.split(',')
|
||
.map((c) => c.trim())
|
||
.filter(truthy_1.truthy)
|
||
: await (0, show_compositions_picker_1.showMultiCompositionsPicker)(comps, logLevel));
|
||
const compositions = ids.map((compId) => {
|
||
const composition = comps.find((c) => c.id === compId);
|
||
if (!composition) {
|
||
throw new Error(`No composition with the ID "${compId}" found.`);
|
||
}
|
||
return composition;
|
||
});
|
||
if (compositions.length === 0) {
|
||
log_1.Log.error({ indent: false, logLevel }, 'No composition IDs passed. Add another argument to the command specifying at least 1 composition ID.');
|
||
}
|
||
const benchmark = {};
|
||
let count = 1;
|
||
const x264Preset = x264Option.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const audioBitrate = audioBitrateOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const configFileCrf = crfOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const jpegQuality = jpegQualityOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const videoBitrate = videoBitrateOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const enforceAudioTrack = enforceAudioOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const muted = mutedOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
const disallowParallelEncoding = disallowParallelEncodingOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const numberOfGifLoops = numberOfGifLoopsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const encodingMaxRate = encodingMaxRateOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const encodingBufferSize = encodingBufferSizeOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const delayRenderInMilliseconds = delayRenderTimeoutInMillisecondsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value;
|
||
const overwrite = overwriteOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}, true).value;
|
||
const metadata = metadataOption.getValue({ commandLine: parsed_cli_1.parsedCli }).value;
|
||
for (const composition of compositions) {
|
||
const { value: videoCodec, source: codecReason } = videoCodecOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}, {
|
||
downloadName: null,
|
||
outName: null,
|
||
configFile: (_b = config_1.ConfigInternals.getOutputCodecOrUndefined()) !== null && _b !== void 0 ? _b : null,
|
||
uiCodec: null,
|
||
compositionCodec: (_c = composition.defaultCodec) !== null && _c !== void 0 ? _c : null,
|
||
});
|
||
const concurrency = getValidConcurrency(unparsedConcurrency);
|
||
benchmark[composition.id] = {};
|
||
for (const con of concurrency) {
|
||
const benchmarkProgress = (0, progress_bar_1.createOverwriteableCliOutput)({
|
||
quiet: (0, parsed_cli_1.quietFlagProvided)(),
|
||
cancelSignal: null,
|
||
updatesDontOverwrite: (0, should_use_non_overlaying_logger_1.shouldUseNonOverlayingLogger)({ logLevel }),
|
||
indent: false,
|
||
});
|
||
log_1.Log.info({ indent: false, logLevel });
|
||
log_1.Log.info({ indent: false, logLevel }, `${chalk_1.chalk.bold(`Benchmark #${count++}:`)} ${chalk_1.chalk.gray(`composition=${composition.id} concurrency=${con} codec=${videoCodec} (${codecReason})`)}`);
|
||
const timeTaken = await runBenchmark(runs, {
|
||
outputLocation: null,
|
||
composition: {
|
||
...composition,
|
||
width: width !== null && width !== void 0 ? width : composition.width,
|
||
height: height !== null && height !== void 0 ? height : composition.height,
|
||
},
|
||
crf: configFileCrf !== null && configFileCrf !== void 0 ? configFileCrf : null,
|
||
envVariables,
|
||
frameRange: defaultFrameRange,
|
||
imageFormat: (0, image_formats_1.getVideoImageFormat)({
|
||
codec: videoCodec,
|
||
uiImageFormat: null,
|
||
}),
|
||
serializedInputPropsWithCustomSchema,
|
||
overwrite,
|
||
pixelFormat,
|
||
proResProfile,
|
||
x264Preset,
|
||
jpegQuality,
|
||
chromiumOptions,
|
||
timeoutInMilliseconds: delayRenderInMilliseconds,
|
||
scale,
|
||
port: (0, preview_server_1.getRendererPortFromConfigFileAndCliFlag)(),
|
||
numberOfGifLoops,
|
||
everyNthFrame,
|
||
logLevel,
|
||
muted,
|
||
enforceAudioTrack,
|
||
browserExecutable,
|
||
ffmpegOverride,
|
||
serveUrl: bundleLocation,
|
||
codec: videoCodec,
|
||
audioBitrate,
|
||
videoBitrate,
|
||
encodingMaxRate,
|
||
encodingBufferSize,
|
||
puppeteerInstance,
|
||
concurrency: con,
|
||
audioCodec: null,
|
||
cancelSignal: undefined,
|
||
disallowParallelEncoding,
|
||
indent: false,
|
||
onBrowserLog: null,
|
||
onCtrlCExit: () => undefined,
|
||
onDownload: () => undefined,
|
||
onStart: () => undefined,
|
||
preferLossless: false,
|
||
server: undefined,
|
||
serializedResolvedPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
|
||
data: composition.props,
|
||
indent: undefined,
|
||
staticBase: null,
|
||
}).serializedString,
|
||
offthreadVideoThreads: offthreadVideoThreadsOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytesOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
colorSpace: colorSpaceOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
repro: false,
|
||
binariesDirectory: binariesDirectoryOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
separateAudioTo: null,
|
||
forSeamlessAacConcatenation: forSeamlessAacConcatenationOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
compositionStart: 0,
|
||
onBrowserDownload,
|
||
onArtifact: () => undefined,
|
||
metadata,
|
||
hardwareAcceleration: hardwareAccelerationOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
chromeMode,
|
||
mediaCacheSizeInBytes: mediaCacheSizeInBytesOption.getValue({
|
||
commandLine: parsed_cli_1.parsedCli,
|
||
}).value,
|
||
onLog: renderer_1.RenderInternals.defaultOnLog,
|
||
licenseKey: null,
|
||
isProduction: null,
|
||
}, (run, progress) => {
|
||
benchmarkProgress.update(makeBenchmarkProgressBar({
|
||
totalRuns: runs,
|
||
run,
|
||
doneIn: null,
|
||
progress,
|
||
}), false);
|
||
});
|
||
benchmarkProgress.update('', false);
|
||
benchmarkProgress.update(getResults(timeTaken, runs), false);
|
||
benchmark[composition.id][`${con}`] =
|
||
timeTaken;
|
||
}
|
||
}
|
||
log_1.Log.info({ indent: false, logLevel });
|
||
};
|
||
exports.benchmarkCommand = benchmarkCommand;
|