diff --git a/src/cap_portal.rs b/src/cap_portal.rs index 2112e21..5332057 100644 --- a/src/cap_portal.rs +++ b/src/cap_portal.rs @@ -405,7 +405,14 @@ fn pipewire_thread(ctx: PwThreadCtx) { let format_info = format_info.clone(); let frame_tx = frame_tx.clone(); let dropped = dropped; + let process_count = Rc::new(Cell::new(0u64)); + let process_count_clone = process_count.clone(); move |stream, _| { + let count = process_count_clone.get() + 1; + process_count_clone.set(count); + if count <= 5 || count % 60 == 0 { + tracing::info!("PipeWire process callback #{count}"); + } // 从流中出队原始 buffer(包含帧数据的元信息) let raw_buf = unsafe { stream.dequeue_raw_buffer() }; if raw_buf.is_null() { diff --git a/src/fps_limit.rs b/src/fps_limit.rs index a617227..efdfd98 100644 --- a/src/fps_limit.rs +++ b/src/fps_limit.rs @@ -1,7 +1,8 @@ use std::time::{Duration, Instant}; pub struct FpsLimit { - on_deck: Option<(T, Instant)>, + on_deck: Option, + last_output_time: Option, min_interval: Duration, } @@ -9,30 +10,32 @@ impl FpsLimit { pub fn new(fps: u32) -> Self { Self { on_deck: None, + last_output_time: None, min_interval: Duration::from_secs_f64(1.0 / fps as f64), } } /// Feed a new frame. Returns: - /// - Some(previous_frame) if enough time elapsed since previous frame - /// - None if frame is buffered (first frame) or previous is dropped (too close) + /// - Some(()) if enough time elapsed since the last output — proceed to encode current frame + /// - None if too close to the last output — drop current frame pub fn on_new_frame(&mut self, frame: T, timestamp: Instant) -> Option { - let old = self.on_deck.replace((frame, timestamp)); - match old { - None => None, // First frame — buffer it - Some((old_frame, old_ts)) => { - if timestamp.duration_since(old_ts) >= self.min_interval { - Some(old_frame) // Enough time — output previous - } else { - None // Too close — discard previous, keep new - } - } + let ready = match self.last_output_time { + None => true, + Some(last) => timestamp.duration_since(last) >= self.min_interval, + }; + + if ready { + self.last_output_time = Some(timestamp); + self.on_deck = Some(frame); + self.on_deck.take() + } else { + let _ = self.on_deck.replace(frame); + None } } - /// Flush the last buffered frame at end of stream pub fn flush(&mut self) -> Option { - self.on_deck.take().map(|(frame, _ts)| frame) + self.on_deck.take() } } @@ -41,15 +44,15 @@ mod tests { use super::*; #[test] - fn first_frame_is_buffered() { + fn first_frame_passes_immediately() { let mut limiter: FpsLimit = FpsLimit::new(30); let now = Instant::now(); let result = limiter.on_new_frame(1u32, now); - assert!(result.is_none()); + assert_eq!(result, Some(1)); } #[test] - fn frames_too_close_drops_old() { + fn frames_too_close_are_dropped() { let mut limiter: FpsLimit = FpsLimit::new(30); let now = Instant::now(); limiter.on_new_frame(1, now); @@ -58,12 +61,29 @@ mod tests { } #[test] - fn frames_far_enough_output_old() { + fn frames_far_enough_pass() { let mut limiter: FpsLimit = FpsLimit::new(30); let now = Instant::now(); limiter.on_new_frame(1, now); - let result = limiter.on_new_frame(2, now + Duration::from_millis(40)); - assert_eq!(result, Some(1)); + let result = limiter.on_new_frame(2, now + Duration::from_millis(34)); + assert_eq!(result, Some(2)); + } + + #[test] + fn high_fps_input_downsampled_correctly() { + let mut limiter: FpsLimit = FpsLimit::new(30); + let base = Instant::now(); + let mut outputs = Vec::new(); + + for i in 0..10u32 { + let t = base + Duration::from_millis(i as u64 * 16); + if let Some(f) = limiter.on_new_frame(i, t) { + outputs.push(f); + } + } + + assert!(outputs.len() >= 3, "expected at least 3 outputs, got {} ({:?})", outputs.len(), outputs); + assert_eq!(outputs[0], 0); } #[test] @@ -71,7 +91,8 @@ mod tests { let mut limiter: FpsLimit = FpsLimit::new(30); let now = Instant::now(); limiter.on_new_frame(1, now); - assert_eq!(limiter.flush(), Some(1)); + limiter.on_new_frame(2, now + Duration::from_millis(1)); + assert_eq!(limiter.flush(), Some(2)); assert_eq!(limiter.flush(), None); } }