mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
finish impl stream for UVC, change to auto default camera format, adjust trait API
This commit is contained in:
@@ -6,12 +6,11 @@ use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::HashMap,
|
||||
mem::MaybeUninit,
|
||||
ops::DerefMut,
|
||||
sync::{atomic::AtomicUsize, Arc},
|
||||
};
|
||||
use uvc::{
|
||||
ActiveStream, Context, Device, DeviceHandle, DeviceList, Error, FrameFormat as UVCFrameFormat,
|
||||
StreamFormat, StreamHandle,
|
||||
ActiveStream, Context, Device, DeviceHandle, FrameFormat as UVCFrameFormat, StreamFormat,
|
||||
StreamHandle,
|
||||
};
|
||||
|
||||
#[cfg(feature = "input_uvc")]
|
||||
@@ -40,23 +39,24 @@ impl From<CameraFormat> for StreamFormat {
|
||||
/// The backend struct that interfaces with libuvc.
|
||||
/// To see what this does, please see [`CaptureBackendTrait`]
|
||||
/// # Quirks
|
||||
/// The indexing for this backend is based off of `libuvc`'s device ordering, not the OS.
|
||||
/// You must call [create()](UVCCaptureDevice::create()) instead `new()`, some methods are auto-generated by the self-referencer and are not meant to be used.
|
||||
/// The [create()](UVCCaptureDevice::create()) method will open the device twice.
|
||||
/// Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat)
|
||||
/// each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
|
||||
/// - The indexing for this backend is based off of `libuvc`'s device ordering, not the OS.
|
||||
/// - You must call [create()](UVCCaptureDevice::create()) instead `new()`, some methods are auto-generated by the self-referencer and are not meant to be used.
|
||||
/// - The [create()](UVCCaptureDevice::create()) method will open the device twice.
|
||||
/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution()), [`set_framerate()`](CaptureBackendTrait::set_framerate()), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat()) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format()).
|
||||
/// - [`get_frame_raw()`](CaptureBackendTrait::get_frame_raw()) returns the same raw data as [`get_frame()`](CaptureBackendTrait::get_frame()), a.k.a. no custom decoding required, all data is automatically RGB
|
||||
/// This backend, once stream is open, will constantly collect frames. When you call [`get_frame()`](CaptureBackendTrait::get_frame()) or one of its variants, it will only give you the latest frame.
|
||||
/// # Safety
|
||||
/// This backend requires use of `unsafe` due to the self-referencing structs involved.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
/// If [`open_stream()`](CaptureBackendTrait::open_stream()) and [`get_frame()`](CaptureBackendTrait::get_frame()) are called in the wrong order this may crash the entire program.
|
||||
#[self_referencing(chain_hack)]
|
||||
pub struct UVCCaptureDevice<'a> {
|
||||
camera_format: Option<CameraFormat>,
|
||||
camera_format: CameraFormat,
|
||||
camera_info: CameraInfo,
|
||||
frame_receiver: Box<Receiver<Vec<u8>>>,
|
||||
frame_sender: Box<Sender<Vec<u8>>>,
|
||||
context: Box<Context<'a>>,
|
||||
stream_handle_init: Cell<bool>,
|
||||
active_stream_init: Cell<bool>,
|
||||
context: Box<Context<'a>>,
|
||||
#[borrows(context)]
|
||||
#[not_covariant]
|
||||
device: Box<Device<'this>>,
|
||||
@@ -73,11 +73,12 @@ pub struct UVCCaptureDevice<'a> {
|
||||
|
||||
impl<'a> UVCCaptureDevice<'a> {
|
||||
/// Creates a UVC Camera device with optional [`CameraFormat`].
|
||||
/// If `camera_format` is `None`, it will be spawned with with 640x480@15 FPS, MJPEG [`CameraFormat`] default.
|
||||
/// # Panics
|
||||
/// This operation may panic! If the UVC Context fails to retrieve the device from the gotten IDs, this operation will panic.
|
||||
/// # Errors
|
||||
/// This may error when the `libuvc` backend fails to retreive the device or its data.
|
||||
pub fn create(index: usize, camera_format: Option<CameraFormat>) -> Result<Self, NokhwaError> {
|
||||
pub fn create(index: usize, cam_fmt: Option<CameraFormat>) -> Result<Self, NokhwaError> {
|
||||
let context = match Context::new() {
|
||||
Ok(ctx) => Box::new(ctx),
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenDevice(why.to_string())),
|
||||
@@ -147,6 +148,11 @@ impl<'a> UVCCaptureDevice<'a> {
|
||||
)
|
||||
};
|
||||
|
||||
let camera_format = match cam_fmt {
|
||||
Some(cfmt) => cfmt,
|
||||
None => CameraFormat::default(),
|
||||
};
|
||||
|
||||
Ok(UVCCaptureDeviceBuilder {
|
||||
camera_format,
|
||||
camera_info,
|
||||
@@ -197,16 +203,8 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
|
||||
self.borrow_camera_info().clone()
|
||||
}
|
||||
|
||||
fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError> {
|
||||
let camera_format = CameraFormat::default();
|
||||
|
||||
self.with_mut(|fields| {});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_camera_format(&self) -> Option<CameraFormat> {
|
||||
todo!()
|
||||
fn get_camera_format(&self) -> CameraFormat {
|
||||
*self.borrow_camera_format()
|
||||
}
|
||||
|
||||
fn get_compatible_list_by_resolution(
|
||||
@@ -221,36 +219,51 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
|
||||
}
|
||||
|
||||
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError> {
|
||||
let ret: Result<(), NokhwaError> = self.with_mut(|fields| {
|
||||
*fields.camera_format = Some(new_fmt);
|
||||
let is_streamh_some = { fields.stream_handle_init.get() };
|
||||
if is_streamh_some {
|
||||
// TODO
|
||||
}
|
||||
let prev_fmt = *self.borrow_camera_format();
|
||||
|
||||
Ok(())
|
||||
self.with_camera_format_mut(|cfmt| {
|
||||
*cfmt = new_fmt;
|
||||
});
|
||||
|
||||
let is_streamh_some = self.borrow_stream_handle_init().get();
|
||||
|
||||
if is_streamh_some {
|
||||
return match self.open_stream() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(why) => {
|
||||
// revert
|
||||
self.with_camera_format_mut(|cfmt| {
|
||||
*cfmt = prev_fmt;
|
||||
});
|
||||
Err(NokhwaError::CouldntSetProperty {
|
||||
property: "CameraFormat".to_string(),
|
||||
value: new_fmt.to_string(),
|
||||
error: why.to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_resolution(&self) -> Option<Resolution> {
|
||||
self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::resoltuion))
|
||||
fn get_resolution(&self) -> Resolution {
|
||||
self.borrow_camera_format().resoltuion()
|
||||
}
|
||||
|
||||
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_framerate(&self) -> Option<u32> {
|
||||
self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::framerate))
|
||||
fn get_framerate(&self) -> u32 {
|
||||
self.borrow_camera_format().framerate()
|
||||
}
|
||||
|
||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_frameformat(&self) -> Option<FrameFormat> {
|
||||
self.with_camera_format(|fmt| fmt.as_ref().map(CameraFormat::format))
|
||||
fn get_frameformat(&self) -> FrameFormat {
|
||||
self.borrow_camera_format().format()
|
||||
}
|
||||
|
||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||
@@ -259,42 +272,20 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
|
||||
|
||||
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
let ret: Result<(), NokhwaError> = self.with_mut(|fields| {
|
||||
let stream_format: StreamFormat = match fields.camera_format {
|
||||
Some(fmt) => {
|
||||
let cameraformat: CameraFormat = *fmt;
|
||||
cameraformat.into()
|
||||
}
|
||||
None => {
|
||||
return Err(NokhwaError::CouldntOpenStream(
|
||||
"Please initialise the CameraFormat first!".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
let stream_format: StreamFormat = CameraFormat::into(*fields.camera_format);
|
||||
|
||||
// first, drop the existing stream by setting it to None
|
||||
{
|
||||
match fields.active_stream.try_borrow_mut() {
|
||||
Ok(raw_astream) => {
|
||||
if fields.active_stream_init.get() {
|
||||
std::mem::drop(raw_astream.assume_init());
|
||||
std::mem::drop(raw_astream);
|
||||
*raw_astream = MaybeUninit::uninit();
|
||||
fields.active_stream_init.set(false);
|
||||
}
|
||||
}
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())),
|
||||
if fields.active_stream_init.get() {
|
||||
let innard_value = fields.active_stream.replace(MaybeUninit::uninit());
|
||||
unsafe { std::mem::drop(innard_value.assume_init()) };
|
||||
fields.active_stream_init.set(false);
|
||||
}
|
||||
|
||||
match fields.stream_handle.try_borrow_mut() {
|
||||
Ok(mut raw_streamh) => {
|
||||
if fields.stream_handle_init.get() {
|
||||
std::mem::drop(raw_streamh.assume_init());
|
||||
std::mem::drop(raw_streamh);
|
||||
*raw_streamh = MaybeUninit::uninit();
|
||||
fields.stream_handle_init.set(false);
|
||||
}
|
||||
}
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())),
|
||||
if fields.stream_handle_init.get() {
|
||||
let innard_value = fields.stream_handle.replace(MaybeUninit::uninit());
|
||||
unsafe { std::mem::drop(innard_value.assume_init()) };
|
||||
fields.stream_handle_init.set(false);
|
||||
}
|
||||
}
|
||||
// second, set the stream handle according to the streamformat
|
||||
@@ -322,27 +313,103 @@ impl<'a> CaptureBackendTrait for UVCCaptureDevice<'a> {
|
||||
// finally, get the active stream
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
let frame_sender: Sender<Vec<u8>> = *(self.with_frame_sender(|send| send)).clone();
|
||||
let mut streamh = unsafe {};
|
||||
// let active_stream = streamh.start_stream(|frame, _cnt| {}, counter);
|
||||
let streamh = unsafe {
|
||||
let raw_ptr =
|
||||
(*fields.stream_handle.borrow_mut()).as_ptr() as *mut MaybeUninit<StreamHandle>;
|
||||
let assume_inited: *mut MaybeUninit<StreamHandle<'static>> =
|
||||
raw_ptr.cast::<MaybeUninit<uvc::StreamHandle>>();
|
||||
&mut *assume_inited
|
||||
};
|
||||
let streamh_init = unsafe {
|
||||
match streamh.as_mut_ptr().as_mut() {
|
||||
Some(sth) => sth,
|
||||
None => {
|
||||
return Err(NokhwaError::CouldntOpenStream(
|
||||
"Failed to get mutable raw pointer to stream handle!".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let active_stream = match streamh_init.start_stream(
|
||||
move |frame, _count| {
|
||||
let vec_frame: Vec<u8> = frame.to_rgb().unwrap().to_bytes().to_vec();
|
||||
if frame_sender.send(vec_frame).is_err() {
|
||||
// do nothing
|
||||
}
|
||||
},
|
||||
counter,
|
||||
) {
|
||||
Ok(active) => active,
|
||||
Err(why) => return Err(NokhwaError::CouldntOpenStream(why.to_string())),
|
||||
};
|
||||
*fields.active_stream.borrow_mut() = MaybeUninit::new(active_stream);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
if ret_2.is_err() {
|
||||
return ret_2;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_stream_open(&self) -> bool {
|
||||
todo!()
|
||||
self.with_active_stream_init(Cell::get)
|
||||
}
|
||||
|
||||
fn get_frame(&mut self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError> {
|
||||
todo!()
|
||||
let data = match self.get_frame_raw() {
|
||||
Ok(d) => d,
|
||||
Err(why) => return Err(why),
|
||||
};
|
||||
|
||||
let resolution: Resolution = self.borrow_camera_format().resoltuion();
|
||||
|
||||
let imagebuf: ImageBuffer<Rgb<u8>, Vec<u8>> =
|
||||
match ImageBuffer::from_vec(resolution.width(), resolution.height(), data) {
|
||||
Some(img) => img,
|
||||
None => {
|
||||
return Err(NokhwaError::CouldntCaptureFrame(
|
||||
"ImageBuffer too small! This is probably a bug, please report it!"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(imagebuf)
|
||||
}
|
||||
|
||||
fn get_frame_raw(&mut self) -> Result<Vec<u8>, NokhwaError> {
|
||||
todo!()
|
||||
// assertions
|
||||
if !self.borrow_active_stream_init().get() {
|
||||
return Err(NokhwaError::CouldntCaptureFrame(
|
||||
"Please call `open_stream()` first!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let f_recv = self.borrow_frame_receiver();
|
||||
let messages_iter = f_recv.drain();
|
||||
match messages_iter.last() {
|
||||
Some(msg) => Ok(msg),
|
||||
None => Err(NokhwaError::CouldntCaptureFrame("Too fast!".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
self.with(|fields| {
|
||||
if fields.active_stream_init.get() {
|
||||
let innard_value = fields.active_stream.replace(MaybeUninit::uninit());
|
||||
unsafe { std::mem::drop(innard_value.assume_init()) };
|
||||
fields.active_stream_init.set(false);
|
||||
}
|
||||
|
||||
if fields.stream_handle_init.get() {
|
||||
let innard_value = fields.stream_handle.replace(MaybeUninit::uninit());
|
||||
unsafe { std::mem::drop(innard_value.assume_init()) };
|
||||
fields.stream_handle_init.set(false);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,10 +30,9 @@ impl From<CameraFormat> for Format {
|
||||
/// The backend struct that interfaces with V4L2.
|
||||
/// To see what this does, please see [`CaptureBackendTrait`]
|
||||
/// # Quirks
|
||||
/// Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat)
|
||||
/// each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
|
||||
/// - Calling [`set_resolution()`](CaptureBackendTrait::set_resolution), [`set_framerate()`](CaptureBackendTrait::set_framerate), or [`set_frameformat()`](CaptureBackendTrait::set_frameformat) each internally calls [`set_camera_format()`](CaptureBackendTrait::set_camera_format).
|
||||
pub struct V4LCaptureDevice<'a> {
|
||||
camera_format: Option<CameraFormat>,
|
||||
camera_format: CameraFormat,
|
||||
camera_info: CameraInfo,
|
||||
device: Device,
|
||||
stream_handle: Option<MmapStream<'a>>,
|
||||
@@ -41,9 +40,11 @@ pub struct V4LCaptureDevice<'a> {
|
||||
|
||||
impl<'a> V4LCaptureDevice<'a> {
|
||||
/// Creates a new capture device using the V4L2 backend. Indexes are gives to devices by the OS, and usually numbered by order of discovery.
|
||||
///
|
||||
/// If `camera_format` is `None`, it will be spawned with with 640x480@15 FPS, MJPEG [`CameraFormat`] default.
|
||||
/// # Errors
|
||||
/// This function will error if the camera is currently busy or if V4L2 can't read device information.
|
||||
pub fn new(index: usize, camera_format: Option<CameraFormat>) -> Result<Self, NokhwaError> {
|
||||
pub fn new(index: usize, cam_fmt: Option<CameraFormat>) -> Result<Self, NokhwaError> {
|
||||
let device = match Device::new(index) {
|
||||
Ok(dev) => dev,
|
||||
Err(why) => {
|
||||
@@ -64,6 +65,11 @@ impl<'a> V4LCaptureDevice<'a> {
|
||||
}
|
||||
};
|
||||
|
||||
let camera_format = match cam_fmt {
|
||||
Some(cfmt) => cfmt,
|
||||
None => CameraFormat::default(),
|
||||
};
|
||||
|
||||
Ok(V4LCaptureDevice {
|
||||
camera_format,
|
||||
camera_info,
|
||||
@@ -92,20 +98,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
self.camera_info.clone()
|
||||
}
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError> {
|
||||
match self.camera_format {
|
||||
Some(_) => {
|
||||
if overwrite {
|
||||
return self.set_camera_format(CameraFormat::default());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
None => self.set_camera_format(CameraFormat::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_camera_format(&self) -> Option<CameraFormat> {
|
||||
fn get_camera_format(&self) -> CameraFormat {
|
||||
self.camera_format
|
||||
}
|
||||
|
||||
@@ -242,60 +235,41 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
}
|
||||
};
|
||||
}
|
||||
self.camera_format = Some(new_fmt);
|
||||
self.camera_format = new_fmt;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_resolution(&self) -> Option<Resolution> {
|
||||
self.camera_format.map(|fmt| fmt.resoltuion())
|
||||
fn get_resolution(&self) -> Resolution {
|
||||
self.camera_format.resolution()
|
||||
}
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError> {
|
||||
if let Some(fmt) = self.camera_format {
|
||||
let mut new_fmt = fmt;
|
||||
new_fmt.set_resolution(new_res);
|
||||
self.set_camera_format(new_fmt)
|
||||
} else {
|
||||
self.camera_format = Some(CameraFormat::new(new_res, FrameFormat::MJPEG, 0));
|
||||
Ok(())
|
||||
}
|
||||
let mut new_fmt = self.camera_format;
|
||||
new_fmt.set_resolution(new_res);
|
||||
self.set_camera_format(new_fmt)
|
||||
}
|
||||
|
||||
fn get_framerate(&self) -> Option<u32> {
|
||||
self.camera_format.map(|fmt| fmt.framerate())
|
||||
fn get_framerate(&self) -> u32 {
|
||||
self.camera_format.framerate()
|
||||
}
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError> {
|
||||
if let Some(fmt) = self.camera_format {
|
||||
let mut new_fmt = fmt;
|
||||
new_fmt.set_framerate(new_fps);
|
||||
self.set_camera_format(new_fmt)
|
||||
} else {
|
||||
self.camera_format = Some(CameraFormat::new(
|
||||
Resolution::new(0, 0),
|
||||
FrameFormat::MJPEG,
|
||||
new_fps,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
let mut new_fmt = self.camera_format;
|
||||
new_fmt.set_framerate(new_fps);
|
||||
self.set_camera_format(new_fmt)
|
||||
}
|
||||
|
||||
fn get_frameformat(&self) -> Option<FrameFormat> {
|
||||
self.camera_format.map(|fmt| fmt.format())
|
||||
fn get_frameformat(&self) -> FrameFormat {
|
||||
self.camera_format.format()
|
||||
}
|
||||
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
fn set_frameformat(&mut self, fourcc: FrameFormat) -> Result<(), NokhwaError> {
|
||||
if let Some(fmt) = self.camera_format {
|
||||
let mut new_fmt = fmt;
|
||||
new_fmt.set_format(fourcc);
|
||||
self.set_camera_format(new_fmt)
|
||||
} else {
|
||||
self.camera_format = Some(CameraFormat::new(Resolution::new(0, 0), fourcc, 0));
|
||||
Ok(())
|
||||
}
|
||||
let mut new_fmt = self.camera_format;
|
||||
new_fmt.set_format(fourcc);
|
||||
self.set_camera_format(new_fmt)
|
||||
}
|
||||
|
||||
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
@@ -313,15 +287,7 @@ impl<'a> CaptureBackendTrait for V4LCaptureDevice<'a> {
|
||||
|
||||
fn get_frame(&mut self) -> Result<ImageBuffer<Rgb<u8>, Vec<u8>>, NokhwaError> {
|
||||
let raw_frame = self.get_frame_raw()?;
|
||||
let cam_fmt = match self.camera_format {
|
||||
Some(fmt) => fmt,
|
||||
None => {
|
||||
return Err(NokhwaError::CouldntCaptureFrame(
|
||||
"CameraFormat isn't initialized! This is probably a bug, please report it"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let cam_fmt = self.camera_format;
|
||||
let conv = match cam_fmt.format() {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb888(&raw_frame)?,
|
||||
FrameFormat::YUYV => yuyv422_to_rgb888(&raw_frame)?,
|
||||
|
||||
+14
-17
@@ -9,50 +9,47 @@ use crate::{
|
||||
|
||||
/// This trait is for any backend that allows you to grab and take frames from a camera.
|
||||
/// Many of the backends are **blocking**, if the camera is occupied the program will halt while it waits for it to become availible.
|
||||
///
|
||||
/// **Note**:
|
||||
/// - Backends, if not provided with a camera format, will be spawned with 640x480@15 FPS, MJPEG [`CameraFormat`].
|
||||
/// - Behaviour can differ from backend to backend. While the [`Capture`] struct abstracts most of this away, if you plan to use the raw backend structs please read the `Quirks` section of each backend.
|
||||
pub trait CaptureBackendTrait {
|
||||
/// Gets the camera information such as Name and Index as a [`CameraInfo`].
|
||||
fn get_info(&self) -> CameraInfo;
|
||||
/// Assigns a sensible default to the backend's [`CameraFormat`]. Usually 640x480 @ 15 FPS + MJPEG.
|
||||
/// If there is already a value assigned to the [`CameraFormat`], it will only be overwritten if `overwrite` is set to `true`.
|
||||
/// If false, this function will do nothing (NO-OP).
|
||||
/// 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.
|
||||
fn init_camera_format_default(&mut self, overwrite: bool) -> Result<(), NokhwaError>;
|
||||
/// Gets the current [`CameraFormat`]. Will return none if no format has been set yet.
|
||||
fn get_camera_format(&self) -> Option<CameraFormat>;
|
||||
/// Gets the current [`CameraFormat`].
|
||||
fn get_camera_format(&self) -> CameraFormat;
|
||||
/// 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 Unsupported Operation (see: [`NokhwaError::UnsupportedOperation`]).
|
||||
/// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`NokhwaError::UnsupportedOperation`]).
|
||||
fn get_compatible_list_by_resolution(
|
||||
&self,
|
||||
fourcc: FrameFormat,
|
||||
) -> Result<HashMap<Resolution, Vec<u32>>, NokhwaError>;
|
||||
/// Gets the supported camera formats.
|
||||
/// # Errors
|
||||
/// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a NO-OP [`NokhwaError::UnsupportedOperation`]
|
||||
/// This will error if the camera is not queryable or a query operation has failed. Some backends will error this out as a Unsupported Operation ([`NokhwaError::UnsupportedOperation`]).
|
||||
fn get_resolution_list(&self, fourcc: FrameFormat) -> Result<Vec<Resolution>, NokhwaError>;
|
||||
/// 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.
|
||||
fn set_camera_format(&mut self, new_fmt: CameraFormat) -> Result<(), NokhwaError>;
|
||||
/// Gets the current camera resolution (See: [`Resolution`]). Will return none if no resolution has been set yet.
|
||||
fn get_resolution(&self) -> Option<Resolution>;
|
||||
/// Gets the current camera resolution (See: [`Resolution`], [`CameraFormat`]).
|
||||
fn get_resolution(&self) -> Resolution;
|
||||
/// Will set the current [`Resolution`]
|
||||
/// This will reset the current stream if used while stream is opened.
|
||||
/// # Errors
|
||||
/// If you started the stream and the camera rejects the new resolution, this will return an error.
|
||||
fn set_resolution(&mut self, new_res: Resolution) -> Result<(), NokhwaError>;
|
||||
/// Gets the current camera framerate. Will return none if no framerate has been set yet.
|
||||
fn get_framerate(&self) -> Option<u32>;
|
||||
/// Gets the current camera framerate (See: [`CameraFormat`]).
|
||||
fn get_framerate(&self) -> u32;
|
||||
/// Will set the current framerate
|
||||
/// This will reset the current stream if used while stream is opened.
|
||||
/// # Errors
|
||||
/// If you started the stream and the camera rejects the new framerate, this will return an error.
|
||||
fn set_framerate(&mut self, new_fps: u32) -> Result<(), NokhwaError>;
|
||||
/// Gets the current camera's frame format (See: [`FrameFormat`]). Will return none if no frame format has been set yet.
|
||||
fn get_frameformat(&self) -> Option<FrameFormat>;
|
||||
/// Gets the current camera's frame format (See: [`FrameFormat`], [`CameraFormat`]).
|
||||
fn get_frameformat(&self) -> FrameFormat;
|
||||
/// Will set the current [`FrameFormat`]
|
||||
/// This will reset the current stream if used while stream is opened.
|
||||
/// # Errors
|
||||
|
||||
+25
-4
@@ -1,9 +1,10 @@
|
||||
use crate::NokhwaError;
|
||||
use mozjpeg::Decompress;
|
||||
use std::fmt::Formatter;
|
||||
use std::{cmp::Ordering, convert::TryFrom, fmt::Display, slice::from_raw_parts};
|
||||
|
||||
/// Describes a frame format (i.e. how the bytes themselves are encoded). Often called `FourCC` <br>
|
||||
/// YUYV is a mathmatical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr) <br>
|
||||
/// YUYV is a mathematical color space. You can read more [here.](https://en.wikipedia.org/wiki/YCbCr) <br>
|
||||
/// MJPEG is a motion-jpeg compressed frame, it allows for high frame rates.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Hash)]
|
||||
pub enum FrameFormat {
|
||||
@@ -84,7 +85,7 @@ impl Ord for Resolution {
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a conveinence struct that holds all information about the format of a webcam stream.
|
||||
/// This is a convenience struct that holds all information about the format of a webcam stream.
|
||||
/// It consists of a [`Resolution`], [`FrameFormat`], and a framerate(u8).
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq)]
|
||||
pub struct CameraFormat {
|
||||
@@ -116,7 +117,7 @@ impl CameraFormat {
|
||||
}
|
||||
|
||||
/// Get the resolution of the current [`CameraFormat`]
|
||||
pub fn resoltuion(&self) -> Resolution {
|
||||
pub fn resolution(&self) -> Resolution {
|
||||
self.resolution
|
||||
}
|
||||
|
||||
@@ -166,6 +167,16 @@ impl Default for CameraFormat {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CameraFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}@{}FPS, {} Format",
|
||||
self.resolution, self.framerate, self.format
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a Camera e.g. its name.
|
||||
/// `description` amd `misc` may contain backend-specific information.
|
||||
/// `index` is a camera's index given to it by (usually) the OS usually in the order it is known to the system.
|
||||
@@ -241,6 +252,16 @@ impl Ord for CameraInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for CameraInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Name: {}, Description: {}, Extra: {}, Index: {}",
|
||||
self.human_name, self.description, self.misc, self.index
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The list of known capture backends to the library. <br>
|
||||
/// - AUTO is special - it tells the Camera struct to automatically choose a backend most suited for the current platform.
|
||||
/// - V4L2 - `Video4Linux2`, a linux specific backend.
|
||||
@@ -267,7 +288,7 @@ impl Display for CaptureAPIBackend {
|
||||
|
||||
/// Converts a MJPEG stream of [u8] into a Vec<u8> of RGB888. (R,G,B,R,G,B,...)
|
||||
/// # Errors
|
||||
/// If `mozjpeg` fails to read scanlines or setup the decompresser, this will error.
|
||||
/// If `mozjpeg` fails to read scanlines or setup the decompressor, this will error.
|
||||
/// # Safety
|
||||
/// This function uses `unsafe`. The caller must ensure that:
|
||||
/// - The input data is of the right size, does not exceed bounds, and/or the final size matches with the initial size.
|
||||
|
||||
Reference in New Issue
Block a user