diff --git a/Cargo.lock b/Cargo.lock index f11aab3..7b93601 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2518,7 +2518,6 @@ dependencies = [ "num-rational 0.4.2", "num-traits", "opencv 0.93.1", - "paste", "rgb", "serde", "thiserror 2.0.0", diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index cabb927..ef1be3b 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -23,7 +23,6 @@ test-fail-warnings = [] [dependencies] thiserror = "2.0" bytes = "1.3" -paste = "1.0" flume = "0.11" num-traits = "0.2" diff --git a/nokhwa-core/src/camera.rs b/nokhwa-core/src/camera.rs index 4cb5727..9e9ae0f 100644 --- a/nokhwa-core/src/camera.rs +++ b/nokhwa-core/src/camera.rs @@ -25,6 +25,7 @@ pub trait Setting { } #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncSetting { async fn enumerate_formats_async(&self) -> Result, NokhwaError>; @@ -53,6 +54,7 @@ pub trait Capture { } #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncStream { async fn open_stream_async(&mut self) -> Result; @@ -62,4 +64,5 @@ pub trait AsyncStream { pub trait Camera: Setting + Capture {} #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncCamera: Camera + AsyncSetting + AsyncStream {} diff --git a/nokhwa-core/src/error.rs b/nokhwa-core/src/error.rs index d2ee3a4..dc494cb 100644 --- a/nokhwa-core/src/error.rs +++ b/nokhwa-core/src/error.rs @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -use crate::{frame_format::FrameFormat, types::ApiBackend}; +use crate::{frame_format::FrameFormat}; use std::fmt::{Debug}; use thiserror::Error; use crate::platform::Backends; @@ -27,9 +27,9 @@ pub enum NokhwaError { #[error("Unitialized Camera. Call `init()` first!")] UnitializedError, #[error("Could not initialize {backend}: {error}")] - InitializeError { backend: ApiBackend, error: String }, + InitializeError { backend: Backends, error: String }, #[error("Could not shutdown {backend}: {error}")] - ShutdownError { backend: ApiBackend, error: String }, + ShutdownError { backend: Backends, error: String }, #[error("Error: {0}")] GeneralError(String), #[error("Could not generate required structure {structure}: {error}")] diff --git a/nokhwa-core/src/format_request.rs b/nokhwa-core/src/format_request.rs index 5750f8b..f24220e 100644 --- a/nokhwa-core/src/format_request.rs +++ b/nokhwa-core/src/format_request.rs @@ -4,139 +4,140 @@ use crate::{ ranges::Range, types::{CameraFormat, FrameRate, Resolution}, }; -use std::cmp::Ordering; use crate::ranges::ValidatableRange; -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] -enum ClosestType { - Resolution, - FrameRate, - Both, - None, -} - -#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum CustomFormatRequestType { - HighestFrameRate, - HighestResolution, - Closest, - Exact, -} /// A helper for choosing a [`CameraFormat`]. /// The use of this is completely optional - for a simpler way try [`crate::camera::Camera::enumerate_formats`]. /// /// The `frame_format` field filters out the [`CameraFormat`]s by [`FrameFormat`]. -pub enum FormatRequest { +#[derive(Clone, Debug, PartialEq)] +pub enum FormatRequestType { /// Pick the closest [`CameraFormat`] to the one requested Closest { resolution: Option>, frame_rate: Option>, - frame_format: Vec, }, HighestFrameRate { frame_rate: Range, - frame_format: Vec, }, HighestResolution { resolution: Range, - frame_format: Vec, }, Exact { resolution: Resolution, frame_rate: FrameRate, - frame_format: Vec, }, + Any, +} + +#[derive(Clone, Debug)] +pub struct FormatRequest { + request_type: FormatRequestType, + allowed_frame_formats: Vec, } impl FormatRequest { - pub fn sort_formats(&self, list_of_formats: &[CameraFormat]) -> Vec { - if list_of_formats.is_empty() { - return vec![]; + pub fn new(format_request_type: FormatRequestType, allowed_frame_formats: Vec) -> Self { + Self { + request_type: format_request_type, + allowed_frame_formats, } + } - match self { - FormatRequest::Closest { + pub fn best<'a>(&self, camera_formats: &'a Vec) -> Option<&'a CameraFormat> { + camera_formats.first() + } + + pub fn sort_foramts(&self, mut camera_formats: Vec) -> Vec { + if camera_formats.is_empty() { + return camera_formats; + } + + match self.request_type { + FormatRequestType::Closest { resolution, frame_rate, - frame_format, + .. } => { let resolution_point = resolution.map(|x| x.preferred()); let frame_rate_point = frame_rate.map(|x| x.preferred()); // lets calcuate distance in 3 dimensions (add both resolution and frame_rate together) - let mut distances = list_of_formats - .iter() - .filter(|x| frame_format.contains(&x.format())) - .map(|fmt| { - let frame_rate_distance = match frame_rate_point { - Some(f_point) => (fmt.frame_rate() - f_point).approximate_float().unwrap_or(f32::INFINITY).abs(), - None => 0_f32, - }; - - let resolution_point_distance = match resolution_point { - Some(res_pt) => fmt.resolution().distance_from(&res_pt) as f32, - None => 0_f32, - }; - - (frame_rate_distance + resolution_point_distance, fmt) - }) - .collect::>(); - distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); - distances.into_iter().map(|x| x.1).copied().collect() + camera_formats.sort_by(|a, b| { + let a_distance = format_distance_to_point(&resolution_point, &frame_rate_point, a); + let b_distance = format_distance_to_point(&resolution_point, &frame_rate_point, b); + + a_distance.total_cmp(&b_distance) + }); + + camera_formats.into_iter().filter(|fmt| { + self.allowed_frame_formats.contains(fmt.format()) + }).filter(|cam_fmt| { + if let Some(res_range) = resolution { + return res_range.validate(cam_fmt.resolution()) + } + + if let Some(frame_rate_range) = frame_rate { + return frame_rate_range.validate(&cam_fmt.frame_rate()) + } + true + }).collect() } - FormatRequest::HighestFrameRate { - frame_rate, - frame_format, + FormatRequestType::HighestFrameRate { + frame_rate } => { - let mut formats = list_of_formats - .iter() - .filter(|x| { - frame_format.contains(&x.format()) && frame_rate.validate(&x.frame_rate()).is_ok() - }) - .collect::>(); - formats.sort(); - formats.into_iter().copied().collect() + camera_formats.sort_by(|a, b| { + a.frame_rate().cmp(b.frame_rate()) + }); + + camera_formats.into_iter().filter(|fmt| { + self.allowed_frame_formats.contains(fmt.format()) + }).filter(|a| { + frame_rate.validate(a.frame_rate()) + }).collect() } - FormatRequest::HighestResolution { - resolution, - frame_format, + FormatRequestType::HighestResolution { + resolution } => { - let mut formats = list_of_formats - .iter() - .filter(|x| { - frame_format.contains(&x.format()) && resolution.validate(&x.resolution()).is_ok() - }) - .collect::>(); - formats.sort(); - formats.into_iter().copied().collect() + camera_formats.sort_by(|a, b| { + a.resolution().cmp(b.resolution()) + }); + + camera_formats.into_iter().filter(|fmt| { + self.allowed_frame_formats.contains(fmt.format()) + }).filter(|a| { + resolution.validate(a.resolution()) + }).collect() } - FormatRequest::Exact { + FormatRequestType::Exact { resolution, frame_rate, - frame_format, } => { - let mut formats = list_of_formats - .iter() - .filter(|x| { - frame_format.contains(&x.format()) - && resolution == &x.resolution() - && frame_rate == &x.frame_rate() - }) - .collect::>(); - formats.sort(); - formats.into_iter().copied().collect() + camera_formats.into_iter().filter(|fmt| { + self.allowed_frame_formats.contains(fmt.format()) + }).filter(|a| { + resolution.eq(a.resolution()) && frame_rate.eq(a.frame_rate()) + }).collect() + } + FormatRequestType::Any => { + // return as-is + camera_formats } } } +} - /// - #[must_use] - pub fn resolve(&self, list_of_formats: &[CameraFormat]) -> Option { - if list_of_formats.is_empty() { - return None; - } +pub fn format_distance_to_point(resolution: &Option, frame_rate: &Option, format: &CameraFormat) -> f32 { + let frame_rate_distance = match frame_rate { + Some(f_point) => (format.frame_rate() - f_point).approximate_float().unwrap_or(f32::INFINITY).abs(), + None => 0_f32, + }; - Some(self.sort_formats(list_of_formats).remove(0)) - } + let resolution_point_distance = match resolution { + Some(res_pt) => format.resolution().distance_from(&res_pt) as f32, + None => 0_f32, + }; + + frame_rate_distance + resolution_point_distance } diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index 7ecab52..b9ed59a 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -4,7 +4,7 @@ #![allow(clippy::cast_sign_loss)] #![allow(clippy::cast_possible_truncation)] #![cfg_attr(feature = "test-fail-warning", deny(warnings))] -#![cfg_attr(feature = "docs-features", feature(doc_cfg))] +// #![cfg_attr(feature = "docs-features", feature(doc_cfg))] /* * Copyright 2022 l1npengtul / The Nokhwa Contributors * @@ -29,10 +29,9 @@ pub mod format_request; pub mod frame_buffer; pub mod frame_format; pub mod properties; -pub mod query; pub mod ranges; pub mod traits; pub mod types; pub mod utils; pub mod stream; -mod platform; +pub mod platform; diff --git a/nokhwa-core/src/platform.rs b/nokhwa-core/src/platform.rs index 48fe6ed..dd51151 100644 --- a/nokhwa-core/src/platform.rs +++ b/nokhwa-core/src/platform.rs @@ -1,3 +1,4 @@ +use std::fmt::{Display, Formatter}; use crate::camera::{AsyncCamera, Camera}; use crate::error::NokhwaResult; use crate::types::{CameraIndex, CameraInformation}; @@ -8,9 +9,16 @@ pub enum Backends { WebWASM, AVFoundation, MicrosoftMediaFoundation, + OpenCV, Custom(&'static str) } +impl Display for Backends { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + pub trait PlatformTrait { const PLATFORM: Backends; type Camera: Camera; @@ -26,6 +34,7 @@ pub trait PlatformTrait { } #[cfg(feature = "async")] +#[cfg_attr(feature = "async", async_trait::async_trait)] pub trait AsyncPlatformTrait { const PLATFORM: Backends; type AsyncCamera: AsyncCamera; diff --git a/nokhwa-core/src/properties.rs b/nokhwa-core/src/properties.rs index ba86d18..192d600 100644 --- a/nokhwa-core/src/properties.rs +++ b/nokhwa-core/src/properties.rs @@ -1,6 +1,5 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{Display, Formatter}; -use std::ops::{ControlFlow}; use crate::error::{NokhwaError, NokhwaResult}; use crate::ranges::{Range, ValidatableRange}; @@ -122,7 +121,7 @@ impl ControlBody { } pub fn set_value(&mut self, value: ControlValue) -> NokhwaResult> { - if let ControlFlow::Break(()) = self.descriptor.validate(&value) { + if self.descriptor.validate(&value) { return Err(NokhwaError::SetPropertyError { property: "Control Body".to_string(), value: value.to_string(), @@ -186,73 +185,68 @@ pub enum ControlValueDescriptor { } impl ControlValueDescriptor { - pub fn validate(&self, value: &ControlValue) -> ControlFlow<()> { + pub fn validate(&self, value: &ControlValue) -> bool { match self { ControlValueDescriptor::Null => { if let &ControlValue::Null = value { - return ControlFlow::Continue(()) + return false } } ControlValueDescriptor::Integer(int_range) => { if let ControlValue::Integer(i) = value { - int_range.validate(i)?; + return int_range.validate(i) } } ControlValueDescriptor::BitMask => { if let &ControlValue::BitMask(_) = value { - return ControlFlow::Continue(()) + return true } } ControlValueDescriptor::Float(float_range) => { if let ControlValue::Float(i) = value { - float_range.validate(i)?; + return float_range.validate(i) } } ControlValueDescriptor::String => { if let &ControlValue::String(_) = value { - return ControlFlow::Continue(()) + return true } } ControlValueDescriptor::Boolean => { if let &ControlValue::Boolean(_) = value { - return ControlFlow::Continue(()) + return true } } ControlValueDescriptor::Array(arr) => { - if arr.is_valid_value(value) { - return ControlFlow::Continue(()) + if let &ControlValue::Array(_) = value { + return arr.is_valid_value(value) } } ControlValueDescriptor::MultiChoice(choices) => { - if let &ControlValue::Array(values) = value { + if let ControlValue::Array(values) = value { for v in values { let mut contains = false; + let vl: ControlValue = v.clone().into(); for choice in choices { - if choice.is_valid_value(v.as_ref()) { + if choice.is_valid_value(&vl) { contains = true; break; } } - if !contains { - return ControlFlow::Break(()) - } + return contains } } } ControlValueDescriptor::Enum(choices) => { for choice in choices { - if choice.is_valid_value(&value) { - return ControlFlow::Continue(()) - } + return choice.is_valid_value(&value) } } ControlValueDescriptor::Map(map) => { if let ControlValue::Map(setting_map) = &value { for (setting_key, setting_value) in setting_map { if let Some(descriptor) = map.get(setting_key) { - if !descriptor.is_valid_value(setting_value.as_ref()) { - return ControlFlow::Break(()) - } + return !descriptor.is_valid_primitive_value(setting_value) } } } @@ -260,15 +254,12 @@ impl ControlValueDescriptor { ControlValueDescriptor::Menu(menu) => { if let ControlValue::KeyValue(k, v) = &value { if let Some(descriptor) = menu.get(k) { - if descriptor.is_valid_value(v.as_ref()) { - return ControlFlow::Continue(()) - } + return descriptor.is_valid_primitive_value(v) } } } } - - ControlFlow::Break(()) + false } } @@ -283,35 +274,71 @@ pub enum ControlValuePrimitiveDescriptor { } impl ControlValuePrimitiveDescriptor { + pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool { + match self { + ControlValuePrimitiveDescriptor::Null => { + if let ControlValuePrimitive::Null = other { + return true + } + } + ControlValuePrimitiveDescriptor::Integer(i) => { + if let ControlValuePrimitive::Integer(v) = other { + return i.validate(v) + } + } + ControlValuePrimitiveDescriptor::BitMask => { + if let ControlValuePrimitive::BitMask(_) = other { + return true + } + } + ControlValuePrimitiveDescriptor::Float(f) => { + if let ControlValuePrimitive::Float(v) = other { + return f.validate(v) + } + } + ControlValuePrimitiveDescriptor::String => { + if let ControlValuePrimitive::String(_) = other { + return true + } + } + ControlValuePrimitiveDescriptor::Boolean => { + if let ControlValuePrimitive::Boolean(_) = other { + return true + } + } + } + false + } + pub fn is_valid_value(&self, other: &ControlValue) -> bool { match self { ControlValuePrimitiveDescriptor::Null => { - if let &ControlValue::Null = other { + if let ControlValue::Null = other { return true } } - ControlValuePrimitiveDescriptor::Integer(int_range) => { - if let ControlValue::Integer(i) = other { - return int_range.validate(i).is_ok() + ControlValuePrimitiveDescriptor::Integer(i) => { + if let ControlValue::Integer(v) = other { + return i.validate(v) } } ControlValuePrimitiveDescriptor::BitMask => { - if let &ControlValue::BitMask(_) = other { + if let ControlValue::BitMask(_) = other { return true } } - ControlValuePrimitiveDescriptor::Float(float_range) => { - if let ControlValue::Float(i) = other { - return float_range.validate(i).is_ok() + ControlValuePrimitiveDescriptor::Float(f) => { + if let ControlValue::Float(v) = other { + return f.validate(v) } } ControlValuePrimitiveDescriptor::String => { - if let &ControlValue::String(_) = other { + if let ControlValue::String(_) = other { return true } } ControlValuePrimitiveDescriptor::Boolean => { - if let &ControlValue::Boolean(_) = other { + if let ControlValue::Boolean(_) = other { return true } } @@ -330,15 +357,15 @@ pub enum ControlValuePrimitive { Boolean(bool), } -impl AsRef for ControlValuePrimitive { - fn as_ref(&self) -> &ControlValue { - match self { - ControlValuePrimitive::Null => &ControlValue::Null, - ControlValuePrimitive::Integer(i) => &ControlValue::Integer(*i), - ControlValuePrimitive::BitMask(b) => &ControlValue::BitMask(*b), - ControlValuePrimitive::Float(f) => &ControlValue::Float(*f), - ControlValuePrimitive::String(s) => &ControlValue::String(s.clone()), - ControlValuePrimitive::Boolean(b) => &ControlValue::Boolean(*b), +impl From for ControlValue { + fn from(value: ControlValuePrimitive) -> Self { + match value { + ControlValuePrimitive::Null => ControlValue::Null, + ControlValuePrimitive::Integer(i) => ControlValue::Integer(i), + ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b), + ControlValuePrimitive::Float(f) => ControlValue::Float(f), + ControlValuePrimitive::String(s) => ControlValue::String(s), + ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b), } } } @@ -357,6 +384,22 @@ pub enum ControlValue { } impl ControlValue { + pub fn primitive_same_type(&self, other: &ControlValuePrimitive) -> bool { + match other { + ControlValuePrimitive::Null => { + if let ControlValue::Null = self { + return true + } + } + ControlValuePrimitive::Integer(_) => {if let ControlValue::Integer(_) = self {return true}} + ControlValuePrimitive::BitMask(_) => {if let ControlValue::BitMask(_) = self {return true}} + ControlValuePrimitive::Float(_) => {if let ControlValue::Float(_) = self {return true}} + ControlValuePrimitive::String(_) => {if let ControlValue::String(_) = self {return true}} + ControlValuePrimitive::Boolean(_) => {if let ControlValue::Boolean(_) = self {return true}} + } + false + } + pub fn same_type(&self, other: &ControlValue) -> bool { match self { ControlValue::Null => { @@ -392,6 +435,8 @@ impl ControlValue { false } + + } impl Display for ControlValue { @@ -399,16 +444,3 @@ impl Display for ControlValue { write!(f, "Control Value: {self:?}") } } - -impl From for ControlValue { - fn from(value: ControlValuePrimitive) -> Self { - match value { - ControlValuePrimitive::Null => ControlValue::Null, - ControlValuePrimitive::Integer(i) => ControlValue::Integer(i), - ControlValuePrimitive::BitMask(b) => ControlValue::BitMask(b), - ControlValuePrimitive::Float(f) => ControlValue::Float(f), - ControlValuePrimitive::String(s) => ControlValue::String(s), - ControlValuePrimitive::Boolean(b) => ControlValue::Boolean(b), - } - } -} diff --git a/nokhwa-core/src/ranges.rs b/nokhwa-core/src/ranges.rs index 88fc142..253c904 100644 --- a/nokhwa-core/src/ranges.rs +++ b/nokhwa-core/src/ranges.rs @@ -15,7 +15,7 @@ pub trait ValidatableRange { type Validation; /// Validates the value. - fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure>; + fn validate(&self, value: &Self::Validation) -> bool; } /// Creates a range of values. @@ -117,7 +117,7 @@ where { type Validation = T; - fn validate(&self, value: &T) -> Result<(), RangeValidationFailure> { + fn validate(&self, value: &T) -> bool { num_range_validate( self.minimum, self.maximum, @@ -195,11 +195,8 @@ where { type Validation = T; - fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure> { - if self.available.contains(value) { - return Ok(()); - } - Err(RangeValidationFailure::default()) + fn validate(&self, value: &Self::Validation) -> bool { + self.available.contains(value) } } @@ -294,11 +291,8 @@ where { type Validation = T; - fn validate(&self, value: &Self::Validation) -> Result<(), RangeValidationFailure> { - if self.appendable_options.contains(value) { - return Ok(()); - } - Err(RangeValidationFailure::default()) + fn validate(&self, value: &Self::Validation) -> bool { + self.appendable_options.contains(value) } } @@ -336,8 +330,8 @@ where impl ValidatableRange for Simple { type Validation = T; - fn validate(&self, _: &Self::Validation) -> Result<(), RangeValidationFailure> { - Ok(()) + fn validate(&self, _: &Self::Validation) -> bool { + true } } @@ -390,7 +384,7 @@ fn num_range_validate( upper_inclusive: bool, step: Option, value: T, -) -> Result<(), RangeValidationFailure> +) -> bool where T: SimpleRangeItem, { @@ -403,12 +397,12 @@ where // 7 - 4 = 3 // 3 % 3 = 0 Valid! if prepared_value % step != T::ZERO { - return Err(RangeValidationFailure::default()); + return false } } if value == default { - return Ok(()); + return true } if let Some(min) = minimum { @@ -418,7 +412,7 @@ where min < value }; if test { - return Err(RangeValidationFailure::default()); + return false } } @@ -429,11 +423,11 @@ where max > value }; if test { - return Err(RangeValidationFailure::default()); + return false } } - Ok(()) + true } pub trait SimpleRangeItem: Copy + Clone + Debug + Div + Sub + Rem + PartialOrd + PartialEq { diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 453a497..c732065 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -289,6 +289,14 @@ impl Sub for FrameRate { } } +impl Sub for &FrameRate { + type Output = FrameRate; + + fn sub(self, rhs: Self) -> Self::Output { + (self.rational - rhs.rational).into() + } +} + impl Rem for FrameRate { type Output = FrameRate; @@ -345,8 +353,8 @@ impl CameraFormat { /// Get the resolution of the current [`CameraFormat`] #[must_use] - pub fn resolution(&self) -> Resolution { - self.resolution + pub fn resolution(&self) -> &Resolution { + &self.resolution } /// Get the width of the resolution of the current [`CameraFormat`] @@ -368,8 +376,8 @@ impl CameraFormat { /// Get the frame rate of the current [`CameraFormat`] #[must_use] - pub fn frame_rate(&self) -> FrameRate { - self.frame_rate + pub fn frame_rate(&self) -> &FrameRate { + &self.frame_rate } /// Set the [`CameraFormat`]'s frame rate. @@ -379,8 +387,8 @@ impl CameraFormat { /// Get the [`CameraFormat`]'s format. #[must_use] - pub fn format(&self) -> FrameFormat { - self.format + pub fn format(&self) -> &FrameFormat { + &self.format } /// Set the [`CameraFormat`]'s format.