1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
use std::{
    collections::HashMap,
    ffi::CStr,
    io,
    marker::PhantomData,
    ops::{Deref, DerefMut},
    ptr::NonNull,
    sync::Mutex,
};

use libcamera_sys::*;

use crate::{
    control::{ControlInfoMap, ControlList, PropertyList},
    request::Request,
    stream::{StreamConfigurationRef, StreamRole},
    utils::Immutable,
};

/// Status of [CameraConfiguration]
#[derive(Debug, Clone, Copy)]
pub enum CameraConfigurationStatus {
    /// Camera configuration was validated without issues.
    Valid,
    /// Camera configuration is valid, but some of the fields were adjusted by libcamera.
    Adjusted,
    /// Camera configuration is invalid.
    Invalid,
}

impl CameraConfigurationStatus {
    pub fn is_valid(&self) -> bool {
        matches!(self, Self::Valid)
    }

    pub fn is_adjusted(&self) -> bool {
        matches!(self, Self::Adjusted)
    }

    pub fn is_invalid(&self) -> bool {
        matches!(self, Self::Invalid)
    }
}

impl TryFrom<libcamera_camera_configuration_status_t> for CameraConfigurationStatus {
    type Error = ();

    fn try_from(value: libcamera_camera_configuration_status_t) -> Result<Self, Self::Error> {
        match value {
            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_VALID => Ok(Self::Valid),
            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_ADJUSTED => Ok(Self::Adjusted),
            libcamera_camera_configuration_status::LIBCAMERA_CAMERA_CONFIGURATION_STATUS_INVALID => Ok(Self::Invalid),
            _ => Err(()),
        }
    }
}

/// Camera configuration.
///
/// Contains [StreamConfigurationRef] for each stream used by the camera.
pub struct CameraConfiguration {
    ptr: NonNull<libcamera_camera_configuration_t>,
}

impl CameraConfiguration {
    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_configuration_t>) -> Self {
        Self { ptr }
    }

    /// Returns immutable [StreamConfigurationRef] for the camera stream.
    ///
    /// # Parameters
    ///
    /// * `index` - Camera stream index.
    pub fn get(&self, index: usize) -> Option<Immutable<StreamConfigurationRef<'_>>> {
        let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
        NonNull::new(ptr).map(|p| Immutable(unsafe { StreamConfigurationRef::from_ptr(p) }))
    }

    /// Returns mutable [StreamConfigurationRef] for the camera stream.
    ///
    /// # Parameters
    ///
    /// * `index` - Camera stream index.
    pub fn get_mut(&mut self, index: usize) -> Option<StreamConfigurationRef<'_>> {
        let ptr = unsafe { libcamera_camera_configuration_at(self.ptr.as_ptr(), index as _) };
        NonNull::new(ptr).map(|p| unsafe { StreamConfigurationRef::from_ptr(p) })
    }

    /// Returns number of streams within camera configuration.
    pub fn len(&self) -> usize {
        unsafe { libcamera_camera_configuration_size(self.ptr.as_ptr()) }
    }

    /// Returns `true` if camera configuration has no streams.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Validates camera configuration.
    pub fn validate(&mut self) -> CameraConfigurationStatus {
        unsafe { libcamera_camera_configuration_validate(self.ptr.as_ptr()) }
            .try_into()
            .unwrap()
    }
}

impl core::fmt::Debug for CameraConfiguration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut list = f.debug_list();
        for i in 0..self.len() {
            list.entry(&self.get(i).unwrap().0);
        }
        list.finish()
    }
}

impl Drop for CameraConfiguration {
    fn drop(&mut self) {
        unsafe { libcamera_camera_configuration_destroy(self.ptr.as_ptr()) }
    }
}

/// A read-only instance of a camera.
///
/// Can be used to obtain camera parameters or supported stream configurations.
/// In order to be used for capturing, it must be turned into an [ActiveCamera] by [Camera::acquire()].
pub struct Camera<'d> {
    pub(crate) ptr: NonNull<libcamera_camera_t>,
    _phantom: PhantomData<&'d ()>,
}

impl<'d> Camera<'d> {
    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_t>) -> Self {
        Self {
            ptr,
            _phantom: Default::default(),
        }
    }

    /// ID of the camera.
    ///
    /// This usually contains hardware path within the system and is not human-friendly.
    /// Use [properties::Model](crate::properties::Model) from [Camera::properties()] to obtain a human readable
    /// identification instead.
    pub fn id(&self) -> &str {
        unsafe { CStr::from_ptr(libcamera_camera_id(self.ptr.as_ptr())) }
            .to_str()
            .unwrap()
    }

    /// Returns a list of available camera controls and their limit.
    pub fn controls(&self) -> &ControlInfoMap {
        unsafe {
            ControlInfoMap::from_ptr(NonNull::new(libcamera_camera_controls(self.ptr.as_ptr()).cast_mut()).unwrap())
        }
    }

    /// Returns a list of camera properties.
    ///
    /// See [properties](crate::properties) for available items.
    pub fn properties(&self) -> &PropertyList {
        unsafe {
            PropertyList::from_ptr(NonNull::new(libcamera_camera_properties(self.ptr.as_ptr()).cast_mut()).unwrap())
        }
    }

    /// Generates default camera configuration for the given [StreamRole]s.
    ///
    /// The resulting [CameraConfiguration] contains stream configurations for each of the requested roles.
    ///
    /// Generated configuration can be adjusted as needed and then passed onto [ActiveCamera::configure()] to apply.
    pub fn generate_configuration(&self, roles: &[StreamRole]) -> Option<CameraConfiguration> {
        let roles: Vec<libcamera_stream_role::Type> = roles.iter().map(|r| (*r).into()).collect();
        let cfg =
            unsafe { libcamera_camera_generate_configuration(self.ptr.as_ptr(), roles.as_ptr(), roles.len() as _) };
        NonNull::new(cfg).map(|p| unsafe { CameraConfiguration::from_ptr(p) })
    }

    /// Acquires exclusive rights to the camera, which allows changing configuration and capturing.
    pub fn acquire(&self) -> io::Result<ActiveCamera<'_>> {
        let ret = unsafe { libcamera_camera_acquire(self.ptr.as_ptr()) };
        if ret < 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            Ok(unsafe { ActiveCamera::from_ptr(NonNull::new(libcamera_camera_copy(self.ptr.as_ptr())).unwrap()) })
        }
    }
}

impl<'d> Drop for Camera<'d> {
    fn drop(&mut self) {
        unsafe { libcamera_camera_destroy(self.ptr.as_ptr()) }
    }
}

extern "C" fn camera_request_completed_cb(ptr: *mut core::ffi::c_void, req: *mut libcamera_request_t) {
    let mut state = unsafe { &*(ptr as *const Mutex<ActiveCameraState<'_>>) }
        .lock()
        .unwrap();
    let req = state.requests.remove(&req).unwrap();

    if let Some(cb) = &mut state.request_completed_cb {
        cb(req);
    }
}

#[derive(Default)]
struct ActiveCameraState<'d> {
    /// List of queued requests that are yet to be executed.
    /// Used to temporarily store [Request] before returning it back to the user.
    requests: HashMap<*mut libcamera_request_t, Request>,
    /// Callback for libcamera `requestCompleted` signal.
    request_completed_cb: Option<Box<dyn FnMut(Request) + Send + 'd>>,
}

/// An active instance of a camera.
///
/// This gives exclusive access to the camera and allows capturing and modifying configuration.
///
/// Obtained by [Camera::acquire()].
pub struct ActiveCamera<'d> {
    cam: Camera<'d>,
    /// Handle to disconnect `requestCompleted` signal.
    request_completed_handle: *mut libcamera_callback_handle_t,
    /// Internal state that is shared with callback handlers.
    state: Box<Mutex<ActiveCameraState<'d>>>,
}

impl<'d> ActiveCamera<'d> {
    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_camera_t>) -> Self {
        let mut state = Box::new(Mutex::new(ActiveCameraState::default()));

        let request_completed_handle = unsafe {
            libcamera_camera_request_completed_connect(
                ptr.as_ptr(),
                Some(camera_request_completed_cb),
                // state is valid for the lifetime of `ActiveCamera` and this callback will be disconnected on drop.
                state.as_mut() as *mut Mutex<ActiveCameraState<'_>> as *mut _,
            )
        };

        Self {
            cam: Camera::from_ptr(ptr),
            request_completed_handle,
            state,
        }
    }

    /// Sets a callback for completed camera requests.
    ///
    /// Callback is executed in the libcamera thread context so it is best to setup a channel to send all requests for
    /// processing elsewhere.
    ///
    /// Only one callback can be set at a time. If there was a previously set callback, it will be discarded when
    /// setting a new one.
    pub fn on_request_completed(&mut self, cb: impl FnMut(Request) + Send + 'd) {
        let mut state = self.state.lock().unwrap();
        state.request_completed_cb = Some(Box::new(cb));
    }

    /// Applies camera configuration.
    ///
    /// Default configuration can be obtained from [Camera::generate_configuration()] and then adjusted as needed.
    pub fn configure(&mut self, config: &mut CameraConfiguration) -> io::Result<()> {
        let ret = unsafe { libcamera_camera_configure(self.ptr.as_ptr(), config.ptr.as_ptr()) };
        if ret < 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            Ok(())
        }
    }

    /// Creates a capture [`Request`].
    ///
    /// To perform a capture, it must firstly be initialized by attaching a framebuffer with [Request::add_buffer()] and
    /// then queued for execution by [ActiveCamera::queue_request()].
    ///
    /// # Arguments
    ///
    /// * `cookie` - An optional user-provided u64 identifier that can be used to uniquely identify request in request
    ///   completed callback.
    pub fn create_request(&mut self, cookie: Option<u64>) -> Option<Request> {
        let req = unsafe { libcamera_camera_create_request(self.ptr.as_ptr(), cookie.unwrap_or(0)) };
        NonNull::new(req).map(|p| unsafe { Request::from_ptr(p) })
    }

    /// Queues [`Request`] for execution. Completed requests are returned in request completed callback, set by the
    /// `ActiveCamera::on_request_completed()`.
    ///
    /// Requests that do not have attached framebuffers are invalid and are rejected without being queued.
    pub fn queue_request(&self, req: Request) -> io::Result<()> {
        let ptr = req.ptr.as_ptr();
        self.state.lock().unwrap().requests.insert(ptr, req);

        let ret = unsafe { libcamera_camera_queue_request(self.ptr.as_ptr(), ptr) };

        if ret < 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            Ok(())
        }
    }

    /// Starts camera capture session.
    ///
    /// Once started, [ActiveCamera::queue_request()] is permitted and camera configuration can no longer be changed.
    pub fn start(&mut self, controls: Option<&ControlList>) -> io::Result<()> {
        let ctrl_ptr = controls.map(|c| c.ptr()).unwrap_or(core::ptr::null_mut());
        let ret = unsafe { libcamera_camera_start(self.ptr.as_ptr(), ctrl_ptr) };
        if ret < 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            Ok(())
        }
    }

    /// Stops camera capture session.
    ///
    /// Once stopped, [ActiveCamera::queue_request()] is no longer permitted and camera configuration can be adjusted.
    pub fn stop(&mut self) -> io::Result<()> {
        let ret = unsafe { libcamera_camera_stop(self.ptr.as_ptr()) };
        if ret < 0 {
            Err(io::Error::from_raw_os_error(ret))
        } else {
            Ok(())
        }
    }
}

impl<'d> Deref for ActiveCamera<'d> {
    type Target = Camera<'d>;

    fn deref(&self) -> &Self::Target {
        &self.cam
    }
}

impl<'d> DerefMut for ActiveCamera<'d> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.cam
    }
}

impl<'d> Drop for ActiveCamera<'d> {
    fn drop(&mut self) {
        unsafe {
            libcamera_camera_request_completed_disconnect(self.ptr.as_ptr(), self.request_completed_handle);
            libcamera_camera_stop(self.ptr.as_ptr());
            libcamera_camera_release(self.ptr.as_ptr());
        }
    }
}