libcamera/
framebuffer_map.rs1use 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
26pub 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 pub fn new(fb: T) -> Result<Self, MemoryMappedFrameBufferError> {
38 struct MapInfo {
39 mapped_len: usize,
41 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 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 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 for (_fd, (ptr, size)) in self.mmaps.drain() {
130 unsafe {
131 libc::munmap(ptr.cast_mut(), size);
132 }
133 }
134 }
135}