1use std::{
2 collections::HashMap,
3 ffi::CStr,
4 io,
5 marker::PhantomData,
6 ops::{Deref, DerefMut},
7 ptr::NonNull,
8 sync::Mutex,
9};
10
11use libcamera_sys::*;
12
13use crate::{
14 camera_manager::CameraTracker,
15 control::{ControlInfoMap, ControlList, PropertyList},
16 geometry::{Rectangle, Size},
17 request::Request,
18 stream::{Stream, StreamConfigurationRef, StreamRole},
19 utils::Immutable,
20};
21
22#[derive(Debug, Clone, Copy)]
24pub enum CameraConfigurationStatus {
25 Valid,
27 Adjusted,
29 Invalid,
31}
32
33impl CameraConfigurationStatus {
34 pub fn is_valid(&self) -> bool {
35 matches!(self, Self::Valid)
36 }
37
38 pub fn is_adjusted(&self) -> bool {
39 matches!(self, Self::Adjusted)
40 }
41
42 pub fn is_invalid(&self) -> bool {
43 matches!(self, Self::Invalid)
44 }
45}
46
47impl TryFrom<libcamera_camera_configuration_status_t> for CameraConfigurationStatus {
48 type Error = ();
49
50 fn try_from(value: libcamera_camera_configuration_status_t) -> Result<Self, Self::Error> {
51 match value {
52 libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_VALID => Ok(Self::Valid),
53 libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_ADJUSTED => Ok(Self::Adjusted),
54 libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_INVALID => Ok(Self::Invalid),
55 _ => Err(()),
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum Orientation {
63 Rotate0,
64 Rotate0Mirror,
65 Rotate180,
66 Rotate180Mirror,
67 Rotate90Mirror,
68 Rotate270,
69 Rotate270Mirror,
70 Rotate90,
71}
72
73impl TryFrom<libcamera_orientation_t> for Orientation {
74 type Error = ();
75
76 fn try_from(value: libcamera_orientation_t) -> Result<Self, Self::Error> {
77 match value {
78 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_0 => Ok(Self::Rotate0),
79 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_0_MIRROR => Ok(Self::Rotate0Mirror),
80 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_180 => Ok(Self::Rotate180),
81 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_180_MIRROR => Ok(Self::Rotate180Mirror),
82 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_90_MIRROR => Ok(Self::Rotate90Mirror),
83 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_270 => Ok(Self::Rotate270),
84 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_270_MIRROR => Ok(Self::Rotate270Mirror),
85 libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_90 => Ok(Self::Rotate90),
86 _ => Err(()),
87 }
88 }
89}
90
91impl From<Orientation> for libcamera_orientation_t {
92 fn from(value: Orientation) -> Self {
93 match value {
94 Orientation::Rotate0 => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_0,
95 Orientation::Rotate0Mirror => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_0_MIRROR,
96 Orientation::Rotate180 => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_180,
97 Orientation::Rotate180Mirror => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_180_MIRROR,
98 Orientation::Rotate90Mirror => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_90_MIRROR,
99 Orientation::Rotate270 => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_270,
100 Orientation::Rotate270Mirror => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_270_MIRROR,
101 Orientation::Rotate90 => libcamera_orientation::LIBCAMERA_ORIENTATION_ROTATE_90,
102 }
103 }
104}
105
106pub struct SensorConfiguration {
107 item: NonNull<libcamera_sensor_configuration_t>,
108}
109
110impl SensorConfiguration {
111 pub fn new() -> Self {
112 let ptr = NonNull::new(unsafe { libcamera_sensor_configuration_create() }).unwrap();
113 Self { item: ptr }
114 }
115
116 pub fn from_ptr(ptr: NonNull<libcamera_sensor_configuration_t>) -> Self {
117 Self { item: ptr }
118 }
119
120 pub fn set_bit_depth(&mut self, depth: u32) {
121 unsafe { libcamera_sensor_configuration_set_bit_depth(self.item.as_ptr(), depth) }
122 }
123
124 pub fn set_output_size(&mut self, width: u32, height: u32) {
125 unsafe { libcamera_sensor_configuration_set_output_size(self.item.as_ptr(), width, height) }
126 }
127
128 pub fn set_analog_crop(&mut self, crop: Rectangle) {
129 let rect: libcamera_rectangle_t = crop.into();
130 unsafe { libcamera_sensor_configuration_set_analog_crop(self.item.as_ptr(), &rect) }
131 }
132
133 pub fn set_binning(&mut self, x: u32, y: u32) {
134 unsafe { libcamera_sensor_configuration_set_binning(self.item.as_ptr(), x, y) }
135 }
136
137 pub fn set_skipping(&mut self, x_odd: u32, x_even: u32, y_odd: u32, y_even: u32) {
138 unsafe { libcamera_sensor_configuration_set_skipping(self.item.as_ptr(), x_odd, x_even, y_odd, y_even) }
139 }
140
141 pub fn is_valid(&self) -> bool {
142 unsafe { libcamera_sensor_configuration_is_valid(self.item.as_ptr()) }
143 }
144
145 pub fn bit_depth(&self) -> u32 {
146 unsafe { libcamera_sensor_configuration_get_bit_depth(self.item.as_ptr()) }
147 }
148
149 pub fn output_size(&self) -> Size {
150 let size = unsafe { libcamera_sensor_configuration_get_output_size(self.item.as_ptr()) };
151 size.into()
152 }
153
154 pub fn analog_crop(&self) -> Rectangle {
155 unsafe { libcamera_sensor_configuration_get_analog_crop(self.item.as_ptr()).into() }
156 }
157
158 pub fn binning(&self) -> (u32, u32) {
159 let mut x = 0;
160 let mut y = 0;
161 unsafe { libcamera_sensor_configuration_get_binning(self.item.as_ptr(), &mut x, &mut y) };
162 (x, y)
163 }
164
165 pub fn skipping(&self) -> (u32, u32, u32, u32) {
166 let (mut x_odd, mut x_even, mut y_odd, mut y_even) = (0, 0, 0, 0);
167 unsafe {
168 libcamera_sensor_configuration_get_skipping(
169 self.item.as_ptr(),
170 &mut x_odd,
171 &mut x_even,
172 &mut y_odd,
173 &mut y_even,
174 )
175 };
176 (x_odd, x_even, y_odd, y_even)
177 }
178}
179
180impl Default for SensorConfiguration {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186impl Drop for SensorConfiguration {
187 fn drop(&mut self) {
188 unsafe { libcamera_sensor_configuration_destroy(self.item.as_ptr()) }
189 }
190}
191
192pub struct CameraConfiguration {
196 ptr: NonNull<libcamera_camera_configuration_t>,
197}
198
199impl CameraConfiguration {
200 pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_configuration_t>) -> Self {
201 Self { ptr }
202 }
203
204 pub fn get(&self, index: usize) -> Option<Immutable<StreamConfigurationRef<'_>>> {
210 let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
211 NonNull::new(ptr).map(|p| Immutable(unsafe { StreamConfigurationRef::from_ptr(p) }))
212 }
213
214 pub fn get_mut(&mut self, index: usize) -> Option<StreamConfigurationRef<'_>> {
220 let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
221 NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) })
222 }
223
224 pub fn add_configuration(&mut self) -> Option<StreamConfigurationRef<'_>> {
226 let ptr = unsafe { libcamera_camera_configuration_add_configuration(self.ptr.as_ptr()) };
227 NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) })
228 }
229
230 pub fn add_configuration_like(
236 &mut self,
237 template: &StreamConfigurationRef<'_>,
238 ) -> Option<StreamConfigurationRef<'_>> {
239 let ptr =
240 unsafe { libcamera_camera_configuration_add_configuration_from(self.ptr.as_ptr(), template.as_ptr()) };
241 NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) })
242 }
243
244 pub fn add_configurations_like<'a>(
248 &mut self,
249 templates: &[&StreamConfigurationRef<'a>],
250 ) -> Vec<StreamConfigurationRef<'_>> {
251 let mut appended = Vec::with_capacity(templates.len());
252 for tmpl in templates {
253 let ptr =
254 unsafe { libcamera_camera_configuration_add_configuration_from(self.ptr.as_ptr(), tmpl.as_ptr()) };
255 if let Some(cfg) = NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) }) {
256 appended.push(cfg);
257 }
258 }
259 appended
260 }
261
262 pub fn set_sensor_configuration(&mut self, mode: SensorConfiguration) {
263 unsafe { libcamera_camera_set_sensor_configuration(self.ptr.as_ptr(), mode.item.as_ptr()) }
264 }
265
266 pub fn len(&self) -> usize {
268 unsafe { libcamera_camera_configuration_size(self.ptr.as_ptr()) }
269 }
270
271 pub fn is_empty(&self) -> bool {
273 self.len() == 0
274 }
275
276 pub fn validate(&mut self) -> CameraConfigurationStatus {
278 unsafe { libcamera_camera_configuration_validate(self.ptr.as_ptr()) }
279 .try_into()
280 .unwrap()
281 }
282
283 pub fn orientation(&self) -> Orientation {
285 unsafe { libcamera_camera_configuration_get_orientation(self.ptr.as_ptr()) }
286 .try_into()
287 .unwrap()
288 }
289
290 pub fn set_orientation(&mut self, orientation: Orientation) {
292 unsafe { libcamera_camera_configuration_set_orientation(self.ptr.as_ptr(), orientation.into()) }
293 }
294
295 pub fn sensor_configuration(&self) -> Option<SensorConfiguration> {
297 let ptr = unsafe { libcamera_camera_configuration_get_sensor_configuration(self.ptr.as_ptr()) };
298 NonNull::new(ptr).map(SensorConfiguration::from_ptr)
299 }
300
301 pub fn validate_and_log(&mut self) -> CameraConfigurationStatus {
303 let status = self.validate();
304 for i in 0..self.len() {
305 if let Some(cfg) = self.get(i) {
306 eprintln!(
307 "Stream {} after validate(): stride={}, frame_size={}",
308 i,
309 cfg.get_stride(),
310 cfg.get_frame_size()
311 );
312 }
313 }
314 status
315 }
316
317 pub fn to_string_repr(&self) -> String {
319 unsafe {
320 let ptr = libcamera_camera_configuration_to_string(self.ptr.as_ptr());
321 if ptr.is_null() {
322 return String::new();
323 }
324 let s = std::ffi::CStr::from_ptr(ptr).to_string_lossy().into_owned();
325 libc::free(ptr.cast());
326 s
327 }
328 }
329}
330
331impl core::fmt::Debug for CameraConfiguration {
332 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
333 let mut list = f.debug_list();
334 for i in 0..self.len() {
335 list.entry(&self.get(i).unwrap().0);
336 }
337 list.finish()
338 }
339}
340
341impl Drop for CameraConfiguration {
342 fn drop(&mut self) {
343 unsafe { libcamera_camera_configuration_destroy(self.ptr.as_ptr()) }
344 }
345}
346
347pub struct Camera<'d> {
352 pub(crate) ptr: NonNull<libcamera_camera_t>,
353 _phantom: PhantomData<&'d ()>,
354 pub(crate) _token: Option<std::sync::Arc<()>>,
355}
356
357impl<'d> Camera<'d> {
358 pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_t>) -> Self {
359 Self {
360 ptr,
361 _phantom: Default::default(),
362 _token: None,
363 }
364 }
365
366 pub(crate) unsafe fn from_ptr_tracked(
367 ptr: NonNull<libcamera_camera_t>,
368 tracker: Option<std::sync::Arc<CameraTracker>>,
369 ) -> Self {
370 let token = tracker.map(|t| t.track());
371 Self {
372 ptr,
373 _phantom: Default::default(),
374 _token: token,
375 }
376 }
377
378 pub fn id(&self) -> &str {
384 unsafe { CStr::from_ptr(libcamera_camera_id(self.ptr.as_ptr())) }
385 .to_str()
386 .unwrap()
387 }
388
389 pub fn controls(&self) -> &ControlInfoMap {
391 unsafe {
392 ControlInfoMap::from_ptr(NonNull::new(libcamera_camera_controls(self.ptr.as_ptr()).cast_mut()).unwrap())
393 }
394 }
395
396 pub fn properties(&self) -> &PropertyList {
400 unsafe {
401 PropertyList::from_ptr(NonNull::new(libcamera_camera_properties(self.ptr.as_ptr()).cast_mut()).unwrap())
402 }
403 }
404
405 pub fn streams(&self) -> Vec<Stream> {
407 let set = unsafe { libcamera_camera_streams(self.ptr.as_ptr()) };
408 if set.is_null() {
409 return Vec::new();
410 }
411 let count = unsafe { libcamera_stream_set_size(set) };
412 let streams = (0..count)
413 .filter_map(|i| unsafe { NonNull::new(libcamera_stream_set_get(set, i)).map(|p| Stream::from_ptr(p)) })
414 .collect();
415 unsafe { libcamera_stream_set_destroy(set as *mut _) };
416 streams
417 }
418
419 pub fn generate_configuration(&self, roles: &[StreamRole]) -> Option<CameraConfiguration> {
425 let roles: Vec<libcamera_stream_role::Type> = roles.iter().map(|r| (*r).into()).collect();
426 let cfg =
427 unsafe { libcamera_camera_generate_configuration(self.ptr.as_ptr(), roles.as_ptr(), roles.len() as _) };
428 NonNull::new(cfg).map(|p| unsafe { CameraConfiguration::from_ptr(p) })
429 }
430
431 pub fn generate_first_supported_configuration(
433 &self,
434 roles: &[StreamRole],
435 ) -> Option<(CameraConfiguration, StreamRole)> {
436 roles
437 .iter()
438 .find_map(|role| self.generate_configuration(&[*role]).map(|cfg| (cfg, *role)))
439 }
440
441 pub fn acquire(&self) -> io::Result<ActiveCamera<'d>> {
443 let ret = unsafe { libcamera_camera_acquire(self.ptr.as_ptr()) };
444 if ret < 0 {
445 Err(io::Error::from_raw_os_error(-ret))
446 } else {
447 Ok(unsafe {
448 ActiveCamera::from_ptr_with_token(
449 NonNull::new(libcamera_camera_copy(self.ptr.as_ptr())).unwrap(),
450 self._token.clone(),
451 )
452 })
453 }
454 }
455}
456
457impl Drop for Camera<'_> {
458 fn drop(&mut self) {
459 unsafe { libcamera_camera_destroy(self.ptr.as_ptr()) }
460 }
461}
462
463extern "C" fn camera_request_completed_cb(ptr: *mut core::ffi::c_void, req: *mut libcamera_request_t) {
464 let mut state = unsafe { &*(ptr as *const Mutex<ActiveCameraState<'_>>) }
465 .lock()
466 .unwrap();
467 let req = state.requests.remove(&req).unwrap();
468
469 if let Some(cb) = &mut state.request_completed_cb {
470 cb(req);
471 }
472}
473
474extern "C" fn camera_buffer_completed_cb(
475 ptr: *mut core::ffi::c_void,
476 req: *mut libcamera_request_t,
477 fb: *mut libcamera_framebuffer_t,
478) {
479 let mut state = unsafe { &*(ptr as *const Mutex<ActiveCameraState<'_>>) }
480 .lock()
481 .unwrap();
482
483 let (req_ptr, stream) = match state
484 .requests
485 .get_mut(&req)
486 .and_then(|r| r.stream_for_buffer_ptr(fb).map(|s| (r as *mut Request, s)))
487 {
488 Some(v) => v,
489 None => return,
490 };
491
492 if let Some(cb) = state.buffer_completed_cb.as_mut() {
493 unsafe {
495 cb(&mut *req_ptr, stream);
496 }
497 }
498}
499
500extern "C" fn camera_disconnected_cb(ptr: *mut core::ffi::c_void) {
501 let mut state = unsafe { &*(ptr as *const Mutex<ActiveCameraState<'_>>) }
502 .lock()
503 .unwrap();
504 if let Some(cb) = state.disconnected_cb.as_mut() {
505 cb();
506 }
507}
508
509#[derive(Default)]
510struct ActiveCameraState<'d> {
511 requests: HashMap<*mut libcamera_request_t, Request>,
514 request_completed_cb: Option<Box<dyn FnMut(Request) + Send + 'd>>,
516 buffer_completed_cb: Option<BufferCompletedCb<'d>>,
518 disconnected_cb: Option<Box<dyn FnMut() + Send + 'd>>,
520}
521
522type BufferCompletedCb<'d> = Box<dyn FnMut(&mut Request, Stream) + Send + 'd>;
523
524pub struct ActiveCamera<'d> {
530 cam: Camera<'d>,
531 request_completed_handle: *mut libcamera_callback_handle_t,
533 buffer_completed_handle: *mut libcamera_callback_handle_t,
535 disconnected_handle: *mut libcamera_callback_handle_t,
537 state: Box<Mutex<ActiveCameraState<'d>>>,
539 _token: Option<std::sync::Arc<()>>,
540}
541
542#[derive(Debug, Clone, Copy)]
544pub struct BufferCompletedEvent {
545 pub stream: Stream,
546 pub request_cookie: u64,
547 pub request_sequence: u32,
548 pub buffer_ptr: usize,
549}
550
551impl<'d> ActiveCamera<'d> {
552 pub(crate) unsafe fn from_ptr_with_token(
553 ptr: NonNull<libcamera_camera_t>,
554 token: Option<std::sync::Arc<()>>,
555 ) -> Self {
556 let mut state = Box::new(Mutex::new(ActiveCameraState::default()));
557
558 let request_completed_handle = unsafe {
559 libcamera_camera_request_completed_connect(
560 ptr.as_ptr(),
561 Some(camera_request_completed_cb),
562 state.as_mut() as *mut Mutex<ActiveCameraState<'_>> as *mut _,
564 )
565 };
566
567 Self {
568 cam: Camera::from_ptr(ptr),
569 request_completed_handle,
570 buffer_completed_handle: core::ptr::null_mut(),
571 disconnected_handle: core::ptr::null_mut(),
572 state,
573 _token: token,
574 }
575 }
576
577 pub fn on_request_completed(&mut self, cb: impl FnMut(Request) + Send + 'd) {
585 let mut state = self.state.lock().unwrap();
586 state.request_completed_cb = Some(Box::new(cb));
587 }
588
589 pub fn on_buffer_completed(&mut self, cb: impl FnMut(&mut Request, Stream) + Send + 'd) {
594 {
595 let mut state = self.state.lock().unwrap();
596 state.buffer_completed_cb = Some(Box::new(cb));
597 }
598 if self.buffer_completed_handle.is_null() {
599 let data = self.state.as_mut() as *mut Mutex<ActiveCameraState<'_>> as *mut _;
600 self.buffer_completed_handle = unsafe {
601 libcamera_camera_buffer_completed_connect(self.ptr.as_ptr(), Some(camera_buffer_completed_cb), data)
602 };
603 }
604 }
605
606 pub fn subscribe_request_completed(&mut self) -> std::sync::mpsc::Receiver<Request> {
610 let (tx, rx) = std::sync::mpsc::channel();
611 self.on_request_completed(move |req| {
612 let _ = tx.send(req);
613 });
614 rx
615 }
616
617 pub fn subscribe_buffer_completed(&mut self) -> std::sync::mpsc::Receiver<BufferCompletedEvent> {
622 let (tx, rx) = std::sync::mpsc::channel();
623 self.on_buffer_completed(move |req, stream| {
624 let event = BufferCompletedEvent {
625 stream,
626 request_cookie: req.cookie(),
627 request_sequence: req.sequence(),
628 buffer_ptr: req.find_buffer(&stream).map_or(0, |p| p as usize),
629 };
630 let _ = tx.send(event);
631 });
632 rx
633 }
634
635 pub fn on_disconnected(&mut self, cb: impl FnMut() + Send + 'd) {
637 {
638 let mut state = self.state.lock().unwrap();
639 state.disconnected_cb = Some(Box::new(cb));
640 }
641 if self.disconnected_handle.is_null() {
642 let data = self.state.as_mut() as *mut Mutex<ActiveCameraState<'_>> as *mut _;
643 self.disconnected_handle =
644 unsafe { libcamera_camera_disconnected_connect(self.ptr.as_ptr(), Some(camera_disconnected_cb), data) };
645 }
646 }
647
648 pub fn configure(&mut self, config: &mut CameraConfiguration) -> io::Result<()> {
652 let ret = unsafe { libcamera_camera_configure(self.ptr.as_ptr(), config.ptr.as_ptr()) };
653 if ret < 0 {
654 Err(io::Error::from_raw_os_error(-ret))
655 } else {
656 Ok(())
657 }
658 }
659
660 pub fn create_request(&mut self, cookie: Option<u64>) -> Option<Request> {
670 let req = unsafe { libcamera_camera_create_request(self.ptr.as_ptr(), cookie.unwrap_or(0)) };
671 NonNull::new(req).map(|p| unsafe { Request::from_ptr(p) })
672 }
673
674 pub fn queue_request(&self, req: Request) -> Result<(), (Request, io::Error)> {
679 let ptr = req.ptr.as_ptr();
680 let mut pending = Some(req);
682 let ret = unsafe { libcamera_camera_queue_request(self.ptr.as_ptr(), ptr) };
683
684 if ret < 0 {
685 Err((pending.take().unwrap(), io::Error::from_raw_os_error(-ret)))
686 } else {
687 self.state.lock().unwrap().requests.insert(ptr, pending.take().unwrap());
688 Ok(())
689 }
690 }
691
692 pub fn start(&mut self, controls: Option<&ControlList>) -> io::Result<()> {
696 let ctrl_ptr = controls.map(|c| c.ptr()).unwrap_or(core::ptr::null_mut());
697 let ret = unsafe { libcamera_camera_start(self.ptr.as_ptr(), ctrl_ptr) };
698 if ret < 0 {
699 Err(io::Error::from_raw_os_error(-ret))
700 } else {
701 Ok(())
702 }
703 }
704
705 pub fn stop(&mut self) -> io::Result<()> {
709 let ret = unsafe { libcamera_camera_stop(self.ptr.as_ptr()) };
710 if ret < 0 {
711 Err(io::Error::from_raw_os_error(-ret))
712 } else {
713 Ok(())
714 }
715 }
716}
717
718impl<'d> Deref for ActiveCamera<'d> {
719 type Target = Camera<'d>;
720
721 fn deref(&self) -> &Self::Target {
722 &self.cam
723 }
724}
725
726impl DerefMut for ActiveCamera<'_> {
727 fn deref_mut(&mut self) -> &mut Self::Target {
728 &mut self.cam
729 }
730}
731
732impl Drop for ActiveCamera<'_> {
733 fn drop(&mut self) {
734 unsafe {
735 libcamera_camera_request_completed_disconnect(self.ptr.as_ptr(), self.request_completed_handle);
736 if !self.buffer_completed_handle.is_null() {
737 libcamera_camera_buffer_completed_disconnect(self.ptr.as_ptr(), self.buffer_completed_handle);
738 }
739 if !self.disconnected_handle.is_null() {
740 libcamera_camera_disconnected_disconnect(self.ptr.as_ptr(), self.disconnected_handle);
741 }
742 libcamera_camera_stop(self.ptr.as_ptr());
743 libcamera_camera_release(self.ptr.as_ptr());
744 }
745 }
746}