browser backend todoed, raw frame format, threaded fixes

This commit is contained in:
l1npengtul
2022-10-25 00:51:51 +09:00
parent 8f08c8a9ef
commit 4677ce0400
7 changed files with 265 additions and 170 deletions
+6
View File
@@ -56,6 +56,12 @@ impl Buffer {
&self.buffer
}
/// Get a owned version of this buffer.
#[must_use]
pub fn buffer_bytes(&self) -> Bytes {
self.buffer.clone()
}
/// Get the [`FrameFormat`] of this buffer.
#[must_use]
pub fn source_frame_format(&self) -> FrameFormat {
+45
View File
@@ -64,6 +64,7 @@ impl FormatDecoder for RgbFormat {
[pxv, pxv, pxv]
})
.collect()),
FrameFormat::RAWRGB => Ok(data.to_vec()),
}
}
@@ -92,6 +93,7 @@ impl FormatDecoder for RgbFormat {
});
Ok(())
}
FrameFormat::RAWRGB => {dest.copy_from_slice(data); Ok(())}
}
}
}
@@ -121,6 +123,13 @@ impl FormatDecoder for RgbAFormat {
[pxv, pxv, pxv, 255]
})
.collect()),
FrameFormat::RAWRGB => Ok(data
.chunks_exact(3)
.flat_map(|x| {
[x[0], x[1], x[2], 255]
})
.collect()
),
}
}
@@ -150,6 +159,20 @@ impl FormatDecoder for RgbAFormat {
});
Ok(())
}
FrameFormat::RAWRGB => {
data
.chunks_exact(3)
.enumerate()
.for_each(|(idx, px)| {
let index = idx * 4;
dest[index] = px[0];
dest[index + 1] = px[1];
dest[index + 2] = px[2];
dest[index + 3] = 255;
});
Ok(())
}
}
}
}
@@ -191,6 +214,11 @@ impl FormatDecoder for LumaFormat {
})
.collect()),
FrameFormat::GRAY => Ok(data.to_vec()),
FrameFormat::RAWRGB => {
Ok(data.chunks(3).map(|px| {
((px[0] as i32 + px[1] as i32 + px[2] as i32) / 3) as u8
}).collect())
},
}
}
@@ -219,6 +247,11 @@ impl FormatDecoder for LumaFormat {
});
Ok(())
}
FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
}),
}
}
}
@@ -260,6 +293,11 @@ impl FormatDecoder for LumaAFormat {
})
.collect()),
FrameFormat::GRAY => Ok(data.iter().flat_map(|x| [*x, 255]).collect()),
FrameFormat::RAWRGB => Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
}),
}
}
@@ -301,6 +339,13 @@ impl FormatDecoder for LumaAFormat {
});
Ok(())
}
FrameFormat::RAWRGB => {
Err(NokhwaError::ProcessFrameError {
src: fcc,
destination: "RGB => RGB".to_string(),
error: "Conversion Error".to_string(),
})
},
}
}
}
+1 -1
View File
@@ -172,7 +172,7 @@ pub trait CaptureBackendTrait {
let cfmt = self.camera_format();
let resolution = cfmt.resolution();
let pxwidth = match cfmt.format() {
FrameFormat::MJPEG | FrameFormat::YUYV => 3,
FrameFormat::MJPEG | FrameFormat::YUYV | FrameFormat::RAWRGB => 3,
FrameFormat::GRAY => 1,
};
if alpha {
+6 -2
View File
@@ -284,6 +284,7 @@ pub enum FrameFormat {
MJPEG,
YUYV,
GRAY,
RAWRGB,
}
impl Display for FrameFormat {
@@ -298,13 +299,16 @@ impl Display for FrameFormat {
FrameFormat::GRAY => {
write!(f, "GRAY")
}
FrameFormat::RAWRGB => {
write!(f, "RAWRGB")
},
}
}
}
#[must_use]
pub const fn frame_formats() -> [FrameFormat; 3] {
[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY]
pub const fn frame_formats() -> &'static [FrameFormat] {
&[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY, FrameFormat::RAWRGB]
}
/// Describes a Resolution.
+104 -123
View File
@@ -14,14 +14,13 @@
* limitations under the License.
*/
use crate::js_camera::{query_js_cameras, JSCameraConstraintsBuilder, JSCameraResizeMode};
use image::{ImageBuffer, Rgb};
use crate::js_camera::{ JSCamera};
use nokhwa_core::{
error::NokhwaError,
traits::CaptureBackendTrait,
types::{
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
FrameFormat, KnownCameraControl, Resolution,
FrameFormat, KnownCameraControl, Resolution, RequestedFormat,
},
};
use std::{borrow::Cow, collections::HashMap};
@@ -45,88 +44,90 @@ impl BrowserCaptureDevice {
///
/// # Errors
/// If the device is not found, browser not supported, or camera is over-constrained this will error.
pub fn new(index: &CameraIndex, cam_fmt: Option<CameraFormat>) -> Result<Self, NokhwaError> {
let (group_id, device_id) = match &index {
CameraIndex::Index(i) => {
let query_devices =
wasm_rs_async_executor::single_threaded::block_on(query_js_cameras())?;
match query_devices.into_iter().nth(*i as usize) {
Some(info) => {
let ids = info
.to_string()
.split(' ')
.map(ToString::to_string)
.collect::<Vec<String>>();
match (ids.get(0), ids.get(1)) {
(Some(group_id), Some(device_id)) => {
(group_id.clone(), device_id.clone())
}
(_, _) => {
return Err(NokhwaError::OpenDeviceError(
"Invalid Index".to_string(),
index.to_string(),
))
}
}
}
None => {
return Err(NokhwaError::OpenDeviceError(
"Device not found".to_string(),
index.to_string(),
))
}
}
}
CameraIndex::String(id) => {
let ids = id
.to_string()
.split(' ')
.map(ToString::to_string)
.collect::<Vec<String>>();
match (ids.get(0), ids.get(1)) {
(Some(group_id), Some(device_id)) => (group_id.clone(), device_id.clone()),
(_, _) => {
return Err(NokhwaError::OpenDeviceError(
"Invalid Index".to_string(),
index.to_string(),
))
}
}
}
};
pub fn new(index: &CameraIndex, requested: RequestedFormat) -> Result<Self, NokhwaError> {
// let (group_id, device_id) = match &index {
// CameraIndex::Index(i) => {
// let query_devices =
// wasm_rs_async_executor::single_threaded::block_on(query_js_cameras())?;
// match query_devices.into_iter().nth(*i as usize) {
// Some(info) => {
// let ids = info
// .to_string()
// .split(' ')
// .map(ToString::to_string)
// .collect::<Vec<String>>();
// match (ids.get(0), ids.get(1)) {
// (Some(group_id), Some(device_id)) => {
// (group_id.clone(), device_id.clone())
// }
// (_, _) => {
// return Err(NokhwaError::OpenDeviceError(
// "Invalid Index".to_string(),
// index.to_string(),
// ))
// }
// }
// }
// None => {
// return Err(NokhwaError::OpenDeviceError(
// "Device not found".to_string(),
// index.to_string(),
// ))
// }
// }
// }
// CameraIndex::String(id) => {
// let ids = id
// .to_string()
// .split(' ')
// .map(ToString::to_string)
// .collect::<Vec<String>>();
// match (ids.get(0), ids.get(1)) {
// (Some(group_id), Some(device_id)) => (group_id.clone(), device_id.clone()),
// (_, _) => {
// return Err(NokhwaError::OpenDeviceError(
// "Invalid Index".to_string(),
// index.to_string(),
// ))
// }
// }
// }
// };
let camera_format = cam_fmt.unwrap_or_default();
// let camera_format = cam_fmt.unwrap_or_default();
let constraints = JSCameraConstraintsBuilder::new()
.frame_rate(camera_format.frame_rate())
.resolution(camera_format.resolution())
.aspect_ratio(f64::from(camera_format.width()) / f64::from(camera_format.height()))
.group_id(&group_id)
.group_id_exact(true)
.device_id(&device_id)
.device_id_exact(true)
.resize_mode(JSCameraResizeMode::Any)
.build();
// let constraints = JSCameraConstraintsBuilder::new()
// .frame_rate(camera_format.frame_rate())
// .resolution(camera_format.resolution())
// .aspect_ratio(f64::from(camera_format.width()) / f64::from(camera_format.height()))
// .group_id(&group_id)
// .group_id_exact(true)
// .device_id(&device_id)
// .device_id_exact(true)
// .resize_mode(JSCameraResizeMode::Any)
// .build();
let camera = wasm_rs_async_executor::single_threaded::block_on(JSCamera::new(constraints))?;
// let camera = wasm_rs_async_executor::single_threaded::block_on(JSCamera::new(constraints))?;
let info = (|| {
let cameras = wasm_rs_async_executor::single_threaded::block_on(query_js_cameras())?;
let giddid = format!("{} {}", group_id, device_id);
for cam in cameras {
if cam.misc() == giddid {
return Ok(cam);
}
}
Ok(CameraInfo::new("", "videoinput", &giddid, index.clone()))
})()?;
Ok(BrowserCaptureDevice { camera, info })
// let info = (|| {
// let cameras = wasm_rs_async_executor::single_threaded::block_on(query_js_cameras())?;
// let giddid = format!("{} {}", group_id, device_id);
// for cam in cameras {
// if cam.misc() == giddid {
// return Ok(cam);
// }
// }
// Ok(CameraInfo::new("", "videoinput", &giddid, index.clone()))
// })()?;
// Ok(BrowserCaptureDevice { camera, info })
Err(NokhwaError::NotImplementedError("TODO".to_string()))
}
/// Creates a new camera from an [`CameraIndex`] and raw parts. It can take [`CameraIndex::Index`] or [`CameraIndex::String`] (NOTE: blocks on [`CameraIndex::Index`])
///
/// # Errors
/// If the device is not found, browser not supported, or camera is over-constrained this will error.
#[deprecated(since = "0.10.0", note = "please use `new` instead.")]
pub fn new_with(
index: &CameraIndex,
width: u32,
@@ -134,10 +135,11 @@ impl BrowserCaptureDevice {
fps: u32,
fourcc: FrameFormat,
) -> Result<Self, NokhwaError> {
Self::new(
index,
Some(CameraFormat::new(Resolution::new(width, height))),
)
Err(NokhwaError::NotImplementedError("TODO".to_string()))
// Self::new(
// index,
// Some(CameraFormat::new(Resolution::new(width, height), fourcc, fps)),
// )
}
}
@@ -151,74 +153,53 @@ impl CaptureBackendTrait for BrowserCaptureDevice {
}
fn refresh_camera_format(&mut self) -> Result<(), NokhwaError> {
todo!()
self.camera.measure_resolution()?;
Ok(())
}
fn camera_format(&self) -> CameraFormat {
CameraFormat::new(self.camera.resolution())
let constraints = self.camera.constraints();
// CameraFormat::new(constraints.resolution(), , constraints.frame_rate())
todo!()
}
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
let current_constraints = self.camera.constraints();
let new_constraints = JSCameraConstraintsBuilder::new()
.resolution(new_fmt.resolution())
.aspect_ratio(f64::from(new_fmt.width()) / f64::from(new_fmt.height()))
.frame_rate(new_fmt.frame_rate())
.group_id(&current_constraints.group_id())
.device_id(&current_constraints.device_id())
.resize_mode(JSCameraResizeMode::Any)
.build();
let _constraint_err = self.camera.set_constraints(new_constraints);
match self.camera.apply_constraints() {
Ok(_) => Ok(()),
Err(why) => {
let _returnerr = self.camera.set_constraints(current_constraints); // swallow errors - revert
Err(why)
}
}
todo!()
}
fn compatible_list_by_resolution(
&mut self,
_: FrameFormat,
fourcc: FrameFormat,
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
Err(NokhwaError::NotImplementedError(
"Not Implemented".to_string(),
))
todo!()
}
fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
Ok(vec![FrameFormat::MJPEG, FrameFormat::YUYV])
todo!()
}
fn resolution(&self) -> Resolution {
self.camera.resolution()
todo!()
}
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
let mut current_format = self.camera_format();
current_format.set_resolution(new_res);
self.set_camera_format(current_format)
todo!()
}
fn frame_rate(&self) -> u32 {
self.camera.constraints().frame_rate()
todo!()
}
fn set_frame_rate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
let mut current_format = self.camera_format();
current_format.set_frame_rate(new_fps);
self.set_camera_format(current_format)
todo!()
}
fn frame_format(&self) -> FrameFormat {
FrameFormat::MJPEG
todo!()
}
fn set_frame_format(&mut self, _: FrameFormat) -> Result<(), NokhwaError> {
Ok(())
fn set_frame_format(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
todo!()
}
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
@@ -238,22 +219,22 @@ impl CaptureBackendTrait for BrowserCaptureDevice {
}
fn open_stream(&mut self) -> Result<(), NokhwaError> {
Ok(())
todo!()
}
fn is_stream_open(&self) -> bool {
self.camera.is_open()
todo!()
}
fn frame(&mut self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError> {
self.camera.frame()
fn frame(&mut self) -> Result<nokhwa_core::buffer::Buffer, NokhwaError> {
todo!()
}
fn frame_raw(&mut self) -> Result<Cow<[u8]>, NokhwaError> {
self.camera.frame_raw()
todo!()
}
fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.camera.stop_all()
todo!()
}
}
}
+4 -4
View File
@@ -32,7 +32,7 @@ use std::{
fmt::{Debug, Display, Formatter},
ops::Deref,
};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{
console::log_1, CanvasRenderingContext2d, Document, Element, HtmlCanvasElement,
@@ -1676,7 +1676,7 @@ impl Deref for JSCameraConstraints {
/// # JS-WASM
/// This is exported as `NokhwaCamera`.
#[cfg(feature = "input-jscam")]
#[cfg_attr(feature = "input-jscam", wasm_bindgen(js_name = NokhwaCamera))]
#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_name = NokhwaCamera))]
#[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-jscam")))]
pub struct JSCamera {
media_stream: MediaStream,
@@ -1688,7 +1688,7 @@ pub struct JSCamera {
canvas_context: Option<CanvasRenderingContext2d>,
}
#[cfg(feature = "output-wasm")]
#[cfg(feature = "input-jscam")]
#[cfg_attr(feature = "output-wasm", wasm_bindgen(js_class = NokhwaCamera))]
impl JSCamera {
/// Creates a new [`JSCamera`] using [`JSCameraConstraints`].
@@ -2007,7 +2007,7 @@ impl JSCamera {
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub fn measure_resolution(&mut self) -> Result<(), NokhwaError> {
let stream = self
.media_stream()
.media_stream
.get_video_tracks()
.iter()
.next()
+99 -40
View File
@@ -15,18 +15,15 @@
*/
use crate::Camera;
use image::{ImageBuffer, Rgb};
use nokhwa_core::{
buffer::Buffer,
error::NokhwaError,
traits::CaptureBackendTrait,
types::{
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueSetter,
FrameFormat, KnownCameraControl, RequestedFormat, Resolution,
},
};
use std::{
any::Any,
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
@@ -76,7 +73,7 @@ impl CallbackCamera {
Ok(CallbackCamera {
camera: arc_camera,
frame_callback: Arc::new(Mutex::new(Box::new(callback))),
last_frame_captured: Arc::new(Mutex::new(Buffer::new_with_vec(
last_frame_captured: Arc::new(Mutex::new(Buffer::new(
Resolution::new(0, 0),
&vec![],
FrameFormat::GRAY,
@@ -86,52 +83,98 @@ impl CallbackCamera {
}
/// Gets the current Camera's index.
#[must_use]
pub fn index(&self) -> usize {
self.camera.lock().index().clone()
pub fn index(&self) -> Result<&CameraIndex, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.index())
}
/// Sets the current Camera's index. Note that this re-initializes the camera.
/// # Errors
/// The Backend may fail to initialize.
pub fn set_index(&mut self, new_idx: usize) -> Result<(), NokhwaError> {
self.camera.lock().set_index(new_idx)
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)
}
/// Gets the current Camera's backend
#[must_use]
pub fn backend(&self) -> ApiBackend {
self.camera.lock().backend()
pub fn backend(&self) -> Result<ApiBackend, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.backend())
}
/// Sets the current Camera's backend. Note that this re-initializes the camera.
/// # Errors
/// The new backend may not exist or may fail to initialize the new camera.
pub fn set_backend(&mut self, new_backend: ApiBackend) -> Result<(), NokhwaError> {
self.camera.lock().set_backend(new_backend)
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_backend(new_backend)
}
/// Gets the camera information such as Name and Index as a [`CameraInfo`].
#[must_use]
pub fn info(&self) -> CameraInfo {
self.camera.lock().info().clone()
pub fn info(&self) -> Result<&CameraInfo, NokhwaError> {
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.info())
}
/// Gets the current [`CameraFormat`].
pub fn camera_format(&self) -> Result<CameraFormat, NokhwaError> {
self.camera.lock().camera_format()
Ok(self
.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.camera_format())
}
/// Will set the current [`CameraFormat`]
/// This will reset the current stream if used while stream is opened.
/// # Errors
/// If you started the stream and the camera rejects the new camera format, this will return an error.
#[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() =
Buffer::new(new_res, &Vec::default(), self.camera_format()?.format());
self.camera.lock().set_camera_format(new_fmt)
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = Buffer::new(
new_fmt.resolution(),
&Vec::default(),
self.camera_format()?.format(),
);
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.set_camera_format(new_fmt)
}
/// Will set the current [`CameraFormat`], using a [`RequestedFormat.`]
/// This will reset the current stream if used while stream is opened.
///
/// This will also update the cache.
///
/// This will return the new [`CameraFormat`]
/// # Errors
/// If nothing fits the requested criteria, this will return an error.
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)
}
/// A hashmap of [`Resolution`]s mapped to framerates
/// # Errors
/// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a [`UnsupportedOperationError`](crate::NokhwaError::UnsupportedOperationError).
@@ -139,14 +182,20 @@ impl CallbackCamera {
&mut self,
fourcc: FrameFormat,
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError> {
self.camera.lock().compatible_list_by_resolution(fourcc)
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.compatible_list_by_resolution(fourcc)
}
/// A Vector of compatible [`FrameFormat`]s.
/// # Errors
/// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a [`UnsupportedOperationError`](crate::NokhwaError::UnsupportedOperationError).
pub fn compatible_fourcc(&mut self) -> Result<Vec<FrameFormat>, NokhwaError> {
self.camera.lock().compatible_fourcc()
self.camera
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))?
.compatible_fourcc()
}
/// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]).
@@ -166,8 +215,11 @@ impl CallbackCamera {
/// # Errors
/// If you started the stream and the camera rejects the new resolution, this will return an error.
pub fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
*self.last_frame_captured.lock() =
Buffer::new_with_vec(new_res, Vec::default(), self.camera_format()?.format());
*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 {
@@ -241,7 +293,7 @@ impl CallbackCamera {
.map_err(|why| NokhwaError::GetPropertyError {
property: "Supported Camera Controls".to_string(),
error: why.to_string(),
})
})?
.supported_camera_controls()
}
@@ -379,29 +431,32 @@ impl CallbackCamera {
.lock()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))?
.frame()?;
*self.last_frame_captured.lock() = frame.clone();
*self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::GeneralError(why.to_string()))? = frame.clone();
Ok(frame)
}
/// Gets the last frame captured by the camera.
#[must_use]
pub fn last_frame(&self) -> Buffer {
self.last_frame_captured
pub fn last_frame(&self) -> Result<Buffer, NokhwaError> {
Ok(self
.last_frame_captured
.lock()
.map_err(|why| NokhwaError::ReadFrameError(why.to_string()))?
.clone()
.clone())
}
/// Checks if stream if open. If it is, it will return true.
#[must_use]
pub fn is_stream_open(&self) -> bool {
self.camera
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()
.is_stream_open())
}
/// Will drop the stream.
@@ -410,7 +465,7 @@ impl CallbackCamera {
pub fn stop_stream(&mut self) -> Result<(), NokhwaError> {
self.camera
.lock()
.map_err(|why| NokhwaError::StreamShutdownError(why.to_string()))
.map_err(|why| NokhwaError::StreamShutdownError(why.to_string()))?
.stop_stream()
}
}
@@ -425,14 +480,18 @@ impl Drop for CallbackCamera {
fn camera_frame_thread_loop(
camera: &AtomicLock<Camera>,
frame_callback: &HeldCallbackType,
last_frame_captured: &AtomicLock<ImageBuffer<Rgb<u8>, Vec<u8>>>,
last_frame_captured: &AtomicLock<Buffer>,
die_bool: &Arc<AtomicBool>,
) {
loop {
if let Ok(img) = camera.lock().fr {
*last_frame_captured.lock() = img.clone();
if let Some(cb) = (*frame_callback.lock()).as_mut() {
cb(img);
if let Ok(mut camera) = camera.lock() {
if let Ok(frame) = camera.frame() {
if let Ok(last_frame) = last_frame_captured.lock() {
*last_frame = frame.clone();
if let Ok(cb) = frame_callback.lock() {
cb(frame);
}
}
}
}
if die_bool.load(Ordering::SeqCst) {