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