libcamera/
camera.rs

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    control::{ControlInfoMap, ControlList, PropertyList},
15    request::Request,
16    stream::{StreamConfigurationRef, StreamRole},
17    utils::Immutable,
18};
19
20/// Status of [CameraConfiguration]
21#[derive(Debug, Clone, Copy)]
22pub enum CameraConfigurationStatus {
23    /// Camera configuration was validated without issues.
24    Valid,
25    /// Camera configuration is valid, but some of the fields were adjusted by libcamera.
26    Adjusted,
27    /// Camera configuration is invalid.
28    Invalid,
29}
30
31impl CameraConfigurationStatus {
32    pub fn is_valid(&self) -> bool {
33        matches!(self, Self::Valid)
34    }
35
36    pub fn is_adjusted(&self) -> bool {
37        matches!(self, Self::Adjusted)
38    }
39
40    pub fn is_invalid(&self) -> bool {
41        matches!(self, Self::Invalid)
42    }
43}
44
45impl TryFrom<libcamera_camera_configuration_status_t> for CameraConfigurationStatus {
46    type Error = ();
47
48    fn try_from(value: libcamera_camera_configuration_status_t) -> Result<Self, Self::Error> {
49        match value {
50            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_VALID => Ok(Self::Valid),
51            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_ADJUSTED => Ok(Self::Adjusted),
52            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_INVALID => Ok(Self::Invalid),
53            _ => Err(()),
54        }
55    }
56}
57
58/// Camera configuration.
59///
60/// Contains [StreamConfigurationRef] for each stream used by the camera.
61pub struct CameraConfiguration {
62    ptr: NonNull<libcamera_camera_configuration_t>,
63}
64
65impl CameraConfiguration {
66    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_configuration_t>) -> Self {
67        Self { ptr }
68    }
69
70    /// Returns immutable [StreamConfigurationRef] for the camera stream.
71    ///
72    /// # Parameters
73    ///
74    /// * `index` - Camera stream index.
75    pub fn get(&self, index: usize) -> Option<Immutable<StreamConfigurationRef<'_>>> {
76        let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
77        NonNull::new(ptr).map(|p| Immutable(unsafe { StreamConfigurationRef::from_ptr(p) }))
78    }
79
80    /// Returns mutable [StreamConfigurationRef] for the camera stream.
81    ///
82    /// # Parameters
83    ///
84    /// * `index` - Camera stream index.
85    pub fn get_mut(&mut self, index: usize) -> Option<StreamConfigurationRef<'_>> {
86        let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
87        NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) })
88    }
89
90    /// Returns number of streams within camera configuration.
91    pub fn len(&self) -> usize {
92        unsafe { libcamera_camera_configuration_size(self.ptr.as_ptr()) }
93    }
94
95    /// Returns `true` if camera configuration has no streams.
96    pub fn is_empty(&self) -> bool {
97        self.len() == 0
98    }
99
100    /// Validates camera configuration.
101    pub fn validate(&mut self) -> CameraConfigurationStatus {
102        unsafe { libcamera_camera_configuration_validate(self.ptr.as_ptr()) }
103            .try_into()
104            .unwrap()
105    }
106}
107
108impl core::fmt::Debug for CameraConfiguration {
109    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        let mut list = f.debug_list();
111        for i in 0..self.len() {
112            list.entry(&self.get(i).unwrap().0);
113        }
114        list.finish()
115    }
116}
117
118impl Drop for CameraConfiguration {
119    fn drop(&mut self) {
120        unsafe { libcamera_camera_configuration_destroy(self.ptr.as_ptr()) }
121    }
122}
123
124/// A read-only instance of a camera.
125///
126/// Can be used to obtain camera parameters or supported stream configurations.
127/// In order to be used for capturing, it must be turned into an [ActiveCamera] by [Camera::acquire()].
128pub struct Camera<'d> {
129    pub(crate) ptr: NonNull<libcamera_camera_t>,
130    _phantom: PhantomData<&'d ()>,
131}
132
133impl Camera<'_> {
134    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_t>) -> Self {
135        Self {
136            ptr,
137            _phantom: Default::default(),
138        }
139    }
140
141    /// ID of the camera.
142    ///
143    /// This usually contains hardware path within the system and is not human-friendly.
144    /// Use [properties::Model](crate::properties::Model) from [Camera::properties()] to obtain a human readable
145    /// identification instead.
146    pub fn id(&self) -> &str {
147        unsafe { CStr::from_ptr(libcamera_camera_id(self.ptr.as_ptr())) }
148            .to_str()
149            .unwrap()
150    }
151
152    /// Returns a list of available camera controls and their limit.
153    pub fn controls(&self) -> &ControlInfoMap {
154        unsafe {
155            ControlInfoMap::from_ptr(NonNull::new(libcamera_camera_controls(self.ptr.as_ptr()).cast_mut()).unwrap())
156        }
157    }
158
159    /// Returns a list of camera properties.
160    ///
161    /// See [properties](crate::properties) for available items.
162    pub fn properties(&self) -> &PropertyList {
163        unsafe {
164            PropertyList::from_ptr(NonNull::new(libcamera_camera_properties(self.ptr.as_ptr()).cast_mut()).unwrap())
165        }
166    }
167
168    /// Generates default camera configuration for the given [StreamRole]s.
169    ///
170    /// The resulting [CameraConfiguration] contains stream configurations for each of the requested roles.
171    ///
172    /// Generated configuration can be adjusted as needed and then passed onto [ActiveCamera::configure()] to apply.
173    pub fn generate_configuration(&self, roles: &[StreamRole]) -> Option<CameraConfiguration> {
174        let roles: Vec<libcamera_stream_role::Type> = roles.iter().map(|r| (*r).into()).collect();
175        let cfg =
176            unsafe { libcamera_camera_generate_configuration(self.ptr.as_ptr(), roles.as_ptr(), roles.len() as _) };
177        NonNull::new(cfg).map(|p| unsafe { CameraConfiguration::from_ptr(p) })
178    }
179
180    /// Acquires exclusive rights to the camera, which allows changing configuration and capturing.
181    pub fn acquire(&self) -> io::Result<ActiveCamera<'_>> {
182        let ret = unsafe { libcamera_camera_acquire(self.ptr.as_ptr()) };
183        if ret < 0 {
184            Err(io::Error::from_raw_os_error(ret))
185        } else {
186            Ok(unsafe { ActiveCamera::from_ptr(NonNull::new(libcamera_camera_copy(self.ptr.as_ptr())).unwrap()) })
187        }
188    }
189}
190
191impl Drop for Camera<'_> {
192    fn drop(&mut self) {
193        unsafe { libcamera_camera_destroy(self.ptr.as_ptr()) }
194    }
195}
196
197extern "C" fn camera_request_completed_cb(ptr: *mut core::ffi::c_void, req: *mut libcamera_request_t) {
198    let mut state = unsafe { &*(ptr as *const Mutex<ActiveCameraState<'_>>) }
199        .lock()
200        .unwrap();
201    let req = state.requests.remove(&req).unwrap();
202
203    if let Some(cb) = &mut state.request_completed_cb {
204        cb(req);
205    }
206}
207
208#[derive(Default)]
209struct ActiveCameraState<'d> {
210    /// List of queued requests that are yet to be executed.
211    /// Used to temporarily store [Request] before returning it back to the user.
212    requests: HashMap<*mut libcamera_request_t, Request>,
213    /// Callback for libcamera `requestCompleted` signal.
214    request_completed_cb: Option<Box<dyn FnMut(Request) + Send + 'd>>,
215}
216
217/// An active instance of a camera.
218///
219/// This gives exclusive access to the camera and allows capturing and modifying configuration.
220///
221/// Obtained by [Camera::acquire()].
222pub struct ActiveCamera<'d> {
223    cam: Camera<'d>,
224    /// Handle to disconnect `requestCompleted` signal.
225    request_completed_handle: *mut libcamera_callback_handle_t,
226    /// Internal state that is shared with callback handlers.
227    state: Box<Mutex<ActiveCameraState<'d>>>,
228}
229
230impl<'d> ActiveCamera<'d> {
231    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_t>) -> Self {
232        let mut state = Box::new(Mutex::new(ActiveCameraState::default()));
233
234        let request_completed_handle = unsafe {
235            libcamera_camera_request_completed_connect(
236                ptr.as_ptr(),
237                Some(camera_request_completed_cb),
238                // state is valid for the lifetime of `ActiveCamera` and this callback will be disconnected on drop.
239                state.as_mut() as *mut Mutex<ActiveCameraState<'_>> as *mut _,
240            )
241        };
242
243        Self {
244            cam: Camera::from_ptr(ptr),
245            request_completed_handle,
246            state,
247        }
248    }
249
250    /// Sets a callback for completed camera requests.
251    ///
252    /// Callback is executed in the libcamera thread context so it is best to setup a channel to send all requests for
253    /// processing elsewhere.
254    ///
255    /// Only one callback can be set at a time. If there was a previously set callback, it will be discarded when
256    /// setting a new one.
257    pub fn on_request_completed(&mut self, cb: impl FnMut(Request) + Send + 'd) {
258        let mut state = self.state.lock().unwrap();
259        state.request_completed_cb = Some(Box::new(cb));
260    }
261
262    /// Applies camera configuration.
263    ///
264    /// Default configuration can be obtained from [Camera::generate_configuration()] and then adjusted as needed.
265    pub fn configure(&mut self, config: &mut CameraConfiguration) -> io::Result<()> {
266        let ret = unsafe { libcamera_camera_configure(self.ptr.as_ptr(), config.ptr.as_ptr()) };
267        if ret < 0 {
268            Err(io::Error::from_raw_os_error(ret))
269        } else {
270            Ok(())
271        }
272    }
273
274    /// Creates a capture [`Request`].
275    ///
276    /// To perform a capture, it must firstly be initialized by attaching a framebuffer with [Request::add_buffer()] and
277    /// then queued for execution by [ActiveCamera::queue_request()].
278    ///
279    /// # Arguments
280    ///
281    /// * `cookie` - An optional user-provided u64 identifier that can be used to uniquely identify request in request
282    ///   completed callback.
283    pub fn create_request(&mut self, cookie: Option<u64>) -> Option<Request> {
284        let req = unsafe { libcamera_camera_create_request(self.ptr.as_ptr(), cookie.unwrap_or(0)) };
285        NonNull::new(req).map(|p| unsafe { Request::from_ptr(p) })
286    }
287
288    /// Queues [`Request`] for execution. Completed requests are returned in request completed callback, set by the
289    /// `ActiveCamera::on_request_completed()`.
290    ///
291    /// Requests that do not have attached framebuffers are invalid and are rejected without being queued.
292    pub fn queue_request(&self, req: Request) -> io::Result<()> {
293        let ptr = req.ptr.as_ptr();
294        self.state.lock().unwrap().requests.insert(ptr, req);
295
296        let ret = unsafe { libcamera_camera_queue_request(self.ptr.as_ptr(), ptr) };
297
298        if ret < 0 {
299            Err(io::Error::from_raw_os_error(ret))
300        } else {
301            Ok(())
302        }
303    }
304
305    /// Starts camera capture session.
306    ///
307    /// Once started, [ActiveCamera::queue_request()] is permitted and camera configuration can no longer be changed.
308    pub fn start(&mut self, controls: Option<&ControlList>) -> io::Result<()> {
309        let ctrl_ptr = controls.map(|c| c.ptr()).unwrap_or(core::ptr::null_mut());
310        let ret = unsafe { libcamera_camera_start(self.ptr.as_ptr(), ctrl_ptr) };
311        if ret < 0 {
312            Err(io::Error::from_raw_os_error(ret))
313        } else {
314            Ok(())
315        }
316    }
317
318    /// Stops camera capture session.
319    ///
320    /// Once stopped, [ActiveCamera::queue_request()] is no longer permitted and camera configuration can be adjusted.
321    pub fn stop(&mut self) -> io::Result<()> {
322        let ret = unsafe { libcamera_camera_stop(self.ptr.as_ptr()) };
323        if ret < 0 {
324            Err(io::Error::from_raw_os_error(ret))
325        } else {
326            Ok(())
327        }
328    }
329}
330
331impl<'d> Deref for ActiveCamera<'d> {
332    type Target = Camera<'d>;
333
334    fn deref(&self) -> &Self::Target {
335        &self.cam
336    }
337}
338
339impl DerefMut for ActiveCamera<'_> {
340    fn deref_mut(&mut self) -> &mut Self::Target {
341        &mut self.cam
342    }
343}
344
345impl Drop for ActiveCamera<'_> {
346    fn drop(&mut self) {
347        unsafe {
348            libcamera_camera_request_completed_disconnect(self.ptr.as_ptr(), self.request_completed_handle);
349            libcamera_camera_stop(self.ptr.as_ptr());
350            libcamera_camera_release(self.ptr.as_ptr());
351        }
352    }
353}