• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::cell::RefCell;
6 use std::fs::File;
7 use std::iter::zip;
8 use std::os::fd::AsRawFd;
9 #[cfg(feature = "v4l2")]
10 use std::os::fd::BorrowedFd;
11 use std::os::fd::FromRawFd;
12 use std::path::Path;
13 use std::ptr;
14 #[cfg(feature = "vaapi")]
15 use std::rc::Rc;
16 use std::slice;
17 use std::sync::Arc;
18 
19 use crate::utils::align_up;
20 use crate::utils::buffer_size_for_area;
21 use crate::video_frame::generic_dma_video_frame::GenericDmaVideoFrame;
22 use crate::video_frame::ReadMapping;
23 use crate::video_frame::VideoFrame;
24 use crate::video_frame::WriteMapping;
25 #[cfg(feature = "vaapi")]
26 use crate::DecodedFormat;
27 use crate::Fourcc;
28 use crate::FrameLayout;
29 use crate::PlaneLayout;
30 use crate::Resolution;
31 
32 use drm_fourcc::DrmFourcc;
33 #[cfg(feature = "v4l2")]
34 use gbm_sys::gbm_import_fd_data;
35 #[cfg(feature = "vaapi")]
36 use gbm_sys::gbm_import_fd_modifier_data;
37 use gbm_sys::{
38     gbm_bo, gbm_bo_create, gbm_bo_destroy, gbm_bo_flags, gbm_bo_get_fd, gbm_bo_get_height,
39     gbm_bo_get_modifier, gbm_bo_get_offset, gbm_bo_get_stride_for_plane, gbm_bo_get_width,
40     gbm_bo_import, gbm_bo_map, gbm_bo_transfer_flags, gbm_bo_unmap, gbm_create_device, gbm_device,
41     gbm_device_destroy,
42 };
43 #[cfg(feature = "vaapi")]
44 use libva::{
45     Display, ExternalBufferDescriptor, MemoryType, Surface, UsageHint, VADRMPRIMESurfaceDescriptor,
46     VADRMPRIMESurfaceDescriptorLayer, VADRMPRIMESurfaceDescriptorObject,
47 };
48 use nix::libc;
49 #[cfg(feature = "v4l2")]
50 use v4l2r::bindings::v4l2_plane;
51 #[cfg(feature = "v4l2")]
52 use v4l2r::device::Device;
53 #[cfg(feature = "v4l2")]
54 use v4l2r::ioctl::V4l2Buffer;
55 #[cfg(feature = "v4l2")]
56 use v4l2r::memory::{DmaBufHandle, DmaBufSource, PlaneHandle};
57 #[cfg(feature = "v4l2")]
58 use v4l2r::Format;
59 
60 // The gbm crate's wrapper for map() doesn't have the lifetime semantics that we want, so we need
61 // to implement our own.
62 // Unfortunately, because the GBM crate's BufferObject class does not expose the raw gbm_bo
63 // pointer, we are going to need to reimplement a lot of its functionality. This means a lot of
64 // "unsafe" code will be needed in this module. We guarantee the safety of this code by using
65 // Rust's native lifetime system to ensure that parent objects do not get deallocated before all of
66 // their children. For example, map objects maintain a reference to the underlying video frame,
67 // and video frames maintain a reference to the GBM device that allocated them.
68 
map_bo( bo: *mut gbm_bo, is_writable: bool, ) -> Result<(*mut libc::c_void, *mut libc::c_void), String>69 fn map_bo(
70     bo: *mut gbm_bo,
71     is_writable: bool,
72 ) -> Result<(*mut libc::c_void, *mut libc::c_void), String> {
73     let permissions = if is_writable {
74         gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ_WRITE
75     } else {
76         gbm_bo_transfer_flags::GBM_BO_TRANSFER_READ
77     };
78     let mut stride: u32 = 0;
79     let mut map_data: *mut libc::c_void = ptr::null_mut();
80     // SAFETY: This should be safe because we validate that bo is non-null on creation and we tie
81     // the lifetimes of the VideoFrames to the underlying GBM device.
82     unsafe {
83         let map_result = gbm_bo_map(
84             bo,
85             0,
86             0,
87             gbm_bo_get_width(bo),
88             gbm_bo_get_height(bo),
89             permissions,
90             &mut stride as *mut u32,
91             &mut map_data as *mut *mut libc::c_void,
92         );
93         if map_result.is_null() {
94             Err(format!(
95                 "Failed to map GBM buffer object read {}!",
96                 if is_writable { "write" } else { "only" }
97             ))
98         } else {
99             Ok((map_result, map_data))
100         }
101     }
102 }
103 
104 #[cfg(feature = "v4l2")]
import_bo_from_dmabuf_fd( device: *mut gbm_device, dma: BorrowedFd<'_>, width: u32, height: u32, stride: u32, format: u32, usage: u32, ) -> *mut gbm_bo105 fn import_bo_from_dmabuf_fd(
106     device: *mut gbm_device,
107     dma: BorrowedFd<'_>,
108     width: u32,
109     height: u32,
110     stride: u32,
111     format: u32,
112     usage: u32,
113 ) -> *mut gbm_bo {
114     let mut import_data = gbm_import_fd_data {
115         fd: dma.as_raw_fd(),
116         width: width,
117         height: height,
118         stride: stride,
119         format: format,
120     };
121     // SAFETY: This assumes that gbm_device is valid and that the FD given points to a DRM Prime FD
122     // with the layout dictated by the other parameters to this function.
123     unsafe {
124         gbm_bo_import(
125             device,
126             gbm_sys::GBM_BO_IMPORT_FD,
127             &mut import_data as *mut gbm_import_fd_data as *mut libc::c_void,
128             usage,
129         )
130     }
131 }
132 
133 pub struct GbmMapping<'a> {
134     map_datas: Vec<*mut libc::c_void>,
135     raw_mems: Vec<*mut libc::c_void>,
136     lens: Vec<usize>,
137     is_writable: bool,
138     frame: &'a GbmVideoFrame,
139 }
140 
141 impl<'a> ReadMapping<'a> for GbmMapping<'a> {
get(&self) -> Vec<&[u8]>142     fn get(&self) -> Vec<&[u8]> {
143         // SAFETY: This should be safe because we validate that gbm_bo_map returns a non-null
144         // pointer before instantiating a GbmMapping object.
145         unsafe {
146             zip(self.raw_mems.iter(), self.lens.iter())
147                 .map(|x| slice::from_raw_parts(*x.0 as *const u8, *x.1))
148                 .collect()
149         }
150     }
151 }
152 
153 impl<'a> WriteMapping<'a> for GbmMapping<'a> {
get(&self) -> Vec<RefCell<&'a mut [u8]>>154     fn get(&self) -> Vec<RefCell<&'a mut [u8]>> {
155         if !self.is_writable {
156             panic!("Attempted to get writable slice to read only mapping!");
157         }
158 
159         // SAFETY: This should be safe because we validate that gbm_bo_map returns a non-null
160         // pointer before instantiating a GbmMapping object. The above check prevents us from
161         // attempting to get a writable slice to a read-only mapping.
162         unsafe {
163             zip(self.raw_mems.iter(), self.lens.iter())
164                 .map(|x| RefCell::new(slice::from_raw_parts_mut(*x.0 as *mut u8, *x.1)))
165                 .collect()
166         }
167     }
168 }
169 
170 impl<'a> Drop for GbmMapping<'a> {
drop(&mut self)171     fn drop(&mut self) {
172         for plane_idx in 0..self.map_datas.len() {
173             let bo =
174                 if self.frame.bo.len() == 1 { self.frame.bo[0] } else { self.frame.bo[plane_idx] };
175             let map_data = self.map_datas[plane_idx];
176             // SAFETY: This should be safe because we validate that gbm_bo_map returns a non-null
177             // pointer before instantiating a GbmMapping object.
178             unsafe { gbm_bo_unmap(bo, map_data) };
179         }
180     }
181 }
182 
183 #[derive(Debug)]
184 pub struct GbmVideoFrame {
185     fourcc: Fourcc,
186     resolution: Resolution,
187     bo: Vec<*mut gbm_bo>,
188     // This reference is just to create a lifetime constraint. We don't want someone to close the
189     // device before free'ing up all allocated VideoFrames. We keep it optional just so that we can
190     // create default video frames for the purpose of signaling a drain event.
191     _device: Option<Arc<GbmDevice>>,
192     // This ties the lifetime of the DRM FDs to the lifetime of this object so that we can export
193     // to V4L2.
194     #[cfg(feature = "v4l2")]
195     export_handles: Vec<DmaBufHandle<File>>,
196 }
197 
198 impl GbmVideoFrame {
get_plane_offset(&self) -> Vec<usize>199     fn get_plane_offset(&self) -> Vec<usize> {
200         let mut ret: Vec<usize> = vec![];
201         for plane_idx in 0..self.num_planes() {
202             // SAFETY: This assumes self.bo contains valid GBM buffer objects.
203             if self.bo.len() > 1 {
204                 ret.push(unsafe { gbm_bo_get_offset(self.bo[plane_idx], 0) as usize });
205             } else {
206                 ret.push(unsafe {
207                     gbm_bo_get_offset(self.bo[0], plane_idx as libc::c_int) as usize
208                 });
209             }
210         }
211         ret
212     }
213 
214     #[allow(dead_code)]
get_modifier(&self) -> u64215     fn get_modifier(&self) -> u64 {
216         // SAFETY: This assumes self.bo[0] contains a valid GBM buffer object.
217         unsafe { gbm_bo_get_modifier(self.bo[0]) }
218     }
219 
map_helper(&self, is_writable: bool) -> Result<GbmMapping, String>220     fn map_helper(&self, is_writable: bool) -> Result<GbmMapping, String> {
221         let offsets = self.get_plane_offset();
222         let mut ret = GbmMapping {
223             map_datas: vec![],
224             raw_mems: vec![],
225             lens: self.get_plane_size(),
226             is_writable: is_writable,
227             frame: self,
228         };
229         for plane_idx in 0..self.num_planes() {
230             let bo = if self.bo.len() == 1 { self.bo[0] } else { self.bo[plane_idx] };
231             let (raw_mem, map_data) = map_bo(bo, is_writable)?;
232             ret.map_datas.push(map_data);
233             // SAFETY: This assumes that GBM gave us a valid offset.
234             ret.raw_mems.push(unsafe { raw_mem.offset(offsets[plane_idx] as isize) });
235         }
236         Ok(ret)
237     }
238 
to_generic_dma_video_frame(self) -> Result<GenericDmaVideoFrame<()>, String>239     pub fn to_generic_dma_video_frame(self) -> Result<GenericDmaVideoFrame<()>, String> {
240         let planes: Vec<PlaneLayout> = zip(self.get_plane_offset(), self.get_plane_pitch())
241             .enumerate()
242             .map(|x| PlaneLayout { buffer_index: x.0, offset: x.1 .0, stride: x.1 .1 })
243             .collect();
244         // SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called, so this should be
245         // safe as long as self.bo contains valid buffer objects.
246         let dma_handles: Vec<File> =
247             self.bo.iter().map(|bo| unsafe { File::from_raw_fd(gbm_bo_get_fd(*bo)) }).collect();
248         GenericDmaVideoFrame::new(
249             (),
250             dma_handles,
251             FrameLayout {
252                 format: (self.fourcc, self.get_modifier()),
253                 size: self.resolution(),
254                 planes: planes,
255             },
256         )
257     }
258 }
259 
260 #[cfg(feature = "vaapi")]
261 pub struct GbmExternalBufferDescriptor {
262     fourcc: Fourcc,
263     modifier: u64,
264     resolution: Resolution,
265     pitches: Vec<usize>,
266     offsets: Vec<usize>,
267     // We use a proper File object here to correctly manage the lifetimes of the exported FDs.
268     // Otherwise we risk exhausting the FD limit on the machine for certain test vectors.
269     export_file: File,
270 }
271 
272 #[cfg(feature = "vaapi")]
273 impl ExternalBufferDescriptor for GbmExternalBufferDescriptor {
274     const MEMORY_TYPE: MemoryType = MemoryType::DrmPrime2;
275     type DescriptorAttribute = VADRMPRIMESurfaceDescriptor;
276 
va_surface_attribute(&mut self) -> Self::DescriptorAttribute277     fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute {
278         let objects = [
279             VADRMPRIMESurfaceDescriptorObject {
280                 fd: self.export_file.as_raw_fd(),
281                 size: self.export_file.metadata().unwrap().len() as u32,
282                 drm_format_modifier: self.modifier,
283             },
284             Default::default(),
285             Default::default(),
286             Default::default(),
287         ];
288 
289         let layers = [
290             VADRMPRIMESurfaceDescriptorLayer {
291                 drm_format: u32::from(self.fourcc),
292                 num_planes: self.pitches.len() as u32,
293                 object_index: [0, 0, 0, 0],
294                 offset: self
295                     .offsets
296                     .iter()
297                     .map(|x| *x as u32)
298                     .chain(std::iter::repeat(0))
299                     .take(4)
300                     .collect::<Vec<_>>()
301                     .try_into()
302                     .unwrap(),
303                 pitch: self
304                     .pitches
305                     .iter()
306                     .map(|x| *x as u32)
307                     .chain(std::iter::repeat(0))
308                     .take(4)
309                     .collect::<Vec<_>>()
310                     .try_into()
311                     .unwrap(),
312             },
313             Default::default(),
314             Default::default(),
315             Default::default(),
316         ];
317         let resolution = self.resolution;
318         let ret = VADRMPRIMESurfaceDescriptor {
319             fourcc: u32::from(self.fourcc),
320             width: resolution.width,
321             height: resolution.height,
322             num_objects: 1,
323             objects,
324             num_layers: 1,
325             layers,
326         };
327         ret
328     }
329 }
330 
331 impl VideoFrame for GbmVideoFrame {
332     #[cfg(feature = "v4l2")]
333     type NativeHandle = DmaBufHandle<File>;
334 
335     #[cfg(feature = "vaapi")]
336     type MemDescriptor = GbmExternalBufferDescriptor;
337     #[cfg(feature = "vaapi")]
338     type NativeHandle = Surface<GbmExternalBufferDescriptor>;
339 
fourcc(&self) -> Fourcc340     fn fourcc(&self) -> Fourcc {
341         self.fourcc.clone()
342     }
343 
resolution(&self) -> Resolution344     fn resolution(&self) -> Resolution {
345         self.resolution.clone()
346     }
347 
348     // GBM allocates all the planes in one buffer and doesn't expose a means to get the size of a
349     // single plane, so just stub this method.
get_plane_size(&self) -> Vec<usize>350     fn get_plane_size(&self) -> Vec<usize> {
351         let mut ret: Vec<usize> = vec![];
352         let vertical_subsampling = self.get_vertical_subsampling();
353         let plane_pitch = self.get_plane_pitch();
354         for plane_idx in 0..self.num_planes() {
355             ret.push(
356                 plane_pitch[plane_idx]
357                     * align_up(self.resolution().height as usize, vertical_subsampling[plane_idx])
358                     / vertical_subsampling[plane_idx],
359             );
360         }
361         ret
362     }
363 
get_plane_pitch(&self) -> Vec<usize>364     fn get_plane_pitch(&self) -> Vec<usize> {
365         let mut ret: Vec<usize> = vec![];
366         for plane_idx in 0..self.num_planes() {
367             // SAFETY: This assumes self.bo contains valid GBM buffer objects.
368             if self.bo.len() > 1 {
369                 ret.push(unsafe { gbm_bo_get_stride_for_plane(self.bo[plane_idx], 0) as usize });
370             } else {
371                 ret.push(unsafe {
372                     gbm_bo_get_stride_for_plane(self.bo[0], plane_idx as libc::c_int) as usize
373                 });
374             }
375         }
376         ret
377     }
378 
map<'a>(&'a self) -> Result<Box<dyn ReadMapping<'a> + 'a>, String>379     fn map<'a>(&'a self) -> Result<Box<dyn ReadMapping<'a> + 'a>, String> {
380         Ok(Box::new(self.map_helper(false)?))
381     }
382 
map_mut<'a>(&'a mut self) -> Result<Box<dyn WriteMapping<'a> + 'a>, String>383     fn map_mut<'a>(&'a mut self) -> Result<Box<dyn WriteMapping<'a> + 'a>, String> {
384         Ok(Box::new(self.map_helper(true)?))
385     }
386 
387     #[cfg(feature = "v4l2")]
fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane)388     fn fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane) {
389         // TODO: Support |data_offset| here.
390         self.export_handles[index].fill_v4l2_plane(plane)
391     }
392 
393     // No-op for GBM buffers since the backing FD already disambiguates them.
394     #[cfg(feature = "v4l2")]
process_dqbuf(&mut self, _device: Arc<Device>, _format: &Format, _buf: &V4l2Buffer)395     fn process_dqbuf(&mut self, _device: Arc<Device>, _format: &Format, _buf: &V4l2Buffer) {}
396 
397     #[cfg(feature = "vaapi")]
to_native_handle(&self, display: &Rc<Display>) -> Result<Self::NativeHandle, String>398     fn to_native_handle(&self, display: &Rc<Display>) -> Result<Self::NativeHandle, String> {
399         if self.is_compressed() {
400             return Err("Compressed buffer export to VA-API is not currently supported".to_string());
401         }
402         if !self.is_contiguous() {
403             return Err(
404                 "Exporting non-contiguous GBM buffers to VA-API is not currently supported"
405                     .to_string(),
406             );
407         }
408 
409         // TODO: Add more supported formats
410         let rt_format = match self.decoded_format().unwrap() {
411             DecodedFormat::I420 | DecodedFormat::NV12 => libva::VA_RT_FORMAT_YUV420,
412             _ => return Err("Format unsupported for VA-API export".to_string()),
413         };
414 
415         let export_descriptor = vec![GbmExternalBufferDescriptor {
416             fourcc: self.fourcc.clone(),
417             modifier: self.get_modifier(),
418             resolution: self.resolution(),
419             pitches: self.get_plane_pitch(),
420             offsets: self.get_plane_offset(),
421             // SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called, so this should
422             // be safe as long as self.bo contains valid buffer objects.
423             export_file: unsafe { File::from_raw_fd(gbm_bo_get_fd(self.bo[0])) },
424         }];
425 
426         let mut ret = display
427             .create_surfaces(
428                 rt_format,
429                 Some(u32::from(self.fourcc)),
430                 self.resolution().width,
431                 self.resolution().height,
432                 Some(UsageHint::USAGE_HINT_DECODER),
433                 export_descriptor,
434             )
435             .map_err(|_| "Error importing GbmVideoFrame to VA-API".to_string())?;
436 
437         Ok(ret.pop().unwrap())
438     }
439 }
440 
441 impl Drop for GbmVideoFrame {
drop(&mut self)442     fn drop(&mut self) {
443         for bo in self.bo.iter() {
444             // SAFETY: This should be safe because we would not instantiate a GbmVideoFrame if the
445             // buffer object creation failed.
446             unsafe { gbm_bo_destroy(*bo) };
447         }
448     }
449 }
450 
451 // SAFETY: We will only access the raw BOs from the worker thread, and there are no other copies of
452 // these pointers.
453 unsafe impl Send for GbmVideoFrame {}
454 unsafe impl Sync for GbmVideoFrame {}
455 
456 #[derive(Clone, Debug, PartialEq)]
457 pub enum GbmUsage {
458     Decode,
459     Encode,
460 }
461 
462 #[derive(Debug)]
463 pub struct GbmDevice {
464     device: *mut gbm_device,
465     // Keeps device file descriptors valid as long as the GbmDevice is alive.
466     _device_file: std::fs::File,
467 }
468 
469 impl GbmDevice {
open<P: AsRef<Path>>(path: P) -> Result<Arc<Self>, String>470     pub fn open<P: AsRef<Path>>(path: P) -> Result<Arc<Self>, String> {
471         let device_file = std::fs::OpenOptions::new()
472             .read(true)
473             .write(true)
474             .open(path)
475             .map_err(|_| "Error opening GBM device!".to_string())?;
476         // SAFETY: We keep the GBM device File inside GbmDevice specifically for the purpose of
477         // making sure this FD is valid for the lifetime of the GbmDevice.
478         let device = unsafe { gbm_create_device(device_file.as_raw_fd()) };
479         if device.is_null() {
480             Err("Could not create GBM device from file!".to_string())
481         } else {
482             Ok(Arc::new(Self { device: device, _device_file: device_file }))
483         }
484     }
485 
new_frame( self: Arc<Self>, fourcc: Fourcc, visible_resolution: Resolution, coded_resolution: Resolution, usage: GbmUsage, ) -> Result<GbmVideoFrame, String>486     pub fn new_frame(
487         self: Arc<Self>,
488         fourcc: Fourcc,
489         visible_resolution: Resolution,
490         coded_resolution: Resolution,
491         usage: GbmUsage,
492     ) -> Result<GbmVideoFrame, String> {
493         let mut ret = GbmVideoFrame {
494             fourcc: fourcc,
495             resolution: visible_resolution,
496             bo: vec![],
497             _device: Some(Arc::clone(&self)),
498             #[cfg(feature = "v4l2")]
499             export_handles: vec![],
500         };
501 
502         if ret.is_compressed() {
503             let buffer_size = buffer_size_for_area(coded_resolution.width, coded_resolution.height);
504             // The width and height values are ultimately arbitrary, but we should try to pick some
505             // that won't trigger the GEM driver to add padding, so we only allocate what we need.
506             // 32 pixels is a common enough alignment that this provides us with a decent guess.
507             let fake_width = align_up((buffer_size as f64).sqrt() as u32, 32);
508             let fake_height = align_up(buffer_size as u32, fake_width) / fake_width;
509             // SAFETY: This should be safe because we would not instantiate a GbmDevice unless the
510             // call to gbm_create_device was successful.
511             let bo = unsafe {
512                 gbm_bo_create(
513                     self.device,
514                     fake_width,
515                     fake_height,
516                     DrmFourcc::R8 as u32,
517                     gbm_bo_flags::GBM_BO_USE_LINEAR as u32,
518                 )
519             };
520             if bo.is_null() {
521                 return Err("Error allocating compressed buffer!".to_string());
522             }
523 
524             ret.bo.push(bo);
525         } else if ret.is_contiguous() {
526             // These flags are not present in every system's GBM headers.
527             const GBM_BO_USE_HW_VIDEO_DECODER: u32 = 1 << 13;
528             const GBM_BO_USE_HW_VIDEO_ENCODER: u32 = 1 << 14;
529             // It's important that we use the correct use flag for platforms that support directly
530             // importing GBM allocated frame buffers to the video decoding hardware because the
531             // video decoding hardware sometimes makes assumptions about the modifier flags. If we
532             // try to force everything to be linear, we can end up getting a tiled frame when we
533             // try to map it.
534             // SAFETY: This should be safe because we would not instantiate a GbmDevice unless the
535             // call to gbm_create_device was successful.
536             let bo = unsafe {
537                 gbm_bo_create(
538                     self.device,
539                     coded_resolution.width,
540                     coded_resolution.height,
541                     u32::from(fourcc),
542                     if usage == GbmUsage::Decode {
543                         GBM_BO_USE_HW_VIDEO_DECODER
544                     } else {
545                         GBM_BO_USE_HW_VIDEO_ENCODER
546                     },
547                 )
548             };
549             if bo.is_null() {
550                 return Err(format!(
551                     "Error allocating contiguous buffer! Fourcc: {} width: {} height: {}",
552                     fourcc.to_string(),
553                     coded_resolution.width,
554                     coded_resolution.height
555                 ));
556             }
557 
558             ret.bo.push(bo);
559         } else {
560             // We hack multiplanar formats into GBM by making a bunch of separate BO's.
561             // The usage flag is ignored here because R8 are generally not supported
562             // decoder and encoder formats, so we just have to accept we're on our own for figuring
563             // out alignment, modifier, fourcc, etc.
564             let horizontal_subsampling = ret.get_horizontal_subsampling();
565             let vertical_subsampling = ret.get_vertical_subsampling();
566             let bytes_per_element = ret.get_bytes_per_element();
567             for plane_idx in 0..ret.num_planes() {
568                 // SAFETY: This should be safe because we would not instantiate a GbmDevice unless
569                 // the call to gbm_create_device was successful.
570                 let bo = unsafe {
571                     gbm_bo_create(
572                         self.device,
573                         (align_up(
574                             coded_resolution.width as usize,
575                             horizontal_subsampling[plane_idx],
576                         ) / horizontal_subsampling[plane_idx]
577                             * bytes_per_element[plane_idx]) as u32,
578                         (align_up(
579                             coded_resolution.height as usize,
580                             vertical_subsampling[plane_idx],
581                         ) / vertical_subsampling[plane_idx]) as u32,
582                         DrmFourcc::R8 as u32,
583                         gbm_bo_flags::GBM_BO_USE_LINEAR as u32,
584                     )
585                 };
586                 if bo.is_null() {
587                     return Err(format!(
588                         "Error allocating plane {} for format {}",
589                         plane_idx,
590                         fourcc.to_string()
591                     ));
592                 }
593 
594                 ret.bo.push(bo);
595             }
596         }
597 
598         #[cfg(feature = "v4l2")]
599         {
600             // SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called, so this
601             // should be safe as long as ret.bo contains valid buffer objects.
602             ret.export_handles = ret
603                 .bo
604                 .iter()
605                 .map(|bo| unsafe {
606                     DmaBufHandle::from(File::from_raw_fd(gbm_bo_get_fd(bo.clone())))
607                 })
608                 .collect::<_>();
609         }
610 
611         Ok(ret)
612     }
613 
614     #[cfg(feature = "v4l2")]
import_from_v4l2<S: DmaBufSource>( self: Arc<Self>, fourcc: Fourcc, resolution: Resolution, strides: Vec<usize>, native_handle: Vec<DmaBufHandle<S>>, ) -> Result<GbmVideoFrame, String>615     pub fn import_from_v4l2<S: DmaBufSource>(
616         self: Arc<Self>,
617         fourcc: Fourcc,
618         resolution: Resolution,
619         strides: Vec<usize>,
620         native_handle: Vec<DmaBufHandle<S>>,
621     ) -> Result<GbmVideoFrame, String> {
622         let mut ret = GbmVideoFrame {
623             fourcc: fourcc,
624             resolution: resolution,
625             bo: vec![],
626             export_handles: vec![],
627             _device: Some(Arc::clone(&self)),
628         };
629 
630         if strides.is_empty() || native_handle.is_empty() {
631             return Err("Cannot import empty V4L2 handle!".to_string());
632         }
633         if !ret.is_contiguous() && strides.len() != ret.num_planes() {
634             return Err(format!("Invalid number of strides for format {}", fourcc.to_string()));
635         }
636         if !ret.is_contiguous() && native_handle.len() != ret.num_planes() {
637             return Err(format!("Invalid number of V4L2 planes for format {}", fourcc.to_string()));
638         }
639 
640         // TODO: Pass in a parameter that determines this usage rather than just OR'ing everything
641         // together.
642         let usage = gbm_bo_flags::GBM_BO_USE_LINEAR as u32;
643         if ret.is_contiguous() {
644             let bo = import_bo_from_dmabuf_fd(
645                 self.device,
646                 native_handle[0].0.as_fd(),
647                 ret.resolution.width,
648                 ret.resolution.height,
649                 strides[0] as u32,
650                 u32::from(fourcc),
651                 usage,
652             );
653             if bo.is_null() {
654                 return Err("Error importing contiguous V4L2 buffer!".to_string());
655             }
656             ret.bo.push(bo);
657         } else {
658             let horizontal_subsampling = ret.get_horizontal_subsampling();
659             let vertical_subsampling = ret.get_vertical_subsampling();
660             let bytes_per_element = ret.get_bytes_per_element();
661             for plane_idx in 0..ret.num_planes() {
662                 let bo = import_bo_from_dmabuf_fd(
663                     self.device,
664                     native_handle[plane_idx].0.as_fd(),
665                     (align_up(resolution.width as usize, horizontal_subsampling[plane_idx])
666                         / horizontal_subsampling[plane_idx]
667                         * bytes_per_element[plane_idx]) as u32,
668                     (align_up(resolution.height as usize, vertical_subsampling[plane_idx])
669                         / vertical_subsampling[plane_idx]) as u32,
670                     strides[plane_idx] as u32,
671                     DrmFourcc::R8 as u32,
672                     usage,
673                 );
674                 if bo.is_null() {
675                     return Err(format!("Error importing plane {}", plane_idx));
676                 }
677                 ret.bo.push(bo);
678             }
679         }
680 
681         // SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called, so this should be
682         // safe as long as self.bo contains valid buffer objects.
683         ret.export_handles = ret
684             .bo
685             .iter()
686             .map(|bo| unsafe { DmaBufHandle::from(File::from_raw_fd(gbm_bo_get_fd(bo.clone()))) })
687             .collect::<_>();
688 
689         Ok(ret)
690     }
691 
692     #[cfg(feature = "vaapi")]
import_from_vaapi( self: Arc<Self>, surface: &Surface<GbmExternalBufferDescriptor>, ) -> Result<GbmVideoFrame, String>693     pub fn import_from_vaapi(
694         self: Arc<Self>,
695         surface: &Surface<GbmExternalBufferDescriptor>,
696     ) -> Result<GbmVideoFrame, String> {
697         let descriptor = surface
698             .export_prime()
699             .map_err(|err| format!("Could not export VA-API surface! {err:?}"))?;
700 
701         if descriptor.layers.len() != 1 {
702             return Err("Cannot import more than 1 layers as a single GBM buffer".to_string());
703         }
704 
705         for idx in descriptor.layers[0].object_index {
706             if idx as usize >= descriptor.objects.len() {
707                 return Err("Object index {idx} out of bounds".to_string());
708             }
709         }
710 
711         let mut ret = GbmVideoFrame {
712             _device: Some(Arc::clone(&self)),
713             fourcc: Fourcc::from(descriptor.fourcc),
714             resolution: Resolution { width: descriptor.width, height: descriptor.height },
715             bo: vec![],
716         };
717 
718         let buffers = [
719             descriptor.objects[descriptor.layers[0].object_index[0] as usize].fd.as_raw_fd()
720                 as libc::c_int,
721             -1,
722             -1,
723             -1,
724         ];
725         let mut import_data = gbm_import_fd_modifier_data {
726             width: descriptor.width,
727             height: descriptor.height,
728             format: descriptor.fourcc,
729             num_fds: 1,
730             fds: buffers,
731             strides: [
732                 descriptor.layers[0].pitch[0] as libc::c_int,
733                 descriptor.layers[0].pitch[1] as libc::c_int,
734                 descriptor.layers[0].pitch[2] as libc::c_int,
735                 descriptor.layers[0].pitch[3] as libc::c_int,
736             ],
737             offsets: [
738                 descriptor.layers[0].offset[0] as libc::c_int,
739                 descriptor.layers[0].offset[1] as libc::c_int,
740                 descriptor.layers[0].offset[2] as libc::c_int,
741                 descriptor.layers[0].offset[3] as libc::c_int,
742             ],
743             modifier: descriptor.objects[0].drm_format_modifier,
744         };
745 
746         // TODO: Plumb real usage flags in here.
747         let usage = gbm_bo_flags::GBM_BO_USE_SCANOUT as u32;
748 
749         // The constant in gbm_sys is wrong for some reason
750         const GBM_BO_IMPORT_FD_MODIFIER: u32 = 0x5505;
751         // SAFETY: This should be safe as long as VA-API correctly exports the DRM Prime FD. The
752         // GBM device is guaranteed to be valid or we wouldn't have instantiated the GbmDevice in
753         // the first place.
754         let bo = unsafe {
755             gbm_bo_import(
756                 self.device,
757                 GBM_BO_IMPORT_FD_MODIFIER,
758                 &mut import_data as *mut gbm_import_fd_modifier_data as *mut libc::c_void,
759                 usage,
760             )
761         };
762 
763         if bo.is_null() {
764             Err("Error importing VA-API surface!".to_string())
765         } else {
766             ret.bo.push(bo);
767             Ok(ret)
768         }
769     }
770 }
771 
772 impl Drop for GbmDevice {
drop(&mut self)773     fn drop(&mut self) {
774         // SAFETY: device should be valid because we wouldn't instantiate a GbmDevice if
775         // gbm_create_device failed.
776         unsafe { gbm_device_destroy(self.device) }
777     }
778 }
779 
780 // UNSAFE: We will only access the raw GBM device from the worker thread, and there are no other
781 // copies of the raw pointer available.
782 unsafe impl Send for GbmDevice {}
783 unsafe impl Sync for GbmDevice {}
784 
785 // TODO: Add unit tests
786