use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::JoinHandle;
use super::channel::FrameChannel;
use crate::Error;
use crate::frame::Frame;
pub(super) struct Geometry {
pub width: u32,
pub height: u32,
pub framerate: Option<u32>,
pub device: String,
}
pub(super) struct PumpGuard {
stop: Arc<AtomicBool>,
handle: Option<JoinHandle<()>>,
}
impl Drop for PumpGuard {
fn drop(&mut self) {
self.stop.store(true, Ordering::SeqCst);
if let Some(handle) = self.handle.take() {
let _ = handle.join();
}
}
}
pub(super) async fn spawn<S, I, R>(
chan: Arc<FrameChannel>,
init: I,
mut read: R,
) -> Result<(Geometry, PumpGuard), Error>
where
I: FnOnce() -> Result<(S, Geometry), Error> + Send + 'static,
R: FnMut(&mut S) -> Result<Option<Frame>, Error> + Send + 'static,
{
let stop = Arc::new(AtomicBool::new(false));
let (geo_tx, geo_rx) = tokio::sync::oneshot::channel();
let handle = std::thread::spawn({
let stop = stop.clone();
let chan = chan.clone();
move || {
let (mut source, geometry) = match init() {
Ok(opened) => opened,
Err(err) => {
let _ = geo_tx.send(Err(err));
return;
}
};
if geo_tx.send(Ok(geometry)).is_err() {
return;
}
while !stop.load(Ordering::SeqCst) {
match read(&mut source) {
Ok(Some(frame)) => chan.push(frame),
Ok(None) => break, Err(err) => {
tracing::warn!(error = %err, "capture read failed; stopping");
break;
}
}
}
chan.close();
}
});
let guard = PumpGuard {
stop,
handle: Some(handle),
};
match geo_rx.await {
Ok(Ok(geometry)) => Ok((geometry, guard)),
Ok(Err(err)) => Err(err),
Err(_) => Err(Error::Codec(anyhow::anyhow!(
"capture thread exited before reporting geometry"
))),
}
}