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