feat(quality): resolution/quality presets + Auto from pre-flight
Add a host-global quality knob (Discord-style) so the sharer can trade resolution + bitrate for upload bandwidth. Quality is host-global by design: one encode pipeline fans out to every viewer, so per-viewer quality is out of scope (it would kill the broadcast fanout). - New `--quality source|high|medium|low|auto` (ValueEnum) bundling a (max-height, bitrate, fps) tuple per preset; `auto` derives the preset from the saved bandwidth pre-flight (safe_mbps / viewer cap), falling back to `medium` when unmeasured. Default is auto; the interactive Host branch shows a picker when --quality is omitted (mirrors pick_app). - `--max-height N` raw override; `--bitrate`/`--framerate` changed to Option so an explicit flag overrides just that field of the preset (precedence rule), leaving the rest of the preset intact. - host/quality.rs: Preset table + resolve(); pure resolve_auto() split from the config read for testability. 5 unit tests lock preset pass-through, the Auto ladder, the unmeasured fallback, and override precedence. - pipeline::build_args inserts `videoscale ! video/x-raw,height=N, pixel-aspect-ratio=1/1,width=[2,8192,2]` only for non-Source presets. PAR 1/1 forces a proportional downscale (without it videoscale keeps full width and squashes PAR — no bandwidth win); the even-stepped width range + even-rounded height satisfy H.264 4:2:0. EffectiveQuality is threaded capture -> wayland/x11 -> pipeline; max_viewers is now sized against the effective (post-preset) bitrate. - Banner gains a quality line (preset label + ≤Np/kbps/fps + provenance). - deps.rs checks `videoscale`; smoke-pipeline.sh adds a 1080->480 downscale check asserting an even width below source. - README: --quality preset table, Auto behavior, host-global note, --max-height/--bitrate/--framerate override precedence. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+48
-8
@@ -25,13 +25,25 @@ pub struct Cli {
|
||||
#[arg(long, value_enum)]
|
||||
pub display_server: Option<DisplayServerArg>,
|
||||
|
||||
/// Encode bitrate in kbps.
|
||||
#[arg(long, default_value_t = 6000)]
|
||||
pub bitrate: u32,
|
||||
/// Quality preset. Bundles a max video height, bitrate, and framerate.
|
||||
/// `auto` derives them from the saved bandwidth pre-flight (falls back to
|
||||
/// `medium` when no measurement exists). Defaults to `auto`; in the
|
||||
/// interactive menu, omitting this shows a picker instead.
|
||||
#[arg(long, value_enum)]
|
||||
pub quality: Option<Quality>,
|
||||
|
||||
/// Capture framerate.
|
||||
#[arg(long, default_value_t = 30)]
|
||||
pub framerate: u32,
|
||||
/// Cap the encoded video height (px); width follows the source aspect.
|
||||
/// Power-user override — takes precedence over the preset's height.
|
||||
#[arg(long, value_name = "N")]
|
||||
pub max_height: Option<u32>,
|
||||
|
||||
/// Encode bitrate in kbps. Overrides the quality preset's bitrate.
|
||||
#[arg(long)]
|
||||
pub bitrate: Option<u32>,
|
||||
|
||||
/// Capture framerate. Overrides the quality preset's framerate.
|
||||
#[arg(long)]
|
||||
pub framerate: Option<u32>,
|
||||
|
||||
/// Disable VAAPI HW encode; force software x264.
|
||||
#[arg(long)]
|
||||
@@ -71,13 +83,37 @@ pub enum DisplayServerArg {
|
||||
X11,
|
||||
}
|
||||
|
||||
/// Quality preset. Each fixed preset bundles a (max-height, bitrate, fps)
|
||||
/// tuple — resolution is a quality-per-bitrate knob, so the three only make
|
||||
/// sense together. `Auto` has no fixed tuple; it picks one of the others from
|
||||
/// the bandwidth pre-flight at host startup. See `host::quality`.
|
||||
#[derive(ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum Quality {
|
||||
/// Native source resolution, 6000 kbps, 30 fps (no downscale).
|
||||
Source,
|
||||
/// Up to 1080p, 4000 kbps, 30 fps.
|
||||
High,
|
||||
/// Up to 720p, 2500 kbps, 30 fps.
|
||||
Medium,
|
||||
/// Up to 480p, 1000 kbps, 30 fps.
|
||||
Low,
|
||||
/// Derive from the measured upstream; falls back to `medium` when unmeasured.
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct HostOpts {
|
||||
pub window: bool,
|
||||
pub app: Option<String>,
|
||||
pub display_server: Option<DisplayServerArg>,
|
||||
pub bitrate: u32,
|
||||
pub framerate: u32,
|
||||
/// Chosen preset (Auto = derive at startup). Defaults to Auto.
|
||||
pub quality: Quality,
|
||||
/// Raw `--bitrate` override (kbps); None = use the preset's bitrate.
|
||||
pub bitrate: Option<u32>,
|
||||
/// Raw `--framerate` override; None = use the preset's framerate.
|
||||
pub framerate: Option<u32>,
|
||||
/// Raw `--max-height` override (px); None = use the preset's height.
|
||||
pub max_height: Option<u32>,
|
||||
pub no_hwencode: bool,
|
||||
pub max_viewers: Option<u32>,
|
||||
pub interactive: bool,
|
||||
@@ -95,8 +131,12 @@ impl Cli {
|
||||
window: self.window,
|
||||
app: self.app,
|
||||
display_server: self.display_server,
|
||||
// No `--quality` and nothing picked interactively → the documented
|
||||
// default, Auto.
|
||||
quality: self.quality.unwrap_or(Quality::Auto),
|
||||
bitrate: self.bitrate,
|
||||
framerate: self.framerate,
|
||||
max_height: self.max_height,
|
||||
no_hwencode: self.no_hwencode,
|
||||
max_viewers: self.max_viewers,
|
||||
interactive,
|
||||
|
||||
Reference in New Issue
Block a user