mirror of
https://github.com/l1npengtul/nokhwa.git
synced 2026-07-04 02:27:26 +00:00
Add controls support for AVFoundation
This commit is contained in:
@@ -194,17 +194,15 @@ pub mod core_media {
|
||||
|
||||
use crate::core_media::{
|
||||
dispatch_queue_create, AVCaptureExposureDurationCurrent, AVCaptureExposureTargetBiasCurrent,
|
||||
AVCaptureISOCurrent, AVCaptureWhiteBalanceGains, AVMediaTypeVideo, CGPoint,
|
||||
AVCaptureISOCurrent, AVCaptureWhiteBalanceGains, AVMediaTypeAudio, AVMediaTypeClosedCaption,
|
||||
AVMediaTypeDepthData, AVMediaTypeMetadata, AVMediaTypeMetadataObject, AVMediaTypeMuxed,
|
||||
AVMediaTypeSubtitle, AVMediaTypeText, AVMediaTypeTimecode, AVMediaTypeVideo, CGPoint,
|
||||
CMSampleBufferGetImageBuffer, CMVideoFormatDescriptionGetDimensions, CVImageBufferRef,
|
||||
CVPixelBufferGetBaseAddress, CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress,
|
||||
CVPixelBufferUnlockBaseAddress, NSObject,
|
||||
};
|
||||
use crate::core_media::{
|
||||
AVMediaTypeAudio, AVMediaTypeClosedCaption, AVMediaTypeDepthData, AVMediaTypeMetadata,
|
||||
AVMediaTypeMetadataObject, AVMediaTypeMuxed, AVMediaTypeSubtitle, AVMediaTypeText,
|
||||
AVMediaTypeTimecode, CVPixelBufferGetPixelFormatType,
|
||||
CVPixelBufferGetBaseAddress, CVPixelBufferGetDataSize, CVPixelBufferGetPixelFormatType,
|
||||
CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress, NSObject,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa_foundation::base::Nil;
|
||||
use cocoa_foundation::foundation::{NSArray, NSInteger, NSString, NSUInteger};
|
||||
use core_media_sys::{
|
||||
kCMPixelFormat_422YpCbCr8_yuvs, kCMPixelFormat_8IndexedGray_WhiteIsZero,
|
||||
@@ -213,19 +211,20 @@ use core_media_sys::{
|
||||
CMVideoDimensions,
|
||||
};
|
||||
use flume::{Receiver, Sender};
|
||||
use nokhwa_core::types::{
|
||||
CameraControl, ControlValueDescription, KnownCameraControl, KnownCameraControlFlag,
|
||||
};
|
||||
use nokhwa_core::types::ControlValueSetter;
|
||||
use nokhwa_core::{
|
||||
error::NokhwaError,
|
||||
types::{ApiBackend, CameraFormat, CameraIndex, CameraInfo, FrameFormat, Resolution},
|
||||
types::{
|
||||
ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo, ControlValueDescription,
|
||||
FrameFormat, KnownCameraControl, KnownCameraControlFlag, Resolution,
|
||||
},
|
||||
};
|
||||
use objc::runtime::NO;
|
||||
use objc::{
|
||||
declare::ClassDecl,
|
||||
runtime::{Class, Object, Protocol, Sel, BOOL, YES},
|
||||
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::BTreeMap;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
cmp::Ordering,
|
||||
@@ -1071,7 +1070,7 @@ impl AVCaptureDevice {
|
||||
// 4 => Exposure Target Bias
|
||||
// 5 => Exposure ISO
|
||||
// 6 => Exposure Duration
|
||||
pub fn get_controls(&mut self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
pub fn get_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
let active_format: *mut Object = unsafe { msg_send![self.inner, activeFormat] };
|
||||
|
||||
let mut controls = vec![];
|
||||
@@ -1123,7 +1122,10 @@ impl AVCaptureDevice {
|
||||
default: (0.5, 0.5),
|
||||
},
|
||||
if focus_poi_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -1147,7 +1149,10 @@ impl AVCaptureDevice {
|
||||
if focus_manual == YES {
|
||||
vec![]
|
||||
} else {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
},
|
||||
focus_manual == YES,
|
||||
));
|
||||
@@ -1204,7 +1209,10 @@ impl AVCaptureDevice {
|
||||
default: (0.5, 0.5),
|
||||
},
|
||||
if exposure_poi_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -1228,7 +1236,10 @@ impl AVCaptureDevice {
|
||||
default: false,
|
||||
},
|
||||
if expposure_face_driven_supported == NO {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -1260,7 +1271,7 @@ impl AVCaptureDevice {
|
||||
unsafe { msg_send![active_format, maxExposureDuration] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(5),
|
||||
KnownCameraControl::Gamma,
|
||||
"ExposureDuration".to_string(),
|
||||
ControlValueDescription::IntegerRange {
|
||||
min: exposure_duration_min.value,
|
||||
@@ -1285,7 +1296,7 @@ impl AVCaptureDevice {
|
||||
let exposure_iso_max: f32 = unsafe { msg_send![active_format, maxISO] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(6),
|
||||
KnownCameraControl::Brightness,
|
||||
"ExposureISO".to_string(),
|
||||
ControlValueDescription::FloatRange {
|
||||
min: exposure_iso_min as f64,
|
||||
@@ -1308,7 +1319,7 @@ impl AVCaptureDevice {
|
||||
let lens_aperture: f32 = unsafe { msg_send![self.inner, lensAperture] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(7),
|
||||
KnownCameraControl::Iris,
|
||||
"LensAperture".to_string(),
|
||||
ControlValueDescription::Float {
|
||||
value: lens_aperture as f64,
|
||||
@@ -1422,15 +1433,18 @@ impl AVCaptureDevice {
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(8),
|
||||
"Flash".to_string(),
|
||||
KnownCameraControl::Other(5),
|
||||
"TorchMode".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: (torch_active == YES) as i64,
|
||||
possible,
|
||||
default: 0,
|
||||
},
|
||||
if has_torch == YES {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -1443,24 +1457,18 @@ impl AVCaptureDevice {
|
||||
let llb_enabled: BOOL = unsafe { msg_send![self.inner, lowLightBoostEnabled] };
|
||||
|
||||
{
|
||||
let mut possible = vec![];
|
||||
|
||||
if has_llb == YES {
|
||||
possible.push(0); // off
|
||||
possible.push(1); // on
|
||||
possible.push(2); // auto
|
||||
}
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::BacklightComp,
|
||||
"LowLightCompensation".to_string(),
|
||||
ControlValueDescription::Enum {
|
||||
value: (llb_enabled == YES) as i64,
|
||||
possible,
|
||||
default: 0,
|
||||
ControlValueDescription::Boolean {
|
||||
value: llb_enabled == YES,
|
||||
default: false,
|
||||
},
|
||||
if has_llb == YES {
|
||||
vec![KnownCameraControlFlag::Disabled]
|
||||
if has_llb == NO {
|
||||
vec![
|
||||
KnownCameraControlFlag::Disabled,
|
||||
KnownCameraControlFlag::ReadOnly,
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
@@ -1494,7 +1502,7 @@ impl AVCaptureDevice {
|
||||
unsafe { msg_send![self.inner, geometricDistortionCorrectionEnabled] };
|
||||
|
||||
controls.push(CameraControl::new(
|
||||
KnownCameraControl::Other(9),
|
||||
KnownCameraControl::Other(6),
|
||||
"DistortionCorrection".to_string(),
|
||||
ControlValueDescription::Boolean {
|
||||
value: distortion_correction_current_value == YES,
|
||||
@@ -1513,6 +1521,717 @@ impl AVCaptureDevice {
|
||||
|
||||
Ok(controls)
|
||||
}
|
||||
|
||||
pub fn set_control(
|
||||
&mut self,
|
||||
id: KnownCameraControl,
|
||||
value: ControlValueSetter,
|
||||
) -> Result<(), NokhwaError> {
|
||||
let rc = self.get_controls()?;
|
||||
let controls = rc
|
||||
.iter()
|
||||
.map(|cc| (cc.control(), cc))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
match id {
|
||||
KnownCameraControl::Brightness => {
|
||||
let isoctrl = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if isoctrl.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Exposure is in improper state to set ISO (Please set to `custom`!)"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if isoctrl.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let current_duration = unsafe { AVCaptureExposureDurationCurrent };
|
||||
let new_iso = *value.as_float().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected float".to_string(),
|
||||
})? as f32;
|
||||
|
||||
if !isoctrl.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, setExposureModeCustomWithDuration:current_duration ISO:new_iso completionHandler:Nil]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Gamma => {
|
||||
let duration_ctrl = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if duration_ctrl
|
||||
.flag()
|
||||
.contains(&KnownCameraControlFlag::ReadOnly)
|
||||
{
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Exposure is in improper state to set Duration (Please set to `custom`!)"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if duration_ctrl
|
||||
.flag()
|
||||
.contains(&KnownCameraControlFlag::Disabled)
|
||||
{
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
let current_duration: CMTime = unsafe { msg_send![self.inner, exposureDuration] };
|
||||
|
||||
let current_iso = unsafe { AVCaptureISOCurrent };
|
||||
let new_duration = CMTime {
|
||||
value: *value.as_integer().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected i64".to_string(),
|
||||
})?,
|
||||
timescale: current_duration.timescale,
|
||||
flags: current_duration.flags,
|
||||
epoch: current_duration.epoch,
|
||||
};
|
||||
|
||||
if !duration_ctrl.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, setExposureModeCustomWithDuration:new_duration ISO:current_iso completionHandler:Nil]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::WhiteBalance => {
|
||||
let wb_enum_value = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if wb_enum_value
|
||||
.flag()
|
||||
.contains(&KnownCameraControlFlag::ReadOnly)
|
||||
{
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if wb_enum_value
|
||||
.flag()
|
||||
.contains(&KnownCameraControlFlag::Disabled)
|
||||
{
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
let setter =
|
||||
NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Enum".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !wb_enum_value.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::BacklightComp => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter =
|
||||
NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Enum".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Gain => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter =
|
||||
NSInteger::from(*value.as_boolean().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Boolean".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Zoom => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected float".to_string(),
|
||||
})? as c_float;
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, rampToVideoZoomFactor: setter withRate: 1.0_f32]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Exposure => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter =
|
||||
NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Enum".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, exposureMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Iris => Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
}),
|
||||
KnownCameraControl::Focus => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter =
|
||||
NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Enum".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, focusMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
KnownCameraControl::Other(i) => match i {
|
||||
0 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = value
|
||||
.as_point()
|
||||
.ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Point".to_string(),
|
||||
})
|
||||
.map(|(x, y)| CGPoint {
|
||||
x: *x as f32,
|
||||
y: *y as f32,
|
||||
})?;
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, focusPointOfInterest: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
1 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected float".to_string(),
|
||||
})? as c_float;
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, setFocusModeLockedWithLensPosition: setter handler: Nil]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
2 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = value
|
||||
.as_point()
|
||||
.ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Point".to_string(),
|
||||
})
|
||||
.map(|(x, y)| CGPoint {
|
||||
x: *x as f32,
|
||||
y: *y as f32,
|
||||
})?;
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, exposurePointOfInterest: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
3 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = if *value.as_boolean().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Boolean".to_string(),
|
||||
})? {
|
||||
YES
|
||||
} else {
|
||||
NO
|
||||
};
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![
|
||||
self.inner,
|
||||
automaticallyAdjustsFaceDrivenAutoExposureEnabled: setter
|
||||
]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
4 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Float".to_string(),
|
||||
})? as f32;
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, setExposureTargetBias: setter handler: Nil]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
5 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter =
|
||||
NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Enum".to_string(),
|
||||
})? as i32);
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![self.inner, torchMode: setter] };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
6 => {
|
||||
let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Control does not exist".to_string(),
|
||||
})?;
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Read Only".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Disabled".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let setter = if *value.as_boolean().ok_or(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Expected Boolean".to_string(),
|
||||
})? {
|
||||
YES
|
||||
} else {
|
||||
NO
|
||||
};
|
||||
|
||||
if !ctrlvalue.description().verify_setter(&value) {
|
||||
return Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Failed to verify value".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let _: () = unsafe {
|
||||
msg_send![self.inner, geometricDistortionCorrectionEnabled: setter]
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Unknown Control".to_string(),
|
||||
}),
|
||||
},
|
||||
_ => Err(NokhwaError::SetPropertyError {
|
||||
property: id.to_string(),
|
||||
value: value.to_string(),
|
||||
error: "Unknown Control".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AVCaptureDevice {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Core type definitions for `nokhwa`
|
||||
|
||||
extern crate core;
|
||||
extern crate core;
|
||||
|
||||
pub mod buffer;
|
||||
|
||||
@@ -21,7 +21,7 @@ use image::{Luma, LumaA, Pixel, Rgb, Rgba};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Trait that has methods to convert raw data from the webcam to a proper raw image.
|
||||
pub trait FormatDecoder: Copy + Clone + Debug + Default + Sized + Send + Sync + {
|
||||
pub trait FormatDecoder: Copy + Clone + Debug + Default + Sized + Send + Sync {
|
||||
type Output: Pixel<Subpixel = u8>;
|
||||
const FORMATS: &'static [FrameFormat];
|
||||
|
||||
@@ -52,7 +52,7 @@ pub struct RgbFormat;
|
||||
impl FormatDecoder for RgbFormat {
|
||||
type Output = Rgb<u8>;
|
||||
const FORMATS: &'static [FrameFormat] = &[FrameFormat::MJPEG, FrameFormat::YUYV];
|
||||
|
||||
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
match fcc {
|
||||
FrameFormat::MJPEG => mjpeg_to_rgb(data, false),
|
||||
@@ -168,7 +168,6 @@ impl FormatDecoder for LumaFormat {
|
||||
|
||||
const FORMATS: &'static [FrameFormat] =
|
||||
&[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY];
|
||||
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn write_output(fcc: FrameFormat, data: &[u8]) -> Result<Vec<u8>, NokhwaError> {
|
||||
|
||||
+229
-85
@@ -37,23 +37,26 @@ impl Display for RequestedFormatType {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
|
||||
pub struct RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
pub struct RequestedFormat {
|
||||
requested_format: RequestedFormatType,
|
||||
wanted_decoder: F,
|
||||
wanted_decoder: &'static [FrameFormat],
|
||||
}
|
||||
|
||||
impl<F> RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
pub fn new<Decoder: FormatDecoder>(requested: RequestedFormatType) -> RequestedFormat<Decoder> {
|
||||
impl RequestedFormat {
|
||||
pub fn new<Decoder: FormatDecoder>(requested: RequestedFormatType) -> RequestedFormat {
|
||||
RequestedFormat {
|
||||
requested_format: requested,
|
||||
wanted_decoder: Decoder::default(),
|
||||
wanted_decoder: Decoder::FORMATS,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_formats(
|
||||
requested: RequestedFormatType,
|
||||
decoder: &'static [FrameFormat],
|
||||
) -> RequestedFormat {
|
||||
RequestedFormat {
|
||||
requested_format: requested,
|
||||
wanted_decoder: decoder,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +71,7 @@ where
|
||||
.into_iter()
|
||||
.filter(|fmt| {
|
||||
fmt.resolution() == resolution.resolution()
|
||||
&& F::FORMATS.contains(&fmt.format())
|
||||
&& self.wanted_decoder.contains(&fmt.format())
|
||||
})
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
format_resolutions.sort_by_key(|a| a.frame_rate());
|
||||
@@ -82,14 +85,14 @@ where
|
||||
.into_iter()
|
||||
.filter(|fmt| {
|
||||
fmt.frame_rate() == frame_rate.frame_rate()
|
||||
&& F::FORMATS.contains(&fmt.format())
|
||||
&& self.wanted_decoder.contains(&fmt.format())
|
||||
})
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
format_framerates.sort_by_key(|a| a.resolution());
|
||||
format_framerates.last().copied()
|
||||
}
|
||||
RequestedFormatType::Exact(fmt) => {
|
||||
if F::FORMATS.contains(&fmt.format()) {
|
||||
if self.wanted_decoder.contains(&fmt.format()) {
|
||||
Some(fmt)
|
||||
} else {
|
||||
None
|
||||
@@ -98,7 +101,9 @@ where
|
||||
RequestedFormatType::Closest(c) => {
|
||||
let same_fmt_formats = all_formats
|
||||
.iter()
|
||||
.filter(|x| x.format() == c.format() && F::FORMATS.contains(&x.format()))
|
||||
.filter(|x| {
|
||||
x.format() == c.format() && self.wanted_decoder.contains(&x.format())
|
||||
})
|
||||
.copied()
|
||||
.collect::<Vec<CameraFormat>>();
|
||||
let mut resolution_map = same_fmt_formats
|
||||
@@ -138,17 +143,14 @@ where
|
||||
}
|
||||
RequestedFormatType::None => all_formats
|
||||
.iter()
|
||||
.filter(|fmt| F::FORMATS.contains(&fmt.format()))
|
||||
.filter(|fmt| self.wanted_decoder.contains(&fmt.format()))
|
||||
.nth(0)
|
||||
.copied(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Display for RequestedFormat<F>
|
||||
where
|
||||
F: FormatDecoder,
|
||||
{
|
||||
impl Display for RequestedFormat {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
@@ -763,74 +765,138 @@ impl ControlValueDescription {
|
||||
/// - `false` => Is not valid.
|
||||
#[must_use]
|
||||
pub fn verify_setter(&self, setter: &ControlValueSetter) -> bool {
|
||||
match setter {
|
||||
ControlValueSetter::None => {
|
||||
matches!(self, ControlValueDescription::None)
|
||||
}
|
||||
ControlValueSetter::Integer(i) => match self {
|
||||
ControlValueDescription::Integer {
|
||||
value,
|
||||
default,
|
||||
step,
|
||||
} => (i - default).abs() % step == 0 || (i - value) % step == 0,
|
||||
ControlValueDescription::IntegerRange {
|
||||
min,
|
||||
max,
|
||||
value,
|
||||
step,
|
||||
default,
|
||||
} => {
|
||||
if value > max || value < min {
|
||||
return false;
|
||||
}
|
||||
|
||||
(i - default) % step == 0 || (i - value) % step == 0
|
||||
}
|
||||
_ => false,
|
||||
match self {
|
||||
ControlValueDescription::None => return setter.as_none().is_some(),
|
||||
ControlValueDescription::Integer {
|
||||
value,
|
||||
default,
|
||||
step,
|
||||
} => match setter.as_integer() {
|
||||
Some(i) => (i + default) % step == 0 || (i + value) % step == 0,
|
||||
None => false,
|
||||
},
|
||||
ControlValueSetter::Float(f) => match self {
|
||||
ControlValueDescription::Float {
|
||||
value,
|
||||
default,
|
||||
step,
|
||||
} => (f - default).abs() % step == 0_f64 || (f - value) % step == 0_f64,
|
||||
ControlValueDescription::FloatRange {
|
||||
min,
|
||||
max,
|
||||
value,
|
||||
step,
|
||||
default,
|
||||
} => {
|
||||
if value > max || value < min {
|
||||
return false;
|
||||
}
|
||||
|
||||
(f - default) % step == 0_f64 || (f - value) % step == 0_f64
|
||||
ControlValueDescription::IntegerRange {
|
||||
min,
|
||||
max,
|
||||
value,
|
||||
step,
|
||||
default,
|
||||
} => match setter.as_integer() {
|
||||
Some(i) => {
|
||||
((i + default) % step == 0 || (i + value) % step == 0) && i >= min && i <= max
|
||||
}
|
||||
_ => false,
|
||||
None => false,
|
||||
},
|
||||
ControlValueDescription::Float {
|
||||
value,
|
||||
default,
|
||||
step,
|
||||
} => match setter.as_float() {
|
||||
Some(f) => (f - default).abs() % step == 0_f64 || (f - value) % step == 0_f64,
|
||||
None => false,
|
||||
},
|
||||
ControlValueDescription::FloatRange {
|
||||
min,
|
||||
max,
|
||||
value,
|
||||
step,
|
||||
default,
|
||||
} => match setter.as_float() {
|
||||
Some(f) => {
|
||||
((f - default).abs() % step == 0_f64 || (f - value) % step == 0_f64)
|
||||
&& f >= min
|
||||
&& f <= max
|
||||
}
|
||||
None => false,
|
||||
},
|
||||
ControlValueDescription::Boolean { .. } => setter.as_boolean().is_some(),
|
||||
ControlValueDescription::String { .. } => setter.as_str().is_some(),
|
||||
ControlValueDescription::Bytes { .. } => setter.as_bytes().is_some(),
|
||||
ControlValueDescription::KeyValuePair { .. } => setter.as_key_value().is_some(),
|
||||
ControlValueDescription::Point { .. } => match setter.as_point() {
|
||||
Some(pt) => {
|
||||
!pt.0.is_nan() && !pt.1.is_nan() && pt.0.is_finite() && pt.1.is_finite()
|
||||
}
|
||||
None => false,
|
||||
},
|
||||
ControlValueDescription::Enum { possible, .. } => match setter.as_enum() {
|
||||
Some(e) => possible.contains(e),
|
||||
None => false,
|
||||
},
|
||||
ControlValueDescription::RGB { max, .. } => match setter.as_rgb() {
|
||||
Some(v) => *v.0 >= max.0 && *v.1 >= max.1 && *v.2 >= max.2,
|
||||
None => false,
|
||||
},
|
||||
ControlValueSetter::Boolean(_) => {
|
||||
matches!(self, ControlValueDescription::Boolean { .. })
|
||||
}
|
||||
ControlValueSetter::String(_) => {
|
||||
matches!(self, ControlValueDescription::String { .. })
|
||||
}
|
||||
ControlValueSetter::Bytes(_) => {
|
||||
matches!(self, ControlValueDescription::Bytes { .. })
|
||||
}
|
||||
ControlValueSetter::KeyValue(_, _) => {
|
||||
matches!(self, ControlValueDescription::KeyValuePair { .. })
|
||||
}
|
||||
ControlValueSetter::Point(_, _) => {
|
||||
matches!(self, ControlValueDescription::Point { .. })
|
||||
}
|
||||
ControlValueSetter::EnumValue(_) => {
|
||||
matches!(self, ControlValueDescription::Enum { .. })
|
||||
}
|
||||
ControlValueSetter::RGB(_, _, _) => {
|
||||
matches!(self, ControlValueDescription::RGB { .. })
|
||||
}
|
||||
}
|
||||
|
||||
// match setter {
|
||||
// ControlValueSetter::None => {
|
||||
// matches!(self, ControlValueDescription::None)
|
||||
// }
|
||||
// ControlValueSetter::Integer(i) => match self {
|
||||
// ControlValueDescription::Integer {
|
||||
// value,
|
||||
// default,
|
||||
// step,
|
||||
// } => (i - default).abs() % step == 0 || (i - value) % step == 0,
|
||||
// ControlValueDescription::IntegerRange {
|
||||
// min,
|
||||
// max,
|
||||
// value,
|
||||
// step,
|
||||
// default,
|
||||
// } => {
|
||||
// if value > max || value < min {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// (i - default) % step == 0 || (i - value) % step == 0
|
||||
// }
|
||||
// _ => false,
|
||||
// },
|
||||
// ControlValueSetter::Float(f) => match self {
|
||||
// ControlValueDescription::Float {
|
||||
// value,
|
||||
// default,
|
||||
// step,
|
||||
// } => (f - default).abs() % step == 0_f64 || (f - value) % step == 0_f64,
|
||||
// ControlValueDescription::FloatRange {
|
||||
// min,
|
||||
// max,
|
||||
// value,
|
||||
// step,
|
||||
// default,
|
||||
// } => {
|
||||
// if value > max || value < min {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// (f - default) % step == 0_f64 || (f - value) % step == 0_f64
|
||||
// }
|
||||
// _ => false,
|
||||
// },
|
||||
// ControlValueSetter::Boolean(b) => {
|
||||
//
|
||||
// }
|
||||
// ControlValueSetter::String(_) => {
|
||||
// matches!(self, ControlValueDescription::String { .. })
|
||||
// }
|
||||
// ControlValueSetter::Bytes(_) => {
|
||||
// matches!(self, ControlValueDescription::Bytes { .. })
|
||||
// }
|
||||
// ControlValueSetter::KeyValue(_, _) => {
|
||||
// matches!(self, ControlValueDescription::KeyValuePair { .. })
|
||||
// }
|
||||
// ControlValueSetter::Point(_, _) => {
|
||||
// matches!(self, ControlValueDescription::Point { .. })
|
||||
// }
|
||||
// ControlValueSetter::EnumValue(_) => {
|
||||
// matches!(self, ControlValueDescription::Enum { .. })
|
||||
// }
|
||||
// ControlValueSetter::RGB(_, _, _) => {
|
||||
// matches!(self, ControlValueDescription::RGB { .. })
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1057,6 +1123,84 @@ pub enum ControlValueSetter {
|
||||
RGB(f64, f64, f64),
|
||||
}
|
||||
|
||||
impl ControlValueSetter {
|
||||
pub fn as_none(&self) -> Option<()> {
|
||||
if let ControlValueSetter::None = self {
|
||||
Some(())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_integer(&self) -> Option<&i64> {
|
||||
if let ControlValueSetter::Integer(i) = self {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_float(&self) -> Option<&f64> {
|
||||
if let ControlValueSetter::Float(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn as_boolean(&self) -> Option<&bool> {
|
||||
if let ControlValueSetter::Boolean(f) = self {
|
||||
Some(f)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
if let ControlValueSetter::String(s) = self {
|
||||
Some(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn as_bytes(&self) -> Option<&[u8]> {
|
||||
if let ControlValueSetter::Bytes(b) = self {
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn as_key_value(&self) -> Option<(&i128, &i128)> {
|
||||
if let ControlValueSetter::KeyValue(k, v) = self {
|
||||
Some((k, v))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_point(&self) -> Option<(&f64, &f64)> {
|
||||
if let ControlValueSetter::Point(x, y) = self {
|
||||
Some((x, y))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_enum(&self) -> Option<&i64> {
|
||||
if let ControlValueSetter::EnumValue(e) = self {
|
||||
Some(e)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_rgb(&self) -> Option<(&f64, &f64, &f64)> {
|
||||
if let ControlValueSetter::RGB(r, g, b) = self {
|
||||
Some((r, g, b))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ControlValueSetter {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -27,6 +27,7 @@ use nokhwa_core::{
|
||||
FrameFormat, KnownCameraControl, RequestedFormat, Resolution,
|
||||
},
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::CString;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@@ -201,11 +202,20 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice {
|
||||
}
|
||||
|
||||
fn camera_control(&self, control: KnownCameraControl) -> Result<CameraControl, NokhwaError> {
|
||||
todo!()
|
||||
for ctrl in self.device.get_controls()? {
|
||||
if ctrl.control() == control {
|
||||
return Ok(ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(NokhwaError::GetPropertyError {
|
||||
property: control.to_string(),
|
||||
error: "Not Found".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
fn camera_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
|
||||
todo!()
|
||||
self.device.get_controls()
|
||||
}
|
||||
|
||||
fn set_camera_control(
|
||||
@@ -213,7 +223,10 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice {
|
||||
id: KnownCameraControl,
|
||||
value: ControlValueSetter,
|
||||
) -> Result<(), NokhwaError> {
|
||||
todo!()
|
||||
self.device.lock()?;
|
||||
let res = self.device.set_control(id, value);
|
||||
self.device.unlock()?;
|
||||
res
|
||||
}
|
||||
|
||||
fn open_stream(&mut self) -> Result<(), NokhwaError> {
|
||||
|
||||
Reference in New Issue
Block a user