From 0646b0abf522033e6dfc2c39b92abcf28fbed22c Mon Sep 17 00:00:00 2001 From: l1npengtul Date: Thu, 5 Dec 2024 12:52:50 +0900 Subject: [PATCH] make control api simpler, because most platforms arnt v4l2 --- Cargo.lock | 22 +- Cargo.toml | 2 +- flake.lock | 18 +- nokhwa-bindings-macos/src/lib.rs | 4 +- nokhwa-bindings-windows/src/lib.rs | 12 +- nokhwa-core/Cargo.toml | 1 + nokhwa-core/src/camera.rs | 1 - nokhwa-core/src/decoder.rs | 2 - nokhwa-core/src/format_request.rs | 32 +- nokhwa-core/src/properties.rs | 711 +++---------------------- nokhwa-core/src/stream.rs | 3 +- nokhwa-core/src/types.rs | 14 +- src/backends/capture/avfoundation.rs | 6 +- src/backends/capture/browser_camera.rs | 6 +- src/backends/capture/msmf_backend.rs | 4 +- src/backends/capture/opencv_backend.rs | 10 +- src/camera.rs | 4 +- src/threaded.rs | 6 +- 18 files changed, 143 insertions(+), 715 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 644149e..f11aab3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "ab_glyph" @@ -2516,6 +2516,7 @@ dependencies = [ "futures", "image 0.25.0", "num-rational 0.4.2", + "num-traits", "opencv 0.93.1", "paste", "rgb", @@ -2524,16 +2525,6 @@ dependencies = [ "wgpu 23.0.1", ] -[[package]] -name = "nokhwa-decoder" -version = "0.1.0" -dependencies = [ - "dcv-color-primitives", - "mozjpeg", - "nokhwa-core", - "yuvutils-rs", -] - [[package]] name = "nokhwactl" version = "0.10.0" @@ -4745,15 +4736,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" -[[package]] -name = "yuvutils-rs" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30a62ce6c5fbf13dbf8d92e7cd805d74574a7f84d0e81462ca3cb8192be5de2" -dependencies = [ - "num-traits", -] - [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 07c2f40..b7f8ab3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/l1npengtul/nokhwa" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] -members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "examples/*", "nokhwa-decoder"] +members = ["nokhwa-bindings-macos", "nokhwa-bindings-windows", "nokhwa-bindings-linux", "nokhwa-core", "examples/*"] exclude = ["examples/jscam"] [lib] diff --git a/flake.lock b/flake.lock index 02611d7..4913fdf 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1730958623, - "narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=", + "lastModified": 1733097829, + "narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "85f7e662eda4fa3a995556527c87b2524b691933", + "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1731292155, - "narHash": "sha256-fYVoUUtSadbOrH0z0epVQDsStBDS/S/fAK//0ECQAAI=", + "lastModified": 1733193245, + "narHash": "sha256-nwvKoPi3S6XyliqBRuC+01QFF0k94ZOvnoZtbGi/ObM=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "7c4cd99ed7604b79e8cb721099ac99c66f656b3a", + "rev": "3458f7f946ba61d1a1069aedcc17d7b7616f23cd", "type": "github" }, "original": { diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index 4d21190..be1865b 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -246,7 +246,7 @@ mod internal { ffi::{c_float, c_void, CStr}, sync::Arc, }; - use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValueSetter, KnownCameraControl}; + use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; const UTF8_ENCODING: usize = 4; type CGFloat = c_float; @@ -1492,7 +1492,7 @@ mod internal { pub fn set_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { let rc = self.get_controls()?; let controls = rc diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index 37be346..619f7eb 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -46,7 +46,7 @@ pub mod wmf { Arc, }, }; - use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValueSetter, KnownCameraControl}; + use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; use windows::Win32::Media::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual}; use windows::Win32::Media::MediaFoundation::{ IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM, @@ -849,7 +849,7 @@ pub mod wmf { pub fn set_control( &mut self, control: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { let current_value = self.control(control)?; @@ -897,8 +897,8 @@ pub mod wmf { })?; let ctrl_value = match value { - ControlValueSetter::Integer(i) => i as i32, - ControlValueSetter::Boolean(b) => i32::from(b), + ControlValue::Integer(i) => i as i32, + ControlValue::Boolean(b) => i32::from(b), v => { return Err(NokhwaError::StructureError { structure: format!("ControlValueSetter {}", v), @@ -1229,7 +1229,7 @@ pub mod wmf { CameraFormat, CameraIndex, CameraInfo, }; use std::borrow::Cow; - use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; + use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; pub fn initialize_mf() -> Result<(), NokhwaError> { Err(NokhwaError::NotImplementedError( @@ -1287,7 +1287,7 @@ pub mod wmf { pub fn set_control( &mut self, _control: KnownCameraControl, - _value: ControlValueSetter, + _value: ControlValue, ) -> Result<(), NokhwaError> { Err(NokhwaError::NotImplementedError( "Only on Windows".to_string(), diff --git a/nokhwa-core/Cargo.toml b/nokhwa-core/Cargo.toml index c96970f..cabb927 100644 --- a/nokhwa-core/Cargo.toml +++ b/nokhwa-core/Cargo.toml @@ -25,6 +25,7 @@ thiserror = "2.0" bytes = "1.3" paste = "1.0" flume = "0.11" +num-traits = "0.2" [dependencies.num-rational] version = "0.4" diff --git a/nokhwa-core/src/camera.rs b/nokhwa-core/src/camera.rs index f1f80c6..a47f566 100644 --- a/nokhwa-core/src/camera.rs +++ b/nokhwa-core/src/camera.rs @@ -1,5 +1,4 @@ use crate::error::{NokhwaError, NokhwaResult}; -use crate::frame_buffer::FrameBuffer; use crate::frame_format::FrameFormat; use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue}; use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution}; diff --git a/nokhwa-core/src/decoder.rs b/nokhwa-core/src/decoder.rs index 2795e58..b751c6c 100644 --- a/nokhwa-core/src/decoder.rs +++ b/nokhwa-core/src/decoder.rs @@ -1,8 +1,6 @@ use crate::{error::NokhwaError, frame_buffer::FrameBuffer, frame_format::FrameFormat}; use image::{ImageBuffer, Pixel}; use std::{ - error::Error, - fmt::{Debug, Display}, ops::{ControlFlow, Deref}, }; diff --git a/nokhwa-core/src/format_request.rs b/nokhwa-core/src/format_request.rs index c1c0581..5750f8b 100644 --- a/nokhwa-core/src/format_request.rs +++ b/nokhwa-core/src/format_request.rs @@ -5,6 +5,7 @@ use crate::{ types::{CameraFormat, FrameRate, Resolution}, }; use std::cmp::Ordering; +use crate::ranges::ValidatableRange; #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] enum ClosestType { @@ -60,24 +61,29 @@ impl FormatRequest { frame_rate, frame_format, } => { - let resolution_point = resolution.map(|x| x.preferred())?; - - let frame_rate_point = frame_rate.map(|x| x.preferred())?; + 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: Vec<(f32, CameraFormat)> = list_of_formats + let mut distances = list_of_formats .iter() .filter(|x| frame_format.contains(&x.format())) .map(|fmt| { - ( - (fmt.frame_rate() - frame_rate_point).abs() - + fmt.resolution().distance_from(&resolution_point) as f32, - 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::>(); + .collect::>(); distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); - distances.into_iter().map(|x| x.1).collect() + distances.into_iter().map(|x| x.1).copied().collect() } FormatRequest::HighestFrameRate { frame_rate, @@ -86,7 +92,7 @@ impl FormatRequest { let mut formats = list_of_formats .iter() .filter(|x| { - frame_format.contains(&x.format()) && frame_rate.in_range(x.frame_rate()) + frame_format.contains(&x.format()) && frame_rate.validate(&x.frame_rate()).is_ok() }) .collect::>(); formats.sort(); @@ -99,7 +105,7 @@ impl FormatRequest { let mut formats = list_of_formats .iter() .filter(|x| { - frame_format.contains(&x.format()) && resolution.in_range(x.resolution()) + frame_format.contains(&x.format()) && resolution.validate(&x.resolution()).is_ok() }) .collect::>(); formats.sort(); diff --git a/nokhwa-core/src/properties.rs b/nokhwa-core/src/properties.rs index f36bc0e..d301325 100644 --- a/nokhwa-core/src/properties.rs +++ b/nokhwa-core/src/properties.rs @@ -1,676 +1,105 @@ -use crate::error::NokhwaError; -use crate::ranges::{ArrayRange, KeyValue, Options, Range, RangeValidationFailure, Simple, ValidatableRange}; -use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use std::{ - collections::{HashMap, HashSet}, - fmt::{Display, Formatter}, -}; +use std::collections::HashMap; +use crate::ranges::Range; #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub struct ControlValidationFailure; - -impl From for ControlValidationFailure { - fn from(_: RangeValidationFailure) -> Self { - Self - } -} - -#[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -pub enum CameraPropertyId { - Brightness, - Contrast, - Hue, - Saturation, - Sharpness, - Gamma, - WhiteBalance, - BacklightCompensation, - ISO, - Pan, - Tilt, - Zoom, - Exposure, - Iris, +pub enum ControlId { Focus, - Facing, - Custom(String), -} - -impl Display for CameraPropertyId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } -} - -// TODO: Replace Controls API with Properties. (this one) -/// Properties of a Camera. -/// -/// If the property is not supported, it is `None`. -/// Custom or platform-specific properties go into `other` -pub struct CameraProperties { - props: HashMap, -} - -macro_rules! def_camera_props { - ( $($property:ident, )* ) => { - paste::paste! { - impl CameraProperties { - $( - pub fn [<$property:snake>] (&self) -> Option<&CameraPropertyDescriptor> { - self.props.get(&CameraPropertyId::$property) - } - - pub fn [] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> { - self.set_property(&CameraPropertyId::$property, value) - } - )* - } - } - }; -} - -def_camera_props!( - Brightness, - Contrast, - Hue, - Saturation, - Sharpness, - Gamma, + Exposure, WhiteBalance, - BacklightCompensation, - ISO, - Pan, - Tilt, Zoom, - Exposure, - Iris, - Focus, - Facing, -); - -impl CameraProperties { - pub fn property(&self, property: &CameraPropertyId) -> Option<&CameraPropertyDescriptor> { - self.props.get(property) - } - - pub fn set_property( - &mut self, - property: &CameraPropertyId, - value: CameraPropertyValue, - ) -> Result<(), NokhwaError> { - match self.props.get_mut(property) { - Some(prop) => { - prop.set_value(value)?; - Ok(()) - } - None => Err(NokhwaError::SetPropertyError { - property: property.to_string(), - value: value.to_string(), - error: String::from("Is null."), - }), - } - } + Lighting, + Other(u64) } -/// Describes an individual property. -#[derive(Clone, Debug)] -pub struct CameraPropertyDescriptor { - flags: HashSet, - mode: CameraPropertyMode, - range: CameraPropertyRange, - value: CameraPropertyValue, - value_type: CameraPropertyValueType, +#[derive(Clone, Debug, PartialEq)] +pub enum ControlGroup { + ModeMultipleValue(ModeAndValuesControl), + Simple(SimpleControl), } -impl CameraPropertyDescriptor { - pub fn new( - flags: &[CameraPropertyFlag], - mode: CameraPropertyMode, - range: CameraPropertyRange, - value: CameraPropertyValue, - value_type: CameraPropertyValueType, - ) -> Result { - if flags.contains(&CameraPropertyFlag::ReadOnly) - && flags.contains(&CameraPropertyFlag::WriteOnly) - { - return Err(NokhwaError::StructureError { - structure: "CameraPropertyDescriptor".to_string(), - error: "conflicting flags".to_string(), - }); - } - - Ok(CameraPropertyDescriptor { - flags: HashSet::from(flags), - mode, - range, - value, - value_type, - }) - } - - pub fn is_read_only(&self) -> bool { - self.flags.contains(&CameraPropertyFlag::ReadOnly) - } - - pub fn is_write_only(&self) -> bool { - self.flags.contains(&CameraPropertyFlag::WriteOnly) - } - - pub fn is_disabled(&self) -> Result<(), NokhwaError> { - if self.flags.contains(&CameraPropertyFlag::Disabled) { - return Err(NokhwaError::StructureError { - structure: "CameraPropertyDescriptor".to_string(), - error: "Disabled".to_string(), - }); - } - Ok(()) - } - - pub fn flags(&self) -> Result<&HashSet, NokhwaError> { - self.is_disabled()?; - Ok(&self.flags) - } - - pub fn mode(&self) -> CameraPropertyMode { - self.mode - } - - pub fn value_type(&self) -> CameraPropertyValueType { - self.value_type - } - - pub fn range(&self) -> &CameraPropertyRange { - &self.range - } - - pub fn value(&self) -> &CameraPropertyValue { - &self.value - } +#[derive(Clone, Debug, PartialEq)] +pub struct ModeAndValuesControl { + id: ControlId, + mode_id: u64, + mode_body: ControlBody, + values: HashMap +} - pub fn set_value(&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> { - self.range - .check_value(&value) - .map_err(|_| NokhwaError::SetPropertyError { - property: "CameraPropertyValue".to_string(), - value: value.to_string(), - error: "Bad Type".to_string(), - })?; - self.value = value; - Ok(()) - } +#[derive(Clone, Debug, PartialEq)] +pub struct SimpleControl { + id: u64, + body: ControlBody, } -/// Platform Specific Camera Property. This is not useful, unless you are manually dealing with -/// camera properties in `other`. -#[derive(Clone, Debug, Hash, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -pub enum CameraCustomPropertyPlatformId { - String(String), - LongInteger(i128), +#[derive(Clone, Debug, PartialEq)] +pub struct ControlBody { + pub typ: ControlType, + pub class: ControlClass, + pub flags: Vec, + pub descriptor: ControlValueDescriptor, + pub value: Option } #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -pub enum CameraPropertyMode { - /// - None, - /// Automatically Set - Automatic, - /// Manually Set - Manual, - /// Continuously Set - Continuous, +pub enum ControlType { + Button, + Integer, + Menu, + IntegerMenu, + BinaryMenu, + Bitmask, + String, } #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -pub enum CameraPropertyValueType { - /// Relative - Relative, - /// Absolute - Absolute, - /// Unknown/Unused/Not Applicable - None, +pub enum ControlClass { + User, + Camera, + Other(u64), } -/// The flags that a camera property may have. #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -pub enum CameraPropertyFlag { - /// The value may only be read from - any attempts to change the value will error. +pub enum ControlFlags { + Disabled, + Busy, ReadOnly, - /// The value can only be written to. + CascadingUpdates, + Inactive, + Slider, WriteOnly, - /// May just randomly poof out of existance. - // FIXME: where the fuck did i find this? replace above doc with actual info. - Volatile, - /// While the platform/driver supports this feature, - /// your camera does not. Setting will be ignored. - Disabled, -} - -impl Display for CameraPropertyFlag { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } + ContinuousChange, + ExecuteOnWrite, } -/// Ranges (Available Options of a Camera -#[non_exhaustive] -#[derive(Clone, Debug)] -pub enum CameraPropertyRange { +#[derive(Clone, Debug, PartialEq)] +pub enum ControlValueDescriptor { Null, - Boolean(Simple), Integer(Range), - LongInteger(Range), - Float(Range), - Double(Range), - String(Simple), - Array(ArrayRange>), - Enumeration(Options), - Binary(Simple>), - Pair(Range, Range), - Triple( - Range, - Range, - Range, - ), - Quadruple( - Range, - Range, - Range, - Range, - ), - KeyValuePair(KeyValue), -} - -impl CameraPropertyRange { - pub fn check_value(&self, value: &CameraPropertyValue) -> Result<(), ControlValidationFailure> { - match self { - CameraPropertyRange::Null => { - if let CameraPropertyValue::Null = value { - return Ok(()); - } - } - CameraPropertyRange::Boolean(chk_b) => { - if let CameraPropertyValue::Boolean(b) = value { - chk_b.validate(b)? - } - } - CameraPropertyRange::Integer(chk_i) => { - if let CameraPropertyValue::Integer(i) = value { - chk_i.validate(i)? - } - } - CameraPropertyRange::LongInteger(chk_long) => { - if let CameraPropertyValue::LongInteger(long) = value { - chk_long.validate(long)? - } - } - CameraPropertyRange::Float(chk_float) => { - if let CameraPropertyValue::Float(fl) = value { - chk_float.validate(fl)?; - } - } - CameraPropertyRange::Double(chk_double) => { - if let CameraPropertyValue::Double(dl) = value { - chk_double.validate(dl)?; - } - } - CameraPropertyRange::String(chk_string) => { - if let CameraPropertyValue::String(st) = value { - chk_string.validate(st)?; - } - } - CameraPropertyRange::Array(chk_array) => { - if let CameraPropertyValue::Array(arr) = value { - chk_array.validate(arr)?; - } - } - CameraPropertyRange::Enumeration(chk_enum) => { - if let CameraPropertyValue::EnumValue(en) = value { - chk_enum.validate(en)?; - } - } - CameraPropertyRange::Binary(chk_bin) => { - if let CameraPropertyValue::Binary(bin) = value { - chk_bin.validate(bin)?; - } - } - CameraPropertyRange::Pair(chk_a, chk_b) => { - if let CameraPropertyValue::Pair(a, b) = value { - chk_a.validate(a)?; - chk_b.validate(b)?; - } - } - CameraPropertyRange::Triple(chk_x, chk_y, chk_z) => { - if let CameraPropertyValue::Triple(x, y, z) = value { - chk_x.validate(x)?; - chk_y.validate(y)?; - chk_z.validate(z)?; - } - } - CameraPropertyRange::Quadruple(chk_x, chk_y, chk_z, chk_w) => { - if let CameraPropertyValue::Quadruple(x, y, z, w) = value { - chk_x.validate(x)?; - chk_y.validate(y)?; - chk_z.validate(z)?; - chk_w.validate(w)?; - } - } - CameraPropertyRange::KeyValuePair(kv) => { - if let CameraPropertyValue::KeyValue(st, va) = value { - if let Some(vk) = kv.by_key(st) { - if vk.is_same_type(va) { - return Ok(()); - } - } - } - } - _ => return Err(ControlValidationFailure), - } - Err(ControlValidationFailure) - } + Bitmap(i64), + Float(Range), + String(String), + Boolean(bool), + Array(Vec), + Map(HashMap) } -/// A possible value of -/// -/// IMPORTANT: Make sure to call [`check_self()`] BEFORE any other operations! -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum CameraPropertyValue { +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub enum ControlValuePrimitive { Null, - Boolean(bool), Integer(i64), - LongInteger(i128), - Float(f32), - Double(f64), + Bitmap(i64), + Float(f64), String(String), - Array(Vec), - EnumValue(Box), - Binary(Vec), - Pair(f32, f32), - Triple(f32, f32, f32), - Quadruple(f32, f32, f32, f32), - KeyValue(String, Box), -} - -impl CameraPropertyValue { - pub fn is_same_type(&self, other: &CameraPropertyValue) -> bool { - match (self, other) { - (CameraPropertyValue::Null, CameraPropertyValue::Null) => true, - (CameraPropertyValue::Boolean(_), CameraPropertyValue::Boolean(_)) => true, - (CameraPropertyValue::Integer(_), CameraPropertyValue::Integer(_)) => true, - (CameraPropertyValue::LongInteger(_), CameraPropertyValue::LongInteger(_)) => true, - (CameraPropertyValue::Float(_), CameraPropertyValue::Float(_)) => true, - (CameraPropertyValue::Double(_), CameraPropertyValue::Double(_)) => true, - (CameraPropertyValue::String(_), CameraPropertyValue::String(_)) => true, - (CameraPropertyValue::Array(_), CameraPropertyValue::Array(_)) => true, - (CameraPropertyValue::EnumValue(_), CameraPropertyValue::EnumValue(_)) => true, - (CameraPropertyValue::Binary(_), CameraPropertyValue::Binary(_)) => true, - (CameraPropertyValue::Pair(..), CameraPropertyValue::Pair(..)) => true, - (CameraPropertyValue::Triple(..), CameraPropertyValue::Triple(..)) => true, - (CameraPropertyValue::Quadruple(..), CameraPropertyValue::Quadruple(..)) => true, - (CameraPropertyValue::KeyValue(..), CameraPropertyValue::KeyValue(..)) => true, - (_, _) => false, - } - } -} - -impl PartialEq for CameraPropertyValue { - fn eq(&self, other: &Self) -> bool { - match &self { - CameraPropertyValue::Null => { - if let CameraPropertyValue::Null = other { - return true; - } - } - CameraPropertyValue::Boolean(b) => { - if let CameraPropertyValue::Boolean(ob) = other { - return b == ob; - } - } - CameraPropertyValue::Integer(i) => { - if let CameraPropertyValue::Integer(oi) = other { - return i == oi; - } - } - CameraPropertyValue::LongInteger(i) => { - if let CameraPropertyValue::LongInteger(oi) = other { - return i == oi; - } - } - CameraPropertyValue::Float(f) => { - if let CameraPropertyValue::Float(of) = other { - return f == of; - } - } - CameraPropertyValue::Double(d) => { - if let CameraPropertyValue::Double(od) = other { - return d == od; - } - } - CameraPropertyValue::String(s) => { - if let CameraPropertyValue::String(os) = other { - return s == os; - } - } - CameraPropertyValue::Array(a) => { - if let CameraPropertyValue::Array(oa) = other { - return a == oa; - } - } - CameraPropertyValue::EnumValue(ev) => { - if let CameraPropertyValue::EnumValue(oev) = other { - return ev == oev; - } - } - CameraPropertyValue::Binary(bin) => { - if let CameraPropertyValue::Binary(obin) = other { - return bin == obin; - } - } - CameraPropertyValue::Pair(a, b) => { - if let CameraPropertyValue::Pair(oa, ob) = other { - return (a == oa) && (b == ob); - } - } - CameraPropertyValue::Triple(x, y, z) => { - if let CameraPropertyValue::Triple(ox, oy, oz) = other { - return (x == ox) && (y == oy) && (z == oz); - } - } - CameraPropertyValue::Quadruple(x, y, z, w) => { - if let CameraPropertyValue::Quadruple(ox, oy, oz, ow) = other { - return (x == ox) && (y == oy) && (z == oz) && (w == ow); - } - } - CameraPropertyValue::KeyValue(k, v) => { - if let CameraPropertyValue::KeyValue(ok, ov) = other { - return (k == ok) && (v == ov); - } - } - _ => {} - } - false - } -} - -impl PartialOrd for CameraPropertyValue { - fn partial_cmp(&self, other: &Self) -> Option { - match self { - CameraPropertyValue::Null => match other { - CameraPropertyValue::Null => Some(Ordering::Greater), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Boolean(b) => match other { - CameraPropertyValue::Null => Some(Ordering::Greater), - CameraPropertyValue::Boolean(o) => { - if o == b { - Some(Ordering::Equal) - } else if o { - Some(Ordering::Less) - } else { - Some(Ordering::Greater) - } - } - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Integer(int) => match other { - CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => { - Some(Ordering::Greater) - } - CameraPropertyValue::Integer(oth) => Some(int.cmp(oth)), - CameraPropertyValue::LongInteger(li) => { - let long = match i64::try_from(li) { - Ok(v) => v, - Err(_) => return None, - }; - Some(int.cmp(&long)) - } - _ => Some(Ordering::Less), - }, - CameraPropertyValue::LongInteger(long) => match other { - CameraPropertyValue::Null | CameraPropertyValue::Boolean(_) => { - Some(Ordering::Greater) - } - CameraPropertyValue::Integer(oth) => Some(long.cmp(&(i128::from(oth)))), - CameraPropertyValue::LongInteger(o) => Some(long.cmp(o)), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Float(fl) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater), - CameraPropertyValue::Float(f) => fl.partial_cmp(f), - CameraPropertyValue::Double(d) => f64::from(fl).partial_cmp(d), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Double(d) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) => Some(Ordering::Greater), - CameraPropertyValue::Float(f) => d.partial_cmp(&(f64::from(f))), - CameraPropertyValue::Double(o) => d.partial_cmp(o), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::String(s) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) - | CameraPropertyValue::Float(_) - | CameraPropertyValue::Double(_) => Some(Ordering::Greater), - CameraPropertyValue::String(os) => s.partial_cmp(os), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Array(a) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) - | CameraPropertyValue::Float(_) - | CameraPropertyValue::Double(_) - | CameraPropertyValue::String(_) => Some(Ordering::Greater), - CameraPropertyValue::Array(oa) => a.partial_cmp(oa), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::EnumValue(_) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) - | CameraPropertyValue::Float(_) - | CameraPropertyValue::Double(_) - | CameraPropertyValue::String(_) - | CameraPropertyValue::Array(_) => Some(Ordering::Greater), - CameraPropertyValue::EnumValue(_) => Some(Ordering::Equal), - _ => Some(Ordering::Less), - }, - CameraPropertyValue::Binary(b) => match other { - CameraPropertyValue::Null - | CameraPropertyValue::Boolean(_) - | CameraPropertyValue::Integer(_) - | CameraPropertyValue::LongInteger(_) - | CameraPropertyValue::Float(_) - | CameraPropertyValue::Double(_) - | CameraPropertyValue::String(_) - | CameraPropertyValue::Array(_) - | CameraPropertyValue::EnumValue(_) => Some(Ordering::Greater), - CameraPropertyValue::Binary(ob) => b.partial_cmp(ob), - _ => Some(Ordering::Less), - }, - // FIXME: implement this lole - CameraPropertyValue::Pair(_, _) => { - // match other { - // CameraPropertyValue::Null | - // CameraPropertyValue::Boolean(_) | - // CameraPropertyValue::Integer(_) | - // CameraPropertyValue::LongInteger(_) | - // CameraPropertyValue::Float(_) | - // CameraPropertyValue::Double(_) | - // CameraPropertyValue::String(_) | - // CameraPropertyValue::Array(_) | - // CameraPropertyValue::EnumValue(_) | - // CameraPropertyValue::Binary(_) => Some(Ordering::Greater), - // CameraPropertyValue::Pair(a, b) => { - // match a.partial_cmp(b) { - // Some(_) => {} - // None => {} - // } - // } - // _ => Some(Ordering::Less) - // } - Some(Ordering::Equal) - } - CameraPropertyValue::Triple(_, _, _) => Some(Ordering::Equal), - CameraPropertyValue::Quadruple(_, _, _, _) => Some(Ordering::Equal), - _ => None, - } - } -} - -impl Display for CameraPropertyValue { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{self:?}") - } + Boolean(bool), } -#[macro_export] -macro_rules! define_back_and_fourth_control { - ($id_type:ty, { $( $control:expr [ $mode:expr, $value_type:expr, $value:expr ] => $control_id:expr, )* }, $id_to_str:expr, $str_to_id:expr) => { - pub struct ControlIntermediate { - pub mode: Option<$id_type>, - pub value_type: Option<$id_type>, - pub value: $id_type, - } - - impl ControlIntermediate { - pub fn - } - - // impl ControlIntermediate { - // pub fn into_control(native_ctrl: $id_type) -> (crate::properties::CameraPropertyId, Option) { - // match native_ctrl { - // $( - // $control_id => ($control, $flag) - // )* - // nc => (crate::properties::CameraPropertyId::Custom($id_to_str(nc)), None) - // } - // } - // - // pub fn into_native(property_id: &crate::properties::CameraPropertyId, flag: Option) -> Option<$id_type> { - // match (property_id, flag) { - // $( - // ($control, $flag) => Some($control_id), - // )* - // (crate::properties::CameraPropertyId::Custom(str_id), _) => $str_to_id(str_id), - // _ => None, - // } - // } - // } - }; +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub enum ControlValue { + Null, + Integer(i64), + Bitmap(i64), + Float(f64), + String(String), + Boolean(bool), + KeyValue(String, ControlValuePrimitive), } diff --git a/nokhwa-core/src/stream.rs b/nokhwa-core/src/stream.rs index 77187ae..d5e38aa 100644 --- a/nokhwa-core/src/stream.rs +++ b/nokhwa-core/src/stream.rs @@ -1,7 +1,6 @@ use crate::error::{NokhwaError, NokhwaResult}; use crate::frame_buffer::FrameBuffer; use flume::Receiver; -use futures::TryFutureExt; use std::sync::Arc; pub trait StreamInnerTrait { @@ -61,6 +60,8 @@ impl Stream { #[cfg(feature = "async")] pub async fn await_frame(&self) -> NokhwaResult { + use futures::TryFutureExt; + if self.inner.receiver().is_disconnected() { return Err(NokhwaError::ReadFrameError( "stream is disconnected!".to_string(), diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 7ea115d..945bd5f 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -13,6 +13,7 @@ use std::num::NonZeroI32; use std::ops::{Div, Rem}; use num_rational::Rational32; use crate::ranges::{SimpleRangeItem}; +use num_traits::FromPrimitive; /// Describes the index of the camera. /// - Index: A numbered index @@ -247,6 +248,17 @@ impl FrameRate { pub fn denominator(&self) -> &i32 { self.rational.denom() } + + pub fn as_raw(&self) -> &Rational32 { + &self.rational + } + + pub fn approximate_float(&self) -> Option { + let numerator_float = f32::from_i32(*self.numerator())?; + let denominator_float = f32::from_i32(*self.denominator())?; + + Some(numerator_float / denominator_float) + } } impl Default for FrameRate { @@ -299,7 +311,7 @@ impl From for FrameRate { /// 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`]. -#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, PartialOrd, Eq, Ord)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct CameraFormat { resolution: Resolution, diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index 38e97e9..2d677f4 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -34,7 +34,7 @@ use nokhwa_core::{ use std::{ffi::CString, sync::Arc}; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; /// The backend struct that interfaces with V4L2. /// To see what this does, please see [`CaptureTrait`]. @@ -231,7 +231,7 @@ impl CaptureTrait for AVFoundationCaptureDevice { fn set_camera_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { self.device.lock()?; let res = self.device.set_control(id, value); @@ -463,7 +463,7 @@ impl CaptureTrait for AVFoundationCaptureDevice { fn set_camera_control( &mut self, _: KnownCameraControl, - _: ControlValueSetter, + _: ControlValue, ) -> Result<(), NokhwaError> { todo!() } diff --git a/src/backends/capture/browser_camera.rs b/src/backends/capture/browser_camera.rs index 1b74d09..ecfd692 100644 --- a/src/backends/capture/browser_camera.rs +++ b/src/backends/capture/browser_camera.rs @@ -7,7 +7,7 @@ use serde::{de, Serialize}; use wasm_bindgen_futures::JsFuture; use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator}; use nokhwa_core::frame_buffer::FrameBuffer; -use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; use nokhwa_core::error::NokhwaError; use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait}; @@ -360,7 +360,7 @@ impl CaptureTrait for BrowserCaptureDevice { fn set_camera_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { todo!() } @@ -414,7 +414,7 @@ impl AsyncCaptureTrait for BrowserCaptureDevice { todo!() } - async fn set_camera_control_async(&mut self, id: KnownCameraControl, value: ControlValueSetter) -> Result<(), NokhwaError> { + async fn set_camera_control_async(&mut self, id: KnownCameraControl, value: ControlValue) -> Result<(), NokhwaError> { todo!() } diff --git a/src/backends/capture/msmf_backend.rs b/src/backends/capture/msmf_backend.rs index cf20ffb..f5c42ee 100644 --- a/src/backends/capture/msmf_backend.rs +++ b/src/backends/capture/msmf_backend.rs @@ -26,7 +26,7 @@ use nokhwa_core::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{all_known_camera_controls, CameraControl, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{all_known_camera_controls, CameraControl, ControlValue, KnownCameraControl}; /// The backend that deals with Media Foundation on Windows. /// To see what this does, please see [`CaptureTrait`]. @@ -229,7 +229,7 @@ impl CaptureTrait for MediaFoundationCaptureDevice { fn set_camera_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { self.inner.set_control(id, value) } diff --git a/src/backends/capture/opencv_backend.rs b/src/backends/capture/opencv_backend.rs index 75e200b..c6dd52a 100644 --- a/src/backends/capture/opencv_backend.rs +++ b/src/backends/capture/opencv_backend.rs @@ -33,7 +33,7 @@ use opencv::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl}; /// Attempts to convert a [`KnownCameraControl`] into a `OpenCV` video capture property. /// If the associated control is not found, this will return `Err` @@ -434,12 +434,12 @@ impl CaptureTrait for OpenCvCaptureDevice { fn set_camera_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { let control_val = match value { - ControlValueSetter::Integer(i) => i as f64, - ControlValueSetter::Float(f) => f, - ControlValueSetter::Boolean(b) => u8::from(b) as f64, + ControlValue::Integer(i) => i as f64, + ControlValue::Float(f) => f, + ControlValue::Boolean(b) => u8::from(b) as f64, val => { return Err(NokhwaError::SetPropertyError { property: "Camera Control".to_string(), diff --git a/src/camera.rs b/src/camera.rs index 88d06f5..a48ff62 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -28,7 +28,7 @@ use nokhwa_core::{ }, }; use std::{borrow::Cow, collections::HashMap}; -use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; /// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use. pub struct Camera { @@ -123,7 +123,7 @@ impl CaptureTrait for Camera { fn set_camera_control( &mut self, id: KnownCameraControl, - value: ControlValueSetter, + value: ControlValue, ) -> Result<(), NokhwaError> { todo!() } diff --git a/src/threaded.rs b/src/threaded.rs index 03df237..13d58e3 100644 --- a/src/threaded.rs +++ b/src/threaded.rs @@ -31,7 +31,7 @@ use std::{ Arc, Mutex, }, }; -use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; +use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl}; type AtomicLock = Arc>; pub type CallbackFn = fn( @@ -420,14 +420,14 @@ impl CallbackCamera { /// Sets the control to `control` in the camera. /// Usually, the pipeline is calling [`camera_control()`](crate::camera_traits::CaptureTrait::camera_control), getting a camera control that way - /// then calling [`value()`](nokhwa_core::properties::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::properties::ControlValueSetter) and setting the value that way. + /// then calling [`value()`](nokhwa_core::properties::CameraControl::value()) to get a [`ControlValueSetter`](nokhwa_core::properties::ControlValue) and setting the value that way. /// # Errors /// If the `control` is not supported, the value is invalid (less than min, greater than max, not in step), or there was an error setting the control, /// this will error. pub fn set_camera_control( &mut self, id: KnownCameraControl, - control: ControlValueSetter, + control: ControlValue, ) -> Result<(), NokhwaError> { self.camera .lock()