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:
@@ -9,9 +9,14 @@ import { spawn } from "child_process";
|
||||
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "fs";
|
||||
import { join, dirname } from "path";
|
||||
import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
|
||||
import { type GpuEncoder, getCachedGpuEncoder, getGpuEncoderName } from "../utils/gpuEncoder.js";
|
||||
import {
|
||||
type GpuEncoder,
|
||||
getCachedGpuEncoder,
|
||||
getGpuEncoderName,
|
||||
mapPresetForGpuEncoder,
|
||||
} from "../utils/gpuEncoder.js";
|
||||
import { type HdrTransfer, getHdrEncoderColorParams } from "../utils/hdr.js";
|
||||
import { runFfmpeg } from "../utils/runFfmpeg.js";
|
||||
import { formatFfmpegError, runFfmpeg } from "../utils/runFfmpeg.js";
|
||||
import type { EncoderOptions, EncodeResult, MuxResult } from "./chunkEncoder.types.js";
|
||||
|
||||
export type { EncoderOptions, EncodeResult, MuxResult } from "./chunkEncoder.types.js";
|
||||
@@ -110,7 +115,7 @@ export function buildEncoderArgs(
|
||||
|
||||
switch (gpuEncoder) {
|
||||
case "nvenc":
|
||||
args.push("-preset", preset);
|
||||
args.push("-preset", mapPresetForGpuEncoder("nvenc", preset));
|
||||
if (bitrate) args.push("-b:v", bitrate);
|
||||
else args.push("-cq", String(quality));
|
||||
break;
|
||||
@@ -129,7 +134,7 @@ export function buildEncoderArgs(
|
||||
else args.push("-qp", String(quality));
|
||||
break;
|
||||
case "qsv":
|
||||
args.push("-preset", preset);
|
||||
args.push("-preset", mapPresetForGpuEncoder("qsv", preset));
|
||||
if (bitrate) args.push("-b:v", bitrate);
|
||||
else args.push("-global_quality", String(quality));
|
||||
break;
|
||||
@@ -320,7 +325,7 @@ export async function encodeFramesFromDir(
|
||||
durationMs,
|
||||
framesEncoded: 0,
|
||||
fileSize: 0,
|
||||
error: `FFmpeg exited with code ${code}`,
|
||||
error: formatFfmpegError(code, stderr),
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -522,11 +527,7 @@ export async function muxVideoWithAudio(
|
||||
success: result.success,
|
||||
outputPath,
|
||||
durationMs: result.durationMs,
|
||||
error: !result.success
|
||||
? result.exitCode !== null
|
||||
? `FFmpeg exited with code ${result.exitCode}`
|
||||
: `[FFmpeg] ${result.stderr}`
|
||||
: undefined,
|
||||
error: !result.success ? formatFfmpegError(result.exitCode, result.stderr) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -559,10 +560,6 @@ export async function applyFaststart(
|
||||
success: result.success,
|
||||
outputPath,
|
||||
durationMs: result.durationMs,
|
||||
error: !result.success
|
||||
? result.exitCode !== null
|
||||
? `FFmpeg exited with code ${result.exitCode}`
|
||||
: `[FFmpeg] ${result.stderr}`
|
||||
: undefined,
|
||||
error: !result.success ? formatFfmpegError(result.exitCode, result.stderr) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user