diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index 3e5d39b..de856b9 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -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, NokhwaError> { + pub fn get_controls(&self) -> Result, 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::>(); + + 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 { diff --git a/nokhwa-core/src/lib.rs b/nokhwa-core/src/lib.rs index 16333bf..d339a33 100644 --- a/nokhwa-core/src/lib.rs +++ b/nokhwa-core/src/lib.rs @@ -1,5 +1,6 @@ //! Core type definitions for `nokhwa` +extern crate core; extern crate core; pub mod buffer; diff --git a/nokhwa-core/src/pixel_format.rs b/nokhwa-core/src/pixel_format.rs index 97f2d15..8f70b66 100644 --- a/nokhwa-core/src/pixel_format.rs +++ b/nokhwa-core/src/pixel_format.rs @@ -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; const FORMATS: &'static [FrameFormat]; @@ -52,7 +52,7 @@ pub struct RgbFormat; impl FormatDecoder for RgbFormat { type Output = Rgb; const FORMATS: &'static [FrameFormat] = &[FrameFormat::MJPEG, FrameFormat::YUYV]; - + fn write_output(fcc: FrameFormat, data: &[u8]) -> Result, 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, NokhwaError> { diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index 119c8f4..e6a22d2 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -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 -where - F: FormatDecoder, -{ +pub struct RequestedFormat { requested_format: RequestedFormatType, - wanted_decoder: F, + wanted_decoder: &'static [FrameFormat], } -impl RequestedFormat -where - F: FormatDecoder, -{ - pub fn new(requested: RequestedFormatType) -> RequestedFormat { +impl RequestedFormat { + pub fn new(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::>(); 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::>(); 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::>(); 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 Display for RequestedFormat -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 { diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index 07c12f1..0dcefd8 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -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 { - 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, 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> {