From b0ff20fe3f37ae81b853fe0c0a76b2f5b0badac5 Mon Sep 17 00:00:00 2001 From: Mollusk Date: Fri, 3 Jul 2026 20:29:44 -0400 Subject: [PATCH] host/x11: default to XDamage capture; drop --untimed from viewers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X11 full-desktop capture used `ximagesrc use-damage=false`, which copies the whole root window every frame. On servers without working MIT-SHM (and CPU-bound everywhere else) this collapses to ~1 fps — a field test over an xlibre host played back at roughly one frame per minute. Default to `use-damage=true` (XDamage re-grabs only changed regions); keep `PIXELPASS_X11_NO_DAMAGE=1` as an escape hatch for driver artifacts. Also drop `--untimed` from both mpv invocations (viewer banner + the interactive launcher). `--untimed` displays each frame as it decodes and ignores audio timestamps, which drifts a shared *video* progressively out of sync with its audio. Pacing to the audio clock keeps A/V synced at a negligible latency cost. Co-Authored-By: Claude Opus 4.8 --- src/host/audio.rs | 4 +++- src/host/mod.rs | 10 ++++++++-- src/host/x11.rs | 18 ++++++++++++++---- src/interactive.rs | 3 ++- src/repair.rs | 4 +++- src/viewer/mod.rs | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/host/audio.rs b/src/host/audio.rs index 8d6b6ac..8eaa342 100644 --- a/src/host/audio.rs +++ b/src/host/audio.rs @@ -280,7 +280,9 @@ impl Drop for Routing { /// initial `lost`. In every other mode (whole-desktop, or best-effort app /// filtering) the loopback keeps audio flowing from the outset, so there is no /// initial gap to report. Pure: no I/O, so the emit decision is unit-testable. -pub(super) fn initial_app_audio_state(opts: &HostOpts) -> Option { +pub(super) fn initial_app_audio_state( + opts: &HostOpts, +) -> Option { (opts.app.is_some() && opts.strict_audio).then_some(crate::common::output::AppAudioState::Lost) } diff --git a/src/host/mod.rs b/src/host/mod.rs index ddaeaa0..bde0594 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -523,7 +523,10 @@ mod tests { #[test] fn capture_summary_reflects_audio_mode() { - assert_eq!(capture_summary(&opts(None, false)), "fullscreen + system-audio"); + assert_eq!( + capture_summary(&opts(None, false)), + "fullscreen + system-audio" + ); assert_eq!( capture_summary(&opts(Some("Firefox"), false)), "fullscreen + app-audio=Firefox" @@ -533,7 +536,10 @@ mod tests { capture_summary(&opts(Some("Firefox"), true)), "fullscreen + app-audio=Firefox (strict)" ); - assert_eq!(capture_summary(&opts(None, true)), "fullscreen + system-audio"); + assert_eq!( + capture_summary(&opts(None, true)), + "fullscreen + system-audio" + ); } #[test] diff --git a/src/host/x11.rs b/src/host/x11.rs index 45198d6..bf1b234 100644 --- a/src/host/x11.rs +++ b/src/host/x11.rs @@ -37,12 +37,22 @@ pub async fn start(opts: &HostOpts, quality: &EffectiveQuality) -> Result crate::common::process::spawn_detached( "mpv", &[ + // No `--untimed`: it ignores audio timestamps and drifts a + // shared video out of sync. Pacing to audio keeps A/V synced. "--profile=low-latency", - "--untimed", "--hwdec=auto", "--audio-buffer=0.2", "--demuxer-max-bytes=2M", diff --git a/src/repair.rs b/src/repair.rs index aa6d81e..fc92e04 100644 --- a/src/repair.rs +++ b/src/repair.rs @@ -172,7 +172,9 @@ fn loopback_capture_pid(args: &str) -> Option { let from_source = extract_kv(args, "source") .and_then(|v| v.strip_prefix(SINK_NAME_PREFIX)) .and_then(|rest| rest.strip_suffix(".monitor")); - from_sink.or(from_source).and_then(|pid| pid.parse::().ok()) + from_sink + .or(from_source) + .and_then(|pid| pid.parse::().ok()) } fn extract_kv<'a>(args: &'a str, key: &str) -> Option<&'a str> { diff --git a/src/viewer/mod.rs b/src/viewer/mod.rs index 4e9ad00..8876b27 100644 --- a/src/viewer/mod.rs +++ b/src/viewer/mod.rs @@ -102,7 +102,7 @@ fn print_viewer_banner(url: &str) { eprintln!("│ Connected to host. Open the stream in your player:"); eprintln!("│"); eprintln!( - "│ mpv --profile=low-latency --untimed --hwdec=auto --audio-buffer=0.2 --demuxer-max-bytes=2M --demuxer-readahead-secs=0.5 {url}" + "│ mpv --profile=low-latency --hwdec=auto --audio-buffer=0.2 --demuxer-max-bytes=2M --demuxer-readahead-secs=0.5 {url}" ); eprintln!("│ vlc --network-caching=200 --live-caching=200 {url}"); eprintln!("│");