camera_stream/platform/macos/
ext.rs1use core::ffi::c_void;
2use std::panic::AssertUnwindSafe;
3
4use objc2_av_foundation::{AVCaptureDevice, AVCaptureExposureMode, AVCaptureFocusMode};
5use objc2_core_foundation::CGPoint;
6
7use crate::error::{Error, PlatformError};
8use crate::platform::macos::catch_objc;
9use crate::platform::macos::device::MacosCameraDevice;
10use crate::platform::macos::frame::MacosFrame;
11use crate::types::Ratio;
12
13pub use objc2_av_foundation::{
15 AVCaptureExposureMode as MacosExposureMode, AVCaptureFocusMode as MacosFocusMode,
16 AVCaptureTorchMode as MacosTorchMode, AVCaptureWhiteBalanceMode as MacosWhiteBalanceMode,
17};
18
19pub struct ConfigLockGuard<'a> {
21 device: &'a AVCaptureDevice,
22}
23
24impl<'a> ConfigLockGuard<'a> {
25 pub fn device(&self) -> &AVCaptureDevice {
26 self.device
27 }
28}
29
30impl<'a> Drop for ConfigLockGuard<'a> {
31 fn drop(&mut self) {
32 unsafe { self.device.unlockForConfiguration() };
33 }
34}
35
36pub trait MacosCameraDeviceExt {
38 fn lock_for_configuration(&self) -> Result<ConfigLockGuard<'_>, Error>;
39
40 fn focus_modes(&self) -> impl Iterator<Item = MacosFocusMode>;
42 fn set_focus_mode(&self, mode: MacosFocusMode) -> Result<(), Error>;
43 fn set_focus_point(&self, x: f64, y: f64) -> Result<(), Error>;
44
45 fn exposure_modes(&self) -> impl Iterator<Item = MacosExposureMode>;
47 fn set_exposure_mode(&self, mode: MacosExposureMode) -> Result<(), Error>;
48 fn set_exposure_point(&self, x: f64, y: f64) -> Result<(), Error>;
49 fn set_exposure_target_bias(&self, bias: f32) -> Result<(), Error>;
50
51 fn set_white_balance_mode(&self, mode: MacosWhiteBalanceMode) -> Result<(), Error>;
53
54 fn has_torch(&self) -> bool;
56 fn set_torch_mode(&self, mode: MacosTorchMode) -> Result<(), Error>;
57
58 fn max_zoom_factor(&self) -> f64;
60 fn set_zoom_factor(&self, factor: f64) -> Result<(), Error>;
61
62 fn set_active_video_min_frame_duration(&self, duration: Ratio) -> Result<(), Error>;
64 fn set_active_video_max_frame_duration(&self, duration: Ratio) -> Result<(), Error>;
65}
66
67impl MacosCameraDeviceExt for MacosCameraDevice {
68 fn lock_for_configuration(&self) -> Result<ConfigLockGuard<'_>, Error> {
69 unsafe { self.device.lockForConfiguration() }
70 .map_err(|e| Error::Platform(PlatformError::NsError(e)))?;
71 Ok(ConfigLockGuard {
72 device: &self.device,
73 })
74 }
75
76 fn focus_modes(&self) -> impl Iterator<Item = MacosFocusMode> {
77 let device = &self.device;
78 [
79 AVCaptureFocusMode(0), AVCaptureFocusMode(1), AVCaptureFocusMode(2), ]
83 .into_iter()
84 .filter(move |mode| unsafe { device.isFocusModeSupported(*mode) })
85 }
86
87 fn set_focus_mode(&self, mode: MacosFocusMode) -> Result<(), Error> {
88 let _guard = self.lock_for_configuration()?;
89 catch_objc(AssertUnwindSafe(|| unsafe { self.device.setFocusMode(mode) }))
90 }
91
92 fn set_focus_point(&self, x: f64, y: f64) -> Result<(), Error> {
93 if !unsafe { self.device.isFocusPointOfInterestSupported() } {
94 return Err(Error::Platform(PlatformError::Message(
95 "focus point of interest not supported",
96 )));
97 }
98 let _guard = self.lock_for_configuration()?;
99 catch_objc(AssertUnwindSafe(|| unsafe {
100 self.device.setFocusPointOfInterest(CGPoint { x, y });
101 }))
102 }
103
104 fn exposure_modes(&self) -> impl Iterator<Item = MacosExposureMode> {
105 let device = &self.device;
106 [
107 AVCaptureExposureMode(0), AVCaptureExposureMode(1), AVCaptureExposureMode(2), AVCaptureExposureMode(3), ]
112 .into_iter()
113 .filter(move |mode| unsafe { device.isExposureModeSupported(*mode) })
114 }
115
116 fn set_exposure_mode(&self, mode: MacosExposureMode) -> Result<(), Error> {
117 let _guard = self.lock_for_configuration()?;
118 catch_objc(AssertUnwindSafe(|| unsafe { self.device.setExposureMode(mode) }))
119 }
120
121 fn set_exposure_point(&self, x: f64, y: f64) -> Result<(), Error> {
122 if !unsafe { self.device.isExposurePointOfInterestSupported() } {
123 return Err(Error::Platform(PlatformError::Message(
124 "exposure point of interest not supported",
125 )));
126 }
127 let _guard = self.lock_for_configuration()?;
128 catch_objc(AssertUnwindSafe(|| unsafe {
129 self.device.setExposurePointOfInterest(CGPoint { x, y });
130 }))
131 }
132
133 fn set_exposure_target_bias(&self, bias: f32) -> Result<(), Error> {
134 let _guard = self.lock_for_configuration()?;
135 catch_objc(AssertUnwindSafe(|| unsafe {
136 self.device
137 .setExposureTargetBias_completionHandler(bias, None);
138 }))
139 }
140
141 fn set_white_balance_mode(&self, mode: MacosWhiteBalanceMode) -> Result<(), Error> {
142 if !unsafe { self.device.isWhiteBalanceModeSupported(mode) } {
143 return Err(Error::Platform(PlatformError::Message(
144 "white balance mode not supported",
145 )));
146 }
147 let _guard = self.lock_for_configuration()?;
148 catch_objc(AssertUnwindSafe(|| unsafe { self.device.setWhiteBalanceMode(mode) }))
149 }
150
151 fn has_torch(&self) -> bool {
152 unsafe { self.device.hasTorch() }
153 }
154
155 fn set_torch_mode(&self, mode: MacosTorchMode) -> Result<(), Error> {
156 if !unsafe { self.device.isTorchModeSupported(mode) } {
157 return Err(Error::Platform(PlatformError::Message(
158 "torch mode not supported",
159 )));
160 }
161 let _guard = self.lock_for_configuration()?;
162 catch_objc(AssertUnwindSafe(|| unsafe { self.device.setTorchMode(mode) }))
163 }
164
165 fn max_zoom_factor(&self) -> f64 {
166 unsafe { self.device.maxAvailableVideoZoomFactor() }
167 }
168
169 fn set_zoom_factor(&self, factor: f64) -> Result<(), Error> {
170 let _guard = self.lock_for_configuration()?;
171 catch_objc(AssertUnwindSafe(|| unsafe { self.device.setVideoZoomFactor(factor) }))
172 }
173
174 fn set_active_video_min_frame_duration(&self, duration: Ratio) -> Result<(), Error> {
175 let _guard = self.lock_for_configuration()?;
176 let cm_time = objc2_core_media::CMTime {
177 value: duration.numerator as i64,
178 timescale: duration.denominator as i32,
179 flags: objc2_core_media::CMTimeFlags(1),
180 epoch: 0,
181 };
182 catch_objc(AssertUnwindSafe(|| unsafe {
183 self.device.setActiveVideoMinFrameDuration(cm_time);
184 }))
185 }
186
187 fn set_active_video_max_frame_duration(&self, duration: Ratio) -> Result<(), Error> {
188 let _guard = self.lock_for_configuration()?;
189 let cm_time = objc2_core_media::CMTime {
190 value: duration.numerator as i64,
191 timescale: duration.denominator as i32,
192 flags: objc2_core_media::CMTimeFlags(1),
193 epoch: 0,
194 };
195 catch_objc(AssertUnwindSafe(|| unsafe {
196 self.device.setActiveVideoMaxFrameDuration(cm_time);
197 }))
198 }
199}
200
201pub trait MacosFrameExt {
203 fn sample_buffer_ptr(&self) -> *const c_void;
204}
205
206impl MacosFrameExt for MacosFrame<'_> {
207 fn sample_buffer_ptr(&self) -> *const c_void {
208 self.pixel_buffer_ptr()
209 }
210}