diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index f1aa678..3e5d39b 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -29,6 +29,7 @@ extern crate objc; pub mod core_media { // all of this is stolen from bindgen // steal it idc + use crate::CGFloat; use core_media_sys::{ CMBlockBufferRef, CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions, FourCharCode, @@ -120,11 +121,28 @@ pub mod core_media { pub fn CVPixelBufferGetPixelFormatType(pixelBuffer: CVPixelBufferRef) -> OSType; } + #[repr(C)] + #[derive(Clone, Debug, PartialEq, PartialOrd)] + pub struct CGPoint { + pub x: CGFloat, + pub y: CGFloat, + } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct __CVBuffer { _unused: [u8; 0], } + + #[allow(non_snake_case)] + #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] + #[repr(C)] + pub struct AVCaptureWhiteBalanceGains { + pub blueGain: f32, + pub greenGain: f32, + pub redGain: f32, + } + pub type CVBufferRef = *mut __CVBuffer; pub type CVImageBufferRef = CVBufferRef; @@ -166,14 +184,20 @@ pub mod core_media { pub static AVMediaTypeMuxed: AVMediaType; pub static AVMediaTypeMetadataObject: AVMediaType; pub static AVMediaTypeDepthData: AVMediaType; + + pub static AVCaptureLensPositionCurrent: f32; + pub static AVCaptureExposureTargetBiasCurrent: f32; + pub static AVCaptureExposureDurationCurrent: CMTime; + pub static AVCaptureISOCurrent: f32; } } use crate::core_media::{ - dispatch_queue_create, AVMediaTypeVideo, CMSampleBufferGetImageBuffer, - CMVideoFormatDescriptionGetDimensions, CVImageBufferRef, CVPixelBufferGetBaseAddress, - CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress, - NSObject, + dispatch_queue_create, AVCaptureExposureDurationCurrent, AVCaptureExposureTargetBiasCurrent, + AVCaptureISOCurrent, AVCaptureWhiteBalanceGains, AVMediaTypeVideo, CGPoint, + CMSampleBufferGetImageBuffer, CMVideoFormatDescriptionGetDimensions, CVImageBufferRef, + CVPixelBufferGetBaseAddress, CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress, + CVPixelBufferUnlockBaseAddress, NSObject, }; use crate::core_media::{ AVMediaTypeAudio, AVMediaTypeClosedCaption, AVMediaTypeDepthData, AVMediaTypeMetadata, @@ -185,14 +209,18 @@ use cocoa_foundation::foundation::{NSArray, NSInteger, NSString, NSUInteger}; use core_media_sys::{ kCMPixelFormat_422YpCbCr8_yuvs, kCMPixelFormat_8IndexedGray_WhiteIsZero, kCMVideoCodecType_422YpCbCr8, kCMVideoCodecType_JPEG, kCMVideoCodecType_JPEG_OpenDML, - CMFormatDescriptionGetMediaSubType, CMFormatDescriptionRef, CMSampleBufferRef, + CMFormatDescriptionGetMediaSubType, CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions, }; use flume::{Receiver, Sender}; +use nokhwa_core::types::{ + CameraControl, ControlValueDescription, KnownCameraControl, KnownCameraControlFlag, +}; use nokhwa_core::{ error::NokhwaError, types::{ApiBackend, CameraFormat, CameraIndex, CameraInfo, FrameFormat, Resolution}, }; +use objc::runtime::NO; use objc::{ declare::ClassDecl, runtime::{Class, Object, Protocol, Sel, BOOL, YES}, @@ -204,11 +232,12 @@ use std::{ collections::HashSet, convert::TryFrom, error::Error, - ffi::{c_void, CStr}, + ffi::{c_float, c_void, CStr}, sync::{Arc, Mutex}, }; const UTF8_ENCODING: usize = 4; +type CGFloat = c_float; macro_rules! create_boilerplate_impl { { @@ -336,8 +365,6 @@ static CALLBACK_CLASS: Lazy<&'static Class> = Lazy::new(|| { // frame stack // oooh scary provenannce-breaking BULLSHIT AAAAAA I LOVE TYPE ERASURE decl.add_ivar::<*const c_void>("_arcmutptr"); // ArkMutex, the not-arknights totally not gacha totally not ripoff new vidya game from l-pleasestop-npengtul - // KILL ME KILL ME KILL ME PLEASE KILL ME I DONT WANT TO LIVE ANYMORE - // i draw myself getting hurt and murdered in various ways to distract from my urges self harm extern "C" fn my_callback_get_arcmutptr(this: &Object, _: Sel) -> *const c_void { unsafe { *this.get_ivar("_arcmutptr") } @@ -1036,6 +1063,456 @@ impl AVCaptureDevice { self.unlock(); Ok(()) } + + // 0 => Focus POI + // 1 => Focus Manual Setting + // 2 => Exposure POI + // 3 => Exposure Face Driven + // 4 => Exposure Target Bias + // 5 => Exposure ISO + // 6 => Exposure Duration + pub fn get_controls(&mut self) -> Result, NokhwaError> { + let active_format: *mut Object = unsafe { msg_send![self.inner, activeFormat] }; + + let mut controls = vec![]; + // get focus modes + + let focus_current: NSInteger = unsafe { msg_send![self.inner, focusMode] }; + let focus_locked: BOOL = + unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(0)] }; + let focus_auto: BOOL = + unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(1)] }; + let focus_continuous: BOOL = + unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(2)] }; + + { + let mut supported_focus_values = vec![]; + + if focus_locked == YES { + supported_focus_values.push(0) + } + if focus_auto == YES { + supported_focus_values.push(1) + } + if focus_continuous == YES { + supported_focus_values.push(2) + } + + controls.push(CameraControl::new( + KnownCameraControl::Focus, + "FocusMode".to_string(), + ControlValueDescription::Enum { + value: focus_current, + possible: supported_focus_values, + default: focus_current, + }, + vec![], + true, + )); + } + + let focus_poi_supported: BOOL = + unsafe { msg_send![self.inner, isFocusPointOfInterestSupported] }; + let focus_poi: CGPoint = unsafe { msg_send![self.inner, focusPointOfInterest] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(0), + "FocusPointOfInterest".to_string(), + ControlValueDescription::Point { + value: (focus_poi.x as f64, focus_poi.y as f64), + default: (0.5, 0.5), + }, + if focus_poi_supported == NO { + vec![KnownCameraControlFlag::Disabled] + } else { + vec![] + }, + focus_auto == YES || focus_continuous == YES, + )); + + let focus_manual: BOOL = + unsafe { msg_send![self.inner, isLockingFocusWithCustomLensPositionSupported] }; + let focus_lenspos: f32 = unsafe { msg_send![self.inner, lensPosition] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(1), + "FocusManualLensPosition".to_string(), + ControlValueDescription::FloatRange { + min: 0.0, + max: 1.0, + value: focus_lenspos as f64, + step: f64::MIN_POSITIVE, + default: 1.0, + }, + if focus_manual == YES { + vec![] + } else { + vec![KnownCameraControlFlag::Disabled] + }, + focus_manual == YES, + )); + + // get exposures + let exposure_current: NSInteger = unsafe { msg_send![self.inner, exposureMode] }; + let exposure_locked: BOOL = + unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(0)] }; + let exposure_auto: BOOL = + unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(1)] }; + let exposure_continuous: BOOL = + unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(2)] }; + let exposure_custom: BOOL = + unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(3)] }; + + { + let mut supported_exposure_values = vec![]; + + if exposure_locked == YES { + supported_exposure_values.push(0); + } + if exposure_auto == YES { + supported_exposure_values.push(1); + } + if exposure_continuous == YES { + supported_exposure_values.push(2); + } + if exposure_custom == YES { + supported_exposure_values.push(3); + } + + controls.push(CameraControl::new( + KnownCameraControl::Exposure, + "ExposureMode".to_string(), + ControlValueDescription::Enum { + value: exposure_current, + possible: supported_exposure_values, + default: exposure_current, + }, + vec![], + true, + )); + } + + let exposure_poi_supported: BOOL = + unsafe { msg_send![self.inner, isExposurePointOfInterestSupported] }; + let exposure_poi: CGPoint = unsafe { msg_send![self.inner, exposurePointOfInterest] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(2), + "ExposurePointOfInterest".to_string(), + ControlValueDescription::Point { + value: (exposure_poi.x as f64, exposure_poi.y as f64), + default: (0.5, 0.5), + }, + if exposure_poi_supported == NO { + vec![KnownCameraControlFlag::Disabled] + } else { + vec![] + }, + focus_auto == YES || focus_continuous == YES, + )); + + let expposure_face_driven_supported: BOOL = + unsafe { msg_send![self.inner, isFaceDrivenAutoExposureEnabled] }; + let exposure_face_driven: BOOL = unsafe { + msg_send![ + self.inner, + automaticallyAdjustsFaceDrivenAutoExposureEnabled + ] + }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(3), + "ExposureFaceDriven".to_string(), + ControlValueDescription::Boolean { + value: exposure_face_driven == YES, + default: false, + }, + if expposure_face_driven_supported == NO { + vec![KnownCameraControlFlag::Disabled] + } else { + vec![] + }, + exposure_poi_supported == YES, + )); + + let exposure_bias: f32 = unsafe { msg_send![self.inner, exposureTargetBias] }; + let exposure_bias_min: f32 = unsafe { msg_send![self.inner, minExposureTargetBias] }; + let exposure_bias_max: f32 = unsafe { msg_send![self.inner, maxExposureTargetBias] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(4), + "ExposureBiasTarget".to_string(), + ControlValueDescription::FloatRange { + min: exposure_bias_min as f64, + max: exposure_bias_max as f64, + value: exposure_bias as f64, + step: f32::MIN_POSITIVE as f64, + default: unsafe { AVCaptureExposureTargetBiasCurrent } as f64, + }, + vec![], + true, + )); + + let exposure_duration: CMTime = unsafe { msg_send![self.inner, exposureDuration] }; + let exposure_duration_min: CMTime = + unsafe { msg_send![active_format, minExposureDuration] }; + let exposure_duration_max: CMTime = + unsafe { msg_send![active_format, maxExposureDuration] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(5), + "ExposureDuration".to_string(), + ControlValueDescription::IntegerRange { + min: exposure_duration_min.value, + max: exposure_duration_max.value, + value: exposure_duration.value, + step: 1, + default: unsafe { AVCaptureExposureDurationCurrent.value }, + }, + if exposure_custom == YES { + vec![ + KnownCameraControlFlag::ReadOnly, + KnownCameraControlFlag::Volatile, + ] + } else { + vec![KnownCameraControlFlag::Volatile] + }, + exposure_custom == YES, + )); + + let exposure_iso: f32 = unsafe { msg_send![self.inner, ISO] }; + let exposure_iso_min: f32 = unsafe { msg_send![active_format, minISO] }; + let exposure_iso_max: f32 = unsafe { msg_send![active_format, maxISO] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(6), + "ExposureISO".to_string(), + ControlValueDescription::FloatRange { + min: exposure_iso_min as f64, + max: exposure_iso_max as f64, + value: exposure_iso as f64, + step: f32::MIN_POSITIVE as f64, + default: unsafe { AVCaptureISOCurrent } as f64, + }, + if exposure_custom == YES { + vec![ + KnownCameraControlFlag::ReadOnly, + KnownCameraControlFlag::Volatile, + ] + } else { + vec![KnownCameraControlFlag::Volatile] + }, + exposure_custom == YES, + )); + + let lens_aperture: f32 = unsafe { msg_send![self.inner, lensAperture] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(7), + "LensAperture".to_string(), + ControlValueDescription::Float { + value: lens_aperture as f64, + default: lens_aperture as f64, + step: lens_aperture as f64, + }, + vec![KnownCameraControlFlag::ReadOnly], + false, + )); + + // get whiteblaance + let white_balance_current: NSInteger = unsafe { msg_send![self.inner, whiteBalanceMode] }; + let white_balance_manual: BOOL = + unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(0)] }; + let white_balance_auto: BOOL = + unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(1)] }; + let white_balance_continuous: BOOL = + unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(2)] }; + + { + let mut possible = vec![]; + + if white_balance_manual == YES { + possible.push(0); + } + if white_balance_auto == YES { + possible.push(1); + } + if white_balance_continuous == YES { + possible.push(2); + } + + controls.push(CameraControl::new( + KnownCameraControl::WhiteBalance, + "WhiteBalanceMode".to_string(), + ControlValueDescription::Enum { + value: white_balance_current as i64, + possible, + default: 0, + }, + vec![], + true, + )); + } + + let white_balance_gains: AVCaptureWhiteBalanceGains = + unsafe { msg_send![self.inner, deviceWhiteBalanceGains] }; + let white_balance_default: AVCaptureWhiteBalanceGains = + unsafe { msg_send![self.inner, grayWorldDeviceWhiteBalanceGains] }; + let white_balancne_max: AVCaptureWhiteBalanceGains = + unsafe { msg_send![self.inner, maxWhiteBalanceGain] }; + let white_balance_gain_supported: BOOL = unsafe { + msg_send![ + self.inner, + isLockingWhiteBalanceWithCustomDeviceGainsSupported + ] + }; + + controls.push(CameraControl::new( + KnownCameraControl::Gain, + "WhiteBalanceGain".to_string(), + ControlValueDescription::RGB { + value: ( + white_balance_gains.redGain as f64, + white_balance_gains.greenGain as f64, + white_balance_gains.blueGain as f64, + ), + max: ( + white_balancne_max.redGain as f64, + white_balancne_max.greenGain as f64, + white_balancne_max.blueGain as f64, + ), + default: ( + white_balance_default.redGain as f64, + white_balance_default.greenGain as f64, + white_balance_default.blueGain as f64, + ), + }, + if white_balance_gain_supported == YES { + vec![ + KnownCameraControlFlag::Disabled, + KnownCameraControlFlag::ReadOnly, + ] + } else { + vec![] + }, + white_balance_gain_supported == YES, + )); + + // get flash + let has_torch: BOOL = unsafe { msg_send![self.inner, torchAvailible] }; + let torch_active: BOOL = unsafe { msg_send![self.inner, isTorchActive] }; + let torch_off: BOOL = + unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(0)] }; + let torch_on: BOOL = + unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(1)] }; + let torch_auto: BOOL = + unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(2)] }; + + { + let mut possible = vec![]; + + if torch_off == YES { + possible.push(0); + } + if torch_on == YES { + possible.push(1); + } + if torch_auto == YES { + possible.push(2); + } + + controls.push(CameraControl::new( + KnownCameraControl::Other(8), + "Flash".to_string(), + ControlValueDescription::Enum { + value: (torch_active == YES) as i64, + possible, + default: 0, + }, + if has_torch == YES { + vec![KnownCameraControlFlag::Disabled] + } else { + vec![] + }, + has_torch == YES, + )); + } + + // get low light boost + let has_llb: BOOL = unsafe { msg_send![self.inner, lowLightBoostSupported] }; + 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, + }, + if has_llb == YES { + vec![KnownCameraControlFlag::Disabled] + } else { + vec![] + }, + has_llb == YES, + )); + } + + // get zoom factor + let zoom_current: CGFloat = unsafe { msg_send![self.inner, videoZoomFactor] }; + let zoom_min: CGFloat = unsafe { msg_send![self.inner, minAvailableVideoZoomFactor] }; + let zoom_max: CGFloat = unsafe { msg_send![self.inner, maxAvailableVideoZoomFactor] }; + + controls.push(CameraControl::new( + KnownCameraControl::Zoom, + "Zoom".to_string(), + ControlValueDescription::FloatRange { + min: zoom_min as f64, + max: zoom_max as f64, + value: zoom_current as f64, + step: f32::MIN_POSITIVE as f64, + default: 1.0, + }, + vec![], + true, + )); + + // zoom distortion correction + let distortion_correction_supported: BOOL = + unsafe { msg_send![self.inner, geometricDistortionCorrectionSupported] }; + let distortion_correction_current_value: BOOL = + unsafe { msg_send![self.inner, geometricDistortionCorrectionEnabled] }; + + controls.push(CameraControl::new( + KnownCameraControl::Other(9), + "DistortionCorrection".to_string(), + ControlValueDescription::Boolean { + value: distortion_correction_current_value == YES, + default: false, + }, + if distortion_correction_supported == YES { + vec![ + KnownCameraControlFlag::ReadOnly, + KnownCameraControlFlag::Disabled, + ] + } else { + vec![] + }, + distortion_correction_supported == YES, + )); + + Ok(controls) + } } impl Drop for AVCaptureDevice { diff --git a/nokhwa-bindings-windows/src/lib.rs b/nokhwa-bindings-windows/src/lib.rs index 6f059db..eca44fa 100644 --- a/nokhwa-bindings-windows/src/lib.rs +++ b/nokhwa-bindings-windows/src/lib.rs @@ -835,8 +835,6 @@ pub mod wmf { control, control.to_string(), ctrl_value_set, - vec![is_manual], - true, )) } diff --git a/nokhwa-core/src/pixel_format.rs b/nokhwa-core/src/pixel_format.rs index 898acfc..97f2d15 100644 --- a/nokhwa-core/src/pixel_format.rs +++ b/nokhwa-core/src/pixel_format.rs @@ -18,10 +18,12 @@ use crate::types::{ buf_mjpeg_to_rgb, buf_yuyv422_to_rgb, mjpeg_to_rgb, yuyv422_to_rgb, FrameFormat, }; 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 { +pub trait FormatDecoder: Copy + Clone + Debug + Default + Sized + Send + Sync + { type Output: Pixel; + const FORMATS: &'static [FrameFormat]; /// Allocates and returns a `Vec` /// # Errors @@ -49,7 +51,8 @@ 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), @@ -105,6 +108,8 @@ pub struct RgbAFormat; impl FormatDecoder for RgbAFormat { type Output = Rgba; + 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, true), @@ -161,6 +166,10 @@ pub struct LumaFormat; impl FormatDecoder for LumaFormat { type Output = Luma; + const FORMATS: &'static [FrameFormat] = + &[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY]; + + #[allow(clippy::cast_possible_truncation)] fn write_output(fcc: FrameFormat, data: &[u8]) -> Result, NokhwaError> { match fcc { @@ -227,6 +236,9 @@ pub struct LumaAFormat; impl FormatDecoder for LumaAFormat { type Output = LumaA; + const FORMATS: &'static [FrameFormat] = + &[FrameFormat::MJPEG, FrameFormat::YUYV, FrameFormat::GRAY]; + #[allow(clippy::cast_possible_truncation)] fn write_output(fcc: FrameFormat, data: &[u8]) -> Result, NokhwaError> { match fcc { diff --git a/nokhwa-core/src/traits.rs b/nokhwa-core/src/traits.rs index 38bc533..ee674ac 100644 --- a/nokhwa-core/src/traits.rs +++ b/nokhwa-core/src/traits.rs @@ -14,6 +14,7 @@ * limitations under the License. */ +use crate::types::KnownCameraControlFlag; use crate::{ buffer::Buffer, error::NokhwaError, diff --git a/nokhwa-core/src/types.rs b/nokhwa-core/src/types.rs index a27ea07..119c8f4 100644 --- a/nokhwa-core/src/types.rs +++ b/nokhwa-core/src/types.rs @@ -1,4 +1,5 @@ use crate::error::NokhwaError; +use crate::pixel_format::FormatDecoder; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use std::{ @@ -15,7 +16,7 @@ use std::{ /// - `None`: Pick a random [`CameraFormat`] #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] -pub enum RequestedFormat { +pub enum RequestedFormatType { HighestResolution, HighestFrameRate, Exact(CameraFormat), @@ -23,37 +24,81 @@ pub enum RequestedFormat { None, } -impl RequestedFormat { +impl Default for RequestedFormatType { + fn default() -> Self { + RequestedFormatType::None + } +} + +impl Display for RequestedFormatType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] +pub struct RequestedFormat +where + F: FormatDecoder, +{ + requested_format: RequestedFormatType, + wanted_decoder: F, +} + +impl RequestedFormat +where + F: FormatDecoder, +{ + pub fn new(requested: RequestedFormatType) -> RequestedFormat { + RequestedFormat { + requested_format: requested, + wanted_decoder: Decoder::default(), + } + } + /// Fulfill the requested using a list of all available formats. pub fn fulfill(&self, all_formats: &[CameraFormat]) -> Option { - match self { - RequestedFormat::HighestResolution => { + match self.requested_format { + RequestedFormatType::HighestResolution => { let mut formats = all_formats.to_vec(); formats.sort_by_key(|a| a.resolution()); let resolution = *formats.iter().last()?; let mut format_resolutions = formats .into_iter() - .filter(|fmt| fmt.resolution() == resolution.resolution()) + .filter(|fmt| { + fmt.resolution() == resolution.resolution() + && F::FORMATS.contains(&fmt.format()) + }) .collect::>(); format_resolutions.sort_by_key(|a| a.frame_rate()); format_resolutions.last().copied() } - RequestedFormat::HighestFrameRate => { + RequestedFormatType::HighestFrameRate => { let mut formats = all_formats.to_vec(); formats.sort_by_key(|a| a.frame_rate()); let frame_rate = *formats.iter().last()?; let mut format_framerates = formats .into_iter() - .filter(|fmt| fmt.frame_rate() == frame_rate.frame_rate()) + .filter(|fmt| { + fmt.frame_rate() == frame_rate.frame_rate() + && F::FORMATS.contains(&fmt.format()) + }) .collect::>(); format_framerates.sort_by_key(|a| a.resolution()); format_framerates.last().copied() } - RequestedFormat::Exact(fmt) => Some(*fmt), - RequestedFormat::Closest(c) => { + RequestedFormatType::Exact(fmt) => { + if F::FORMATS.contains(&fmt.format()) { + Some(fmt) + } else { + None + } + } + RequestedFormatType::Closest(c) => { let same_fmt_formats = all_formats .iter() - .filter(|x| x.format() == c.format()) + .filter(|x| x.format() == c.format() && F::FORMATS.contains(&x.format())) .copied() .collect::>(); let mut resolution_map = same_fmt_formats @@ -91,12 +136,19 @@ impl RequestedFormat { let frame_rate = framerate_map.first()?.1; Some(CameraFormat::new(resolution, c.format(), frame_rate)) } - RequestedFormat::None => all_formats.first().copied(), + RequestedFormatType::None => all_formats + .iter() + .filter(|fmt| F::FORMATS.contains(&fmt.format())) + .nth(0) + .copied(), } } } -impl Display for RequestedFormat { +impl Display for RequestedFormat +where + F: FormatDecoder, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self) } @@ -594,11 +646,11 @@ impl Display for KnownCameraControl { pub enum KnownCameraControlFlag { Automatic, Manual, + Continuous, ReadOnly, WriteOnly, Volatile, Disabled, - Inactive, } impl Display for KnownCameraControlFlag { @@ -651,6 +703,25 @@ pub enum ControlValueDescription { value: Vec, default: Vec, }, + KeyValuePair { + key: i128, + value: i128, + default: (i128, i128), + }, + Point { + value: (f64, f64), + default: (f64, f64), + }, + Enum { + value: i64, + possible: Vec, + default: i64, + }, + RGB { + value: (f64, f64, f64), + max: (f64, f64, f64), + default: (f64, f64, f64), + }, } impl ControlValueDescription { @@ -674,6 +745,16 @@ impl ControlValueDescription { ControlValueDescription::Bytes { value, .. } => { ControlValueSetter::Bytes(value.clone()) } + ControlValueDescription::KeyValuePair { key, value, .. } => { + ControlValueSetter::KeyValue(*key, *value) + } + ControlValueDescription::Point { value, .. } => { + ControlValueSetter::Point(value.0, value.1) + } + ControlValueDescription::Enum { value, .. } => ControlValueSetter::EnumValue(*value), + ControlValueDescription::RGB { value, .. } => { + ControlValueSetter::RGB(value.0, value.1, value.2) + } } } @@ -737,6 +818,18 @@ impl ControlValueDescription { 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 { .. }) + } } } } @@ -804,6 +897,46 @@ impl Display for ControlValueDescription { ControlValueDescription::Bytes { value, default } => { write!(f, "(Current: {:x?}, Default: {:x?})", value, default) } + ControlValueDescription::KeyValuePair { + key, + value, + default, + } => { + write!( + f, + "Current: ({}, {}), Default: ({}, {})", + key, value, default.0, default.1 + ) + } + ControlValueDescription::Point { value, default } => { + write!( + f, + "Current: ({}, {}), Default: ({}, {})", + value.0, value.1, default.0, default.1 + ) + } + ControlValueDescription::Enum { + value, + possible, + default, + } => { + write!( + f, + "Current: {}, Possible Values: {:?}, Default: {}", + value, possible, default + ) + } + ControlValueDescription::RGB { + value, + max, + default, + } => { + write!( + f, + "Current: ({}, {}, {}), Max: ({}, {}, {}), Default: ({}, {}, {})", + value.0, value.1, value.2, max.0, max.1, max.2, default.0, default.1, default.2 + ) + } } } } @@ -918,6 +1051,10 @@ pub enum ControlValueSetter { Boolean(bool), String(String), Bytes(Vec), + KeyValue(i128, i128), + Point(f64, f64), + EnumValue(i64), + RGB(f64, f64, f64), } impl Display for ControlValueSetter { @@ -927,19 +1064,31 @@ impl Display for ControlValueSetter { write!(f, "Value: None") } ControlValueSetter::Integer(i) => { - write!(f, "Value: {}", i) + write!(f, "IntegerValue: {}", i) } ControlValueSetter::Float(d) => { - write!(f, "Value: {}", d) + write!(f, "FloatValue: {}", d) } ControlValueSetter::Boolean(b) => { - write!(f, "Value: {}", b) + write!(f, "BoolValue: {}", b) } ControlValueSetter::String(s) => { - write!(f, "Value: {}", s) + write!(f, "StrValue: {}", s) } ControlValueSetter::Bytes(b) => { - write!(f, "Value: {:x?}", b) + write!(f, "BytesValue: {:x?}", b) + } + ControlValueSetter::KeyValue(k, v) => { + write!(f, "KVValue: ({}, {})", k, v) + } + ControlValueSetter::Point(x, y) => { + write!(f, "PointValue: ({}, {})", x, y) + } + ControlValueSetter::EnumValue(v) => { + write!(f, "EnumValue: {}", v) + } + ControlValueSetter::RGB(r, g, b) => { + write!(f, "RGBValue: ({}, {}, {})", r, g, b) } } } diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index 41be8d8..07c12f1 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -200,26 +200,20 @@ impl CaptureBackendTrait for AVFoundationCaptureDevice { self.set_camera_format(format) } - fn camera_control(&self, _: KnownCameraControl) -> Result { - Err(NokhwaError::NotImplementedError( - "Not Implemented".to_string(), - )) + fn camera_control(&self, control: KnownCameraControl) -> Result { + todo!() } fn camera_controls(&self) -> Result, NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Not Implemented".to_string(), - )) + todo!() } fn set_camera_control( &mut self, - _: KnownCameraControl, - _: ControlValueSetter, + id: KnownCameraControl, + value: ControlValueSetter, ) -> Result<(), NokhwaError> { - Err(NokhwaError::NotImplementedError( - "Not Implemented".to_string(), - )) + todo!() } fn open_stream(&mut self) -> Result<(), NokhwaError> {