Skip to main content

nokhwa_bindings_macos/
lib.rs

1/*
2* Copyright 2022 l1npengtul <l1npengtul@protonmail.com> / The Nokhwa Contributors
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8*     http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17// hello, future peng here
18// whatever is written here will induce horrors uncomprehendable.
19// save yourselves. write apple code in swift and bind it to rust.
20
21// <some change so we can call this 0.10.4>
22
23#![allow(clippy::not_unsafe_ptr_arg_deref)]
24
25#[cfg(any(target_os = "macos", target_os = "ios"))]
26#[macro_use]
27extern crate objc;
28
29#[cfg(any(target_os = "macos", target_os = "ios"))]
30mod internal {
31
32    #[allow(non_snake_case)]
33    pub mod core_media {
34        // all of this is stolen from bindgen
35        // steal it idc
36        use crate::internal::CGFloat;
37        use core_media_sys::{
38            CMBlockBufferRef, CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions,
39            FourCharCode,
40        };
41        use objc::{runtime::Object, Message};
42        use std::ops::Deref;
43
44        pub type Id = *mut Object;
45
46        #[repr(transparent)]
47        #[derive(Clone)]
48        pub struct NSObject(pub Id);
49        impl Deref for NSObject {
50            type Target = Object;
51            fn deref(&self) -> &Self::Target {
52                unsafe { &*self.0 }
53            }
54        }
55        unsafe impl Message for NSObject {}
56        impl NSObject {
57            pub fn alloc() -> Self {
58                Self(unsafe { msg_send!(objc::class!(NSObject), alloc) })
59            }
60        }
61
62        #[repr(transparent)]
63        #[derive(Clone)]
64        pub struct NSString(pub Id);
65        impl Deref for NSString {
66            type Target = Object;
67            fn deref(&self) -> &Self::Target {
68                unsafe { &*self.0 }
69            }
70        }
71        unsafe impl Message for NSString {}
72        impl NSString {
73            pub fn alloc() -> Self {
74                Self(unsafe { msg_send!(objc::class!(NSString), alloc) })
75            }
76        }
77
78        pub type AVMediaType = NSString;
79
80        #[allow(non_snake_case)]
81        #[link(name = "CoreMedia", kind = "framework")]
82        extern "C" {
83            pub fn CMVideoFormatDescriptionGetDimensions(
84                videoDesc: CMFormatDescriptionRef,
85            ) -> CMVideoDimensions;
86
87            pub fn CMTimeMake(value: i64, scale: i32) -> CMTime;
88
89            pub fn CMBlockBufferGetDataLength(theBuffer: CMBlockBufferRef) -> std::os::raw::c_int;
90
91            pub fn CMBlockBufferCopyDataBytes(
92                theSourceBuffer: CMBlockBufferRef,
93                offsetToData: usize,
94                dataLength: usize,
95                destination: *mut std::os::raw::c_void,
96            ) -> std::os::raw::c_int;
97
98            pub fn CMSampleBufferGetDataBuffer(sbuf: CMSampleBufferRef) -> CMBlockBufferRef;
99
100            pub fn CMSampleBufferGetPresentationTimeStamp(sbuf: CMSampleBufferRef) -> CMTime;
101
102            pub fn dispatch_queue_create(
103                label: *const std::os::raw::c_char,
104                attr: NSObject,
105            ) -> NSObject;
106
107            pub fn dispatch_release(object: NSObject);
108
109            pub fn CMSampleBufferGetImageBuffer(sbuf: CMSampleBufferRef) -> CVImageBufferRef;
110
111            pub fn CVPixelBufferLockBaseAddress(
112                pixelBuffer: CVPixelBufferRef,
113                lockFlags: CVPixelBufferLockFlags,
114            ) -> CVReturn;
115
116            pub fn CVPixelBufferUnlockBaseAddress(
117                pixelBuffer: CVPixelBufferRef,
118                unlockFlags: CVPixelBufferLockFlags,
119            ) -> CVReturn;
120
121            pub fn CVPixelBufferGetDataSize(pixelBuffer: CVPixelBufferRef)
122                -> std::os::raw::c_ulong;
123
124            pub fn CVPixelBufferGetBaseAddress(
125                pixelBuffer: CVPixelBufferRef,
126            ) -> *mut std::os::raw::c_void;
127
128            pub fn CVPixelBufferGetPixelFormatType(pixelBuffer: CVPixelBufferRef) -> OSType;
129        }
130
131        #[repr(C)]
132        #[derive(Clone, Debug, PartialEq, PartialOrd)]
133        pub struct CGPoint {
134            pub x: CGFloat,
135            pub y: CGFloat,
136        }
137
138        #[repr(C)]
139        #[derive(Debug, Copy, Clone)]
140        pub struct __CVBuffer {
141            _unused: [u8; 0],
142        }
143
144        #[allow(non_snake_case)]
145        #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
146        #[repr(C)]
147        pub struct AVCaptureWhiteBalanceGains {
148            pub blueGain: f32,
149            pub greenGain: f32,
150            pub redGain: f32,
151        }
152
153        pub type CVBufferRef = *mut __CVBuffer;
154
155        pub type CVImageBufferRef = CVBufferRef;
156        pub type CVPixelBufferRef = CVImageBufferRef;
157        pub type CVPixelBufferLockFlags = u64;
158        pub type CVReturn = i32;
159
160        pub type OSType = FourCharCode;
161        pub type AVVideoCodecType = NSString;
162
163        #[link(name = "AVFoundation", kind = "framework")]
164        extern "C" {
165            pub static AVVideoCodecKey: NSString;
166            pub static AVVideoCodecTypeHEVC: AVVideoCodecType;
167            pub static AVVideoCodecTypeH264: AVVideoCodecType;
168            pub static AVVideoCodecTypeJPEG: AVVideoCodecType;
169            pub static AVVideoCodecTypeAppleProRes4444: AVVideoCodecType;
170            pub static AVVideoCodecTypeAppleProRes422: AVVideoCodecType;
171            pub static AVVideoCodecTypeAppleProRes422HQ: AVVideoCodecType;
172            pub static AVVideoCodecTypeAppleProRes422LT: AVVideoCodecType;
173            pub static AVVideoCodecTypeAppleProRes422Proxy: AVVideoCodecType;
174            pub static AVVideoCodecTypeHEVCWithAlpha: AVVideoCodecType;
175            pub static AVVideoCodecHEVC: NSString;
176            pub static AVVideoCodecH264: NSString;
177            pub static AVVideoCodecJPEG: NSString;
178            pub static AVVideoCodecAppleProRes4444: NSString;
179            pub static AVVideoCodecAppleProRes422: NSString;
180            pub static AVVideoWidthKey: NSString;
181            pub static AVVideoHeightKey: NSString;
182            pub static AVVideoExpectedSourceFrameRateKey: NSString;
183
184            pub static AVMediaTypeVideo: AVMediaType;
185            pub static AVMediaTypeAudio: AVMediaType;
186            pub static AVMediaTypeText: AVMediaType;
187            pub static AVMediaTypeClosedCaption: AVMediaType;
188            pub static AVMediaTypeSubtitle: AVMediaType;
189            pub static AVMediaTypeTimecode: AVMediaType;
190            pub static AVMediaTypeMetadata: AVMediaType;
191            pub static AVMediaTypeMuxed: AVMediaType;
192            pub static AVMediaTypeMetadataObject: AVMediaType;
193            pub static AVMediaTypeDepthData: AVMediaType;
194
195            pub static AVCaptureLensPositionCurrent: f32;
196            pub static AVCaptureExposureTargetBiasCurrent: f32;
197            pub static AVCaptureExposureDurationCurrent: CMTime;
198            pub static AVCaptureISOCurrent: f32;
199        }
200    }
201
202    use crate::core_media::{
203        dispatch_queue_create, AVCaptureExposureDurationCurrent,
204        AVCaptureExposureTargetBiasCurrent, AVCaptureISOCurrent, AVCaptureWhiteBalanceGains,
205        AVMediaTypeAudio, AVMediaTypeClosedCaption, AVMediaTypeDepthData, AVMediaTypeMetadata,
206        AVMediaTypeMetadataObject, AVMediaTypeMuxed, AVMediaTypeSubtitle, AVMediaTypeText,
207        AVMediaTypeTimecode, AVMediaTypeVideo, CGPoint, CMSampleBufferGetImageBuffer,
208        CMVideoFormatDescriptionGetDimensions, CVImageBufferRef, CVPixelBufferGetBaseAddress,
209        CVPixelBufferGetDataSize, CVPixelBufferLockBaseAddress, CVPixelBufferUnlockBaseAddress,
210        NSObject, OSType,
211    };
212
213    use block::ConcreteBlock;
214    use cocoa_foundation::{
215        base::Nil,
216        foundation::{NSArray, NSDictionary, NSInteger, NSString, NSUInteger},
217    };
218    use core_media_sys::{
219        kCMPixelFormat_24RGB, kCMPixelFormat_422YpCbCr8_yuvs,
220        kCMPixelFormat_8IndexedGray_WhiteIsZero, kCMVideoCodecType_422YpCbCr8,
221        kCMVideoCodecType_JPEG, kCMVideoCodecType_JPEG_OpenDML, CMFormatDescriptionGetMediaSubType,
222        CMFormatDescriptionRef, CMSampleBufferRef, CMTime, CMVideoDimensions,
223    };
224    use core_video_sys::{
225        kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange,
226        kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
227        kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
228    };
229    use flume::{Receiver, Sender};
230    use nokhwa_core::{
231        error::NokhwaError,
232        types::{
233            ApiBackend, CameraControl, CameraFormat, CameraIndex, CameraInfo,
234            ControlValueDescription, ControlValueSetter, FrameFormat, KnownCameraControl,
235            KnownCameraControlFlag, Resolution,
236        },
237    };
238    use objc::runtime::objc_getClass;
239    use objc::{
240        declare::ClassDecl,
241        runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
242    };
243    use once_cell::sync::Lazy;
244    use std::ffi::CString;
245    use std::{
246        borrow::Cow,
247        cmp::Ordering,
248        collections::BTreeMap,
249        convert::TryFrom,
250        error::Error,
251        ffi::{c_float, c_void, CStr},
252        sync::Arc,
253        time::Duration,
254    };
255
256    const UTF8_ENCODING: usize = 4;
257    type CGFloat = c_float;
258
259    extern "C" {
260        fn mach_absolute_time() -> u64;
261    }
262
263    #[repr(C)]
264    struct MachTimebaseInfo {
265        numer: u32,
266        denom: u32,
267    }
268
269    extern "C" {
270        fn mach_timebase_info(info: *mut MachTimebaseInfo) -> i32;
271    }
272
273    fn mach_absolute_time_nanos() -> u64 {
274        static TIMEBASE: once_cell::sync::Lazy<(u32, u32)> = once_cell::sync::Lazy::new(|| {
275            let mut info = MachTimebaseInfo { numer: 0, denom: 0 };
276            unsafe { mach_timebase_info(&mut info) };
277            (info.numer, info.denom)
278        });
279        let ticks = unsafe { mach_absolute_time() };
280        let (numer, denom) = *TIMEBASE;
281        ticks.wrapping_mul(numer as u64) / (denom as u64)
282    }
283
284    macro_rules! create_boilerplate_impl {
285        {
286            $( [$class_vis:vis $class_name:ident : $( {$field_vis:vis $field_name:ident : $field_type:ty} ),*] ),+
287        } => {
288            $(
289                $class_vis struct $class_name {
290                    inner: *mut Object,
291                    $(
292                        $field_vis $field_name : $field_type
293                    )*
294                }
295
296                impl $class_name {
297                    pub fn inner(&self) -> *mut Object {
298                        self.inner
299                    }
300                }
301            )+
302        };
303
304        {
305            $( [$class_vis:vis $class_name:ident ] ),+
306        } => {
307            $(
308                $class_vis struct $class_name {
309                    inner: *mut Object,
310                }
311
312                impl $class_name {
313                    pub fn inner(&self) -> *mut Object {
314                        self.inner
315                    }
316                }
317
318                impl From<*mut Object> for $class_name {
319                    fn from(obj: *mut Object) -> Self {
320                        $class_name {
321                            inner: obj,
322                        }
323                    }
324                }
325            )+
326        };
327    }
328
329    fn str_to_nsstr(string: &str) -> *mut Object {
330        let cls = class!(NSString);
331        let bytes = string.as_ptr() as *const c_void;
332        unsafe {
333            let obj: *mut Object = msg_send![cls, alloc];
334            let obj: *mut Object = msg_send![
335                obj,
336                initWithBytes:bytes
337                length:string.len()
338                encoding:UTF8_ENCODING
339            ];
340            obj
341        }
342    }
343
344    fn nsstr_to_str<'a>(nsstr: *mut Object) -> Cow<'a, str> {
345        let data = unsafe { CStr::from_ptr(nsstr.UTF8String()) };
346        data.to_string_lossy()
347    }
348
349    fn vec_to_ns_arr<T: Into<*mut Object>>(data: Vec<T>) -> *mut Object {
350        let cstr = CString::new("NSMutableArray").unwrap();
351        let ns_arr_cls = unsafe { objc_getClass(cstr.as_ptr()) };
352        let mutable_array: *mut Object = unsafe { msg_send![ns_arr_cls, array] };
353        data.into_iter().for_each(|item| {
354            let item_obj: *mut Object = item.into();
355            let _: () = unsafe { msg_send![mutable_array, addObject: item_obj] };
356        });
357        mutable_array
358    }
359
360    fn ns_arr_to_vec<T: From<*mut Object>>(data: *mut Object) -> Vec<T> {
361        let length = unsafe { NSArray::count(data) };
362
363        let mut out_vec: Vec<T> = Vec::with_capacity(length as usize);
364        for index in 0..length {
365            let item = unsafe { NSArray::objectAtIndex(data, index) };
366            out_vec.push(T::from(item));
367        }
368        out_vec
369    }
370
371    fn try_ns_arr_to_vec<T, TE>(data: *mut Object) -> Result<Vec<T>, TE>
372    where
373        TE: Error,
374        T: TryFrom<*mut Object, Error = TE>,
375    {
376        let length = unsafe { NSArray::count(data) };
377
378        let mut out_vec: Vec<T> = Vec::with_capacity(length as usize);
379        for index in 0..length {
380            let item = unsafe { NSArray::objectAtIndex(data, index) };
381            out_vec.push(T::try_from(item)?);
382        }
383        Ok(out_vec)
384    }
385
386    fn compare_ns_string(this: *mut Object, other: core_media::NSString) -> bool {
387        unsafe {
388            let equal: BOOL = msg_send![this, isEqualToString: other];
389            equal == YES
390        }
391    }
392
393    #[allow(non_upper_case_globals)]
394    fn raw_fcc_to_frameformat(raw: OSType) -> Option<FrameFormat> {
395        match raw {
396            kCMVideoCodecType_422YpCbCr8 | kCMPixelFormat_422YpCbCr8_yuvs => {
397                Some(FrameFormat::YUYV)
398            }
399            kCMVideoCodecType_JPEG | kCMVideoCodecType_JPEG_OpenDML => Some(FrameFormat::MJPEG),
400            kCMPixelFormat_8IndexedGray_WhiteIsZero => Some(FrameFormat::GRAY),
401            kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange
402            | kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
403            | kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange => Some(FrameFormat::YUYV),
404            kCMPixelFormat_24RGB => Some(FrameFormat::RAWRGB),
405            _ => None,
406        }
407    }
408
409    pub type CompressionData<'a> = (Cow<'a, [u8]>, FrameFormat, Option<Duration>);
410    pub type DataPipe<'a> = (Sender<CompressionData<'a>>, Receiver<CompressionData<'a>>);
411
412    static CALLBACK_CLASS: Lazy<&'static Class> = Lazy::new(|| {
413        {
414            let mut decl = ClassDecl::new("MyCaptureCallback", class!(NSObject)).unwrap();
415
416            // frame stack
417            // oooh scary provenannce-breaking BULLSHIT AAAAAA I LOVE TYPE ERASURE
418            decl.add_ivar::<*const c_void>("_arcmutptr"); // ArkMutex, the not-arknights totally not gacha totally not ripoff new vidya game from l-pleasestop-npengtul
419
420            extern "C" fn my_callback_get_arcmutptr(this: &Object, _: Sel) -> *const c_void {
421                unsafe { *this.get_ivar("_arcmutptr") }
422            }
423            extern "C" fn my_callback_set_arcmutptr(
424                this: &mut Object,
425                _: Sel,
426                new_arcmutptr: *const c_void,
427            ) {
428                unsafe {
429                    this.set_ivar("_arcmutptr", new_arcmutptr);
430                }
431            }
432
433            // Delegate compliance method
434            // SAFETY: This assumes that the buffer byte size is a u8. Any other size will cause unsafety.
435            #[allow(non_snake_case)]
436            #[allow(non_upper_case_globals)]
437            extern "C" fn capture_out_callback(
438                this: &mut Object,
439                _: Sel,
440                _: *mut Object,
441                didOutputSampleBuffer: CMSampleBufferRef,
442                _: *mut Object,
443            ) {
444                let image_buffer: CVImageBufferRef =
445                    unsafe { CMSampleBufferGetImageBuffer(didOutputSampleBuffer) };
446                unsafe {
447                    CVPixelBufferLockBaseAddress(image_buffer, 0);
448                };
449
450                let buffer_length = unsafe { CVPixelBufferGetDataSize(image_buffer) };
451                let buffer_ptr = unsafe { CVPixelBufferGetBaseAddress(image_buffer) };
452                let buffer_as_vec = unsafe {
453                    std::slice::from_raw_parts_mut(buffer_ptr as *mut u8, buffer_length as usize)
454                        .to_vec()
455                };
456
457                unsafe { CVPixelBufferUnlockBaseAddress(image_buffer, 0) };
458
459                // CMSampleBufferGetPresentationTimeStamp returns the sensor
460                // capture instant on a monotonic clock (mach_absolute_time
461                // timebase).  Convert to Unix wallclock:
462                //   wall = SystemTime::now() - (mach_now - pts)
463                let capture_ts = {
464                    let pts = unsafe {
465                        core_media::CMSampleBufferGetPresentationTimeStamp(
466                            didOutputSampleBuffer,
467                        )
468                    };
469                    if pts.timescale > 0 {
470                        let pts_nanos = (pts.value as u128)
471                            .saturating_mul(1_000_000_000)
472                            / (pts.timescale as u128);
473                        let mono_now_nanos = mach_absolute_time_nanos() as u128;
474                        let wall_now = std::time::SystemTime::now();
475
476                        let age = Duration::from_nanos(
477                            mono_now_nanos.saturating_sub(pts_nanos) as u64,
478                        );
479                        wall_now
480                            .duration_since(std::time::UNIX_EPOCH)
481                            .ok()
482                            .and_then(|wall_dur| wall_dur.checked_sub(age))
483                    } else {
484                        None
485                    }
486                };
487
488                // oooooh scarey unsafe
489                // AAAAAAAAAAAAAAAAAAAAAAAAA
490                // https://c.tenor.com/0e_zWtFLOzQAAAAC/needy-streamer-overload-needy-girl-overdose.gif
491                let bufferlck_cv: *const c_void = unsafe { msg_send![this, bufferPtr] };
492                let buffer_sndr = unsafe {
493                    let ptr = bufferlck_cv.cast::<Sender<(Vec<u8>, FrameFormat, Option<Duration>)>>();
494                    Arc::from_raw(ptr)
495                };
496                if let Err(_) = buffer_sndr.send((buffer_as_vec, FrameFormat::GRAY, capture_ts)) {
497                    // FIXME: dont, what the fuck???
498                    return;
499                }
500                std::mem::forget(buffer_sndr);
501            }
502
503            #[allow(non_snake_case)]
504            extern "C" fn capture_drop_callback(
505                _: &mut Object,
506                _: Sel,
507                _: *mut Object,
508                _: *mut Object,
509                _: *mut Object,
510            ) {
511            }
512
513            unsafe {
514                decl.add_method(
515                    sel!(bufferPtr),
516                    my_callback_get_arcmutptr as extern "C" fn(&Object, Sel) -> *const c_void,
517                );
518                decl.add_method(
519                    sel!(SetBufferPtr:),
520                    my_callback_set_arcmutptr as extern "C" fn(&mut Object, Sel, *const c_void),
521                );
522                decl.add_method(
523                    sel!(captureOutput:didOutputSampleBuffer:fromConnection:),
524                    capture_out_callback
525                        as extern "C" fn(
526                            &mut Object,
527                            Sel,
528                            *mut Object,
529                            CMSampleBufferRef,
530                            *mut Object,
531                        ),
532                );
533                decl.add_method(
534                    sel!(captureOutput:didDropSampleBuffer:fromConnection:),
535                    capture_drop_callback
536                        as extern "C" fn(&mut Object, Sel, *mut Object, *mut Object, *mut Object),
537                );
538
539                decl.add_protocol(
540                    Protocol::get("AVCaptureVideoDataOutputSampleBufferDelegate").unwrap(),
541                );
542            }
543
544            decl.register()
545        }
546    });
547
548    pub fn request_permission_with_callback(callback: impl Fn(bool) + Send + Sync + 'static) {
549        let cls = class!(AVCaptureDevice);
550
551        let wrapper = move |bool: BOOL| {
552            callback(bool == YES);
553        };
554
555        let objc_fn_block: ConcreteBlock<(BOOL,), (), _> = ConcreteBlock::new(wrapper);
556        let objc_fn_pass = objc_fn_block.copy();
557
558        unsafe {
559            let _: () = msg_send![cls, requestAccessForMediaType:(AVMediaTypeVideo.clone()) completionHandler:objc_fn_pass];
560        }
561    }
562
563    pub fn current_authorization_status() -> AVAuthorizationStatus {
564        let cls = class!(AVCaptureDevice);
565        let status: AVAuthorizationStatus = unsafe {
566            msg_send![cls, authorizationStatusForMediaType:AVMediaType::Video.into_ns_str()]
567        };
568        status
569    }
570
571    // fuck it, use deprecated APIs
572    pub fn query_avfoundation() -> Result<Vec<CameraInfo>, NokhwaError> {
573        Ok(AVCaptureDeviceDiscoverySession::new(vec![
574            AVCaptureDeviceType::UltraWide,
575            AVCaptureDeviceType::WideAngle,
576            AVCaptureDeviceType::Telephoto,
577            AVCaptureDeviceType::TrueDepth,
578            AVCaptureDeviceType::External,
579        ])?
580        .devices())
581    }
582
583    pub fn get_raw_device_info(index: CameraIndex, device: *mut Object) -> CameraInfo {
584        let name = nsstr_to_str(unsafe { msg_send![device, localizedName] });
585        let manufacturer = nsstr_to_str(unsafe { msg_send![device, manufacturer] });
586        let position: AVCaptureDevicePosition = unsafe { msg_send![device, position] };
587        let lens_aperture: f64 = unsafe { msg_send![device, lensAperture] };
588        let device_type = nsstr_to_str(unsafe { msg_send![device, deviceType] });
589        let model_id = nsstr_to_str(unsafe { msg_send![device, modelID] });
590        let description = format!(
591            "{}: {} - {}, {:?} f{}",
592            manufacturer, model_id, device_type, position, lens_aperture
593        );
594        let misc = nsstr_to_str(unsafe { msg_send![device, uniqueID] });
595
596        CameraInfo::new(name.as_ref(), &description, misc.as_ref(), index)
597    }
598
599    #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
600    pub enum AVCaptureDeviceType {
601        Dual,
602        DualWide,
603        Triple,
604        WideAngle,
605        UltraWide,
606        Telephoto,
607        TrueDepth,
608        External,
609    }
610
611    impl From<AVCaptureDeviceType> for *mut Object {
612        fn from(device_type: AVCaptureDeviceType) -> Self {
613            match device_type {
614                AVCaptureDeviceType::Dual => str_to_nsstr("AVCaptureDeviceTypeBuiltInDualCamera"),
615                AVCaptureDeviceType::DualWide => {
616                    str_to_nsstr("AVCaptureDeviceTypeBuiltInDualWideCamera")
617                }
618                AVCaptureDeviceType::Triple => {
619                    str_to_nsstr("AVCaptureDeviceTypeBuiltInTripleCamera")
620                }
621                AVCaptureDeviceType::WideAngle => {
622                    str_to_nsstr("AVCaptureDeviceTypeBuiltInWideAngleCamera")
623                }
624                AVCaptureDeviceType::UltraWide => {
625                    str_to_nsstr("AVCaptureDeviceTypeBuiltInUltraWideCamera")
626                }
627                AVCaptureDeviceType::Telephoto => {
628                    str_to_nsstr("AVCaptureDeviceTypeBuiltInTelephotoCamera")
629                }
630                AVCaptureDeviceType::TrueDepth => {
631                    str_to_nsstr("AVCaptureDeviceTypeBuiltInTrueDepthCamera")
632                }
633                AVCaptureDeviceType::External => str_to_nsstr("AVCaptureDeviceTypeExternal"),
634            }
635        }
636    }
637
638    impl AVCaptureDeviceType {
639        pub fn into_ns_str(self) -> *mut Object {
640            <*mut Object>::from(self)
641        }
642    }
643
644    #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
645    pub enum AVMediaType {
646        Audio,
647        ClosedCaption,
648        DepthData,
649        Metadata,
650        MetadataObject,
651        Muxed,
652        Subtitle,
653        Text,
654        Timecode,
655        Video,
656    }
657
658    impl From<AVMediaType> for *mut Object {
659        fn from(media_type: AVMediaType) -> Self {
660            match media_type {
661                AVMediaType::Audio => unsafe { AVMediaTypeAudio.0 },
662                AVMediaType::ClosedCaption => unsafe { AVMediaTypeClosedCaption.0 },
663                AVMediaType::DepthData => unsafe { AVMediaTypeDepthData.0 },
664                AVMediaType::Metadata => unsafe { AVMediaTypeMetadata.0 },
665                AVMediaType::MetadataObject => unsafe { AVMediaTypeMetadataObject.0 },
666                AVMediaType::Muxed => unsafe { AVMediaTypeMuxed.0 },
667                AVMediaType::Subtitle => unsafe { AVMediaTypeSubtitle.0 },
668                AVMediaType::Text => unsafe { AVMediaTypeText.0 },
669                AVMediaType::Timecode => unsafe { AVMediaTypeTimecode.0 },
670                AVMediaType::Video => unsafe { AVMediaTypeVideo.0 },
671            }
672        }
673    }
674
675    impl TryFrom<*mut Object> for AVMediaType {
676        type Error = NokhwaError;
677
678        fn try_from(value: *mut Object) -> Result<Self, Self::Error> {
679            unsafe {
680                if compare_ns_string(value, (AVMediaTypeAudio).clone()) {
681                    Ok(AVMediaType::Audio)
682                } else if compare_ns_string(value, (AVMediaTypeClosedCaption).clone()) {
683                    Ok(AVMediaType::ClosedCaption)
684                } else if compare_ns_string(value, (AVMediaTypeDepthData).clone()) {
685                    Ok(AVMediaType::DepthData)
686                } else if compare_ns_string(value, (AVMediaTypeMetadata).clone()) {
687                    Ok(AVMediaType::Metadata)
688                } else if compare_ns_string(value, (AVMediaTypeMetadataObject).clone()) {
689                    Ok(AVMediaType::MetadataObject)
690                } else if compare_ns_string(value, (AVMediaTypeMuxed).clone()) {
691                    Ok(AVMediaType::Muxed)
692                } else if compare_ns_string(value, (AVMediaTypeSubtitle).clone()) {
693                    Ok(AVMediaType::Subtitle)
694                } else if compare_ns_string(value, (AVMediaTypeText).clone()) {
695                    Ok(AVMediaType::Text)
696                } else if compare_ns_string(value, (AVMediaTypeTimecode).clone()) {
697                    Ok(AVMediaType::Timecode)
698                } else if compare_ns_string(value, (AVMediaTypeVideo).clone()) {
699                    Ok(AVMediaType::Video)
700                } else {
701                    let name = nsstr_to_str(value);
702                    Err(NokhwaError::GetPropertyError {
703                        property: "AVMediaType".to_string(),
704                        error: format!("Invalid AVMediaType {name}"),
705                    })
706                }
707            }
708        }
709    }
710
711    impl AVMediaType {
712        pub fn into_ns_str(self) -> *mut Object {
713            <*mut Object>::from(self)
714        }
715    }
716
717    #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
718    #[repr(isize)]
719    pub enum AVCaptureDevicePosition {
720        Unspecified = 0,
721        Back = 1,
722        Front = 2,
723    }
724
725    #[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
726    #[repr(isize)]
727    pub enum AVAuthorizationStatus {
728        NotDetermined = 0,
729        Restricted = 1,
730        Denied = 2,
731        Authorized = 3,
732    }
733
734    pub struct AVCaptureVideoCallback {
735        delegate: *mut Object,
736        queue: NSObject,
737    }
738
739    impl AVCaptureVideoCallback {
740        pub fn new(
741            device_spec: &CStr,
742            buffer: &Arc<Sender<(Vec<u8>, FrameFormat, Option<Duration>)>>,
743        ) -> Result<Self, NokhwaError> {
744            let cls = &CALLBACK_CLASS as &Class;
745            let delegate: *mut Object = unsafe { msg_send![cls, alloc] };
746            let delegate: *mut Object = unsafe { msg_send![delegate, init] };
747            let buffer_as_ptr = {
748                let arc_raw = Arc::as_ptr(buffer);
749                arc_raw.cast::<c_void>()
750            };
751            unsafe {
752                let _: () = msg_send![delegate, SetBufferPtr: buffer_as_ptr];
753            }
754
755            let queue = unsafe {
756                dispatch_queue_create(device_spec.as_ptr(), NSObject(std::ptr::null_mut()))
757            };
758
759            Ok(AVCaptureVideoCallback { delegate, queue })
760        }
761
762        pub fn data_len(&self) -> usize {
763            unsafe { msg_send![self.delegate, dataLength] }
764        }
765
766        pub fn inner(&self) -> *mut Object {
767            self.delegate
768        }
769
770        pub fn queue(&self) -> &NSObject {
771            &self.queue
772        }
773    }
774
775    create_boilerplate_impl! {
776        [pub AVFrameRateRange],
777        [pub AVCaptureDeviceDiscoverySession],
778        [pub AVCaptureDeviceInput],
779        [pub AVCaptureSession]
780    }
781
782    impl AVFrameRateRange {
783        pub fn max(&self) -> f64 {
784            unsafe { msg_send![self.inner, maxFrameRate] }
785        }
786
787        pub fn min(&self) -> f64 {
788            unsafe { msg_send![self.inner, minFrameRate] }
789        }
790    }
791
792    #[derive(Debug)]
793    pub struct AVCaptureDeviceFormat {
794        pub(crate) internal: *mut Object,
795        pub resolution: CMVideoDimensions,
796        pub fps_list: Vec<f64>,
797        pub fourcc: FrameFormat,
798    }
799
800    impl TryFrom<*mut Object> for AVCaptureDeviceFormat {
801        type Error = NokhwaError;
802
803        fn try_from(value: *mut Object) -> Result<Self, Self::Error> {
804            let media_type_raw: *mut Object = unsafe { msg_send![value, mediaType] };
805            let media_type = AVMediaType::try_from(media_type_raw)?;
806            if media_type != AVMediaType::Video {
807                return Err(NokhwaError::StructureError {
808                    structure: "AVMediaType".to_string(),
809                    error: "Not Video".to_string(),
810                });
811            }
812            let mut fps_list = ns_arr_to_vec::<AVFrameRateRange>(unsafe {
813                msg_send![value, videoSupportedFrameRateRanges]
814            })
815            .into_iter()
816            .flat_map(|v| {
817                if v.min() != 0_f64 && v.min() != 1_f64 {
818                    vec![v.min(), v.max()]
819                } else {
820                    vec![v.max()] // this gets deduped!
821                }
822            })
823            .collect::<Vec<f64>>();
824            fps_list.sort_by(|n, m| n.partial_cmp(m).unwrap_or(Ordering::Equal));
825            fps_list.dedup();
826            let description_obj: *mut Object = unsafe { msg_send![value, formatDescription] };
827            let resolution =
828                unsafe { CMVideoFormatDescriptionGetDimensions(description_obj as *mut c_void) };
829            let fcc_raw =
830                unsafe { CMFormatDescriptionGetMediaSubType(description_obj as *mut c_void) };
831            #[allow(non_upper_case_globals)]
832            let fourcc = match raw_fcc_to_frameformat(fcc_raw) {
833                Some(fcc) => fcc,
834                None => {
835                    return Err(NokhwaError::StructureError {
836                        structure: "FourCharCode".to_string(),
837                        error: format!("Unknown FourCharCode {fcc_raw:?}"),
838                    })
839                }
840            };
841
842            Ok(AVCaptureDeviceFormat {
843                internal: value,
844                resolution,
845                fps_list,
846                fourcc,
847            })
848        }
849    }
850
851    impl AVCaptureDeviceDiscoverySession {
852        pub fn new(device_types: Vec<AVCaptureDeviceType>) -> Result<Self, NokhwaError> {
853            let device_types = vec_to_ns_arr(device_types);
854            let position = 0 as NSInteger;
855
856            let media_type_video = unsafe { AVMediaTypeVideo.clone() }.0;
857
858            let discovery_session_cls = class!(AVCaptureDeviceDiscoverySession);
859            let discovery_session: *mut Object = unsafe {
860                msg_send![discovery_session_cls, discoverySessionWithDeviceTypes:device_types mediaType:media_type_video position:position]
861            };
862
863            Ok(AVCaptureDeviceDiscoverySession {
864                inner: discovery_session,
865            })
866        }
867
868        pub fn default() -> Result<Self, NokhwaError> {
869            AVCaptureDeviceDiscoverySession::new(vec![
870                AVCaptureDeviceType::UltraWide,
871                AVCaptureDeviceType::Telephoto,
872                AVCaptureDeviceType::External,
873                AVCaptureDeviceType::Dual,
874                AVCaptureDeviceType::DualWide,
875                AVCaptureDeviceType::Triple,
876            ])
877        }
878
879        pub fn devices(&self) -> Vec<CameraInfo> {
880            let device_ns_array: *mut Object = unsafe { msg_send![self.inner, devices] };
881            let objects_len: NSUInteger = unsafe { NSArray::count(device_ns_array) };
882            let mut devices = Vec::with_capacity(objects_len as usize);
883            for index in 0..objects_len {
884                let device = unsafe { device_ns_array.objectAtIndex(index) };
885                devices.push(get_raw_device_info(
886                    CameraIndex::Index(index as u32),
887                    device,
888                ));
889            }
890
891            devices
892        }
893    }
894
895    pub struct AVCaptureDevice {
896        inner: *mut Object,
897        device: CameraInfo,
898        locked: bool,
899    }
900
901    impl AVCaptureDevice {
902        pub fn inner(&self) -> *mut Object {
903            self.inner
904        }
905    }
906
907    impl AVCaptureDevice {
908        pub fn new(index: &CameraIndex) -> Result<Self, NokhwaError> {
909            match &index {
910                CameraIndex::Index(idx) => {
911                    let devices = query_avfoundation()?;
912
913                    match devices.get(*idx as usize) {
914                        Some(device) => Ok(AVCaptureDevice::from_id(
915                            &device.misc(),
916                            Some(index.clone()),
917                        )?),
918                        None => Err(NokhwaError::OpenDeviceError(
919                            idx.to_string(),
920                            "Not Found".to_string(),
921                        )),
922                    }
923                }
924                CameraIndex::String(id) => Ok(AVCaptureDevice::from_id(id, None)?),
925            }
926        }
927
928        pub fn from_id(id: &str, index_hint: Option<CameraIndex>) -> Result<Self, NokhwaError> {
929            let nsstr_id = str_to_nsstr(id);
930            let avfoundation_capture_cls = class!(AVCaptureDevice);
931            let capture: *mut Object =
932                unsafe { msg_send![avfoundation_capture_cls, deviceWithUniqueID: nsstr_id] };
933            if capture.is_null() {
934                return Err(NokhwaError::OpenDeviceError(
935                    id.to_string(),
936                    "Device is null".to_string(),
937                ));
938            }
939            let camera_info = get_raw_device_info(
940                index_hint.unwrap_or_else(|| CameraIndex::String(id.to_string())),
941                capture,
942            );
943
944            Ok(AVCaptureDevice {
945                inner: capture,
946                device: camera_info,
947                locked: false,
948            })
949        }
950
951        pub fn info(&self) -> &CameraInfo {
952            &self.device
953        }
954
955        pub fn supported_formats_raw(&self) -> Result<Vec<AVCaptureDeviceFormat>, NokhwaError> {
956            try_ns_arr_to_vec::<AVCaptureDeviceFormat, NokhwaError>(unsafe {
957                msg_send![self.inner, formats]
958            })
959        }
960
961        pub fn supported_formats(&self) -> Result<Vec<CameraFormat>, NokhwaError> {
962            Ok(self
963                .supported_formats_raw()?
964                .iter()
965                .flat_map(|av_fmt| {
966                    let resolution = av_fmt.resolution;
967                    av_fmt.fps_list.iter().map(move |fps_f64| {
968                        let fps = *fps_f64 as u32;
969
970                        let resolution =
971                            Resolution::new(resolution.width as u32, resolution.height as u32); // FIXME: what the fuck?
972                        CameraFormat::new(resolution, av_fmt.fourcc, fps)
973                    })
974                })
975                .filter(|x| x.frame_rate() != 0)
976                .collect())
977        }
978
979        pub fn already_in_use(&self) -> bool {
980            unsafe {
981                let result: BOOL = msg_send![self.inner(), isInUseByAnotherApplication];
982                result == YES
983            }
984        }
985
986        pub fn is_suspended(&self) -> bool {
987            unsafe {
988                let result: BOOL = msg_send![self.inner, isSuspended];
989                result == YES
990            }
991        }
992
993        pub fn lock(&self) -> Result<(), NokhwaError> {
994            if self.locked {
995                return Ok(());
996            }
997            if self.already_in_use() {
998                return Err(NokhwaError::InitializeError {
999                    backend: ApiBackend::AVFoundation,
1000                    error: "Already in use".to_string(),
1001                });
1002            }
1003            let err_ptr: *mut c_void = std::ptr::null_mut();
1004            let accepted: BOOL = unsafe { msg_send![self.inner, lockForConfiguration: err_ptr] };
1005            if !err_ptr.is_null() {
1006                return Err(NokhwaError::SetPropertyError {
1007                    property: "lockForConfiguration".to_string(),
1008                    value: "Locked".to_string(),
1009                    error: "Cannot lock for configuration".to_string(),
1010                });
1011            }
1012            // Space these out for debug purposes
1013            if !accepted == YES {
1014                return Err(NokhwaError::SetPropertyError {
1015                    property: "lockForConfiguration".to_string(),
1016                    value: "Locked".to_string(),
1017                    error: "Lock Rejected".to_string(),
1018                });
1019            }
1020            Ok(())
1021        }
1022
1023        pub fn unlock(&mut self) {
1024            if self.locked {
1025                self.locked = false;
1026                unsafe { msg_send![self.inner, unlockForConfiguration] }
1027            }
1028        }
1029
1030        // thank you ffmpeg
1031        pub fn set_all(&mut self, descriptor: CameraFormat) -> Result<(), NokhwaError> {
1032            self.lock()?;
1033            let format_list = try_ns_arr_to_vec::<AVCaptureDeviceFormat, NokhwaError>(unsafe {
1034                msg_send![self.inner, formats]
1035            })?;
1036            let format_description_sel = sel!(formatDescription);
1037
1038            let mut selected_format: *mut Object = std::ptr::null_mut();
1039            let mut selected_range: *mut Object = std::ptr::null_mut();
1040
1041            for format in format_list {
1042                let format_desc_ref: CMFormatDescriptionRef =
1043                    unsafe { msg_send![format.internal, performSelector: format_description_sel] };
1044                let dimensions = unsafe { CMVideoFormatDescriptionGetDimensions(format_desc_ref) };
1045
1046                if dimensions.height == descriptor.resolution().height() as i32
1047                    && dimensions.width == descriptor.resolution().width() as i32
1048                {
1049                    selected_format = format.internal;
1050
1051                    for range in ns_arr_to_vec::<AVFrameRateRange>(unsafe {
1052                        msg_send![format.internal, videoSupportedFrameRateRanges]
1053                    }) {
1054                        let max_fps: f64 = unsafe { msg_send![range.inner, maxFrameRate] };
1055                        // Older Apple cameras (i.e. iMac 2013) return 29.97000002997 as FPS.
1056                        if (f64::from(descriptor.frame_rate()) - max_fps).abs() < 0.999 {
1057                            selected_range = range.inner;
1058                            break;
1059                        }
1060                    }
1061                }
1062            }
1063            if selected_range.is_null() || selected_format.is_null() {
1064                return Err(NokhwaError::SetPropertyError {
1065                    property: "CameraFormat".to_string(),
1066                    value: descriptor.to_string(),
1067                    error: "Not Found/Rejected/Unsupported".to_string(),
1068                });
1069            }
1070
1071            let activefmtkey = str_to_nsstr("activeFormat");
1072            let min_frame_duration = str_to_nsstr("minFrameDuration");
1073            let active_video_min_frame_duration = str_to_nsstr("activeVideoMinFrameDuration");
1074            let active_video_max_frame_duration = str_to_nsstr("activeVideoMaxFrameDuration");
1075            let _: () =
1076                unsafe { msg_send![self.inner, setValue:selected_format forKey:activefmtkey] };
1077            let min_frame_duration: *mut Object =
1078                unsafe { msg_send![selected_range, valueForKey: min_frame_duration] };
1079            let _: () = unsafe {
1080                msg_send![self.inner, setValue:min_frame_duration forKey:active_video_min_frame_duration]
1081            };
1082            let _: () = unsafe {
1083                msg_send![self.inner, setValue:min_frame_duration forKey:active_video_max_frame_duration]
1084            };
1085            self.unlock();
1086            Ok(())
1087        }
1088
1089        // 0 => Focus POI
1090        // 1 => Focus Manual Setting
1091        // 2 => Exposure POI
1092        // 3 => Exposure Face Driven
1093        // 4 => Exposure Target Bias
1094        // 5 => Exposure ISO
1095        // 6 => Exposure Duration
1096        pub fn get_controls(&self) -> Result<Vec<CameraControl>, NokhwaError> {
1097            let active_format: *mut Object = unsafe { msg_send![self.inner, activeFormat] };
1098
1099            let mut controls = vec![];
1100            // get focus modes
1101
1102            let focus_current: NSInteger = unsafe { msg_send![self.inner, focusMode] };
1103            let focus_locked: BOOL =
1104                unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(0)] };
1105            let focus_auto: BOOL =
1106                unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(1)] };
1107            let focus_continuous: BOOL =
1108                unsafe { msg_send![self.inner, isFocusModeSupported:NSInteger::from(2)] };
1109
1110            {
1111                let mut supported_focus_values = vec![];
1112
1113                if focus_locked == YES {
1114                    supported_focus_values.push(0)
1115                }
1116                if focus_auto == YES {
1117                    supported_focus_values.push(1)
1118                }
1119                if focus_continuous == YES {
1120                    supported_focus_values.push(2)
1121                }
1122
1123                controls.push(CameraControl::new(
1124                    KnownCameraControl::Focus,
1125                    "FocusMode".to_string(),
1126                    ControlValueDescription::Enum {
1127                        value: focus_current,
1128                        possible: supported_focus_values,
1129                        default: focus_current,
1130                    },
1131                    vec![],
1132                    true,
1133                ));
1134            }
1135
1136            let focus_poi_supported: BOOL =
1137                unsafe { msg_send![self.inner, isFocusPointOfInterestSupported] };
1138            let focus_poi: CGPoint = unsafe { msg_send![self.inner, focusPointOfInterest] };
1139
1140            controls.push(CameraControl::new(
1141                KnownCameraControl::Other(0),
1142                "FocusPointOfInterest".to_string(),
1143                ControlValueDescription::Point {
1144                    value: (focus_poi.x as f64, focus_poi.y as f64),
1145                    default: (0.5, 0.5),
1146                },
1147                if focus_poi_supported == NO {
1148                    vec![
1149                        KnownCameraControlFlag::Disabled,
1150                        KnownCameraControlFlag::ReadOnly,
1151                    ]
1152                } else {
1153                    vec![]
1154                },
1155                focus_auto == YES || focus_continuous == YES,
1156            ));
1157
1158            let focus_manual: BOOL =
1159                unsafe { msg_send![self.inner, isLockingFocusWithCustomLensPositionSupported] };
1160            let focus_lenspos: f32 = unsafe { msg_send![self.inner, lensPosition] };
1161
1162            controls.push(CameraControl::new(
1163                KnownCameraControl::Other(1),
1164                "FocusManualLensPosition".to_string(),
1165                ControlValueDescription::FloatRange {
1166                    min: 0.0,
1167                    max: 1.0,
1168                    value: focus_lenspos as f64,
1169                    step: f64::MIN_POSITIVE,
1170                    default: 1.0,
1171                },
1172                if focus_manual == YES {
1173                    vec![]
1174                } else {
1175                    vec![
1176                        KnownCameraControlFlag::Disabled,
1177                        KnownCameraControlFlag::ReadOnly,
1178                    ]
1179                },
1180                focus_manual == YES,
1181            ));
1182
1183            // get exposures
1184            let exposure_current: NSInteger = unsafe { msg_send![self.inner, exposureMode] };
1185            let exposure_locked: BOOL =
1186                unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(0)] };
1187            let exposure_auto: BOOL =
1188                unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(1)] };
1189            let exposure_continuous: BOOL =
1190                unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(2)] };
1191            let exposure_custom: BOOL =
1192                unsafe { msg_send![self.inner, isExposureModeSupported:NSInteger::from(3)] };
1193
1194            {
1195                let mut supported_exposure_values = vec![];
1196
1197                if exposure_locked == YES {
1198                    supported_exposure_values.push(0);
1199                }
1200                if exposure_auto == YES {
1201                    supported_exposure_values.push(1);
1202                }
1203                if exposure_continuous == YES {
1204                    supported_exposure_values.push(2);
1205                }
1206                if exposure_custom == YES {
1207                    supported_exposure_values.push(3);
1208                }
1209
1210                controls.push(CameraControl::new(
1211                    KnownCameraControl::Exposure,
1212                    "ExposureMode".to_string(),
1213                    ControlValueDescription::Enum {
1214                        value: exposure_current,
1215                        possible: supported_exposure_values,
1216                        default: exposure_current,
1217                    },
1218                    vec![],
1219                    true,
1220                ));
1221            }
1222
1223            let exposure_poi_supported: BOOL =
1224                unsafe { msg_send![self.inner, isExposurePointOfInterestSupported] };
1225            let exposure_poi: CGPoint = unsafe { msg_send![self.inner, exposurePointOfInterest] };
1226
1227            controls.push(CameraControl::new(
1228                KnownCameraControl::Other(2),
1229                "ExposurePointOfInterest".to_string(),
1230                ControlValueDescription::Point {
1231                    value: (exposure_poi.x as f64, exposure_poi.y as f64),
1232                    default: (0.5, 0.5),
1233                },
1234                if exposure_poi_supported == NO {
1235                    vec![
1236                        KnownCameraControlFlag::Disabled,
1237                        KnownCameraControlFlag::ReadOnly,
1238                    ]
1239                } else {
1240                    vec![]
1241                },
1242                focus_auto == YES || focus_continuous == YES,
1243            ));
1244
1245            let expposure_face_driven_supported: BOOL =
1246                unsafe { msg_send![self.inner, isFaceDrivenAutoExposureEnabled] };
1247            let exposure_face_driven: BOOL = unsafe {
1248                msg_send![
1249                    self.inner,
1250                    automaticallyAdjustsFaceDrivenAutoExposureEnabled
1251                ]
1252            };
1253
1254            controls.push(CameraControl::new(
1255                KnownCameraControl::Other(3),
1256                "ExposureFaceDriven".to_string(),
1257                ControlValueDescription::Boolean {
1258                    value: exposure_face_driven == YES,
1259                    default: false,
1260                },
1261                if expposure_face_driven_supported == NO {
1262                    vec![
1263                        KnownCameraControlFlag::Disabled,
1264                        KnownCameraControlFlag::ReadOnly,
1265                    ]
1266                } else {
1267                    vec![]
1268                },
1269                exposure_poi_supported == YES,
1270            ));
1271
1272            let exposure_bias: f32 = unsafe { msg_send![self.inner, exposureTargetBias] };
1273            let exposure_bias_min: f32 = unsafe { msg_send![self.inner, minExposureTargetBias] };
1274            let exposure_bias_max: f32 = unsafe { msg_send![self.inner, maxExposureTargetBias] };
1275
1276            controls.push(CameraControl::new(
1277                KnownCameraControl::Other(4),
1278                "ExposureBiasTarget".to_string(),
1279                ControlValueDescription::FloatRange {
1280                    min: exposure_bias_min as f64,
1281                    max: exposure_bias_max as f64,
1282                    value: exposure_bias as f64,
1283                    step: f32::MIN_POSITIVE as f64,
1284                    default: unsafe { AVCaptureExposureTargetBiasCurrent } as f64,
1285                },
1286                vec![],
1287                true,
1288            ));
1289
1290            let exposure_duration: CMTime = unsafe { msg_send![self.inner, exposureDuration] };
1291            let exposure_duration_min: CMTime =
1292                unsafe { msg_send![active_format, minExposureDuration] };
1293            let exposure_duration_max: CMTime =
1294                unsafe { msg_send![active_format, maxExposureDuration] };
1295
1296            controls.push(CameraControl::new(
1297                KnownCameraControl::Gamma,
1298                "ExposureDuration".to_string(),
1299                ControlValueDescription::IntegerRange {
1300                    min: exposure_duration_min.value,
1301                    max: exposure_duration_max.value,
1302                    value: exposure_duration.value,
1303                    step: 1,
1304                    default: unsafe { AVCaptureExposureDurationCurrent.value },
1305                },
1306                if exposure_custom == YES {
1307                    vec![
1308                        KnownCameraControlFlag::ReadOnly,
1309                        KnownCameraControlFlag::Volatile,
1310                    ]
1311                } else {
1312                    vec![KnownCameraControlFlag::Volatile]
1313                },
1314                exposure_custom == YES,
1315            ));
1316
1317            let exposure_iso: f32 = unsafe { msg_send![self.inner, ISO] };
1318            let exposure_iso_min: f32 = unsafe { msg_send![active_format, minISO] };
1319            let exposure_iso_max: f32 = unsafe { msg_send![active_format, maxISO] };
1320
1321            controls.push(CameraControl::new(
1322                KnownCameraControl::Brightness,
1323                "ExposureISO".to_string(),
1324                ControlValueDescription::FloatRange {
1325                    min: exposure_iso_min as f64,
1326                    max: exposure_iso_max as f64,
1327                    value: exposure_iso as f64,
1328                    step: f32::MIN_POSITIVE as f64,
1329                    default: unsafe { AVCaptureISOCurrent } as f64,
1330                },
1331                if exposure_custom == YES {
1332                    vec![
1333                        KnownCameraControlFlag::ReadOnly,
1334                        KnownCameraControlFlag::Volatile,
1335                    ]
1336                } else {
1337                    vec![KnownCameraControlFlag::Volatile]
1338                },
1339                exposure_custom == YES,
1340            ));
1341
1342            let lens_aperture: f32 = unsafe { msg_send![self.inner, lensAperture] };
1343
1344            controls.push(CameraControl::new(
1345                KnownCameraControl::Iris,
1346                "LensAperture".to_string(),
1347                ControlValueDescription::Float {
1348                    value: lens_aperture as f64,
1349                    default: lens_aperture as f64,
1350                    step: lens_aperture as f64,
1351                },
1352                vec![KnownCameraControlFlag::ReadOnly],
1353                false,
1354            ));
1355
1356            // get whiteblaance
1357            let white_balance_current: NSInteger =
1358                unsafe { msg_send![self.inner, whiteBalanceMode] };
1359            let white_balance_manual: BOOL =
1360                unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(0)] };
1361            let white_balance_auto: BOOL =
1362                unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(1)] };
1363            let white_balance_continuous: BOOL =
1364                unsafe { msg_send![self.inner, isWhiteBalanceModeSupported:NSInteger::from(2)] };
1365
1366            {
1367                let mut possible = vec![];
1368
1369                if white_balance_manual == YES {
1370                    possible.push(0);
1371                }
1372                if white_balance_auto == YES {
1373                    possible.push(1);
1374                }
1375                if white_balance_continuous == YES {
1376                    possible.push(2);
1377                }
1378
1379                controls.push(CameraControl::new(
1380                    KnownCameraControl::WhiteBalance,
1381                    "WhiteBalanceMode".to_string(),
1382                    ControlValueDescription::Enum {
1383                        value: white_balance_current as i64,
1384                        possible,
1385                        default: 0,
1386                    },
1387                    vec![],
1388                    true,
1389                ));
1390            }
1391
1392            let white_balance_gains: AVCaptureWhiteBalanceGains =
1393                unsafe { msg_send![self.inner, deviceWhiteBalanceGains] };
1394            let white_balance_default: AVCaptureWhiteBalanceGains =
1395                unsafe { msg_send![self.inner, grayWorldDeviceWhiteBalanceGains] };
1396            let white_balancne_max: AVCaptureWhiteBalanceGains =
1397                unsafe { msg_send![self.inner, maxWhiteBalanceGain] };
1398            let white_balance_gain_supported: BOOL = unsafe {
1399                msg_send![
1400                    self.inner,
1401                    isLockingWhiteBalanceWithCustomDeviceGainsSupported
1402                ]
1403            };
1404
1405            controls.push(CameraControl::new(
1406                KnownCameraControl::Gain,
1407                "WhiteBalanceGain".to_string(),
1408                ControlValueDescription::RGB {
1409                    value: (
1410                        white_balance_gains.redGain as f64,
1411                        white_balance_gains.greenGain as f64,
1412                        white_balance_gains.blueGain as f64,
1413                    ),
1414                    max: (
1415                        white_balancne_max.redGain as f64,
1416                        white_balancne_max.greenGain as f64,
1417                        white_balancne_max.blueGain as f64,
1418                    ),
1419                    default: (
1420                        white_balance_default.redGain as f64,
1421                        white_balance_default.greenGain as f64,
1422                        white_balance_default.blueGain as f64,
1423                    ),
1424                },
1425                if white_balance_gain_supported == YES {
1426                    vec![
1427                        KnownCameraControlFlag::Disabled,
1428                        KnownCameraControlFlag::ReadOnly,
1429                    ]
1430                } else {
1431                    vec![]
1432                },
1433                white_balance_gain_supported == YES,
1434            ));
1435
1436            // get flash
1437            let has_torch: BOOL = unsafe { msg_send![self.inner, isTorchAvailable] };
1438            let torch_active: BOOL = unsafe { msg_send![self.inner, isTorchActive] };
1439            let torch_off: BOOL =
1440                unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(0)] };
1441            let torch_on: BOOL =
1442                unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(1)] };
1443            let torch_auto: BOOL =
1444                unsafe { msg_send![self.inner, isTorchModeSupported:NSInteger::from(2)] };
1445
1446            {
1447                let mut possible = vec![];
1448
1449                if torch_off == YES {
1450                    possible.push(0);
1451                }
1452                if torch_on == YES {
1453                    possible.push(1);
1454                }
1455                if torch_auto == YES {
1456                    possible.push(2);
1457                }
1458
1459                controls.push(CameraControl::new(
1460                    KnownCameraControl::Other(5),
1461                    "TorchMode".to_string(),
1462                    ControlValueDescription::Enum {
1463                        value: (torch_active == YES) as i64,
1464                        possible,
1465                        default: 0,
1466                    },
1467                    if has_torch == YES {
1468                        vec![
1469                            KnownCameraControlFlag::Disabled,
1470                            KnownCameraControlFlag::ReadOnly,
1471                        ]
1472                    } else {
1473                        vec![]
1474                    },
1475                    has_torch == YES,
1476                ));
1477            }
1478
1479            // get low light boost
1480            let has_llb: BOOL = unsafe { msg_send![self.inner, isLowLightBoostSupported] };
1481            let llb_enabled: BOOL = unsafe { msg_send![self.inner, isLowLightBoostEnabled] };
1482
1483            {
1484                controls.push(CameraControl::new(
1485                    KnownCameraControl::BacklightComp,
1486                    "LowLightCompensation".to_string(),
1487                    ControlValueDescription::Boolean {
1488                        value: llb_enabled == YES,
1489                        default: false,
1490                    },
1491                    if has_llb == NO {
1492                        vec![
1493                            KnownCameraControlFlag::Disabled,
1494                            KnownCameraControlFlag::ReadOnly,
1495                        ]
1496                    } else {
1497                        vec![]
1498                    },
1499                    has_llb == YES,
1500                ));
1501            }
1502
1503            // get zoom factor
1504            let zoom_current: CGFloat = unsafe { msg_send![self.inner, videoZoomFactor] };
1505            let zoom_min: CGFloat = unsafe { msg_send![self.inner, minAvailableVideoZoomFactor] };
1506            let zoom_max: CGFloat = unsafe { msg_send![self.inner, maxAvailableVideoZoomFactor] };
1507
1508            controls.push(CameraControl::new(
1509                KnownCameraControl::Zoom,
1510                "Zoom".to_string(),
1511                ControlValueDescription::FloatRange {
1512                    min: zoom_min as f64,
1513                    max: zoom_max as f64,
1514                    value: zoom_current as f64,
1515                    step: f32::MIN_POSITIVE as f64,
1516                    default: 1.0,
1517                },
1518                vec![],
1519                true,
1520            ));
1521
1522            // zoom distortion correction
1523            let distortion_correction_supported: BOOL =
1524                unsafe { msg_send![self.inner, isGeometricDistortionCorrectionSupported] };
1525            let distortion_correction_current_value: BOOL =
1526                unsafe { msg_send![self.inner, isGeometricDistortionCorrectionEnabled] };
1527
1528            controls.push(CameraControl::new(
1529                KnownCameraControl::Other(6),
1530                "DistortionCorrection".to_string(),
1531                ControlValueDescription::Boolean {
1532                    value: distortion_correction_current_value == YES,
1533                    default: false,
1534                },
1535                if distortion_correction_supported == YES {
1536                    vec![
1537                        KnownCameraControlFlag::ReadOnly,
1538                        KnownCameraControlFlag::Disabled,
1539                    ]
1540                } else {
1541                    vec![]
1542                },
1543                distortion_correction_supported == YES,
1544            ));
1545
1546            Ok(controls)
1547        }
1548
1549        pub fn set_control(
1550            &mut self,
1551            id: KnownCameraControl,
1552            value: ControlValueSetter,
1553        ) -> Result<(), NokhwaError> {
1554            let rc = self.get_controls()?;
1555            let controls = rc
1556                .iter()
1557                .map(|cc| (cc.control(), cc))
1558                .collect::<BTreeMap<_, _>>();
1559
1560            match id {
1561                KnownCameraControl::Brightness => {
1562                    let isoctrl = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1563                        property: id.to_string(),
1564                        value: value.to_string(),
1565                        error: "Control does not exist".to_string(),
1566                    })?;
1567
1568                    if isoctrl.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1569                        return Err(NokhwaError::SetPropertyError {
1570                            property: id.to_string(),
1571                            value: value.to_string(),
1572                            error:
1573                                "Exposure is in improper state to set ISO (Please set to `custom`!)"
1574                                    .to_string(),
1575                        });
1576                    }
1577
1578                    if isoctrl.flag().contains(&KnownCameraControlFlag::Disabled) {
1579                        return Err(NokhwaError::SetPropertyError {
1580                            property: id.to_string(),
1581                            value: value.to_string(),
1582                            error: "Disabled".to_string(),
1583                        });
1584                    }
1585
1586                    let current_duration = unsafe { AVCaptureExposureDurationCurrent };
1587                    let new_iso = *value.as_float().ok_or(NokhwaError::SetPropertyError {
1588                        property: id.to_string(),
1589                        value: value.to_string(),
1590                        error: "Expected float".to_string(),
1591                    })? as f32;
1592
1593                    if !isoctrl.description().verify_setter(&value) {
1594                        return Err(NokhwaError::SetPropertyError {
1595                            property: id.to_string(),
1596                            value: value.to_string(),
1597                            error: "Failed to verify value".to_string(),
1598                        });
1599                    }
1600
1601                    let _: () = unsafe {
1602                        msg_send![self.inner, setExposureModeCustomWithDuration:current_duration ISO:new_iso completionHandler:Nil]
1603                    };
1604
1605                    Ok(())
1606                }
1607                KnownCameraControl::Gamma => {
1608                    let duration_ctrl = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1609                        property: id.to_string(),
1610                        value: value.to_string(),
1611                        error: "Control does not exist".to_string(),
1612                    })?;
1613
1614                    if duration_ctrl
1615                        .flag()
1616                        .contains(&KnownCameraControlFlag::ReadOnly)
1617                    {
1618                        return Err(NokhwaError::SetPropertyError {
1619                            property: id.to_string(),
1620                            value: value.to_string(),
1621                            error: "Exposure is in improper state to set Duration (Please set to `custom`!)"
1622                                .to_string(),
1623                        });
1624                    }
1625
1626                    if duration_ctrl
1627                        .flag()
1628                        .contains(&KnownCameraControlFlag::Disabled)
1629                    {
1630                        return Err(NokhwaError::SetPropertyError {
1631                            property: id.to_string(),
1632                            value: value.to_string(),
1633                            error: "Disabled".to_string(),
1634                        });
1635                    }
1636                    let current_duration: CMTime =
1637                        unsafe { msg_send![self.inner, exposureDuration] };
1638
1639                    let current_iso = unsafe { AVCaptureISOCurrent };
1640                    let new_duration = CMTime {
1641                        value: *value.as_integer().ok_or(NokhwaError::SetPropertyError {
1642                            property: id.to_string(),
1643                            value: value.to_string(),
1644                            error: "Expected i64".to_string(),
1645                        })?,
1646                        timescale: current_duration.timescale,
1647                        flags: current_duration.flags,
1648                        epoch: current_duration.epoch,
1649                    };
1650
1651                    if !duration_ctrl.description().verify_setter(&value) {
1652                        return Err(NokhwaError::SetPropertyError {
1653                            property: id.to_string(),
1654                            value: value.to_string(),
1655                            error: "Failed to verify value".to_string(),
1656                        });
1657                    }
1658
1659                    let _: () = unsafe {
1660                        msg_send![self.inner, setExposureModeCustomWithDuration:new_duration ISO:current_iso completionHandler:Nil]
1661                    };
1662
1663                    Ok(())
1664                }
1665                KnownCameraControl::WhiteBalance => {
1666                    let wb_enum_value = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1667                        property: id.to_string(),
1668                        value: value.to_string(),
1669                        error: "Control does not exist".to_string(),
1670                    })?;
1671
1672                    if wb_enum_value
1673                        .flag()
1674                        .contains(&KnownCameraControlFlag::ReadOnly)
1675                    {
1676                        return Err(NokhwaError::SetPropertyError {
1677                            property: id.to_string(),
1678                            value: value.to_string(),
1679                            error: "Read Only".to_string(),
1680                        });
1681                    }
1682
1683                    if wb_enum_value
1684                        .flag()
1685                        .contains(&KnownCameraControlFlag::Disabled)
1686                    {
1687                        return Err(NokhwaError::SetPropertyError {
1688                            property: id.to_string(),
1689                            value: value.to_string(),
1690                            error: "Disabled".to_string(),
1691                        });
1692                    }
1693                    let setter =
1694                        NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
1695                            property: id.to_string(),
1696                            value: value.to_string(),
1697                            error: "Expected Enum".to_string(),
1698                        })? as i32);
1699
1700                    if !wb_enum_value.description().verify_setter(&value) {
1701                        return Err(NokhwaError::SetPropertyError {
1702                            property: id.to_string(),
1703                            value: value.to_string(),
1704                            error: "Failed to verify value".to_string(),
1705                        });
1706                    }
1707
1708                    let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
1709
1710                    Ok(())
1711                }
1712                KnownCameraControl::BacklightComp => {
1713                    let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1714                        property: id.to_string(),
1715                        value: value.to_string(),
1716                        error: "Control does not exist".to_string(),
1717                    })?;
1718
1719                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1720                        return Err(NokhwaError::SetPropertyError {
1721                            property: id.to_string(),
1722                            value: value.to_string(),
1723                            error: "Read Only".to_string(),
1724                        });
1725                    }
1726
1727                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1728                        return Err(NokhwaError::SetPropertyError {
1729                            property: id.to_string(),
1730                            value: value.to_string(),
1731                            error: "Disabled".to_string(),
1732                        });
1733                    }
1734
1735                    let setter =
1736                        NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
1737                            property: id.to_string(),
1738                            value: value.to_string(),
1739                            error: "Expected Enum".to_string(),
1740                        })? as i32);
1741
1742                    if !ctrlvalue.description().verify_setter(&value) {
1743                        return Err(NokhwaError::SetPropertyError {
1744                            property: id.to_string(),
1745                            value: value.to_string(),
1746                            error: "Failed to verify value".to_string(),
1747                        });
1748                    }
1749
1750                    let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
1751
1752                    Ok(())
1753                }
1754                KnownCameraControl::Gain => {
1755                    let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1756                        property: id.to_string(),
1757                        value: value.to_string(),
1758                        error: "Control does not exist".to_string(),
1759                    })?;
1760
1761                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1762                        return Err(NokhwaError::SetPropertyError {
1763                            property: id.to_string(),
1764                            value: value.to_string(),
1765                            error: "Read Only".to_string(),
1766                        });
1767                    }
1768
1769                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1770                        return Err(NokhwaError::SetPropertyError {
1771                            property: id.to_string(),
1772                            value: value.to_string(),
1773                            error: "Disabled".to_string(),
1774                        });
1775                    }
1776
1777                    let setter = NSInteger::from(*value.as_boolean().ok_or(
1778                        NokhwaError::SetPropertyError {
1779                            property: id.to_string(),
1780                            value: value.to_string(),
1781                            error: "Expected Boolean".to_string(),
1782                        },
1783                    )? as i32);
1784
1785                    if !ctrlvalue.description().verify_setter(&value) {
1786                        return Err(NokhwaError::SetPropertyError {
1787                            property: id.to_string(),
1788                            value: value.to_string(),
1789                            error: "Failed to verify value".to_string(),
1790                        });
1791                    }
1792
1793                    let _: () = unsafe { msg_send![self.inner, whiteBalanceMode: setter] };
1794
1795                    Ok(())
1796                }
1797                KnownCameraControl::Zoom => {
1798                    let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1799                        property: id.to_string(),
1800                        value: value.to_string(),
1801                        error: "Control does not exist".to_string(),
1802                    })?;
1803
1804                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1805                        return Err(NokhwaError::SetPropertyError {
1806                            property: id.to_string(),
1807                            value: value.to_string(),
1808                            error: "Read Only".to_string(),
1809                        });
1810                    }
1811
1812                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1813                        return Err(NokhwaError::SetPropertyError {
1814                            property: id.to_string(),
1815                            value: value.to_string(),
1816                            error: "Disabled".to_string(),
1817                        });
1818                    }
1819
1820                    let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
1821                        property: id.to_string(),
1822                        value: value.to_string(),
1823                        error: "Expected float".to_string(),
1824                    })? as c_float;
1825
1826                    if !ctrlvalue.description().verify_setter(&value) {
1827                        return Err(NokhwaError::SetPropertyError {
1828                            property: id.to_string(),
1829                            value: value.to_string(),
1830                            error: "Failed to verify value".to_string(),
1831                        });
1832                    }
1833
1834                    let _: () = unsafe {
1835                        msg_send![self.inner, rampToVideoZoomFactor: setter withRate: 1.0_f32]
1836                    };
1837
1838                    Ok(())
1839                }
1840                KnownCameraControl::Exposure => {
1841                    let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1842                        property: id.to_string(),
1843                        value: value.to_string(),
1844                        error: "Control does not exist".to_string(),
1845                    })?;
1846
1847                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1848                        return Err(NokhwaError::SetPropertyError {
1849                            property: id.to_string(),
1850                            value: value.to_string(),
1851                            error: "Read Only".to_string(),
1852                        });
1853                    }
1854
1855                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1856                        return Err(NokhwaError::SetPropertyError {
1857                            property: id.to_string(),
1858                            value: value.to_string(),
1859                            error: "Disabled".to_string(),
1860                        });
1861                    }
1862
1863                    let setter =
1864                        NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
1865                            property: id.to_string(),
1866                            value: value.to_string(),
1867                            error: "Expected Enum".to_string(),
1868                        })? as i32);
1869
1870                    if !ctrlvalue.description().verify_setter(&value) {
1871                        return Err(NokhwaError::SetPropertyError {
1872                            property: id.to_string(),
1873                            value: value.to_string(),
1874                            error: "Failed to verify value".to_string(),
1875                        });
1876                    }
1877
1878                    let _: () = unsafe { msg_send![self.inner, exposureMode: setter] };
1879
1880                    Ok(())
1881                }
1882                KnownCameraControl::Iris => Err(NokhwaError::SetPropertyError {
1883                    property: id.to_string(),
1884                    value: value.to_string(),
1885                    error: "Read Only".to_string(),
1886                }),
1887                KnownCameraControl::Focus => {
1888                    let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1889                        property: id.to_string(),
1890                        value: value.to_string(),
1891                        error: "Control does not exist".to_string(),
1892                    })?;
1893
1894                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1895                        return Err(NokhwaError::SetPropertyError {
1896                            property: id.to_string(),
1897                            value: value.to_string(),
1898                            error: "Read Only".to_string(),
1899                        });
1900                    }
1901
1902                    if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1903                        return Err(NokhwaError::SetPropertyError {
1904                            property: id.to_string(),
1905                            value: value.to_string(),
1906                            error: "Disabled".to_string(),
1907                        });
1908                    }
1909
1910                    let setter =
1911                        NSInteger::from(*value.as_enum().ok_or(NokhwaError::SetPropertyError {
1912                            property: id.to_string(),
1913                            value: value.to_string(),
1914                            error: "Expected Enum".to_string(),
1915                        })? as i32);
1916
1917                    if !ctrlvalue.description().verify_setter(&value) {
1918                        return Err(NokhwaError::SetPropertyError {
1919                            property: id.to_string(),
1920                            value: value.to_string(),
1921                            error: "Failed to verify value".to_string(),
1922                        });
1923                    }
1924
1925                    let _: () = unsafe { msg_send![self.inner, focusMode: setter] };
1926
1927                    Ok(())
1928                }
1929                KnownCameraControl::Other(i) => match i {
1930                    0 => {
1931                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1932                            property: id.to_string(),
1933                            value: value.to_string(),
1934                            error: "Control does not exist".to_string(),
1935                        })?;
1936
1937                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1938                            return Err(NokhwaError::SetPropertyError {
1939                                property: id.to_string(),
1940                                value: value.to_string(),
1941                                error: "Read Only".to_string(),
1942                            });
1943                        }
1944
1945                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1946                            return Err(NokhwaError::SetPropertyError {
1947                                property: id.to_string(),
1948                                value: value.to_string(),
1949                                error: "Disabled".to_string(),
1950                            });
1951                        }
1952
1953                        let setter = value
1954                            .as_point()
1955                            .ok_or(NokhwaError::SetPropertyError {
1956                                property: id.to_string(),
1957                                value: value.to_string(),
1958                                error: "Expected Point".to_string(),
1959                            })
1960                            .map(|(x, y)| CGPoint {
1961                                x: *x as f32,
1962                                y: *y as f32,
1963                            })?;
1964
1965                        if !ctrlvalue.description().verify_setter(&value) {
1966                            return Err(NokhwaError::SetPropertyError {
1967                                property: id.to_string(),
1968                                value: value.to_string(),
1969                                error: "Failed to verify value".to_string(),
1970                            });
1971                        }
1972
1973                        let _: () = unsafe { msg_send![self.inner, focusPointOfInterest: setter] };
1974
1975                        Ok(())
1976                    }
1977                    1 => {
1978                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
1979                            property: id.to_string(),
1980                            value: value.to_string(),
1981                            error: "Control does not exist".to_string(),
1982                        })?;
1983
1984                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
1985                            return Err(NokhwaError::SetPropertyError {
1986                                property: id.to_string(),
1987                                value: value.to_string(),
1988                                error: "Read Only".to_string(),
1989                            });
1990                        }
1991
1992                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
1993                            return Err(NokhwaError::SetPropertyError {
1994                                property: id.to_string(),
1995                                value: value.to_string(),
1996                                error: "Disabled".to_string(),
1997                            });
1998                        }
1999
2000                        let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
2001                            property: id.to_string(),
2002                            value: value.to_string(),
2003                            error: "Expected float".to_string(),
2004                        })? as c_float;
2005
2006                        if !ctrlvalue.description().verify_setter(&value) {
2007                            return Err(NokhwaError::SetPropertyError {
2008                                property: id.to_string(),
2009                                value: value.to_string(),
2010                                error: "Failed to verify value".to_string(),
2011                            });
2012                        }
2013
2014                        let _: () = unsafe {
2015                            msg_send![self.inner, setFocusModeLockedWithLensPosition: setter handler: Nil]
2016                        };
2017
2018                        Ok(())
2019                    }
2020                    2 => {
2021                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
2022                            property: id.to_string(),
2023                            value: value.to_string(),
2024                            error: "Control does not exist".to_string(),
2025                        })?;
2026
2027                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
2028                            return Err(NokhwaError::SetPropertyError {
2029                                property: id.to_string(),
2030                                value: value.to_string(),
2031                                error: "Read Only".to_string(),
2032                            });
2033                        }
2034
2035                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
2036                            return Err(NokhwaError::SetPropertyError {
2037                                property: id.to_string(),
2038                                value: value.to_string(),
2039                                error: "Disabled".to_string(),
2040                            });
2041                        }
2042
2043                        let setter = value
2044                            .as_point()
2045                            .ok_or(NokhwaError::SetPropertyError {
2046                                property: id.to_string(),
2047                                value: value.to_string(),
2048                                error: "Expected Point".to_string(),
2049                            })
2050                            .map(|(x, y)| CGPoint {
2051                                x: *x as f32,
2052                                y: *y as f32,
2053                            })?;
2054
2055                        if !ctrlvalue.description().verify_setter(&value) {
2056                            return Err(NokhwaError::SetPropertyError {
2057                                property: id.to_string(),
2058                                value: value.to_string(),
2059                                error: "Failed to verify value".to_string(),
2060                            });
2061                        }
2062
2063                        let _: () =
2064                            unsafe { msg_send![self.inner, exposurePointOfInterest: setter] };
2065
2066                        Ok(())
2067                    }
2068                    3 => {
2069                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
2070                            property: id.to_string(),
2071                            value: value.to_string(),
2072                            error: "Control does not exist".to_string(),
2073                        })?;
2074
2075                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
2076                            return Err(NokhwaError::SetPropertyError {
2077                                property: id.to_string(),
2078                                value: value.to_string(),
2079                                error: "Read Only".to_string(),
2080                            });
2081                        }
2082
2083                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
2084                            return Err(NokhwaError::SetPropertyError {
2085                                property: id.to_string(),
2086                                value: value.to_string(),
2087                                error: "Disabled".to_string(),
2088                            });
2089                        }
2090
2091                        let setter =
2092                            if *value.as_boolean().ok_or(NokhwaError::SetPropertyError {
2093                                property: id.to_string(),
2094                                value: value.to_string(),
2095                                error: "Expected Boolean".to_string(),
2096                            })? {
2097                                YES
2098                            } else {
2099                                NO
2100                            };
2101
2102                        if !ctrlvalue.description().verify_setter(&value) {
2103                            return Err(NokhwaError::SetPropertyError {
2104                                property: id.to_string(),
2105                                value: value.to_string(),
2106                                error: "Failed to verify value".to_string(),
2107                            });
2108                        }
2109
2110                        let _: () = unsafe {
2111                            msg_send![
2112                                self.inner,
2113                                automaticallyAdjustsFaceDrivenAutoExposureEnabled: setter
2114                            ]
2115                        };
2116
2117                        Ok(())
2118                    }
2119                    4 => {
2120                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
2121                            property: id.to_string(),
2122                            value: value.to_string(),
2123                            error: "Control does not exist".to_string(),
2124                        })?;
2125
2126                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
2127                            return Err(NokhwaError::SetPropertyError {
2128                                property: id.to_string(),
2129                                value: value.to_string(),
2130                                error: "Read Only".to_string(),
2131                            });
2132                        }
2133
2134                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
2135                            return Err(NokhwaError::SetPropertyError {
2136                                property: id.to_string(),
2137                                value: value.to_string(),
2138                                error: "Disabled".to_string(),
2139                            });
2140                        }
2141
2142                        let setter = *value.as_float().ok_or(NokhwaError::SetPropertyError {
2143                            property: id.to_string(),
2144                            value: value.to_string(),
2145                            error: "Expected Float".to_string(),
2146                        })? as f32;
2147
2148                        if !ctrlvalue.description().verify_setter(&value) {
2149                            return Err(NokhwaError::SetPropertyError {
2150                                property: id.to_string(),
2151                                value: value.to_string(),
2152                                error: "Failed to verify value".to_string(),
2153                            });
2154                        }
2155
2156                        let _: () = unsafe {
2157                            msg_send![self.inner, setExposureTargetBias: setter handler: Nil]
2158                        };
2159
2160                        Ok(())
2161                    }
2162                    5 => {
2163                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
2164                            property: id.to_string(),
2165                            value: value.to_string(),
2166                            error: "Control does not exist".to_string(),
2167                        })?;
2168
2169                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
2170                            return Err(NokhwaError::SetPropertyError {
2171                                property: id.to_string(),
2172                                value: value.to_string(),
2173                                error: "Read Only".to_string(),
2174                            });
2175                        }
2176
2177                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
2178                            return Err(NokhwaError::SetPropertyError {
2179                                property: id.to_string(),
2180                                value: value.to_string(),
2181                                error: "Disabled".to_string(),
2182                            });
2183                        }
2184
2185                        let setter = NSInteger::from(*value.as_enum().ok_or(
2186                            NokhwaError::SetPropertyError {
2187                                property: id.to_string(),
2188                                value: value.to_string(),
2189                                error: "Expected Enum".to_string(),
2190                            },
2191                        )? as i32);
2192
2193                        if !ctrlvalue.description().verify_setter(&value) {
2194                            return Err(NokhwaError::SetPropertyError {
2195                                property: id.to_string(),
2196                                value: value.to_string(),
2197                                error: "Failed to verify value".to_string(),
2198                            });
2199                        }
2200
2201                        let _: () = unsafe { msg_send![self.inner, torchMode: setter] };
2202
2203                        Ok(())
2204                    }
2205                    6 => {
2206                        let ctrlvalue = controls.get(&id).ok_or(NokhwaError::SetPropertyError {
2207                            property: id.to_string(),
2208                            value: value.to_string(),
2209                            error: "Control does not exist".to_string(),
2210                        })?;
2211
2212                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::ReadOnly) {
2213                            return Err(NokhwaError::SetPropertyError {
2214                                property: id.to_string(),
2215                                value: value.to_string(),
2216                                error: "Read Only".to_string(),
2217                            });
2218                        }
2219
2220                        if ctrlvalue.flag().contains(&KnownCameraControlFlag::Disabled) {
2221                            return Err(NokhwaError::SetPropertyError {
2222                                property: id.to_string(),
2223                                value: value.to_string(),
2224                                error: "Disabled".to_string(),
2225                            });
2226                        }
2227
2228                        let setter =
2229                            if *value.as_boolean().ok_or(NokhwaError::SetPropertyError {
2230                                property: id.to_string(),
2231                                value: value.to_string(),
2232                                error: "Expected Boolean".to_string(),
2233                            })? {
2234                                YES
2235                            } else {
2236                                NO
2237                            };
2238
2239                        if !ctrlvalue.description().verify_setter(&value) {
2240                            return Err(NokhwaError::SetPropertyError {
2241                                property: id.to_string(),
2242                                value: value.to_string(),
2243                                error: "Failed to verify value".to_string(),
2244                            });
2245                        }
2246
2247                        let _: () = unsafe {
2248                            msg_send![self.inner, geometricDistortionCorrectionEnabled: setter]
2249                        };
2250
2251                        Ok(())
2252                    }
2253                    _ => Err(NokhwaError::SetPropertyError {
2254                        property: id.to_string(),
2255                        value: value.to_string(),
2256                        error: "Unknown Control".to_string(),
2257                    }),
2258                },
2259                _ => Err(NokhwaError::SetPropertyError {
2260                    property: id.to_string(),
2261                    value: value.to_string(),
2262                    error: "Unknown Control".to_string(),
2263                }),
2264            }
2265        }
2266
2267        pub fn active_format(&self) -> Result<CameraFormat, NokhwaError> {
2268            let af: *mut Object = unsafe { msg_send![self.inner, activeFormat] };
2269            let avf_format = AVCaptureDeviceFormat::try_from(af)?;
2270            let resolution = avf_format.resolution;
2271            let fourcc = avf_format.fourcc;
2272            let mut a = avf_format
2273                .fps_list
2274                .into_iter()
2275                .map(move |fps_f64| {
2276                    let fps = fps_f64 as u32;
2277
2278                    let resolution =
2279                        Resolution::new(resolution.width as u32, resolution.height as u32); // FIXME: what the fuck?
2280                    CameraFormat::new(resolution, fourcc, fps)
2281                })
2282                .collect::<Vec<_>>();
2283            a.sort_by(|a, b| a.frame_rate().cmp(&b.frame_rate()));
2284
2285            if a.len() != 0 {
2286                Ok(a[a.len() - 1])
2287            } else {
2288                Err(NokhwaError::GetPropertyError {
2289                    property: "activeFormat".to_string(),
2290                    error: "None??".to_string(),
2291                })
2292            }
2293        }
2294    }
2295
2296    impl AVCaptureDeviceInput {
2297        pub fn new(capture_device: &AVCaptureDevice) -> Result<Self, NokhwaError> {
2298            let cls = class!(AVCaptureDeviceInput);
2299            let err_ptr: *mut c_void = std::ptr::null_mut();
2300            let capture_input: *mut Object = unsafe {
2301                let allocated: *mut Object = msg_send![cls, alloc];
2302                msg_send![allocated, initWithDevice:capture_device.inner() error:err_ptr]
2303            };
2304            if !err_ptr.is_null() {
2305                return Err(NokhwaError::InitializeError {
2306                    backend: ApiBackend::AVFoundation,
2307                    error: "Failed to create input".to_string(),
2308                });
2309            }
2310
2311            Ok(AVCaptureDeviceInput {
2312                inner: capture_input,
2313            })
2314        }
2315    }
2316
2317    pub struct AVCaptureVideoDataOutput {
2318        inner: *mut Object,
2319    }
2320
2321    impl AVCaptureVideoDataOutput {
2322        pub fn new() -> Self {
2323            AVCaptureVideoDataOutput::default()
2324        }
2325
2326        pub fn add_delegate(&self, delegate: &AVCaptureVideoCallback) -> Result<(), NokhwaError> {
2327            unsafe {
2328                let _: () = msg_send![
2329                    self.inner,
2330                    setSampleBufferDelegate: delegate.delegate
2331                    queue: delegate.queue().0
2332                ];
2333            };
2334            Ok(())
2335        }
2336
2337        pub fn set_frame_format(&self, format: FrameFormat) -> Result<(), NokhwaError> {
2338            let cmpixelfmt = match format {
2339                FrameFormat::YUYV => kCMPixelFormat_422YpCbCr8_yuvs,
2340                FrameFormat::MJPEG => kCMVideoCodecType_JPEG,
2341                FrameFormat::GRAY => kCMPixelFormat_8IndexedGray_WhiteIsZero,
2342                FrameFormat::NV12 => kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange,
2343                FrameFormat::RAWRGB => kCMPixelFormat_24RGB,
2344                FrameFormat::RAWBGR => {
2345                    return Err(NokhwaError::SetPropertyError {
2346                        property: "setVideoSettings".to_string(),
2347                        value: "set frame format".to_string(),
2348                        error: "Unsupported frame format BGR".to_string(),
2349                    });
2350                }
2351            };
2352            let obj = CFNumber::from(cmpixelfmt as i32);
2353            let obj = obj.as_CFTypeRef() as *mut Object;
2354            let key = unsafe { kCVPixelBufferPixelFormatTypeKey } as *mut Object;
2355            let dict = unsafe { NSDictionary::dictionaryWithObject_forKey_(nil, obj, key) };
2356            let _: () = unsafe { msg_send![self.inner, setVideoSettings:dict] };
2357            Ok(())
2358        }
2359    }
2360
2361    use cocoa_foundation::base::nil;
2362    use core_foundation::base::TCFType;
2363    use core_foundation::number::CFNumber;
2364    use core_video_sys::kCVPixelBufferPixelFormatTypeKey;
2365    impl Default for AVCaptureVideoDataOutput {
2366        fn default() -> Self {
2367            let cls = class!(AVCaptureVideoDataOutput);
2368            let inner: *mut Object = unsafe { msg_send![cls, new] };
2369
2370            AVCaptureVideoDataOutput { inner }
2371        }
2372    }
2373
2374    impl AVCaptureSession {
2375        pub fn new() -> Self {
2376            AVCaptureSession::default()
2377        }
2378
2379        pub fn begin_configuration(&self) {
2380            unsafe { msg_send![self.inner, beginConfiguration] }
2381        }
2382
2383        pub fn commit_configuration(&self) {
2384            unsafe { msg_send![self.inner, commitConfiguration] }
2385        }
2386
2387        pub fn can_add_input(&self, input: &AVCaptureDeviceInput) -> bool {
2388            let result: BOOL = unsafe { msg_send![self.inner, canAddInput:input.inner] };
2389            result == YES
2390        }
2391
2392        pub fn add_input(&self, input: &AVCaptureDeviceInput) -> Result<(), NokhwaError> {
2393            if self.can_add_input(input) {
2394                let _: () = unsafe { msg_send![self.inner, addInput:input.inner] };
2395                return Ok(());
2396            }
2397            Err(NokhwaError::SetPropertyError {
2398                property: "AVCaptureDeviceInput".to_string(),
2399                value: "add new input".to_string(),
2400                error: "Rejected".to_string(),
2401            })
2402        }
2403
2404        pub fn remove_input(&self, input: &AVCaptureDeviceInput) {
2405            unsafe { msg_send![self.inner, removeInput:input.inner] }
2406        }
2407
2408        pub fn can_add_output(&self, output: &AVCaptureVideoDataOutput) -> bool {
2409            let result: BOOL = unsafe { msg_send![self.inner, canAddOutput:output.inner] };
2410            result == YES
2411        }
2412
2413        pub fn add_output(&self, output: &AVCaptureVideoDataOutput) -> Result<(), NokhwaError> {
2414            if self.can_add_output(output) {
2415                let _: () = unsafe { msg_send![self.inner, addOutput:output.inner] };
2416                return Ok(());
2417            }
2418            Err(NokhwaError::SetPropertyError {
2419                property: "AVCaptureVideoDataOutput".to_string(),
2420                value: "add new output".to_string(),
2421                error: "Rejected".to_string(),
2422            })
2423        }
2424
2425        pub fn remove_output(&self, output: &AVCaptureVideoDataOutput) {
2426            unsafe { msg_send![self.inner, removeOutput:output.inner] }
2427        }
2428
2429        pub fn is_running(&self) -> bool {
2430            let running: BOOL = unsafe { msg_send![self.inner, isRunning] };
2431            running == YES
2432        }
2433
2434        pub fn start(&self) -> Result<(), NokhwaError> {
2435            let start_stream_fn = || {
2436                let _: () = unsafe { msg_send![self.inner, startRunning] };
2437            };
2438
2439            if std::panic::catch_unwind(start_stream_fn).is_err() {
2440                return Err(NokhwaError::OpenStreamError(
2441                    "Cannot run AVCaptureSession".to_string(),
2442                ));
2443            }
2444            Ok(())
2445        }
2446
2447        pub fn stop(&self) {
2448            unsafe { msg_send![self.inner, stopRunning] }
2449        }
2450
2451        pub fn is_interrupted(&self) -> bool {
2452            let interrupted: BOOL = unsafe { msg_send![self.inner, isInterrupted] };
2453            interrupted == YES
2454        }
2455    }
2456
2457    impl Default for AVCaptureSession {
2458        fn default() -> Self {
2459            let cls = class!(AVCaptureSession);
2460            let session: *mut Object = {
2461                let alloc: *mut Object = unsafe { msg_send![cls, alloc] };
2462                unsafe { msg_send![alloc, init] }
2463            };
2464            AVCaptureSession { inner: session }
2465        }
2466    }
2467}
2468
2469#[cfg(any(target_os = "macos", target_os = "ios"))]
2470pub use crate::internal::*;