use crate::{
DevicePixels, ForegroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream,
Size, SourceMetadata,
};
use anyhow::Result;
use futures::channel::oneshot;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{self, AtomicBool};
pub fn scap_screen_sources(
foreground_executor: &ForegroundExecutor,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
let (sources_tx, sources_rx) = oneshot::channel();
get_screen_targets(sources_tx);
to_dyn_screen_capture_sources(sources_rx, foreground_executor)
}
struct ScapCaptureSource {
target: scap::Display,
size: Size<DevicePixels>,
}
fn get_screen_targets(sources_tx: oneshot::Sender<Result<Vec<ScapCaptureSource>>>) {
std::thread::spawn(|| {
let targets = match scap::get_all_targets() {
Ok(targets) => targets,
Err(err) => {
sources_tx.send(Err(err)).ok();
return;
}
};
let sources = targets
.into_iter()
.filter_map(|target| match target {
scap::Target::Display(display) => {
let size = Size {
width: DevicePixels(display.width as i32),
height: DevicePixels(display.height as i32),
};
Some(ScapCaptureSource {
target: display,
size,
})
}
scap::Target::Window(_) => None,
})
.collect::<Vec<_>>();
sources_tx.send(Ok(sources)).ok();
});
}
impl ScreenCaptureSource for ScapCaptureSource {
fn metadata(&self) -> Result<SourceMetadata> {
Ok(SourceMetadata {
resolution: self.size,
label: Some(self.target.title.clone().into()),
is_main: None,
id: self.target.id as u64,
})
}
fn stream(
&self,
foreground_executor: &ForegroundExecutor,
frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
let (stream_tx, stream_rx) = oneshot::channel();
let target = self.target.clone();
std::thread::spawn(move || {
match new_scap_capturer(Some(scap::Target::Display(target.clone()))) {
Ok(mut capturer) => {
capturer.start_capture();
run_capture(capturer, target.clone(), frame_callback, stream_tx);
}
Err(e) => {
stream_tx.send(Err(e)).ok();
}
}
});
to_dyn_screen_capture_stream(stream_rx, foreground_executor)
}
}
fn new_scap_capturer(target: Option<scap::Target>) -> Result<scap::capturer::Capturer> {
scap::capturer::Capturer::build(scap::capturer::Options {
fps: 60,
show_cursor: true,
show_highlight: true,
output_type: scap::frame::FrameType::YUVFrame,
output_resolution: scap::capturer::Resolution::Captured,
crop_area: None,
target,
excluded_targets: None,
})
}
fn run_capture(
mut capturer: scap::capturer::Capturer,
display: scap::Display,
frame_callback: Box<dyn Fn(ScreenCaptureFrame) + Send>,
stream_tx: oneshot::Sender<Result<ScapStream>>,
) {
let cancel_stream = Arc::new(AtomicBool::new(false));
let size = Size {
width: DevicePixels(display.width as i32),
height: DevicePixels(display.height as i32),
};
let stream_send_result = stream_tx.send(Ok(ScapStream {
cancel_stream: cancel_stream.clone(),
display,
size,
}));
if stream_send_result.is_err() {
return;
}
while !cancel_stream.load(std::sync::atomic::Ordering::SeqCst) {
match capturer.get_next_frame() {
Ok(frame) => frame_callback(ScreenCaptureFrame(frame)),
Err(err) => {
log::error!("Halting screen capture due to error: {err}");
break;
}
}
}
capturer.stop_capture();
}
struct ScapStream {
cancel_stream: Arc<AtomicBool>,
display: scap::Display,
size: Size<DevicePixels>,
}
impl ScreenCaptureStream for ScapStream {
fn metadata(&self) -> Result<SourceMetadata> {
Ok(SourceMetadata {
resolution: self.size,
label: Some(self.display.title.clone().into()),
is_main: None,
id: self.display.id as u64,
})
}
}
impl Drop for ScapStream {
fn drop(&mut self) {
self.cancel_stream.store(true, atomic::Ordering::SeqCst);
}
}
fn to_dyn_screen_capture_sources<T: ScreenCaptureSource + 'static>(
sources_rx: oneshot::Receiver<Result<Vec<T>>>,
foreground_executor: &ForegroundExecutor,
) -> oneshot::Receiver<Result<Vec<Rc<dyn ScreenCaptureSource>>>> {
let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
foreground_executor
.spawn(async move {
match sources_rx.await {
Ok(Ok(results)) => dyn_sources_tx
.send(Ok(results
.into_iter()
.map(|source| Rc::new(source) as Rc<dyn ScreenCaptureSource>)
.collect::<Vec<_>>()))
.ok(),
Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
Err(oneshot::Canceled) => None,
}
})
.detach();
dyn_sources_rx
}
fn to_dyn_screen_capture_stream<T: ScreenCaptureStream + 'static>(
sources_rx: oneshot::Receiver<Result<T>>,
foreground_executor: &ForegroundExecutor,
) -> oneshot::Receiver<Result<Box<dyn ScreenCaptureStream>>> {
let (dyn_sources_tx, dyn_sources_rx) = oneshot::channel();
foreground_executor
.spawn(async move {
match sources_rx.await {
Ok(Ok(stream)) => dyn_sources_tx
.send(Ok(Box::new(stream) as Box<dyn ScreenCaptureStream>))
.ok(),
Ok(Err(err)) => dyn_sources_tx.send(Err(err)).ok(),
Err(oneshot::Canceled) => None,
}
})
.detach();
dyn_sources_rx
}