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