libcamera/
framebuffer.rs

1use std::{
2    io,
3    marker::PhantomData,
4    os::fd::{IntoRawFd, OwnedFd},
5    ptr::NonNull,
6};
7
8use libcamera_sys::*;
9use num_enum::{IntoPrimitive, TryFromPrimitive};
10
11use crate::{fence::Fence, utils::Immutable};
12
13#[derive(Debug, Clone, Copy, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)]
14#[repr(u32)]
15pub enum FrameMetadataStatus {
16    Success = libcamera_frame_metadata_status::LIBCAMERA_FRAME_METADATA_STATUS_SUCCESS,
17    Error = libcamera_frame_metadata_status::LIBCAMERA_FRAME_METADATA_STATUS_ERROR,
18    Cancelled = libcamera_frame_metadata_status::LIBCAMERA_FRAME_METADATA_STATUS_CANCELLED,
19    Startup = libcamera_frame_metadata_status::LIBCAMERA_FRAME_METADATA_STATUS_STARTUP,
20}
21
22pub type FrameMetadataPlane = libcamera_frame_metadata_plane_t;
23
24pub struct FrameMetadataPlanes {
25    pub(crate) ptr: NonNull<libcamera_frame_metadata_planes_t>,
26}
27
28impl FrameMetadataPlanes {
29    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_frame_metadata_planes_t>) -> Self {
30        Self { ptr }
31    }
32
33    /// Number of planes within framebuffer metadata.
34    ///
35    /// Should be consistent with other planes within framebuffer.
36    pub fn len(&self) -> usize {
37        unsafe { libcamera_frame_metadata_planes_size(self.ptr.as_ptr()) as _ }
38    }
39
40    /// Returns `true` if there are no planes.
41    pub fn is_empty(&self) -> bool {
42        self.len() == 0
43    }
44
45    /// Returns framebuffer plane metadata at a given index.
46    ///
47    /// Return None if given index is out of range of available planes.
48    pub fn get(&self, index: usize) -> Option<FrameMetadataPlane> {
49        if index >= self.len() {
50            None
51        } else {
52            Some(unsafe { libcamera_frame_metadata_planes_at(self.ptr.as_ptr(), index as _).read() })
53        }
54    }
55}
56
57impl core::fmt::Debug for FrameMetadataPlanes {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        let mut list = f.debug_list();
60        for plane in self.into_iter() {
61            list.entry(&plane);
62        }
63        list.finish()
64    }
65}
66
67impl Drop for FrameMetadataPlanes {
68    fn drop(&mut self) {
69        unsafe { libcamera_frame_metadata_planes_destroy(self.ptr.as_ptr()) }
70    }
71}
72
73impl<'d> IntoIterator for &'d FrameMetadataPlanes {
74    type Item = FrameMetadataPlane;
75
76    type IntoIter = FrameMetadataPlanesIterator<'d>;
77
78    fn into_iter(self) -> Self::IntoIter {
79        FrameMetadataPlanesIterator { planes: self, index: 0 }
80    }
81}
82
83pub struct FrameMetadataPlanesIterator<'d> {
84    planes: &'d FrameMetadataPlanes,
85    index: usize,
86}
87
88impl Iterator for FrameMetadataPlanesIterator<'_> {
89    type Item = FrameMetadataPlane;
90
91    fn next(&mut self) -> Option<Self::Item> {
92        if let Some(plane) = self.planes.get(self.index) {
93            self.index += 1;
94            Some(plane)
95        } else {
96            None
97        }
98    }
99}
100
101pub struct FrameMetadataRef<'d> {
102    pub(crate) ptr: NonNull<libcamera_frame_metadata_t>,
103    _phantom: PhantomData<&'d ()>,
104}
105
106impl FrameMetadataRef<'_> {
107    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_frame_metadata_t>) -> Self {
108        Self {
109            ptr,
110            _phantom: Default::default(),
111        }
112    }
113
114    pub fn status(&self) -> FrameMetadataStatus {
115        FrameMetadataStatus::try_from(unsafe { libcamera_frame_metadata_status(self.ptr.as_ptr()) }).unwrap()
116    }
117
118    pub fn sequence(&self) -> u32 {
119        unsafe { libcamera_frame_metadata_sequence(self.ptr.as_ptr()) }
120    }
121
122    pub fn timestamp(&self) -> u64 {
123        unsafe { libcamera_frame_metadata_timestamp(self.ptr.as_ptr()) }
124    }
125
126    pub fn planes(&self) -> FrameMetadataPlanes {
127        unsafe {
128            FrameMetadataPlanes::from_ptr(NonNull::new(libcamera_frame_metadata_planes(self.ptr.as_ptr())).unwrap())
129        }
130    }
131}
132
133impl core::fmt::Debug for FrameMetadataRef<'_> {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("FrameMetadataRef")
136            .field("status", &self.status())
137            .field("sequence", &self.sequence())
138            .field("timestamp", &self.timestamp())
139            .field("planes", &self.planes())
140            .finish()
141    }
142}
143
144pub struct FrameBufferPlaneRef<'d> {
145    pub(crate) ptr: NonNull<libcamera_framebuffer_plane_t>,
146    _phantom: PhantomData<&'d ()>,
147}
148
149impl FrameBufferPlaneRef<'_> {
150    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_framebuffer_plane_t>) -> Self {
151        Self {
152            ptr,
153            _phantom: Default::default(),
154        }
155    }
156
157    /// File descriptor to the framebuffer plane data.
158    ///
159    /// Multiple planes may point to the same file descriptor at different offsets.
160    pub fn fd(&self) -> i32 {
161        unsafe { libcamera_framebuffer_plane_fd(self.ptr.as_ptr()) }
162    }
163
164    /// Offset of data within the file descriptor.
165    pub fn offset(&self) -> Option<usize> {
166        if unsafe { libcamera_framebuffer_plane_offset_valid(self.ptr.as_ptr()) } {
167            Some(unsafe { libcamera_framebuffer_plane_offset(self.ptr.as_ptr()) as _ })
168        } else {
169            None
170        }
171    }
172
173    /// Data length of the plane in bytes
174    pub fn len(&self) -> usize {
175        unsafe { libcamera_framebuffer_plane_length(self.ptr.as_ptr()) as _ }
176    }
177
178    /// Returns `true` if plane has no data
179    pub fn is_empty(&self) -> bool {
180        self.len() == 0
181    }
182}
183
184impl core::fmt::Debug for FrameBufferPlaneRef<'_> {
185    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186        f.debug_struct("FrameBufferPlaneRef")
187            .field("fd", &self.fd())
188            .field("offset", &self.offset())
189            .field("len", &self.len())
190            .finish()
191    }
192}
193
194pub struct FrameBufferPlanesRef<'d> {
195    pub(crate) ptr: NonNull<libcamera_framebuffer_planes_t>,
196    _phantom: PhantomData<&'d ()>,
197}
198
199impl FrameBufferPlanesRef<'_> {
200    pub(crate) unsafe fn from_ptr(ptr: NonNull<libcamera_framebuffer_planes_t>) -> Self {
201        Self {
202            ptr,
203            _phantom: Default::default(),
204        }
205    }
206
207    /// Number of planes within framebuffer
208    pub fn len(&self) -> usize {
209        unsafe { libcamera_framebuffer_planes_size(self.ptr.as_ptr()) as _ }
210    }
211
212    /// Returns `true` if framebuffer has no planes
213    pub fn is_empty(&self) -> bool {
214        self.len() == 0
215    }
216
217    /// Returns framebuffer plane at a given index
218    pub fn get(&self, index: usize) -> Option<Immutable<FrameBufferPlaneRef<'_>>> {
219        if index >= self.len() {
220            None
221        } else {
222            Some(Immutable(unsafe {
223                FrameBufferPlaneRef::from_ptr(
224                    NonNull::new(libcamera_framebuffer_planes_at(self.ptr.as_ptr(), index as _)).unwrap(),
225                )
226            }))
227        }
228    }
229}
230
231impl core::fmt::Debug for FrameBufferPlanesRef<'_> {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        let mut list = f.debug_list();
234        for plane in self.into_iter() {
235            list.entry(&plane);
236        }
237        list.finish()
238    }
239}
240
241impl Drop for FrameBufferPlanesRef<'_> {
242    fn drop(&mut self) {
243        unsafe { libcamera_framebuffer_planes_destroy(self.ptr.as_ptr()) }
244    }
245}
246
247impl<'d> IntoIterator for &'d FrameBufferPlanesRef<'d> {
248    type Item = Immutable<FrameBufferPlaneRef<'d>>;
249
250    type IntoIter = FrameBufferPlanesRefIterator<'d>;
251
252    fn into_iter(self) -> Self::IntoIter {
253        FrameBufferPlanesRefIterator { planes: self, index: 0 }
254    }
255}
256
257pub struct FrameBufferPlanesRefIterator<'d> {
258    planes: &'d FrameBufferPlanesRef<'d>,
259    index: usize,
260}
261
262impl<'d> Iterator for FrameBufferPlanesRefIterator<'d> {
263    type Item = Immutable<FrameBufferPlaneRef<'d>>;
264
265    fn next(&mut self) -> Option<Self::Item> {
266        if let Some(plane) = self.planes.get(self.index) {
267            self.index += 1;
268            Some(plane)
269        } else {
270            None
271        }
272    }
273}
274
275pub trait AsFrameBuffer: Send {
276    /// Returns raw framebuffer used by libcamera.
277    ///
278    /// It is expected that metadata status field is initialized with u32::MAX on a new buffer, which indicates that
279    /// metadata is not yet available. This "hackfix" prevents read of uninitialized data in [Self::metadata()].
280    ///
281    /// # Safety
282    ///
283    /// This function must return a valid instance of `libcamera::FrameBuffer`.
284    unsafe fn ptr(&self) -> NonNull<libcamera_framebuffer_t>;
285
286    /// Returns framebuffer metadata information.
287    ///
288    /// Only available after associated [Request](crate::request::Request) has completed.
289    fn metadata(&self) -> Option<Immutable<FrameMetadataRef<'_>>> {
290        let ptr = NonNull::new(unsafe { libcamera_framebuffer_metadata(self.ptr().as_ptr()) }.cast_mut()).unwrap();
291        if unsafe { libcamera_frame_metadata_status(ptr.as_ptr()) } != u32::MAX {
292            Some(unsafe { Immutable(FrameMetadataRef::from_ptr(ptr)) })
293        } else {
294            None
295        }
296    }
297
298    /// Provides access to framebuffer data by exposing file descriptors, offsets and lengths of the planes.
299    fn planes(&self) -> Immutable<FrameBufferPlanesRef<'_>> {
300        unsafe {
301            Immutable(FrameBufferPlanesRef::from_ptr(
302                NonNull::new(libcamera_framebuffer_planes(self.ptr().as_ptr())).unwrap(),
303            ))
304        }
305    }
306
307    /// User cookie associated with the buffer.
308    fn cookie(&self) -> u64 {
309        unsafe { libcamera_framebuffer_cookie(self.ptr().as_ptr()) }
310    }
311
312    /// Set user cookie associated with the buffer.
313    fn set_cookie(&self, cookie: u64) {
314        unsafe { libcamera_framebuffer_set_cookie(self.ptr().as_ptr(), cookie) }
315    }
316
317    /// Releases the acquire fence associated with this framebuffer, if any.
318    ///
319    /// Ownership of the fence is transferred to the caller.
320    fn release_fence(&self) -> Option<Fence> {
321        unsafe { Fence::from_ptr(libcamera_framebuffer_release_fence_handle(self.ptr().as_ptr())) }
322    }
323
324    /// Returns a non-owning view of the Request owning this framebuffer, if any.
325    fn request(&self) -> Option<crate::request::RequestRef<'_>> {
326        let ptr = unsafe { libcamera_framebuffer_request(self.ptr().as_ptr()) };
327        NonNull::new(ptr).map(|p| unsafe { crate::request::RequestRef::from_ptr(p) })
328    }
329}
330
331/// Description of a framebuffer plane for importing buffers.
332pub struct FrameBufferPlane {
333    pub fd: OwnedFd,
334    pub offset: u32,
335    pub length: u32,
336}
337
338/// FrameBuffer created from user-provided DMABUFs.
339pub struct OwnedFrameBuffer {
340    ptr: NonNull<libcamera_framebuffer_t>,
341}
342
343unsafe impl Send for OwnedFrameBuffer {}
344
345impl OwnedFrameBuffer {
346    /// Create a framebuffer from a set of planes. Ownership of the fds is transferred.
347    pub fn new(planes: Vec<FrameBufferPlane>, cookie: Option<u64>) -> io::Result<Self> {
348        if planes.is_empty() {
349            return Err(io::Error::new(io::ErrorKind::InvalidInput, "no planes provided"));
350        }
351
352        let mut infos: Vec<libcamera_framebuffer_plane_info> = Vec::with_capacity(planes.len());
353        let mut raw_fds = Vec::with_capacity(planes.len());
354
355        for plane in planes {
356            if plane.offset == u32::MAX || plane.length == 0 {
357                return Err(io::Error::new(
358                    io::ErrorKind::InvalidInput,
359                    "plane offset/length must be valid and non-zero",
360                ));
361            }
362            let fd = plane.fd.into_raw_fd();
363            raw_fds.push(fd);
364            infos.push(libcamera_framebuffer_plane_info {
365                fd,
366                offset: plane.offset,
367                length: plane.length,
368            });
369        }
370
371        let ptr = unsafe { libcamera_framebuffer_create(infos.as_ptr(), infos.len(), cookie.unwrap_or(0)) };
372
373        if let Some(ptr) = NonNull::new(ptr) {
374            // Initialize metadata status sentinel to avoid uninitialized reads.
375            unsafe {
376                libcamera_framebuffer_metadata(ptr.as_ptr())
377                    .cast_mut()
378                    .cast::<u32>()
379                    .write(u32::MAX)
380            };
381            Ok(Self { ptr })
382        } else {
383            for fd in raw_fds {
384                unsafe { libc::close(fd) };
385            }
386            Err(io::Error::last_os_error())
387        }
388    }
389}
390
391impl AsFrameBuffer for OwnedFrameBuffer {
392    unsafe fn ptr(&self) -> NonNull<libcamera_framebuffer_t> {
393        self.ptr
394    }
395}
396
397impl OwnedFrameBuffer {
398    /// Returns true if planes are contiguous within a single FD and ordered without gaps.
399    pub fn is_contiguous(&self) -> bool {
400        let planes = self.planes();
401        if planes.is_empty() {
402            return false;
403        }
404        // Gather (fd, offset, length)
405        let mut entries: Vec<(i32, usize, usize)> = planes
406            .into_iter()
407            .filter_map(|p| p.offset().map(|off| (p.fd(), off, p.len())))
408            .collect();
409        // Must all share the same fd
410        if entries
411            .iter()
412            .map(|e| e.0)
413            .collect::<std::collections::HashSet<_>>()
414            .len()
415            != 1
416        {
417            return false;
418        }
419        entries.sort_by_key(|e| e.1);
420        // Check contiguous offsets
421        let mut expected = entries[0].1;
422        for (_, off, len) in entries {
423            if off != expected {
424                return false;
425            }
426            expected = off + len;
427        }
428        true
429    }
430}
431
432impl core::fmt::Debug for OwnedFrameBuffer {
433    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434        f.debug_struct("OwnedFrameBuffer")
435            .field("cookie", &self.cookie())
436            .field("planes", &self.planes())
437            .finish()
438    }
439}
440
441impl Drop for OwnedFrameBuffer {
442    fn drop(&mut self) {
443        unsafe { libcamera_framebuffer_destroy(self.ptr.as_ptr()) }
444    }
445}