diff --git a/nokhwa-bindings-macos/src/lib.rs b/nokhwa-bindings-macos/src/lib.rs index 2349d54..02519ce 100644 --- a/nokhwa-bindings-macos/src/lib.rs +++ b/nokhwa-bindings-macos/src/lib.rs @@ -13,6 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +// hello, future peng here +// whatever is written here will induce horrors uncomprehendable. +// save yourselves. write apple code in swift and bind it to rust. + #![allow(clippy::not_unsafe_ptr_arg_deref)] #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -68,7 +73,7 @@ pub mod core_media { #[derive(Clone)] pub struct NSObject(pub Id); impl Deref for NSObject { - type Target = objc::runtime::Object; + type Target = Object; fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } @@ -84,7 +89,7 @@ pub mod core_media { #[derive(Clone)] pub struct NSString(pub Id); impl Deref for NSString { - type Target = objc::runtime::Object; + type Target = Object; fn deref(&self) -> &Self::Target { unsafe { &*self.0 } } @@ -113,13 +118,13 @@ pub mod core_media { theSourceBuffer: CMBlockBufferRef, offsetToData: usize, dataLength: usize, - destination: *mut ::std::os::raw::c_void, + destination: *mut std::os::raw::c_void, ) -> std::os::raw::c_int; pub fn CMSampleBufferGetDataBuffer(sbuf: CMSampleBufferRef) -> CMBlockBufferRef; pub fn dispatch_queue_create( - label: *const ::std::os::raw::c_char, + label: *const std::os::raw::c_char, attr: NSObject, ) -> NSObject; @@ -141,7 +146,7 @@ pub mod core_media { pub fn CVPixelBufferGetBaseAddress( pixelBuffer: CVPixelBufferRef, - ) -> *mut ::std::os::raw::c_void; + ) -> *mut std::os::raw::c_void; pub fn CVPixelBufferGetPixelFormatType(pixelBuffer: CVPixelBufferRef) -> OSType; } @@ -216,7 +221,8 @@ pub mod avfoundation { use core_media_sys::{ kCMPixelFormat_422YpCbCr8_yuvs, kCMPixelFormat_8IndexedGray_WhiteIsZero, kCMVideoCodecType_422YpCbCr8, kCMVideoCodecType_JPEG, kCMVideoCodecType_JPEG_OpenDML, - CMFormatDescriptionGetMediaSubType, CMSampleBufferRef, CMVideoDimensions, + CMFormatDescriptionGetMediaSubType, CMFormatDescriptionRef, CMSampleBufferRef, + CMVideoDimensions, }; use dashmap::DashMap; use flume::{Receiver, Sender}; @@ -224,6 +230,7 @@ pub mod avfoundation { declare::ClassDecl, runtime::{Class, Object, Protocol, Sel, BOOL, YES}, }; + use std::convert::TryInto; use std::{ borrow::Cow, cmp::Ordering, @@ -294,7 +301,7 @@ pub mod avfoundation { length:string.len() encoding:UTF8_ENCODING ]; - let _: *mut std::ffi::c_void = msg_send![obj, autorelease]; + let _: *mut c_void = msg_send![obj, autorelease]; obj } } @@ -314,8 +321,8 @@ pub mod avfoundation { }); let immutable_array: *mut Object = unsafe { msg_send![ns_array_cls, arrayWithArray: mutable_array] }; - let _: *mut std::ffi::c_void = unsafe { msg_send![mutable_array, autorelease] }; - let _: *mut std::ffi::c_void = unsafe { msg_send![immutable_array, autorelease] }; + let _: *mut c_void = unsafe { msg_send![mutable_array, autorelease] }; + let _: *mut c_void = unsafe { msg_send![immutable_array, autorelease] }; immutable_array } @@ -327,7 +334,7 @@ pub mod avfoundation { let item = unsafe { NSArray::objectAtIndex(data, index) }; out_vec.push(T::from(item)); } - let _: *mut std::ffi::c_void = unsafe { msg_send![data, autorelease] }; + let _: *mut c_void = unsafe { msg_send![data, autorelease] }; out_vec } @@ -343,7 +350,7 @@ pub mod avfoundation { let item = unsafe { NSArray::objectAtIndex(data, index) }; out_vec.push(T::try_from(item)?); } - let _: *mut std::ffi::c_void = unsafe { msg_send![data, autorelease] }; + let _: *mut c_void = unsafe { msg_send![data, autorelease] }; Ok(out_vec) } @@ -812,7 +819,7 @@ pub mod avfoundation { #[derive(Debug)] pub struct AVCaptureDeviceFormat { - internal: *mut Object, + pub(crate) internal: *mut Object, pub resolution: CMVideoDimensions, pub fps_list: Vec, pub fourcc: AVFourCC, @@ -854,7 +861,7 @@ pub mod avfoundation { }) } }; - let _: *mut std::ffi::c_void = unsafe { msg_send![description_obj, autorelease] }; + let _: *mut c_void = unsafe { msg_send![description_obj, autorelease] }; Ok(AVCaptureDeviceFormat { internal: value, resolution, @@ -929,7 +936,7 @@ pub mod avfoundation { index, }) } - let _: *mut std::ffi::c_void = unsafe { msg_send![device_ns_array, release] }; + let _: *mut c_void = unsafe { msg_send![device_ns_array, release] }; devices } } @@ -1013,26 +1020,57 @@ pub mod avfoundation { unsafe { msg_send![self.inner, unlockForConfiguration] } } - pub fn set_frame_rate(&mut self, _: u32) {} - + // thank you ffmpeg pub fn set_all( &mut self, descriptor: CaptureDeviceFormatDescriptor, ) -> Result<(), AVFError> { let format_list = self.supported_formats()?; + let format_description_sel = sel!(formatDescription); + + let mut selected_format: *mut Object = std::ptr::null_mut(); + let mut selected_range: *mut Object = std::ptr::null_mut(); + for format in format_list { - if descriptor.compatible_with_capture_format(&format) { - unsafe { - msg_send![ - self.inner, - setActiveFormat:format.internal - ] + let format_desc_ref: CMFormatDescriptionRef = + unsafe { msg_send![format.internal, performSelector: format_description_sel] }; + let dimensions = unsafe { CMVideoFormatDescriptionGetDimensions(format_desc_ref) }; + + if dimensions.height == descriptor.resolution.height + && dimensions.width == descriptor.resolution.width + { + selected_format = format.internal; + + for range in ns_arr_to_vec::(unsafe { + msg_send![format.internal, valueForKey:"videoSupportedFrameRateRanges"] + }) { + let max_fps: f64 = + unsafe { msg_send![range.inner, valueForKey:"maxFrameRate"] }; + + if (f64::from(descriptor.fps) - max_fps).abs() < 0.01 { + selected_range = range.inner; + break; + } } - self.set_frame_rate(descriptor.fps as u32); - return Ok(()); } } - Err(AVFError::ConfigNotAccepted) + + if selected_range.is_null() || selected_format.is_null() { + return Err(AVFError::ConfigNotAccepted); + } + + self.lock()?; + let _: () = + unsafe { msg_send![self.inner, setValue:selected_format forKey:"activeFormat"] }; + let min_frame_duration: *mut Object = + unsafe { msg_send![selected_range, valueForKey:"minFrameDuration"] }; + let _: () = unsafe { + msg_send![self.inner, setValue:min_frame_duration forKey:"activeVideoMinFrameDuration"] + }; + let _: () = unsafe { + msg_send![self.inner, setValue:min_frame_duration forKey:"activeVideoMaxFrameDuration"] + }; + Ok(()) } } diff --git a/src/backends/capture/avfoundation.rs b/src/backends/capture/avfoundation.rs index c9212cb..a8e78ee 100644 --- a/src/backends/capture/avfoundation.rs +++ b/src/backends/capture/avfoundation.rs @@ -14,11 +14,7 @@ * limitations under the License. */ -use crate::{ - mjpeg_to_rgb, yuyv422_to_rgb, CameraControl, CameraFormat, CameraInfo, CaptureAPIBackend, - CaptureBackendTrait, ControlValueSetter, FrameFormat, KnownCameraControl, NokhwaError, - PixelFormat, Resolution, -}; +use crate::{mjpeg_to_rgb, yuyv422_to_rgb, CameraControl, CameraFormat, CameraInfo, CaptureAPIBackend, CaptureBackendTrait, ControlValueSetter, FrameFormat, KnownCameraControl, NokhwaError, PixelFormat, Resolution, nokhwa_initialize, nokhwa_check}; use image::{ImageBuffer, Rgb}; use nokhwa_bindings_macos::avfoundation::{ query_avfoundation, AVCaptureDevice, AVCaptureDeviceInput, AVCaptureSession, @@ -33,6 +29,7 @@ use std::{any::Any, borrow::Borrow, borrow::Cow, collections::HashMap, ops::Dere /// - You **must** call [`nokhwa_initialize`](crate::nokhwa_initialize) **before** doing anything with `AVFoundation`. /// - This only works on 64 bit platforms. /// - FPS adjustment does not work. +/// - If permission has not been granted and you call `init()` it will error. #[cfg_attr(feature = "docs-features", doc(cfg(feature = "input-avfoundation")))] pub struct AVFoundationCaptureDevice { device: AVCaptureDevice, @@ -99,7 +96,11 @@ impl AVFoundationCaptureDevice { impl CaptureBackendTrait for AVFoundationCaptureDevice { fn init(&mut self) -> Result { - todo!() + if !nokhwa_check() { + return Err(NokhwaError::InitializeError { backend: CaptureAPIBackend::AVFoundation, error: "User permission not granted yet.".to_string() }) + } + + } fn backend(&self) -> CaptureAPIBackend {