1#![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 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 decl.add_ivar::<*const c_void>("_arcmutptr"); 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 #[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 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 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 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 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()] }
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); 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 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 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 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 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 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 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 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 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 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 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 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); 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::*;