cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix(capture): close teardown edge cases

Dami committed 3d722d9
Validate probe thresholds before source startup, always terminate test-pattern ffmpeg before joining, and prove cross-state external-CQP contention in hardware UAT.
commit
3d722d9f3bc86092ac718040bc8d51997b339f52
parent
8b596c4de1d71ba3f1ccf36a719fb9767ca9bc14
author
Dami <dami.etoile@gmail.com>
3 files changed, +41 -13
Mcrates/sigil-host/src/main.rs39++++++++++++++++++++++++------
Mcrates/sigil-host/src/source/mod.rs4+--
Mscripts/run-bazzite-hardware-uat.sh11+++++++--
M crates/sigil-host/src/main.rs +31 -8
@@ -622,6 +622,7 @@ async fn probe_test_pattern(args: CaptureProbeArgs) -> Result<()> {
622622 config.validate()?;623623 624624 let configured_framerate = config.framerate;625+ validate_capture_probe_args(&args, configured_framerate)?;625626 let source = source::spawn_test_pattern(config, clock::SessionClock::start());626627 consume_capture_probe(args, source, None, "test-pattern", configured_framerate).await627628 }
@@ -659,6 +660,7 @@ async fn probe_gamescope_pipewire(args: CaptureProbeArgs) -> Result<()> {
659660 observed.0, observed.1, preflight.video_mode.framerate660661 );661662 let configured_framerate = preflight.video_mode.framerate;663+ validate_capture_probe_args(&args, configured_framerate)?;662664 let source::ProbeEncodedSource { source, shutdown } =663665 source::spawn_gamescope_pipewire_for_probe(664666 config,
@@ -682,14 +684,7 @@ async fn consume_capture_probe(
682684 source_name: &str,683685 configured_framerate: u32,684686 ) -> Result<()> {685- if let Some(minimum_fps) = args.minimum_fps {686- ensure!(687- minimum_fps.is_finite()688- && minimum_fps > 0.0689- && minimum_fps <= f64::from(configured_framerate),690- "--minimum-fps must be finite, positive, and no greater than configured framerate {configured_framerate}"691- );692- }687+ debug_assert!(validate_capture_probe_args(&args, configured_framerate).is_ok());693688 let source::EncodedSource {694689 frames: mut receiver,695690 current_gop,
@@ -778,6 +773,18 @@ async fn consume_capture_probe(
778773 Ok(())779774 }780775 776+fn validate_capture_probe_args(args: &CaptureProbeArgs, configured_framerate: u32) -> Result<()> {777+ if let Some(minimum_fps) = args.minimum_fps {778+ ensure!(779+ minimum_fps.is_finite()780+ && minimum_fps > 0.0781+ && minimum_fps <= f64::from(configured_framerate),782+ "--minimum-fps must be finite, positive, and no greater than configured framerate {configured_framerate}"783+ );784+ }785+ Ok(())786+}787+781788 async fn serve_command(args: ServeArgs) -> Result<()> {782789 let configured_service = args.config.is_some();783790 let (config, loaded_config_revision, _lifecycle) = if let Some(config_path) = &args.config {
@@ -1129,6 +1136,22 @@ mod tests {
11291136 assert!(parse_size("1280").is_err());11301137 }11311138 1139+ #[test]1140+ fn capture_probe_threshold_is_validated_before_source_start() {1141+ let args = |minimum_fps| CaptureProbeArgs {1142+ source: SourceArg::TestPattern,1143+ frames: 1,1144+ expect_size: None,1145+ minimum_fps,1146+ config: None,1147+ };1148+ assert!(validate_capture_probe_args(&args(None), 60).is_ok());1149+ assert!(validate_capture_probe_args(&args(Some(55.0)), 60).is_ok());1150+ assert!(validate_capture_probe_args(&args(Some(0.0)), 60).is_err());1151+ assert!(validate_capture_probe_args(&args(Some(61.0)), 60).is_err());1152+ assert!(validate_capture_probe_args(&args(Some(f64::NAN)), 60).is_err());1153+ }1154+11321155 #[test]11331156 fn direct_serve_requires_identity_and_source() {11341157 assert!(Cli::try_parse_from(["sigil", "serve"]).is_err());
M crates/sigil-host/src/source/mod.rs +1 -3
M scripts/run-bazzite-hardware-uat.sh +9 -2
@@ -182,6 +182,7 @@ timeout --signal=TERM --kill-after=2s 5s "$probe" --version
182182 host_identity="$private/host.key"183183 probe_identity="$private/probe.key"184184 state="$private/state"185+external_cqp_state="$private/external-cqp-state"185186 fixed_config="$private/host-1280x800.toml"186187 external_cqp_config="$private/host-1280x800-external-cqp.toml"187188 native_config="$private/host-native.toml"
@@ -253,7 +254,7 @@ EOF
253254 254255 cat >"$external_cqp_config" <<EOF255256 identity_path = "$host_identity"256-state_path = "$state"257+state_path = "$external_cqp_state"257258 source = "gamescope-pipewire"258259 width = 1280259260 height = 800
@@ -731,8 +732,11 @@ native_unique_sessions="$(awk -F= '$1 == "session_id" { print $2 }' \
731732 [[ "$fixed_unique_sessions" -eq 20 && "$native_unique_sessions" -eq 20 ]]732733 total_sessions="${#all_raw_probes[@]}"733734 fixed_observed_fps="$(sed -n 's/^observed_fps=//p' "$evidence/fixed-capture.log")"735+external_cqp_observed_fps="$(sed -n 's/^observed_fps=//p' \736+ "$evidence/external-cqp-capture.log")"734737 native_observed_fps="$(sed -n 's/^observed_fps=//p' "$evidence/native-capture.log")"735-[[ -n "$fixed_observed_fps" && -n "$native_observed_fps" ]]738+[[ -n "$fixed_observed_fps" && -n "$external_cqp_observed_fps" \739+ && -n "$native_observed_fps" ]]736740 if awk -v fps="$native_observed_fps" 'BEGIN { exit !(fps >= 55) }'; then737741 native_55fps_status=met738742 else
@@ -746,6 +750,9 @@ fi
746750 echo fixed_size=1280x800747751 echo fixed_minimum_fps=55748752 echo "fixed_observed_fps=$fixed_observed_fps"753+ echo external_cqp_minimum_fps=55754+ echo "external_cqp_observed_fps=$external_cqp_observed_fps"755+ echo cross_state_capture_contention=rejected749756 echo "native_size=$native_size"750757 echo native_performance_gate=observational751758 echo "native_observed_fps=$native_observed_fps"