mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
fix(engine): accept libx264 preset names with NVENC and QSV (#442)
NVENC rejects the libx264 preset vocabulary (ultrafast / medium / slow /
...) with AVERROR(EINVAL) ("Error applying encoder options: Invalid
argument"), which surfaces as a bare `FFmpeg exited with code -22` from
spawn(). Because ENCODER_PRESETS passes these names straight through to
h264_nvenc / hevc_nvenc, every `--gpu` render using the `draft` tier
failed; `standard` (medium) and `high` (slow) only worked coincidentally
on ffmpeg builds that happened to accept those aliases. QSV has the same
problem on a narrower set (ultrafast / superfast / placebo).
Add `mapPresetForGpuEncoder` in utils/gpuEncoder.ts that translates the
libx264 vocabulary to each encoder's native names:
- nvenc: libx264 -> p1..p7 (already-native pN values pass through);
unknown values fall back to p4 (medium)
- qsv: ultrafast / superfast -> veryfast; placebo -> veryslow;
everything else passes through
- videotoolbox / vaapi / null: unchanged
Both buildEncoderArgs (chunkEncoder.ts) and buildStreamingArgs
(streamingEncoder.ts) now route through the helper before pushing
`-preset` to the ffmpeg arg vector.
To make the next encoder-options failure diagnosable without re-running
ffmpeg by hand, \`formatFfmpegError\` in utils/runFfmpeg.ts now appends
the last 15 non-empty stderr lines to the error string. The four call
sites that previously swallowed stderr (encodeFramesFromDir,
muxVideoWithAudio, applyFaststart, and the streaming encoder exit
handler) have been updated.
Tested end-to-end on an RTX 4080 with ffmpeg 8.1 NVENC across
\`--quality draft|standard|high\` plus \`--video-bitrate\` and \`--crf\`
overrides; the 6 renders were visually equivalent to the CPU baseline.
Co-authored-by: roi32 <75878108+roi32@users.noreply.github.com>
This commit is contained in:
@@ -148,6 +148,69 @@ describe("buildEncoderArgs anti-banding", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildEncoderArgs GPU preset mapping", () => {
|
||||
const baseOptions = { fps: 30, width: 1920, height: 1080 };
|
||||
const inputArgs = ["-framerate", "30", "-i", "frames/%04d.png"];
|
||||
|
||||
function presetArg(args: string[]): string | undefined {
|
||||
const idx = args.indexOf("-preset");
|
||||
return idx === -1 ? undefined : args[idx + 1];
|
||||
}
|
||||
|
||||
// Regression for the "draft quality + --gpu fails with code -22" bug:
|
||||
// NVENC rejects the libx264 preset name `ultrafast` with AVERROR(EINVAL),
|
||||
// so the `draft` quality tier must not forward that value to h264_nvenc.
|
||||
it("translates the draft ultrafast preset to NVENC p1", () => {
|
||||
const args = buildEncoderArgs(
|
||||
{ ...baseOptions, codec: "h264", preset: "ultrafast", quality: 28, useGpu: true },
|
||||
inputArgs,
|
||||
"out.mp4",
|
||||
"nvenc",
|
||||
);
|
||||
expect(presetArg(args)).toBe("p1");
|
||||
});
|
||||
|
||||
it("translates the standard medium preset to NVENC p4", () => {
|
||||
const args = buildEncoderArgs(
|
||||
{ ...baseOptions, codec: "h264", preset: "medium", quality: 18, useGpu: true },
|
||||
inputArgs,
|
||||
"out.mp4",
|
||||
"nvenc",
|
||||
);
|
||||
expect(presetArg(args)).toBe("p4");
|
||||
});
|
||||
|
||||
it("translates the high slow preset to NVENC p5", () => {
|
||||
const args = buildEncoderArgs(
|
||||
{ ...baseOptions, codec: "h264", preset: "slow", quality: 15, useGpu: true },
|
||||
inputArgs,
|
||||
"out.mp4",
|
||||
"nvenc",
|
||||
);
|
||||
expect(presetArg(args)).toBe("p5");
|
||||
});
|
||||
|
||||
it("rewrites QSV's unsupported ultrafast preset to veryfast", () => {
|
||||
const args = buildEncoderArgs(
|
||||
{ ...baseOptions, codec: "h264", preset: "ultrafast", quality: 28, useGpu: true },
|
||||
inputArgs,
|
||||
"out.mp4",
|
||||
"qsv",
|
||||
);
|
||||
expect(presetArg(args)).toBe("veryfast");
|
||||
});
|
||||
|
||||
it("passes QSV-supported preset names through unchanged", () => {
|
||||
const args = buildEncoderArgs(
|
||||
{ ...baseOptions, codec: "h264", preset: "medium", quality: 23, useGpu: true },
|
||||
inputArgs,
|
||||
"out.mp4",
|
||||
"qsv",
|
||||
);
|
||||
expect(presetArg(args)).toBe("medium");
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildEncoderArgs color space", () => {
|
||||
const baseOptions = { fps: 30, width: 1920, height: 1080 };
|
||||
const inputArgs = ["-framerate", "30", "-i", "frames/%04d.png"];
|
||||
|
||||
Reference in New Issue
Block a user