#![warn(missing_docs)]
pub use cameras;
#[cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows")))]
mod discover;
#[cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows")))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "discover", any(target_os = "macos", target_os = "windows"))))
)]
pub use discover::{
DiscoverySession, cancel_discovery, poll_discovery, show_discovery, show_discovery_results,
show_discovery_status, start_discovery,
};
use std::sync::{Arc, Mutex, PoisonError};
use cameras::{Camera, Frame, pump};
use egui::{ColorImage, Context, TextureHandle, TextureOptions, Ui};
pub use cameras::pump::{Pump, capture_frame, set_active, stop_and_join};
const DEFAULT_TEXTURE_NAME: &str = "cameras-frame";
pub struct Stream {
pub pump: Pump,
pub sink: Sink,
pub texture: Option<TextureHandle>,
pub name: String,
}
#[derive(Clone, Default)]
pub struct Sink {
frame: Arc<Mutex<Option<Frame>>>,
}
pub fn spawn(camera: Camera) -> Stream {
spawn_named(camera, DEFAULT_TEXTURE_NAME)
}
pub fn spawn_named(camera: Camera, name: impl Into<String>) -> Stream {
let sink = Sink::default();
let pump = spawn_pump(camera, sink.clone());
Stream {
pump,
sink,
texture: None,
name: name.into(),
}
}
pub fn spawn_pump(camera: Camera, sink: Sink) -> Pump {
pump::spawn(camera, move |frame| publish_frame(&sink, frame))
}
pub fn publish_frame(sink: &Sink, frame: Frame) {
let mut slot = sink.frame.lock().unwrap_or_else(PoisonError::into_inner);
*slot = Some(frame);
}
pub fn take_frame(sink: &Sink) -> Option<Frame> {
sink.frame
.lock()
.unwrap_or_else(PoisonError::into_inner)
.take()
}
pub fn frame_to_color_image(frame: &Frame) -> Result<ColorImage, cameras::Error> {
let rgba = cameras::to_rgba8(frame)?;
Ok(ColorImage::from_rgba_unmultiplied(
[frame.width as usize, frame.height as usize],
&rgba,
))
}
pub fn update_texture(stream: &mut Stream, ctx: &Context) -> Result<bool, cameras::Error> {
let Some(frame) = take_frame(&stream.sink) else {
return Ok(false);
};
let image = frame_to_color_image(&frame)?;
match &mut stream.texture {
Some(texture) => texture.set(image, TextureOptions::LINEAR),
None => {
stream.texture = Some(ctx.load_texture(&stream.name, image, TextureOptions::LINEAR));
}
}
Ok(true)
}
pub fn show(stream: &Stream, ui: &mut Ui) {
let Some(texture) = &stream.texture else {
return;
};
let aspect = texture.aspect_ratio();
let available = ui.available_size();
let width = available.x.min(available.y * aspect);
let height = width / aspect;
ui.image((texture.id(), egui::vec2(width, height)));
}