use crate::{
Camera, CameraFormat, CameraInfo, CaptureAPIBackend, FrameFormat, NokhwaError, Resolution,
};
use image::{ImageBuffer, Rgb};
use parking_lot::FairMutex;
use std::{
collections::HashMap,
ops::Deref,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
#[derive(Clone)]
pub struct ThreadedCamera {
camera: Arc<FairMutex<Camera>>,
frame_callback: Arc<FairMutex<Option<fn(ImageBuffer<Rgb<u8>, Vec<u8>>)>>>,
die_bool: Arc<AtomicBool>,
}
impl ThreadedCamera {
pub fn new(index: usize, format: Option<CameraFormat>) -> Result<Self, NokhwaError> {
ThreadedCamera::with_backend(index, format, CaptureAPIBackend::Auto)
}
pub fn with_backend(
index: usize,
format: Option<CameraFormat>,
backend: CaptureAPIBackend,
) -> Result<Self, NokhwaError> {
let camera = Arc::new(FairMutex::new(Camera::with_backend(
index, format, backend,
)?));
let frame_callback = Arc::new(FairMutex::new(None));
let die_bool = Arc::new(AtomicBool::new(false));
let die_clone = die_bool.clone();
let camera_clone = camera.clone();
let callback_clone = frame_callback.clone();
std::thread::spawn(move || {
camera_frame_thread_loop(camera_clone, callback_clone, die_clone)
});
Ok(ThreadedCamera {
camera,
frame_callback,
die_bool,
})
}
pub fn new_with(
index: usize,
width: u32,
height: u32,
fps: u32,
fourcc: FrameFormat,
backend: CaptureAPIBackend,
) -> Result<Self, NokhwaError> {
let camera_format = CameraFormat::new_from(width, height, fourcc, fps);
ThreadedCamera::with_backend(index, Some(camera_format), backend)
}
#[must_use]
pub fn index(&self) -> usize {
self.camera.lock().index()
}
pub fn set_index(&mut self, new_idx: usize) -> Result<(), NokhwaError> {
self.camera.lock().set_index(new_idx)
}
#[must_use]
pub fn backend(&self) -> CaptureAPIBackend {
self.camera.lock().backend()
}
pub fn set_backend(&mut self, new_backend: CaptureAPIBackend) -> Result<(), NokhwaError> {
self.camera.lock().set_backend(new_backend)
}
#[must_use]
pub fn info(&self) -> CameraInfo {
self.camera.lock().info().clone()
}
#[must_use]
pub fn camera_format(&self) -> CameraFormat {
self.camera.lock().camera_format()
}
pub fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
self.camera.lock().set_camera_format(new_fmt)
}
pub fn compatible_list_by_resolution(
&mut self,
fourcc: FrameFormat,
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
self.camera.lock().compatible_list_by_resolution(fourcc)
}
pub fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
self.camera.lock().compatible_fourcc()
}
#[must_use]
pub fn resolution(&self) -> Resolution {
self.camera.lock().resolution()
}
pub fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
self.camera.lock().set_resolution(new_res)
}
#[must_use]
pub fn frame_rate(&self) -> u32 {
self.camera.lock().frame_rate()
}
pub fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
self.camera.lock().set_frame_rate(new_fps)
}
#[must_use]
pub fn frame_format(&self) -> FrameFormat {
self.camera.lock().frame_format()
}
pub fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
self.camera.lock().set_frame_format(fourcc)
}
pub fn open_stream(
&mut self,
callback: fn(ImageBuffer<Rgb<u8>, Vec<u8>>),
) -> Result<(), NokhwaError> {
*self.frame_callback.lock() = Some(callback);
self.camera.lock().open_stream()
}
pub fn set_callback(&mut self, callback: fn(ImageBuffer<Rgb<u8>, Vec<u8>>)) {
*self.frame_callback.lock() = Some(callback);
}
pub fn is_stream_open(&self) -> bool {
self.camera.lock().is_stream_open()
}
pub fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.camera.lock().stop_stream()
}
}
impl Drop for ThreadedCamera {
fn drop(&mut self) {
let _ = self.stop_stream();
self.die_bool.store(true, Ordering::SeqCst);
}
}
fn camera_frame_thread_loop(
camera: Arc<FairMutex<Camera>>,
callback: Arc<FairMutex<Option<fn(ImageBuffer<Rgb<u8>, Vec<u8>>)>>>,
die_bool: Arc<AtomicBool>,
) {
loop {
if let Ok(img) = camera.lock().frame() {
if let Some(cb) = callback.lock().deref() {
cb(img)
}
}
if die_bool.load(Ordering::SeqCst) {
break;
}
}
}