use crate::Camera;
use nokhwa_core::{
buffer::Buffer,
error::NokhwaError,
types::{
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
FrameFormat, KnownCameraControl, RequestedFormat, RequestedFormatType, Resolution,
},
};
use std::thread::JoinHandle;
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
type AtomicLock<T> = Arc<Mutex<T>>;
pub type CallbackFn = fn(
_camera: &Arc<Mutex<Camera>>,
_frame_callback: &Arc<Mutex<Option<Box<dyn FnMut(Buffer) + Send + 'static>>>>,
_last_frame_captured: &Arc<Mutex<Buffer>>,
_die_bool: &Arc<AtomicBool>,
);
type HeldCallbackType = Arc<Mutex<Box<dyn FnMut(Buffer) + Send + 'static>>>;
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "output-threaded")))]
pub struct CallbackCamera {
camera: AtomicLock<Camera>,
frame_callback: HeldCallbackType,
last_frame_captured: AtomicLock<Buffer>,
die_bool: Arc<AtomicBool>,
current_camera: CameraInfo,
handle: AtomicLock<Option<JoinHandle<()>>>,
}
impl CallbackCamera {
pub fn new(
index: CameraIndex,
format: RequestedFormat,
callback: impl FnMut(Buffer) + Send + 'static,
) -> Result<Self, NokhwaError> {
let arc_camera = Arc::new(Mutex::new(Camera::new(index, format)?));
let current_camera = arc_camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "CameraInfo".to_string(),
error: why.to_string(),
})?
.info()
.clone();
Ok(CallbackCamera {
camera: arc_camera,
frame_callback: Arc::new(Mutex::new(Box::new(callback))),
last_frame_captured: Arc::new(Mutex::new(Buffer::new(
Resolution::new(0, 0),
&vec![],
FrameFormat::GRAY,
))),
die_bool: Arc::new(Default::default()),
current_camera,
handle: Arc::new(Mutex::new(None)),
})
}
pub fn with_custom(camera: Camera, callback: impl FnMut(Buffer) + Send + 'static) -> Self {
let current_camera = camera.info().clone();
CallbackCamera {
camera: Arc::new(Mutex::new(camera)),
frame_callback: Arc::new(Mutex::new(Box::new(callback))),
last_frame_captured: Arc::new(Mutex::new(Buffer::new(
Resolution::new(0, 0),
&vec![],
FrameFormat::GRAY,
))),
die_bool: Arc::new(Default::default()),
current_camera,
handle: Arc::new(Mutex::new(None)),
}
}
pub fn index(&self) -> &CameraIndex {
&self.current_camera.index()
}
pub fn set_index(&mut self, new_idx: &CameraIndex) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_index(new_idx)?;
self.current_camera = self
.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "CameraInfo".to_string(),
error: why.to_string(),
})?
.info()
.clone();
Ok(())
}
pub fn backend(&self) -> Result<ApiBackend, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.backend())
}
pub fn set_backend(&mut self, new_backend: ApiBackend) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_backend(new_backend)
}
pub fn info(&self) -> &CameraInfo {
&self.current_camera
}
pub fn camera_format(&self) -> Result<CameraFormat, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.camera_format())
}
#[deprecated(since = "0.10.0", note = "please use `set_camera_requset` instead.")]
pub fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = Buffer::new(
new_fmt.resolution(),
&Vec::default(),
self.camera_format()?.format(),
);
let formats = vec![new_fmt.format()];
let request = RequestedFormat::with_formats(RequestedFormatType::Exact(new_fmt), &formats);
let set_fmt = self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_camera_requset(request)?;
if new_fmt != set_fmt {
return Err(NokhwaError::SetPropertyError {
property: "CameraFormat".to_string(),
value: "CameraFormat".to_string(),
error: "Requested Format Not Consistant".to_string(),
});
}
Ok(())
}
pub fn set_camera_requset(
&mut self,
request: RequestedFormat,
) -> Result<CameraFormat, NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_camera_requset(request)
}
pub fn compatible_list_by_resolution(
&mut self,
fourcc: FrameFormat,
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.compatible_list_by_resolution(fourcc)
}
pub fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.compatible_fourcc()
}
pub fn resolution(&self) -> Result<Resolution, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "Resolution".to_string(),
error: why.to_string(),
})?
.resolution())
}
pub fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? =
Buffer::new(new_res, &Vec::default(), self.camera_format()?.format());
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
property: "Resolution".to_string(),
value: new_res.to_string(),
error: why.to_string(),
})?
.set_resolution(new_res)
}
pub fn frame_rate(&self) -> Result<u32, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "Framerate".to_string(),
error: why.to_string(),
})?
.frame_rate())
}
pub fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
property: "Framerate".to_string(),
value: new_fps.to_string(),
error: why.to_string(),
})?
.set_frame_rate(new_fps)
}
pub fn frame_format(&self) -> Result<FrameFormat, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "Frameformat".to_string(),
error: why.to_string(),
})?
.frame_format())
}
pub fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
property: "Framerate".to_string(),
value: fourcc.to_string(),
error: why.to_string(),
})?
.set_frame_format(fourcc)
}
pub fn supported_camera_controls(&self) -> Result<Vec<KnownCameraControl>, NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "Supported Camera Controls".to_string(),
error: why.to_string(),
})?
.supported_camera_controls()
}
pub fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
let known_controls = self.supported_camera_controls()?;
let maybe_camera_controls = known_controls
.iter()
.map(|x| self.camera_control(*x))
.filter(Result::is_ok)
.map(Result::unwrap)
.collect::<Vec<CameraControl>>();
Ok(maybe_camera_controls)
}
pub fn camera_controls_string(&self) -> Result<HashMap<String, CameraControl>, NokhwaError> {
let known_controls = self.supported_camera_controls()?;
let maybe_camera_controls = known_controls
.iter()
.map(|x| (x.to_string(), self.camera_control(*x)))
.filter(|(_, x)| x.is_ok())
.map(|(c, x)| (c, Result::unwrap(x)))
.collect::<Vec<(String, CameraControl)>>();
let mut control_map = HashMap::with_capacity(maybe_camera_controls.len());
for (kc, cc) in maybe_camera_controls {
control_map.insert(kc, cc);
}
Ok(control_map)
}
pub fn camera_controls_known_camera_controls(
&self,
) -> Result<HashMap<KnownCameraControl, CameraControl>, NokhwaError> {
let known_controls = self.supported_camera_controls()?;
let maybe_camera_controls = known_controls
.iter()
.map(|x| (*x, self.camera_control(*x)))
.filter(|(_, x)| x.is_ok())
.map(|(c, x)| (c, Result::unwrap(x)))
.collect::<Vec<(KnownCameraControl, CameraControl)>>();
let mut control_map = HashMap::with_capacity(maybe_camera_controls.len());
for (kc, cc) in maybe_camera_controls {
control_map.insert(kc, cc);
}
Ok(control_map)
}
pub fn camera_control(
&self,
control: KnownCameraControl,
) -> Result<CameraControl, NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "Camera Control".to_string(),
error: why.to_string(),
})?
.camera_control(control)
}
pub fn set_camera_control(
&mut self,
id: KnownCameraControl,
control: ControlValueSetter,
) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
property: "Camera Control".to_string(),
value: format!("{}: {}", id, control),
error: why.to_string(),
})?
.set_camera_control(id, control)
}
pub fn open_stream(&mut self) -> Result<(), NokhwaError> {
let mut handle_lock = self
.handle
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "thread handle".to_string(),
error: why.to_string(),
})?;
if handle_lock.is_none() {
self.camera
.lock()
.map_err(|why| NokhwaError::SetPropertyError {
property: "camera".to_string(),
value: "callback".to_string(),
error: why.to_string(),
})?
.open_stream()?;
let die_bool_clone = self.die_bool.clone();
let camera_clone = self.camera.clone();
let last_frame = self.last_frame_captured.clone();
let callback = self.frame_callback.clone();
let handle = std::thread::spawn(move || {
camera_frame_thread_loop(camera_clone, callback, last_frame, die_bool_clone)
});
*handle_lock = Some(handle);
Ok(())
} else {
Err(NokhwaError::OpenStreamError(
"Stream Already Open".to_string(),
))
}
}
pub fn set_callback(
&mut self,
callback: impl FnMut(Buffer) + Send + 'static,
) -> Result<(), NokhwaError> {
*self
.frame_callback
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "frame_callback".to_string(),
error: why.to_string(),
})? = Box::new(callback);
Ok(())
}
pub fn poll_frame(&mut self) -> Result<Buffer, NokhwaError> {
let frame = self
.camera
.lock()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))?
.frame()?;
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = frame.clone();
Ok(frame)
}
pub fn last_frame(&self) -> Result<Buffer, NokhwaError> {
Ok(self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))?
.clone())
}
pub fn is_stream_open(&self) -> Result<bool, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GetPropertyError {
property: "is stream open".to_string(),
error: why.to_string(),
})?
.is_stream_open())
}
pub fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::StreamShutdownError(why.to_string()))?
.stop_stream()
}
}
impl Drop for CallbackCamera {
fn drop(&mut self) {
let _stop_stream_err = self.stop_stream();
self.die_bool.store(true, Ordering::SeqCst);
}
}
fn camera_frame_thread_loop(
camera: AtomicLock<Camera>,
frame_callback: HeldCallbackType,
last_frame_captured: AtomicLock<Buffer>,
die_bool: Arc<AtomicBool>,
) {
loop {
if let Ok(mut camera) = camera.lock() {
if let Ok(frame) = camera.frame() {
if let Ok(mut last_frame) = last_frame_captured.lock() {
*last_frame = frame.clone();
if let Ok(mut cb) = frame_callback.lock() {
cb(frame);
}
}
}
}
if die_bool.load(Ordering::SeqCst) {
break;
}
}
}