make control api simpler, because most platforms arnt v4l2

This commit is contained in:
l1npengtul
2024-12-05 12:52:50 +09:00
parent 5d79e46d06
commit 0646b0abf5
18 changed files with 148 additions and 720 deletions
Generated
+2 -20
View File
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
@@ -2516,6 +2516,7 @@ dependencies = [
"futures", "futures",
"image 0.25.0", "image 0.25.0",
"num-rational 0.4.2", "num-rational 0.4.2",
"num-traits",
"opencv 0.93.1", "opencv 0.93.1",
"paste", "paste",
"rgb", "rgb",
@@ -2524,16 +2525,6 @@ dependencies = [
"wgpu 23.0.1", "wgpu 23.0.1",
] ]
[[package]]
name = "nokhwa-decoder"
version = "0.1.0"
dependencies = [
"dcv-color-primitives",
"mozjpeg",
"nokhwa-core",
"yuvutils-rs",
]
[[package]] [[package]]
name = "nokhwactl" name = "nokhwactl"
version = "0.10.0" version = "0.10.0"
@@ -4745,15 +4736,6 @@ version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" 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]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.7.32" version = "0.7.32"
+1 -1
View File
@@ -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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[workspace] [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"] exclude = ["examples/jscam"]
[lib] [lib]
Generated
+9 -9
View File
@@ -5,11 +5,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1726560853, "lastModified": 1731533236,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1730958623, "lastModified": 1733097829,
"narHash": "sha256-JwQZIGSYnRNOgDDoIgqKITrPVil+RMWHsZH1eE1VGN0=", "narHash": "sha256-9hbb1rqGelllb4kVUCZ307G2k3/UhmA8PPGBoyuWaSw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "85f7e662eda4fa3a995556527c87b2524b691933", "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -62,11 +62,11 @@
"nixpkgs": "nixpkgs_2" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1731292155, "lastModified": 1733193245,
"narHash": "sha256-fYVoUUtSadbOrH0z0epVQDsStBDS/S/fAK//0ECQAAI=", "narHash": "sha256-nwvKoPi3S6XyliqBRuC+01QFF0k94ZOvnoZtbGi/ObM=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "7c4cd99ed7604b79e8cb721099ac99c66f656b3a", "rev": "3458f7f946ba61d1a1069aedcc17d7b7616f23cd",
"type": "github" "type": "github"
}, },
"original": { "original": {
+2 -2
View File
@@ -246,7 +246,7 @@ mod internal {
ffi::{c_float, c_void, CStr}, ffi::{c_float, c_void, CStr},
sync::Arc, sync::Arc,
}; };
use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValueSetter, KnownCameraControl}; use nokhwa_core::properties::{CameraControl, ControlValueDescription, ControlValue, KnownCameraControl};
const UTF8_ENCODING: usize = 4; const UTF8_ENCODING: usize = 4;
type CGFloat = c_float; type CGFloat = c_float;
@@ -1492,7 +1492,7 @@ mod internal {
pub fn set_control( pub fn set_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
let rc = self.get_controls()?; let rc = self.get_controls()?;
let controls = rc let controls = rc
+6 -6
View File
@@ -46,7 +46,7 @@ pub mod wmf {
Arc, 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::DirectShow::{CameraControl_Flags_Auto, CameraControl_Flags_Manual};
use windows::Win32::Media::MediaFoundation::{ use windows::Win32::Media::MediaFoundation::{
IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM, IMFMediaType, MFCreateSample, MF_SOURCE_READER_FIRST_VIDEO_STREAM,
@@ -849,7 +849,7 @@ pub mod wmf {
pub fn set_control( pub fn set_control(
&mut self, &mut self,
control: KnownCameraControl, control: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
let current_value = self.control(control)?; let current_value = self.control(control)?;
@@ -897,8 +897,8 @@ pub mod wmf {
})?; })?;
let ctrl_value = match value { let ctrl_value = match value {
ControlValueSetter::Integer(i) => i as i32, ControlValue::Integer(i) => i as i32,
ControlValueSetter::Boolean(b) => i32::from(b), ControlValue::Boolean(b) => i32::from(b),
v => { v => {
return Err(NokhwaError::StructureError { return Err(NokhwaError::StructureError {
structure: format!("ControlValueSetter {}", v), structure: format!("ControlValueSetter {}", v),
@@ -1229,7 +1229,7 @@ pub mod wmf {
CameraFormat, CameraIndex, CameraInfo, CameraFormat, CameraIndex, CameraInfo,
}; };
use std::borrow::Cow; use std::borrow::Cow;
use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
pub fn initialize_mf() -> Result<(), NokhwaError> { pub fn initialize_mf() -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError( Err(NokhwaError::NotImplementedError(
@@ -1287,7 +1287,7 @@ pub mod wmf {
pub fn set_control( pub fn set_control(
&mut self, &mut self,
_control: KnownCameraControl, _control: KnownCameraControl,
_value: ControlValueSetter, _value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
Err(NokhwaError::NotImplementedError( Err(NokhwaError::NotImplementedError(
"Only on Windows".to_string(), "Only on Windows".to_string(),
+1
View File
@@ -25,6 +25,7 @@ thiserror = "2.0"
bytes = "1.3" bytes = "1.3"
paste = "1.0" paste = "1.0"
flume = "0.11" flume = "0.11"
num-traits = "0.2"
[dependencies.num-rational] [dependencies.num-rational]
version = "0.4" version = "0.4"
-1
View File
@@ -1,5 +1,4 @@
use crate::error::{NokhwaError, NokhwaResult}; use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_buffer::FrameBuffer;
use crate::frame_format::FrameFormat; use crate::frame_format::FrameFormat;
use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue}; use crate::properties::{CameraProperties, CameraPropertyId, CameraPropertyValue};
use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution}; use crate::types::{CameraFormat, CameraIndex, FrameRate, Resolution};
-2
View File
@@ -1,8 +1,6 @@
use crate::{error::NokhwaError, frame_buffer::FrameBuffer, frame_format::FrameFormat}; use crate::{error::NokhwaError, frame_buffer::FrameBuffer, frame_format::FrameFormat};
use image::{ImageBuffer, Pixel}; use image::{ImageBuffer, Pixel};
use std::{ use std::{
error::Error,
fmt::{Debug, Display},
ops::{ControlFlow, Deref}, ops::{ControlFlow, Deref},
}; };
+19 -13
View File
@@ -5,6 +5,7 @@ use crate::{
types::{CameraFormat, FrameRate, Resolution}, types::{CameraFormat, FrameRate, Resolution},
}; };
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::ranges::ValidatableRange;
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
enum ClosestType { enum ClosestType {
@@ -60,24 +61,29 @@ impl FormatRequest {
frame_rate, frame_rate,
frame_format, frame_format,
} => { } => {
let resolution_point = resolution.map(|x| x.preferred())?; let resolution_point = resolution.map(|x| x.preferred());
let frame_rate_point = frame_rate.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) // 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() .iter()
.filter(|x| frame_format.contains(&x.format())) .filter(|x| frame_format.contains(&x.format()))
.map(|fmt| { .map(|fmt| {
( let frame_rate_distance = match frame_rate_point {
(fmt.frame_rate() - frame_rate_point).abs() Some(f_point) => (fmt.frame_rate() - f_point).approximate_float().unwrap_or(f32::INFINITY).abs(),
+ fmt.resolution().distance_from(&resolution_point) as f32, None => 0_f32,
fmt, };
)
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<_>>(); .collect::<Vec<(f32, &CameraFormat)>>();
distances.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal)); 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 { FormatRequest::HighestFrameRate {
frame_rate, frame_rate,
@@ -86,7 +92,7 @@ impl FormatRequest {
let mut formats = list_of_formats let mut formats = list_of_formats
.iter() .iter()
.filter(|x| { .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::<Vec<_>>(); .collect::<Vec<_>>();
formats.sort(); formats.sort();
@@ -99,7 +105,7 @@ impl FormatRequest {
let mut formats = list_of_formats let mut formats = list_of_formats
.iter() .iter()
.filter(|x| { .filter(|x| {
frame_format.contains(&x.format()) && resolution.in_range(x.resolution()) frame_format.contains(&x.format()) && resolution.validate(&x.resolution()).is_ok()
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
formats.sort(); formats.sort();
+75 -646
View File
@@ -1,676 +1,105 @@
use crate::error::NokhwaError; use std::collections::HashMap;
use crate::ranges::{ArrayRange, KeyValue, Options, Range, RangeValidationFailure, Simple, ValidatableRange}; use crate::ranges::Range;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::{
collections::{HashMap, HashSet},
fmt::{Display, Formatter},
};
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct ControlValidationFailure; pub enum ControlId {
impl From<RangeValidationFailure> 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,
Focus, 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<CameraPropertyId, CameraPropertyDescriptor>,
}
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 [<set_ $property:snake>] (&mut self, value: CameraPropertyValue) -> Result<(), NokhwaError> {
self.set_property(&CameraPropertyId::$property, value)
}
)*
}
}
};
}
def_camera_props!(
Brightness,
Contrast,
Hue,
Saturation,
Sharpness,
Gamma,
WhiteBalance,
BacklightCompensation,
ISO,
Pan,
Tilt,
Zoom,
Exposure, Exposure,
Iris, WhiteBalance,
Focus, Zoom,
Facing, Lighting,
); Other(u64)
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."),
}),
}
}
} }
/// Describes an individual property. #[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug)] pub enum ControlGroup {
pub struct CameraPropertyDescriptor { ModeMultipleValue(ModeAndValuesControl),
flags: HashSet<CameraPropertyFlag>, Simple(SimpleControl),
mode: CameraPropertyMode,
range: CameraPropertyRange,
value: CameraPropertyValue,
value_type: CameraPropertyValueType,
} }
impl CameraPropertyDescriptor { #[derive(Clone, Debug, PartialEq)]
pub fn new( pub struct ModeAndValuesControl {
flags: &[CameraPropertyFlag], id: ControlId,
mode: CameraPropertyMode, mode_id: u64,
range: CameraPropertyRange, mode_body: ControlBody,
value: CameraPropertyValue, values: HashMap<String, SimpleControl>
value_type: CameraPropertyValueType,
) -> Result<Self, NokhwaError> {
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<CameraPropertyFlag>, 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
}
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(())
}
} }
/// Platform Specific Camera Property. This is not useful, unless you are manually dealing with #[derive(Clone, Debug, PartialEq)]
/// camera properties in `other`. pub struct SimpleControl {
#[derive(Clone, Debug, Hash, PartialOrd, Eq, PartialEq)] id: u64,
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] body: ControlBody,
pub enum CameraCustomPropertyPlatformId { }
String(String),
LongInteger(i128), #[derive(Clone, Debug, PartialEq)]
pub struct ControlBody {
pub typ: ControlType,
pub class: ControlClass,
pub flags: Vec<ControlFlags>,
pub descriptor: ControlValueDescriptor,
pub value: Option<ControlValue>
} }
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum ControlType {
pub enum CameraPropertyMode { Button,
/// Integer,
None, Menu,
/// Automatically Set IntegerMenu,
Automatic, BinaryMenu,
/// Manually Set Bitmask,
Manual, String,
/// Continuously Set
Continuous,
} }
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum ControlClass {
pub enum CameraPropertyValueType { User,
/// Relative Camera,
Relative, Other(u64),
/// Absolute
Absolute,
/// Unknown/Unused/Not Applicable
None,
} }
/// The flags that a camera property may have.
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub enum ControlFlags {
pub enum CameraPropertyFlag {
/// The value may only be read from - any attempts to change the value will error.
ReadOnly,
/// The value can only be written to.
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, Disabled,
Busy,
ReadOnly,
CascadingUpdates,
Inactive,
Slider,
WriteOnly,
ContinuousChange,
ExecuteOnWrite,
} }
impl Display for CameraPropertyFlag { #[derive(Clone, Debug, PartialEq)]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { pub enum ControlValueDescriptor {
write!(f, "{self:?}")
}
}
/// Ranges (Available Options of a Camera
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum CameraPropertyRange {
Null, Null,
Boolean(Simple<bool>),
Integer(Range<i64>), Integer(Range<i64>),
LongInteger(Range<i128>), Bitmap(i64),
Float(Range<f32>), Float(Range<i64>),
Double(Range<f64>),
String(Simple<String>),
Array(ArrayRange<Vec<CameraPropertyValue>>),
Enumeration(Options<CameraPropertyValue>),
Binary(Simple<Vec<u8>>),
Pair(Range<f32>, Range<f32>),
Triple(
Range<f32>,
Range<f32>,
Range<f32>,
),
Quadruple(
Range<f32>,
Range<f32>,
Range<f32>,
Range<f32>,
),
KeyValuePair(KeyValue<String, CameraPropertyValue>),
}
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)
}
}
/// A possible value of
///
/// IMPORTANT: Make sure to call [`check_self()`] BEFORE any other operations!
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum CameraPropertyValue {
Null,
Boolean(bool),
Integer(i64),
LongInteger(i128),
Float(f32),
Double(f64),
String(String), String(String),
Array(Vec<CameraPropertyValue>), Boolean(bool),
EnumValue(Box<CameraPropertyValue>), Array(Vec<ControlValuePrimitive>),
Binary(Vec<u8>), Map(HashMap<String, ControlValuePrimitive>)
Pair(f32, f32),
Triple(f32, f32, f32),
Quadruple(f32, f32, f32, f32),
KeyValue(String, Box<CameraPropertyValue>),
} }
impl CameraPropertyValue { #[derive(Clone, Debug, PartialEq, PartialOrd)]
pub fn is_same_type(&self, other: &CameraPropertyValue) -> bool { pub enum ControlValuePrimitive {
match (self, other) { Null,
(CameraPropertyValue::Null, CameraPropertyValue::Null) => true, Integer(i64),
(CameraPropertyValue::Boolean(_), CameraPropertyValue::Boolean(_)) => true, Bitmap(i64),
(CameraPropertyValue::Integer(_), CameraPropertyValue::Integer(_)) => true, Float(f64),
(CameraPropertyValue::LongInteger(_), CameraPropertyValue::LongInteger(_)) => true, String(String),
(CameraPropertyValue::Float(_), CameraPropertyValue::Float(_)) => true, Boolean(bool),
(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 { #[derive(Clone, Debug, PartialEq, PartialOrd)]
fn eq(&self, other: &Self) -> bool { pub enum ControlValue {
match &self { Null,
CameraPropertyValue::Null => { Integer(i64),
if let CameraPropertyValue::Null = other { Bitmap(i64),
return true; Float(f64),
} String(String),
} Boolean(bool),
CameraPropertyValue::Boolean(b) => { KeyValue(String, ControlValuePrimitive),
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<Ordering> {
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:?}")
}
}
#[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<crate::properties::CameraPropertyFlag>) {
// 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<crate::properties::CameraPropertyFlag>) -> Option<$id_type> {
// match (property_id, flag) {
// $(
// ($control, $flag) => Some($control_id),
// )*
// (crate::properties::CameraPropertyId::Custom(str_id), _) => $str_to_id(str_id),
// _ => None,
// }
// }
// }
};
} }
+2 -1
View File
@@ -1,7 +1,6 @@
use crate::error::{NokhwaError, NokhwaResult}; use crate::error::{NokhwaError, NokhwaResult};
use crate::frame_buffer::FrameBuffer; use crate::frame_buffer::FrameBuffer;
use flume::Receiver; use flume::Receiver;
use futures::TryFutureExt;
use std::sync::Arc; use std::sync::Arc;
pub trait StreamInnerTrait { pub trait StreamInnerTrait {
@@ -61,6 +60,8 @@ impl Stream {
#[cfg(feature = "async")] #[cfg(feature = "async")]
pub async fn await_frame(&self) -> NokhwaResult<FrameBuffer> { pub async fn await_frame(&self) -> NokhwaResult<FrameBuffer> {
use futures::TryFutureExt;
if self.inner.receiver().is_disconnected() { if self.inner.receiver().is_disconnected() {
return Err(NokhwaError::ReadFrameError( return Err(NokhwaError::ReadFrameError(
"stream is disconnected!".to_string(), "stream is disconnected!".to_string(),
+13 -1
View File
@@ -13,6 +13,7 @@ use std::num::NonZeroI32;
use std::ops::{Div, Rem}; use std::ops::{Div, Rem};
use num_rational::Rational32; use num_rational::Rational32;
use crate::ranges::{SimpleRangeItem}; use crate::ranges::{SimpleRangeItem};
use num_traits::FromPrimitive;
/// Describes the index of the camera. /// Describes the index of the camera.
/// - Index: A numbered index /// - Index: A numbered index
@@ -247,6 +248,17 @@ impl FrameRate {
pub fn denominator(&self) -> &i32 { pub fn denominator(&self) -> &i32 {
self.rational.denom() self.rational.denom()
} }
pub fn as_raw(&self) -> &Rational32 {
&self.rational
}
pub fn approximate_float(&self) -> Option<f32> {
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 { impl Default for FrameRate {
@@ -299,7 +311,7 @@ impl From<Rational32> for FrameRate {
/// This is a convenience 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`]. /// 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))] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct CameraFormat { pub struct CameraFormat {
resolution: Resolution, resolution: Resolution,
+3 -3
View File
@@ -34,7 +34,7 @@ use nokhwa_core::{
use std::{ffi::CString, sync::Arc}; use std::{ffi::CString, sync::Arc};
use std::{borrow::Cow, collections::HashMap}; 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. /// The backend struct that interfaces with V4L2.
/// To see what this does, please see [`CaptureTrait`]. /// To see what this does, please see [`CaptureTrait`].
@@ -231,7 +231,7 @@ impl CaptureTrait for AVFoundationCaptureDevice {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
self.device.lock()?; self.device.lock()?;
let res = self.device.set_control(id, value); let res = self.device.set_control(id, value);
@@ -463,7 +463,7 @@ impl CaptureTrait for AVFoundationCaptureDevice {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
_: KnownCameraControl, _: KnownCameraControl,
_: ControlValueSetter, _: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
todo!() todo!()
} }
+3 -3
View File
@@ -7,7 +7,7 @@ use serde::{de, Serialize};
use wasm_bindgen_futures::JsFuture; use wasm_bindgen_futures::JsFuture;
use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator}; use web_sys::{window, MediaDeviceInfo, MediaDevices, MediaStream, MediaStreamConstraints, MediaStreamTrack, MediaTrackConstraints, Navigator};
use nokhwa_core::frame_buffer::FrameBuffer; 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::error::NokhwaError;
use nokhwa_core::frame_format::FrameFormat; use nokhwa_core::frame_format::FrameFormat;
use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait}; use nokhwa_core::traits::{AsyncCaptureTrait, AsyncOpenCaptureTrait, CaptureTrait, OpenCaptureTrait};
@@ -360,7 +360,7 @@ impl CaptureTrait for BrowserCaptureDevice {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
todo!() todo!()
} }
@@ -414,7 +414,7 @@ impl AsyncCaptureTrait for BrowserCaptureDevice {
todo!() 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!() todo!()
} }
+2 -2
View File
@@ -26,7 +26,7 @@ use nokhwa_core::{
}, },
}; };
use std::{borrow::Cow, collections::HashMap}; 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. /// The backend that deals with Media Foundation on Windows.
/// To see what this does, please see [`CaptureTrait`]. /// To see what this does, please see [`CaptureTrait`].
@@ -229,7 +229,7 @@ impl CaptureTrait for MediaFoundationCaptureDevice {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
self.inner.set_control(id, value) self.inner.set_control(id, value)
} }
+5 -5
View File
@@ -33,7 +33,7 @@ use opencv::{
}, },
}; };
use std::{borrow::Cow, collections::HashMap}; 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. /// Attempts to convert a [`KnownCameraControl`] into a `OpenCV` video capture property.
/// If the associated control is not found, this will return `Err` /// If the associated control is not found, this will return `Err`
@@ -434,12 +434,12 @@ impl CaptureTrait for OpenCvCaptureDevice {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
let control_val = match value { let control_val = match value {
ControlValueSetter::Integer(i) => i as f64, ControlValue::Integer(i) => i as f64,
ControlValueSetter::Float(f) => f, ControlValue::Float(f) => f,
ControlValueSetter::Boolean(b) => u8::from(b) as f64, ControlValue::Boolean(b) => u8::from(b) as f64,
val => { val => {
return Err(NokhwaError::SetPropertyError { return Err(NokhwaError::SetPropertyError {
property: "Camera Control".to_string(), property: "Camera Control".to_string(),
+2 -2
View File
@@ -28,7 +28,7 @@ use nokhwa_core::{
}, },
}; };
use std::{borrow::Cow, collections::HashMap}; 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. /// The main `Camera` struct. This is the struct that abstracts over all the backends, providing a simplified interface for use.
pub struct Camera { pub struct Camera {
@@ -123,7 +123,7 @@ impl CaptureTrait for Camera {
fn set_camera_control( fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
value: ControlValueSetter, value: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
todo!() todo!()
} }
+3 -3
View File
@@ -31,7 +31,7 @@ use std::{
Arc, Mutex, Arc, Mutex,
}, },
}; };
use nokhwa_core::properties::{CameraControl, ControlValueSetter, KnownCameraControl}; use nokhwa_core::properties::{CameraControl, ControlValue, KnownCameraControl};
type AtomicLock<T> = Arc<Mutex<T>>; type AtomicLock<T> = Arc<Mutex<T>>;
pub type CallbackFn = fn( pub type CallbackFn = fn(
@@ -420,14 +420,14 @@ impl CallbackCamera {
/// Sets the control to `control` in the camera. /// 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 /// 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 /// # 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, /// 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. /// this will error.
pub fn set_camera_control( pub fn set_camera_control(
&mut self, &mut self,
id: KnownCameraControl, id: KnownCameraControl,
control: ControlValueSetter, control: ControlValue,
) -> Result<(), NokhwaError> { ) -> Result<(), NokhwaError> {
self.camera self.camera
.lock() .lock()