libcamera/
camera_manager.rs1use std::{
2 ffi::{CStr, CString},
3 io,
4 marker::PhantomData,
5 ptr::NonNull,
6 sync::{mpsc, Arc, Mutex},
7};
8
9use libcamera_sys::*;
10
11use crate::{camera::Camera, logging::LoggingLevel, utils::handle_result};
12
13struct ManagerCallbacks {
14 added: Option<Box<dyn FnMut(Camera<'static>) + Send>>,
15 removed: Option<Box<dyn FnMut(Camera<'static>) + Send>>,
16 hotplug_tx: Option<mpsc::Sender<HotplugEvent>>,
17}
18
19#[derive(Debug, Clone)]
21pub enum HotplugEvent {
22 Added(String),
23 Removed(String),
24}
25
26pub struct CameraManager {
28 ptr: NonNull<libcamera_camera_manager_t>,
29 callbacks: Box<ManagerCallbacks>,
30 added_handle: *mut libcamera_callback_handle_t,
31 removed_handle: *mut libcamera_callback_handle_t,
32 started: bool,
33 tracker: Arc<CameraTracker>,
34}
35
36impl CameraManager {
37 pub fn new() -> io::Result<Self> {
39 let mut mgr = Self::new_unstarted()?;
40 mgr.start()?;
41 Ok(mgr)
42 }
43
44 pub fn new_unstarted() -> io::Result<Self> {
48 let ptr = NonNull::new(unsafe { libcamera_camera_manager_create() }).unwrap();
49 Ok(CameraManager {
50 ptr,
51 callbacks: Box::new(ManagerCallbacks {
52 added: None,
53 removed: None,
54 hotplug_tx: None,
55 }),
56 added_handle: core::ptr::null_mut(),
57 removed_handle: core::ptr::null_mut(),
58 started: false,
59 tracker: Arc::new(CameraTracker::default()),
60 })
61 }
62
63 pub fn start(&mut self) -> io::Result<()> {
65 if self.started {
66 return Ok(());
67 }
68 let ret = unsafe { libcamera_camera_manager_start(self.ptr.as_ptr()) };
69 handle_result(ret)?;
70 self.started = true;
71 Ok(())
72 }
73
74 pub fn stop(&mut self) -> io::Result<()> {
76 if !self.started {
77 return Ok(());
78 }
79 unsafe { libcamera_camera_manager_stop(self.ptr.as_ptr()) };
80 self.started = false;
81 Ok(())
82 }
83
84 pub fn try_stop(&mut self) -> io::Result<()> {
86 if self.tracker.has_live_handles() {
87 return Err(io::Error::other(
88 "cannot stop CameraManager while cameras are still alive",
89 ));
90 }
91 self.stop()
92 }
93
94 pub fn restart(&mut self) -> io::Result<()> {
96 self.try_stop()?;
97 self.start()
98 }
99
100 pub fn is_started(&self) -> bool {
102 self.started
103 }
104
105 pub fn version(&self) -> &str {
107 unsafe { CStr::from_ptr(libcamera_version_string()) }.to_str().unwrap()
108 }
109
110 pub fn cameras<'a>(&self) -> CameraList<'a> {
112 unsafe {
113 CameraList::from_ptr(
114 NonNull::new(libcamera_camera_manager_cameras(self.ptr.as_ptr())).unwrap(),
115 Some(self.tracker.clone()),
116 )
117 }
118 }
119
120 pub fn get<'a>(&self, id: &str) -> Option<Camera<'a>> {
122 let id_cstr = CString::new(id).ok()?;
123 let cam_ptr = unsafe { libcamera_camera_manager_get_id(self.ptr.as_ptr(), id_cstr.as_ptr()) };
124 NonNull::new(cam_ptr).map(|p| unsafe { Camera::from_ptr_tracked(p, Some(self.tracker.clone())) })
125 }
126
127 pub fn log_set_level(&self, category: &str, level: LoggingLevel) {
135 let category = CString::new(category).expect("category contains null byte");
136 let level: &CStr = level.into();
137 unsafe {
138 libcamera_log_set_level(category.as_ptr(), level.as_ptr());
139 }
140 }
141
142 pub fn on_camera_added(&mut self, cb: impl FnMut(Camera<'static>) + Send + 'static) {
148 self.callbacks.added = Some(Box::new(cb));
149 if self.added_handle.is_null() {
150 let data = self.callbacks.as_mut() as *mut _ as *mut _;
151 self.added_handle = unsafe {
152 libcamera_camera_manager_camera_added_connect(self.ptr.as_ptr(), Some(camera_added_cb), data)
153 };
154 }
155 }
156
157 pub fn on_camera_removed(&mut self, cb: impl FnMut(Camera<'static>) + Send + 'static) {
163 self.callbacks.removed = Some(Box::new(cb));
164 if self.removed_handle.is_null() {
165 let data = self.callbacks.as_mut() as *mut _ as *mut _;
166 self.removed_handle = unsafe {
167 libcamera_camera_manager_camera_removed_connect(self.ptr.as_ptr(), Some(camera_removed_cb), data)
168 };
169 }
170 }
171
172 pub fn subscribe_hotplug_events(&mut self) -> mpsc::Receiver<HotplugEvent> {
178 let (tx, rx) = mpsc::channel();
179 self.callbacks.hotplug_tx = Some(tx);
180 if self.added_handle.is_null() {
182 let data = self.callbacks.as_mut() as *mut _ as *mut _;
183 self.added_handle = unsafe {
184 libcamera_camera_manager_camera_added_connect(self.ptr.as_ptr(), Some(camera_added_cb), data)
185 };
186 }
187 if self.removed_handle.is_null() {
188 let data = self.callbacks.as_mut() as *mut _ as *mut _;
189 self.removed_handle = unsafe {
190 libcamera_camera_manager_camera_removed_connect(self.ptr.as_ptr(), Some(camera_removed_cb), data)
191 };
192 }
193 rx
194 }
195}
196
197impl Drop for CameraManager {
198 fn drop(&mut self) {
199 if self.started {
200 unsafe { libcamera_camera_manager_stop(self.ptr.as_ptr()) };
201 self.started = false;
202 }
203 unsafe {
204 if !self.added_handle.is_null() {
205 libcamera_camera_manager_camera_signal_disconnect(self.ptr.as_ptr(), self.added_handle);
206 }
207 if !self.removed_handle.is_null() {
208 libcamera_camera_manager_camera_signal_disconnect(self.ptr.as_ptr(), self.removed_handle);
209 }
210 libcamera_camera_manager_destroy(self.ptr.as_ptr());
211 }
212 }
213}
214
215#[derive(Default)]
216pub(crate) struct CameraTracker {
217 handles: Mutex<Vec<std::sync::Weak<()>>>,
218}
219
220impl CameraTracker {
221 pub(crate) fn track(&self) -> std::sync::Arc<()> {
222 let token = std::sync::Arc::new(());
223 self.handles.lock().unwrap().push(std::sync::Arc::downgrade(&token));
224 token
225 }
226
227 fn has_live_handles(&self) -> bool {
228 self.handles.lock().unwrap().iter().any(|w| w.upgrade().is_some())
229 }
230}
231
232pub struct CameraList<'d> {
233 ptr: NonNull<libcamera_camera_list_t>,
234 _phantom: PhantomData<&'d ()>,
235 tracker: Option<Arc<CameraTracker>>,
236}
237
238impl<'d> CameraList<'d> {
239 pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_list_t>, tracker: Option<Arc<CameraTracker>>) -> Self {
240 Self {
241 ptr,
242 _phantom: Default::default(),
243 tracker,
244 }
245 }
246
247 pub fn len(&self) -> usize {
249 unsafe { libcamera_camera_list_size(self.ptr.as_ptr()) }
250 }
251
252 pub fn is_empty(&self) -> bool {
254 self.len() == 0
255 }
256
257 pub fn get(&self, index: usize) -> Option<Camera<'d>> {
261 let cam_ptr = unsafe { libcamera_camera_list_get(self.ptr.as_ptr(), index as _) };
262 let tracker = self.tracker.clone();
263 NonNull::new(cam_ptr).map(|p| unsafe { Camera::from_ptr_tracked(p, tracker) })
264 }
265
266 pub fn iter(&'d self) -> CameraListIter<'d> {
268 CameraListIter { list: self, index: 0 }
269 }
270}
271
272impl Drop for CameraList<'_> {
273 fn drop(&mut self) {
274 unsafe {
275 libcamera_camera_list_destroy(self.ptr.as_ptr());
276 }
277 }
278}
279
280pub struct CameraListIter<'d> {
281 list: &'d CameraList<'d>,
282 index: usize,
283}
284
285impl<'d> Iterator for CameraListIter<'d> {
286 type Item = Camera<'d>;
287
288 fn next(&mut self) -> Option<Self::Item> {
289 if self.index < self.list.len() {
290 let camera = self.list.get(self.index);
291 self.index += 1;
292 camera
293 } else {
294 None
295 }
296 }
297
298 fn size_hint(&self) -> (usize, Option<usize>) {
299 let len = self.list.len().saturating_sub(self.index);
300 (len, Some(len))
301 }
302}
303
304impl<'d> ExactSizeIterator for CameraListIter<'d> {}
305
306unsafe extern "C" fn camera_added_cb(data: *mut core::ffi::c_void, cam: *mut libcamera_camera_t) {
307 if data.is_null() || cam.is_null() {
308 return;
309 }
310 let state = &mut *(data as *mut ManagerCallbacks);
312 if let Some(ptr) = NonNull::new(cam) {
313 let cam_copy = unsafe { libcamera_camera_copy(ptr.as_ptr()) };
315 if let Some(copy_ptr) = NonNull::new(cam_copy) {
316 let cam = unsafe { Camera::from_ptr(copy_ptr) };
317 let cam_id = cam.id().to_string();
318 if let Some(cb) = state.added.as_mut() {
319 cb(cam);
320 } else {
321 drop(cam);
322 }
323 if let Some(tx) = state.hotplug_tx.as_ref() {
324 let _ = tx.send(HotplugEvent::Added(cam_id));
325 }
326 }
327 }
328}
329
330unsafe extern "C" fn camera_removed_cb(data: *mut core::ffi::c_void, cam: *mut libcamera_camera_t) {
331 if data.is_null() || cam.is_null() {
332 return;
333 }
334 let state = &mut *(data as *mut ManagerCallbacks);
335 if let Some(ptr) = NonNull::new(cam) {
336 let cam_copy = unsafe { libcamera_camera_copy(ptr.as_ptr()) };
337 if let Some(copy_ptr) = NonNull::new(cam_copy) {
338 let cam = unsafe { Camera::from_ptr(copy_ptr) };
339 let cam_id = cam.id().to_string();
340 if let Some(cb) = state.removed.as_mut() {
341 cb(cam);
342 } else {
343 drop(cam);
344 }
345 if let Some(tx) = state.hotplug_tx.as_ref() {
346 let _ = tx.send(HotplugEvent::Removed(cam_id));
347 }
348 }
349 }
350}