camera_stream/platform/macos/
ext.rs1use std::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) -> Vec<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) -> Vec<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::Message(e.to_string())))?;
68 Ok(ConfigLockGuard {
69 device: &self.device,
70 })
71 }
72
73 fn focus_modes(&self) -> Vec<MacosFocusMode> {
74 let mut modes = Vec::new();
75 let candidates = [
76 AVCaptureFocusMode(0), AVCaptureFocusMode(1), AVCaptureFocusMode(2), ];
80 for mode in &candidates {
81 if unsafe { self.device.isFocusModeSupported(*mode) } {
82 modes.push(*mode);
83 }
84 }
85 modes
86 }
87
88 fn set_focus_mode(&self, mode: MacosFocusMode) -> Result<(), Error> {
89 let _guard = self.lock_for_configuration()?;
90 unsafe { self.device.setFocusMode(mode) };
91 Ok(())
92 }
93
94 fn set_focus_point(&self, x: f64, y: f64) -> Result<(), Error> {
95 if !unsafe { self.device.isFocusPointOfInterestSupported() } {
96 return Err(Error::Platform(PlatformError::Message(
97 "focus point of interest not supported".into(),
98 )));
99 }
100 let _guard = self.lock_for_configuration()?;
101 unsafe {
102 self.device.setFocusPointOfInterest(CGPoint { x, y });
103 }
104 Ok(())
105 }
106
107 fn exposure_modes(&self) -> Vec<MacosExposureMode> {
108 let mut modes = Vec::new();
109 let candidates = [
110 AVCaptureExposureMode(0), AVCaptureExposureMode(1), AVCaptureExposureMode(2), AVCaptureExposureMode(3), ];
115 for mode in &candidates {
116 if unsafe { self.device.isExposureModeSupported(*mode) } {
117 modes.push(*mode);
118 }
119 }
120 modes
121 }
122
123 fn set_exposure_mode(&self, mode: MacosExposureMode) -> Result<(), Error> {
124 let _guard = self.lock_for_configuration()?;
125 unsafe { self.device.setExposureMode(mode) };
126 Ok(())
127 }
128
129 fn set_exposure_point(&self, x: f64, y: f64) -> Result<(), Error> {
130 if !unsafe { self.device.isExposurePointOfInterestSupported() } {
131 return Err(Error::Platform(PlatformError::Message(
132 "exposure point of interest not supported".into(),
133 )));
134 }
135 let _guard = self.lock_for_configuration()?;
136 unsafe {
137 self.device.setExposurePointOfInterest(CGPoint { x, y });
138 }
139 Ok(())
140 }
141
142 fn set_exposure_target_bias(&self, bias: f32) -> Result<(), Error> {
143 let _guard = self.lock_for_configuration()?;
144 unsafe {
145 self.device
146 .setExposureTargetBias_completionHandler(bias, None);
147 }
148 Ok(())
149 }
150
151 fn set_white_balance_mode(&self, mode: MacosWhiteBalanceMode) -> Result<(), Error> {
152 if !unsafe { self.device.isWhiteBalanceModeSupported(mode) } {
153 return Err(Error::Platform(PlatformError::Message(
154 "white balance mode not supported".into(),
155 )));
156 }
157 let _guard = self.lock_for_configuration()?;
158 unsafe { self.device.setWhiteBalanceMode(mode) };
159 Ok(())
160 }
161
162 fn has_torch(&self) -> bool {
163 unsafe { self.device.hasTorch() }
164 }
165
166 fn set_torch_mode(&self, mode: MacosTorchMode) -> Result<(), Error> {
167 if !unsafe { self.device.isTorchModeSupported(mode) } {
168 return Err(Error::Platform(PlatformError::Message(
169 "torch mode not supported".into(),
170 )));
171 }
172 let _guard = self.lock_for_configuration()?;
173 unsafe { self.device.setTorchMode(mode) };
174 Ok(())
175 }
176
177 fn max_zoom_factor(&self) -> f64 {
178 unsafe { self.device.maxAvailableVideoZoomFactor() }
179 }
180
181 fn set_zoom_factor(&self, factor: f64) -> Result<(), Error> {
182 let _guard = self.lock_for_configuration()?;
183 unsafe { self.device.setVideoZoomFactor(factor) };
184 Ok(())
185 }
186
187 fn set_active_frame_rate(&self, rate: FrameRate) -> Result<(), Error> {
188 let _guard = self.lock_for_configuration()?;
189 let duration = objc2_core_media::CMTime {
190 value: rate.denominator as i64,
191 timescale: rate.numerator as i32,
192 flags: objc2_core_media::CMTimeFlags(1),
193 epoch: 0,
194 };
195 unsafe { self.device.setActiveVideoMinFrameDuration(duration) };
196 unsafe { self.device.setActiveVideoMaxFrameDuration(duration) };
197 Ok(())
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}