libcamera/
framebuffer_map.rs

1use std::collections::HashMap;
2
3use thiserror::Error;
4
5use crate::framebuffer::AsFrameBuffer;
6
7#[derive(Debug, Error)]
8pub enum MemoryMappedFrameBufferError {
9    #[error("Plane {index} with offset {offset} and size {len} exceeds file descriptor size of {fd_len}")]
10    PlaneOutOfBounds {
11        index: usize,
12        offset: usize,
13        len: usize,
14        fd_len: usize,
15    },
16    #[error("mmap failed with {0:?}")]
17    MemoryMapError(std::io::Error),
18}
19
20struct MappedPlane {
21    fd: i32,
22    offset: usize,
23    len: usize,
24}
25
26/// FrameBuffer wrapper, which exposes internal file descriptors as memory mapped [&[u8]] plane slices.
27pub struct MemoryMappedFrameBuffer<T: AsFrameBuffer> {
28    fb: T,
29    mmaps: HashMap<i32, (*const core::ffi::c_void, usize)>,
30    planes: Vec<MappedPlane>,
31}
32
33impl<T: AsFrameBuffer> MemoryMappedFrameBuffer<T> {
34    /// Memory map framebuffer, which implements [AsFrameBuffer].
35    ///
36    /// This might fail if framebuffer has invalid plane sizes/offsets or if [libc::mmap] fails itself.
37    pub fn new(fb: T) -> Result<Self, MemoryMappedFrameBufferError> {
38        struct MapInfo {
39            /// Maximum offset used by data planes
40            mapped_len: usize,
41            /// Total file descriptor size
42            total_len: usize,
43        }
44
45        let mut planes = Vec::new();
46        let mut map_info: HashMap<i32, MapInfo> = HashMap::new();
47
48        for (index, plane) in fb.planes().into_iter().enumerate() {
49            let fd = plane.fd();
50            let offset = plane.offset().unwrap();
51            let len = plane.len();
52
53            planes.push(MappedPlane { fd, offset, len });
54
55            // Find total FD length if not known yet
56            map_info.entry(fd).or_insert_with(|| {
57                let total_len = unsafe { libc::lseek64(fd, 0, libc::SEEK_END) } as usize;
58                MapInfo {
59                    mapped_len: 0,
60                    total_len,
61                }
62            });
63
64            let info = map_info.get_mut(&fd).unwrap();
65
66            if offset + len > info.total_len {
67                return Err(MemoryMappedFrameBufferError::PlaneOutOfBounds {
68                    index,
69                    offset,
70                    len,
71                    fd_len: info.total_len,
72                });
73            }
74
75            info.mapped_len = info.mapped_len.max(offset + len);
76        }
77
78        let mmaps = map_info
79            .iter()
80            .map(|(fd, info)| {
81                let addr = unsafe {
82                    libc::mmap64(
83                        core::ptr::null_mut(),
84                        info.mapped_len,
85                        libc::PROT_READ,
86                        libc::MAP_SHARED,
87                        *fd,
88                        0,
89                    )
90                };
91
92                if addr == libc::MAP_FAILED {
93                    Err(MemoryMappedFrameBufferError::MemoryMapError(
94                        std::io::Error::last_os_error(),
95                    ))
96                } else {
97                    Ok((*fd, (addr.cast_const(), info.mapped_len)))
98                }
99            })
100            .collect::<Result<HashMap<i32, (*const core::ffi::c_void, usize)>, MemoryMappedFrameBufferError>>()
101            .unwrap();
102
103        Ok(Self { fb, mmaps, planes })
104    }
105
106    /// Returns data slice for each plane within the framebuffer.
107    pub fn data(&self) -> Vec<&[u8]> {
108        self.planes
109            .iter()
110            .map(|plane| {
111                let mmap_ptr: *const u8 = self.mmaps[&plane.fd].0.cast();
112                unsafe { core::slice::from_raw_parts(mmap_ptr.add(plane.offset), plane.len) }
113            })
114            .collect()
115    }
116}
117
118impl<T: AsFrameBuffer> AsFrameBuffer for MemoryMappedFrameBuffer<T> {
119    unsafe fn ptr(&self) -> std::ptr::NonNull<libcamera_sys::libcamera_framebuffer_t> {
120        self.fb.ptr()
121    }
122}
123
124unsafe impl<T: AsFrameBuffer> Send for MemoryMappedFrameBuffer<T> {}
125
126impl<T: AsFrameBuffer> Drop for MemoryMappedFrameBuffer<T> {
127    fn drop(&mut self) {
128        // Unmap
129        for (_fd, (ptr, size)) in self.mmaps.drain() {
130            unsafe {
131                libc::munmap(ptr.cast_mut(), size);
132            }
133        }
134    }
135}