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:
+40
-1
@@ -3,7 +3,7 @@ use dialoguer::{Input, Select, theme::ColorfulTheme};
|
||||
use iroh_tickets::endpoint::EndpointTicket;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::cli::Cli;
|
||||
use crate::cli::{Cli, Quality};
|
||||
use crate::common::{bandwidth, config};
|
||||
use crate::{host, viewer};
|
||||
|
||||
@@ -27,6 +27,9 @@ pub async fn run(cli: Cli) -> Result<()> {
|
||||
if cli.app.is_none() {
|
||||
cli.app = pick_app(&theme)?;
|
||||
}
|
||||
if cli.quality.is_none() {
|
||||
cli.quality = Some(pick_quality(&theme)?);
|
||||
}
|
||||
host::run(cli.into_host_opts(true)).await
|
||||
}
|
||||
_ => {
|
||||
@@ -80,6 +83,42 @@ fn pick_app(theme: &ColorfulTheme) -> Result<Option<String>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Picker for the encode quality preset. Mirrors [`pick_app`]; bypassed when
|
||||
/// `--quality` was given on the CLI. Quality is host-global (the same stream
|
||||
/// fans out to every viewer), so this is the one choice that sets it for all.
|
||||
fn pick_quality(theme: &ColorfulTheme) -> Result<Quality> {
|
||||
eprintln!();
|
||||
eprintln!("Quality");
|
||||
eprintln!("───────");
|
||||
eprintln!("Lower presets trade resolution + bitrate for less upload usage.");
|
||||
eprintln!("The same quality is sent to every viewer.");
|
||||
eprintln!();
|
||||
|
||||
// Order mirrors the labels below; index maps back to a Quality.
|
||||
let choices = [
|
||||
Quality::Auto,
|
||||
Quality::Source,
|
||||
Quality::High,
|
||||
Quality::Medium,
|
||||
Quality::Low,
|
||||
];
|
||||
let items = [
|
||||
"Auto — pick from my measured upstream (recommended)",
|
||||
"Source — native resolution, 6000 kbps",
|
||||
"High — up to 1080p, 4000 kbps",
|
||||
"Medium — up to 720p, 2500 kbps",
|
||||
"Low — up to 480p, 1000 kbps",
|
||||
];
|
||||
|
||||
let choice = Select::with_theme(theme)
|
||||
.with_prompt("What quality should the viewer(s) get?")
|
||||
.items(&items)
|
||||
.default(0)
|
||||
.interact()?;
|
||||
|
||||
Ok(choices[choice])
|
||||
}
|
||||
|
||||
/// `pixelpass --reconfigure` entry point: unconditionally re-run the
|
||||
/// bandwidth pre-flight test, save the result, and return. Used to
|
||||
/// refresh a stale measurement (e.g. user moved house, changed ISP).
|
||||
|
||||
Reference in New Issue
Block a user