feat(quality): retarget the ladder — 1080p60@12, 1080p30@10, 720p@6

Every 1080 tier carried 8 Mbps, inherited from TASK 4's "mainstream ceiling,
upload headroom stays comfortable". It was never derived from anything: the same
number served 30 fps and 60 fps, landscape and vertical, which no per-tier
derivation would produce. With CBR now actually holding the rate (700c7a8) that
number becomes the picture quality, so it has to be chosen rather than inherited.

New defaults sit between YouTube's H.264 recommendation (17 at 1080p60, 14 at
1080p30, 8 at 720p — support.google.com/youtube/answer/2853702) and the rate
practitioner OBS guides converge on for YouTube (9–12 Mbps at 1080p60). Vertical
mirrors its landscape pair: 1080x1920 is the same pixel count as 1920x1080.

The free-profile fallback in OutputProfile.Resolve moves with it (8.0 -> 10.0) so
a build with no shipped tiers still emits the 1080p30 rate rather than a stale
one.

668 tests (1 audio-pipe flake under full-suite load, passes alone), 0 warnings.
This commit is contained in:
2026-11-12 08:01:01 -08:00
parent 700c7a8c23
commit 1708667602
4 changed files with 20 additions and 12 deletions
+12 -4
View File
@@ -446,14 +446,22 @@ public partial class MainViewModel : ViewModelBase
private const double MasterFrameWidth = 1920;
private const double MasterFrameHeight = 1080;
/// <summary>
/// The shipped ladder. The rate on each tier is the creator-facing target the encoder is asked to
/// hold (CBR — see <c>FfmpegArgs.AddRateControl</c>), between YouTube's H.264 recommended value
/// (17 Mbps at 1080p60, 14 at 1080p30, 8 at 720p) and the practitioner sweet spot most guides land
/// on for YouTube. The old flat 8 Mbps was inherited from TASK 4 as a "mainstream ceiling" — an
/// unmeasured round number that produced a 5.1 Mbps average. Vertical mirrors its landscape pair:
/// 1080×1920 is the same pixel count as 1920×1080.
/// </summary>
public QualityOption[] QualityOptions { get; } =
{
new("1080p60", 60, 8.0, 1920, 1080),
new("1080p30", 30, 8.0, 1920, 1080),
new("1080p60", 60, 12.0, 1920, 1080),
new("1080p30", 30, 10.0, 1920, 1080),
new("720p60", 60, 6.0, 1280, 720),
new("720p30", 30, 6.0, 1280, 720),
new("Vertical 1080p30", 30, 8.0, 1080, 1920),
new("Vertical 1080p60", 60, 8.0, 1080, 1920),
new("Vertical 1080p30", 30, 10.0, 1080, 1920),
new("Vertical 1080p60", 60, 12.0, 1080, 1920),
};
public string ResolutionHelp { get; } =