v0.2.0 core

This commit is contained in:
l1npengtul
2025-01-06 10:01:28 +09:00
parent 0e2d000842
commit 021e4e1323
10 changed files with 228 additions and 184 deletions
Generated
-1
View File
@@ -2518,7 +2518,6 @@ dependencies = [
"num-rational 0.4.2",
"num-traits",
"opencv 0.93.1",
"paste",
"rgb",
"serde",
"thiserror 2.0.0",
-1
View File
@@ -23,7 +23,6 @@ test-fail-warnings = []
[dependencies]
thiserror = "2.0"
bytes = "1.3"
paste = "1.0"
flume = "0.11"
num-traits = "0.2"
+3
View File
@@ -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<Vec<CameraFormat>, 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<Stream, NokhwaError>;
@@ -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 {}
+3 -3
View File
@@ -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}")]
+91 -90
View File
@@ -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<Range<Resolution>>,
frame_rate: Option<Range<FrameRate>>,
frame_format: Vec<FrameFormat>,
},
HighestFrameRate {
frame_rate: Range<FrameRate>,
frame_format: Vec<FrameFormat>,
},
HighestResolution {
resolution: Range<Resolution>,
frame_format: Vec<FrameFormat>,
},
Exact {
resolution: Resolution,
frame_rate: FrameRate,
frame_format: Vec<FrameFormat>,
},
Any,
}
#[derive(Clone, Debug)]
pub struct FormatRequest {
request_type: FormatRequestType,
allowed_frame_formats: Vec<FrameFormat>,
}
impl FormatRequest {
pub fn sort_formats(&self, list_of_formats: &[CameraFormat]) -> Vec<CameraFormat> {
if list_of_formats.is_empty() {
return vec![];
pub fn new(format_request_type: FormatRequestType, allowed_frame_formats: Vec<FrameFormat>) -> Self {
Self {
request_type: format_request_type,
allowed_frame_formats,
}
}
pub fn best<'a>(&self, camera_formats: &'a Vec<CameraFormat>) -> Option<&'a CameraFormat> {
camera_formats.first()
}
pub fn sort_foramts(&self, mut camera_formats: Vec<CameraFormat>) -> Vec<CameraFormat> {
if camera_formats.is_empty() {
return camera_formats;
}
match self {
FormatRequest::Closest {
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::<Vec<(f32, &CameraFormat)>>();
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::<Vec<_>>();
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::<Vec<_>>();
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::<Vec<_>>();
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<CameraFormat> {
if list_of_formats.is_empty() {
return None;
}
Some(self.sort_formats(list_of_formats).remove(0))
}
}
pub fn format_distance_to_point(resolution: &Option<Resolution>, frame_rate: &Option<FrameRate>, 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,
};
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
}
+2 -3
View File
@@ -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 <l1npengtul@protonmail.com> / 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;
+9
View File
@@ -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;
+92 -60
View File
@@ -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<Option<ControlValue>> {
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_value(&self, other: &ControlValue) -> bool {
pub fn is_valid_primitive_value(&self, other: &ControlValuePrimitive) -> bool {
match self {
ControlValuePrimitiveDescriptor::Null => {
if let &ControlValue::Null = other {
if let ControlValuePrimitive::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 ControlValuePrimitive::Integer(v) = other {
return i.validate(v)
}
}
ControlValuePrimitiveDescriptor::BitMask => {
if let &ControlValue::BitMask(_) = other {
if let ControlValuePrimitive::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 ControlValuePrimitive::Float(v) = other {
return f.validate(v)
}
}
ControlValuePrimitiveDescriptor::String => {
if let &ControlValue::String(_) = other {
if let ControlValuePrimitive::String(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Boolean => {
if let &ControlValue::Boolean(_) = other {
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 {
return true
}
}
ControlValuePrimitiveDescriptor::Integer(i) => {
if let ControlValue::Integer(v) = other {
return i.validate(v)
}
}
ControlValuePrimitiveDescriptor::BitMask => {
if let ControlValue::BitMask(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Float(f) => {
if let ControlValue::Float(v) = other {
return f.validate(v)
}
}
ControlValuePrimitiveDescriptor::String => {
if let ControlValue::String(_) = other {
return true
}
}
ControlValuePrimitiveDescriptor::Boolean => {
if let ControlValue::Boolean(_) = other {
return true
}
}
@@ -330,15 +357,15 @@ pub enum ControlValuePrimitive {
Boolean(bool),
}
impl AsRef<ControlValue> 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<ControlValuePrimitive> 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<ControlValuePrimitive> 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),
}
}
}
+14 -20
View File
@@ -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<T> ValidatableRange for Simple<T> {
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<T>(
upper_inclusive: bool,
step: Option<T>,
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<Output = Self> + Sub<Output = Self> + Rem<Output = Self> + PartialOrd + PartialEq {
+14 -6
View File
@@ -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.