fix(encoder): ask for CBR — the capped-VBR triad was never a constant rate
The stream was configured as "@8 Mbps" and averaged 5.1. The cause is the argument set, not the machine and not YouTube: `-b:v K -maxrate K -bufsize 2K` is capped VBR. Measured on the bundled BtbN build with a static 640x480 frame at -b:v 2000k: default rate control 9,858 bytes / 8 s = 9 kbps -rc cbr 2,001,989 bytes / 8 s = 1,951 kbps -rc vbr 9,858 bytes / 8 s = 9 kbps On high-motion testsrc2 the same target produced 2006 kbps (CBR) against 1741 (VBR): VBR undershoots by however easy the picture is, and a composited scene with a still background is very easy. Spelling is per encoder, verified rather than assumed: - h264_nvenc, h264_amf: `-rc cbr`. - h264_qsv: nothing. It has no `-rc`, and libavcodec/qsvenc.c (select_rc_mode, FFmpeg d263fce2b2) selects MFX_RATECONTROL_CBR when bitrate == maxrate — which the triad already sets. Passing -rc here would be an unknown option. - libopenh264: nothing. `-rc_mode bitrate` changed no measurable output on this build (3,832 vs 3,830 bytes), so it stays out rather than adding a line whose effect nobody can demonstrate. Reference: YouTube's encoder spec requires CBR (support.google.com/youtube/answer/2853702). 668 tests pass, build 0 warnings.
This commit is contained in:
@@ -77,9 +77,29 @@ public static class FfmpegArgs
|
||||
args.AddRange(["-vf", $"scale={options.Width}:{options.Height}:flags=bilinear,setsar=1"]);
|
||||
}
|
||||
args.AddRange(["-c:v", videoEncoder]);
|
||||
AddRateControl(args, videoEncoder);
|
||||
args.AddRange(["-b:v", $"{options.BitrateKbps}k", "-maxrate", $"{options.BitrateKbps}k",
|
||||
"-bufsize", $"{options.BitrateKbps * 2}k"]);
|
||||
args.AddRange(["-g", gop.ToString(), "-keyint_min", gop.ToString(),
|
||||
"-sc_threshold", "0", "-bf", "0", "-pix_fmt", "yuv420p"]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constant bitrate, spelled the one way each encoder understands.
|
||||
///
|
||||
/// <para><b>Why this is not a shared flag:</b> the <c>-b:v/-maxrate/-bufsize</c> triad is capped VBR,
|
||||
/// not CBR — measured 2026-11-12 on the bundled BtbN build, a static frame at <c>-b:v 2000k</c>
|
||||
/// produced <b>9 kbps</b> with NVENC's default rate control and <b>1951 kbps</b> with <c>-rc cbr</c>.
|
||||
/// That is the gap behind an @8 stream that averaged 5.1 Mbps.</para>
|
||||
///
|
||||
/// <para>Per encoder: NVENC and AMF take an explicit mode. QSV has no <c>-rc</c> and already selects
|
||||
/// CBR when <c>bitrate == maxrate</c> (<c>libavcodec/qsvenc.c</c>, <c>select_rc_mode</c>). OpenH264's
|
||||
/// <c>-rc_mode bitrate</c> made no measurable difference on this build, so it is deliberately not
|
||||
/// passed. YouTube's encoder spec requires CBR (support.google.com/youtube/answer/2853702).</para>
|
||||
/// </summary>
|
||||
private static void AddRateControl(List<string> args, string videoEncoder)
|
||||
{
|
||||
if (videoEncoder is "h264_nvenc" or "h264_amf")
|
||||
args.AddRange(["-rc", "cbr"]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user