From fb323cb5cc96f89a9aaa2aaa01188d511cdcfee9 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Sun, 26 Apr 2026 16:11:15 -0300 Subject: [PATCH] fix(sync): keep probe video on fresh pacing --- Cargo.lock | 6 ++-- client/Cargo.toml | 2 +- client/src/sync_probe/capture/runtime.rs | 45 +++++++++++++++++------- client/src/sync_probe/capture/tests.rs | 2 +- common/Cargo.toml | 2 +- server/Cargo.toml | 2 +- 6 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 36194ca..b6c6498 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.14.3" +version = "0.14.6" dependencies = [ "anyhow", "async-stream", @@ -1676,7 +1676,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.14.3" +version = "0.14.6" dependencies = [ "anyhow", "base64", @@ -1688,7 +1688,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.14.3" +version = "0.14.6" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index c7272f2..9d4639b 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.14.5" +version = "0.14.6" edition = "2024" [dependencies] diff --git a/client/src/sync_probe/capture/runtime.rs b/client/src/sync_probe/capture/runtime.rs index e5ce1fe..8dd4f65 100644 --- a/client/src/sync_probe/capture/runtime.rs +++ b/client/src/sync_probe/capture/runtime.rs @@ -1,15 +1,5 @@ use super::*; -pub(super) fn rebase_probe_packet_pts( - pts_rebaser: &crate::live_capture_clock::SourcePtsRebaser, - source_pts_us: u64, - lag_cap: Duration, -) -> u64 { - pts_rebaser - .rebase_with_lag_cap(Some(source_pts_us), 1, Some(lag_cap)) - .packet_pts_us -} - fn rebase_probe_audio_packet_pts( pts_rebaser: &crate::live_capture_clock::DurationPacedSourcePtsRebaser, source_pts_us: u64, @@ -19,6 +9,17 @@ fn rebase_probe_audio_packet_pts( pts_rebaser.rebase_with_packet_duration(Some(source_pts_us), packet_duration_us, lag_cap) } +#[cfg(test)] +pub(super) fn rebase_probe_packet_pts( + pts_rebaser: &crate::live_capture_clock::DurationPacedSourcePtsRebaser, + source_pts_us: u64, + lag_cap: Duration, +) -> u64 { + pts_rebaser + .rebase_with_packet_duration(Some(source_pts_us), 1, lag_cap) + .packet_pts_us +} + pub struct SyncProbeCapture { pipeline: gst::Pipeline, running: Arc, @@ -164,7 +165,7 @@ fn spawn_video_thread( queue: FreshPacketQueue, ) -> JoinHandle<()> { thread::spawn(move || { - let pts_rebaser = crate::live_capture_clock::SourcePtsRebaser::default(); + let pts_rebaser = crate::live_capture_clock::DurationPacedSourcePtsRebaser::default(); let lag_cap = crate::live_capture_clock::upstream_source_lag_cap(); let dark_frame = build_dark_probe_frame(camera.width as usize, camera.height as usize); let regular_pulse_frame = @@ -207,14 +208,24 @@ fn spawn_video_thread( break; } - if let Some(sample) = sink.try_pull_sample(gst::ClockTime::from_mseconds(250)) + if let Some(sample) = freshest_probe_video_sample(&sink) && let Some(buffer) = sample.buffer() && let Ok(map) = buffer.map_readable() { let source_pts_us = buffer.pts().unwrap_or(gst::ClockTime::ZERO).nseconds() / 1_000; + let packet_duration_us = buffer + .duration() + .map(|ts| (ts.nseconds() / 1_000).max(1)) + .unwrap_or(frame_step.as_micros().min(u64::MAX as u128) as u64); let packet = VideoPacket { id: 2, - pts: rebase_probe_packet_pts(&pts_rebaser, source_pts_us, lag_cap), + pts: pts_rebaser + .rebase_with_packet_duration( + Some(source_pts_us), + packet_duration_us, + lag_cap, + ) + .packet_pts_us, data: map.as_slice().to_vec(), ..Default::default() }; @@ -229,6 +240,14 @@ fn spawn_video_thread( }) } +fn freshest_probe_video_sample(sink: &gst_app::AppSink) -> Option { + let mut newest = sink.try_pull_sample(gst::ClockTime::from_mseconds(250)); + while let Some(sample) = sink.try_pull_sample(gst::ClockTime::ZERO) { + newest = Some(sample); + } + newest +} + fn spawn_audio_thread( schedule: PulseSchedule, duration: Duration, diff --git a/client/src/sync_probe/capture/tests.rs b/client/src/sync_probe/capture/tests.rs index eca4e62..56bd63d 100644 --- a/client/src/sync_probe/capture/tests.rs +++ b/client/src/sync_probe/capture/tests.rs @@ -143,7 +143,7 @@ fn probe_video_frames_render_distinct_idle_regular_and_marker_patterns() { #[test] fn probe_video_pts_are_lag_capped_like_audio() { - let rebaser = crate::live_capture_clock::SourcePtsRebaser::default(); + let rebaser = crate::live_capture_clock::DurationPacedSourcePtsRebaser::default(); let _first = super::runtime::rebase_probe_packet_pts(&rebaser, 1_000_000, Duration::from_millis(2)); std::thread::sleep(Duration::from_millis(8)); diff --git a/common/Cargo.toml b/common/Cargo.toml index b61759d..8496091 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.14.5" +version = "0.14.6" edition = "2024" build = "build.rs" diff --git a/server/Cargo.toml b/server/Cargo.toml index e6ef156..6a253f1 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.14.5" +version = "0.14.6" edition = "2024" autobins = false