use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use screencapturekit::prelude::*;
use screencapturekit::screenshot_manager::SCScreenshotManager;
pub struct CaptureSession {
stop: Arc<AtomicBool>,
join_handle: Option<thread::JoinHandle<()>>,
}
impl CaptureSession {
pub fn new<H>(
filter: SCContentFilter,
output_width: u32,
output_height: u32,
fps: u32,
mut handler: H,
) -> Self
where
H: FnMut(CMSampleBuffer, SCStreamOutputType) + Send + 'static,
{
let mut config = SCStreamConfiguration::default();
config
.set_width(output_width)
.set_height(output_height)
.set_pixel_format(PixelFormat::BGRA)
.set_shows_cursor(false);
let interval = Duration::from_secs_f64(1.0 / f64::from(fps));
let stop = Arc::new(AtomicBool::new(false));
let stop_c = stop.clone();
const CAPTURE_ERROR_THROTTLE: Duration = Duration::from_secs(5);
let join_handle = thread::spawn(move || {
let mut next_frame = Instant::now();
let mut last_error_log = Instant::now();
while !stop_c.load(Ordering::Relaxed) {
match SCScreenshotManager::capture_sample_buffer(&filter, &config) {
Ok(sample) => {
handler(sample, SCStreamOutputType::Screen);
}
Err(e) => {
let now = Instant::now();
if now - last_error_log >= CAPTURE_ERROR_THROTTLE {
eprintln!(" Screenshot capture error: {}", e);
last_error_log = now;
}
}
}
next_frame += interval;
let now = Instant::now();
if next_frame > now {
spin_sleep::sleep(next_frame - now);
} else {
next_frame = now;
}
}
});
CaptureSession {
stop,
join_handle: Some(join_handle),
}
}
pub fn stop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(h) = self.join_handle.take() {
let _ = h.join();
}
}
}
impl Drop for CaptureSession {
fn drop(&mut self) {
self.stop();
}
}