• 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::fmt::Debug;
7 #[cfg(feature = "vaapi")]
8 use std::rc::Rc;
9 #[cfg(feature = "v4l2")]
10 use std::sync::Arc;
11 
12 use crate::utils::align_up;
13 use crate::DecodedFormat;
14 use crate::EncodedFormat;
15 use crate::Fourcc;
16 use crate::Resolution;
17 
18 pub mod frame_pool;
19 #[cfg(feature = "backend")]
20 pub mod gbm_video_frame;
21 #[cfg(feature = "backend")]
22 pub mod generic_dma_video_frame;
23 #[cfg(feature = "v4l2")]
24 pub mod v4l2_mmap_video_frame;
25 
26 #[cfg(feature = "vaapi")]
27 use libva::{Display, Surface, SurfaceMemoryDescriptor};
28 #[cfg(feature = "v4l2")]
29 use v4l2r::bindings::v4l2_plane;
30 #[cfg(feature = "v4l2")]
31 use v4l2r::device::Device;
32 #[cfg(feature = "v4l2")]
33 use v4l2r::ioctl::V4l2Buffer;
34 #[cfg(feature = "v4l2")]
35 use v4l2r::memory::{BufferHandles, Memory, MemoryType, PlaneHandle, PrimitiveBufferHandles};
36 #[cfg(feature = "v4l2")]
37 use v4l2r::Format;
38 
39 pub const Y_PLANE: usize = 0;
40 pub const UV_PLANE: usize = 1;
41 pub const U_PLANE: usize = 1;
42 pub const V_PLANE: usize = 2;
43 
44 // RAII wrappers for video memory mappings. The Drop method should implement any necessary
45 // munmap()'ing and cache flushing.
46 pub trait ReadMapping<'a> {
get(&self) -> Vec<&[u8]>47     fn get(&self) -> Vec<&[u8]>;
48 }
49 
50 pub trait WriteMapping<'a> {
get(&self) -> Vec<RefCell<&'a mut [u8]>>51     fn get(&self) -> Vec<RefCell<&'a mut [u8]>>;
52 }
53 
54 // Rust doesn't allow type aliases in traits, so we use this stupid hack to accomplish effectively
55 // the same thing.
56 pub trait Equivalent<A>: From<A> + Into<A> {
from_ref(value: &A) -> &Self57     fn from_ref(value: &A) -> &Self;
into_ref(self: &Self) -> &A58     fn into_ref(self: &Self) -> &A;
59 }
60 
61 impl<A> Equivalent<A> for A {
from_ref(value: &A) -> &Self62     fn from_ref(value: &A) -> &Self {
63         value
64     }
into_ref(self: &Self) -> &A65     fn into_ref(self: &Self) -> &A {
66         self
67     }
68 }
69 
70 // Unified abstraction for any kind of frame data that might be sent to the hardware.
71 pub trait VideoFrame: Send + Sync + Sized + Debug + 'static {
72     #[cfg(feature = "v4l2")]
73     type NativeHandle: PlaneHandle;
74 
75     #[cfg(feature = "vaapi")]
76     type MemDescriptor: SurfaceMemoryDescriptor;
77     #[cfg(feature = "vaapi")]
78     type NativeHandle: Equivalent<Surface<Self::MemDescriptor>>;
79 
fourcc(&self) -> Fourcc80     fn fourcc(&self) -> Fourcc;
81 
82     // Outputs visible resolution. Use pitch and plane size for coded resolution calculations.
resolution(&self) -> Resolution83     fn resolution(&self) -> Resolution;
84 
is_compressed(&self) -> bool85     fn is_compressed(&self) -> bool {
86         match self.fourcc().to_string().as_str() {
87             "H264" | "HEVC" | "VP80" | "VP90" | "AV1F" => true,
88             _ => false,
89         }
90     }
91 
92     // Whether or not all the planes are in a contiguous memory allocation. For example, returns
93     // true for NV12 and false for NM12.
is_contiguous(&self) -> bool94     fn is_contiguous(&self) -> bool {
95         if self.is_compressed() {
96             return false;
97         }
98 
99         // TODO: Add more formats.
100         match self.fourcc().to_string().as_str() {
101             "MM21" | "NM12" => false,
102             _ => true,
103         }
104     }
105 
decoded_format(&self) -> Result<DecodedFormat, String>106     fn decoded_format(&self) -> Result<DecodedFormat, String> {
107         if self.is_compressed() {
108             return Err("Cannot convert compressed format into decoded format".to_string());
109         }
110 
111         Ok(DecodedFormat::from(self.fourcc()))
112     }
113 
encoded_format(&self) -> Result<EncodedFormat, String>114     fn encoded_format(&self) -> Result<EncodedFormat, String> {
115         if !self.is_compressed() {
116             return Err("Cannot convert uncompressed format into encoded format".to_string());
117         }
118 
119         Ok(EncodedFormat::from(self.fourcc()))
120     }
121 
num_planes(&self) -> usize122     fn num_planes(&self) -> usize {
123         if self.is_compressed() {
124             return 1;
125         }
126 
127         match self.decoded_format().unwrap() {
128             DecodedFormat::I420
129             | DecodedFormat::I422
130             | DecodedFormat::I444
131             | DecodedFormat::I010
132             | DecodedFormat::I012
133             | DecodedFormat::I210
134             | DecodedFormat::I212
135             | DecodedFormat::I410
136             | DecodedFormat::I412 => 3,
137             DecodedFormat::NV12 | DecodedFormat::MM21 => 2,
138         }
139     }
140 
get_horizontal_subsampling(&self) -> Vec<usize>141     fn get_horizontal_subsampling(&self) -> Vec<usize> {
142         let mut ret: Vec<usize> = vec![];
143         for plane_idx in 0..self.num_planes() {
144             if self.is_compressed() {
145                 ret.push(1);
146             } else {
147                 ret.push(match self.decoded_format().unwrap() {
148                     DecodedFormat::I420
149                     | DecodedFormat::NV12
150                     | DecodedFormat::I422
151                     | DecodedFormat::I010
152                     | DecodedFormat::I012
153                     | DecodedFormat::I210
154                     | DecodedFormat::I212
155                     | DecodedFormat::MM21 => {
156                         if plane_idx == 0 {
157                             1
158                         } else {
159                             2
160                         }
161                     }
162                     DecodedFormat::I444 | DecodedFormat::I410 | DecodedFormat::I412 => 1,
163                 });
164             }
165         }
166         ret
167     }
168 
get_vertical_subsampling(&self) -> Vec<usize>169     fn get_vertical_subsampling(&self) -> Vec<usize> {
170         let mut ret: Vec<usize> = vec![];
171         for plane_idx in 0..self.num_planes() {
172             if self.is_compressed() {
173                 ret.push(1);
174             } else {
175                 ret.push(match self.decoded_format().unwrap() {
176                     DecodedFormat::I420
177                     | DecodedFormat::NV12
178                     | DecodedFormat::I010
179                     | DecodedFormat::I012
180                     | DecodedFormat::MM21 => {
181                         if plane_idx == 0 {
182                             1
183                         } else {
184                             2
185                         }
186                     }
187                     DecodedFormat::I422
188                     | DecodedFormat::I444
189                     | DecodedFormat::I210
190                     | DecodedFormat::I212
191                     | DecodedFormat::I410
192                     | DecodedFormat::I412 => 1,
193                 })
194             }
195         }
196         ret
197     }
198 
get_bytes_per_element(&self) -> Vec<usize>199     fn get_bytes_per_element(&self) -> Vec<usize> {
200         let mut ret: Vec<usize> = vec![];
201         for plane_idx in 0..self.num_planes() {
202             if self.is_compressed() {
203                 ret.push(1);
204             } else {
205                 ret.push(match self.decoded_format().unwrap() {
206                     DecodedFormat::I420 | DecodedFormat::I422 | DecodedFormat::I444 => 1,
207                     DecodedFormat::I010
208                     | DecodedFormat::I012
209                     | DecodedFormat::I210
210                     | DecodedFormat::I212
211                     | DecodedFormat::I410
212                     | DecodedFormat::I412 => 2,
213                     DecodedFormat::NV12 | DecodedFormat::MM21 => {
214                         if plane_idx == 0 {
215                             1
216                         } else {
217                             2
218                         }
219                     }
220                 })
221             }
222         }
223         ret
224     }
225 
get_plane_size(&self) -> Vec<usize>226     fn get_plane_size(&self) -> Vec<usize>;
227 
228     // Pitch is measured in bytes while stride is measured in pixels.
get_plane_pitch(&self) -> Vec<usize>229     fn get_plane_pitch(&self) -> Vec<usize>;
230 
validate_frame(&self) -> Result<(), String>231     fn validate_frame(&self) -> Result<(), String> {
232         if self.is_compressed() {
233             return Ok(());
234         }
235 
236         let horizontal_subsampling = self.get_horizontal_subsampling();
237         let vertical_subsampling = self.get_vertical_subsampling();
238         let bytes_per_element = self.get_bytes_per_element();
239         let plane_pitch = self.get_plane_pitch();
240         let plane_size = self.get_plane_size();
241 
242         for plane in 0..self.num_planes() {
243             let minimum_pitch =
244                 align_up(self.resolution().width as usize, horizontal_subsampling[plane])
245                     * bytes_per_element[plane]
246                     / horizontal_subsampling[plane];
247             if plane_pitch[plane] < minimum_pitch {
248                 return Err(
249                     "Pitch of plane {plane} is insufficient to accomodate format!".to_string()
250                 );
251             }
252             let minimum_size =
253                 align_up(self.resolution().height as usize, vertical_subsampling[plane])
254                     / vertical_subsampling[plane]
255                     * plane_pitch[plane];
256             if plane_size[plane] < minimum_size {
257                 return Err(
258                     "Size of plane {plane} is insufficient to accomodate format!".to_string()
259                 );
260             }
261         }
262 
263         Ok(())
264     }
265 
map<'a>(&'a self) -> Result<Box<dyn ReadMapping<'a> + 'a>, String>266     fn map<'a>(&'a self) -> Result<Box<dyn ReadMapping<'a> + 'a>, String>;
267 
map_mut<'a>(&'a mut self) -> Result<Box<dyn WriteMapping<'a> + 'a>, String>268     fn map_mut<'a>(&'a mut self) -> Result<Box<dyn WriteMapping<'a> + 'a>, String>;
269 
270     #[cfg(feature = "v4l2")]
fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane)271     fn fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane);
272 
273     #[cfg(feature = "v4l2")]
process_dqbuf(&mut self, device: Arc<Device>, format: &Format, buf: &V4l2Buffer)274     fn process_dqbuf(&mut self, device: Arc<Device>, format: &Format, buf: &V4l2Buffer);
275 
276     #[cfg(feature = "vaapi")]
to_native_handle(&self, display: &Rc<Display>) -> Result<Self::NativeHandle, String>277     fn to_native_handle(&self, display: &Rc<Display>) -> Result<Self::NativeHandle, String>;
278 }
279 
280 // Rust has restrictions about implementing foreign types, so this is a stupid workaround to get
281 // VideoFrame to implement BufferHandles.
282 #[cfg(feature = "v4l2")]
283 #[derive(Debug)]
284 pub struct V4l2VideoFrame<V: VideoFrame>(pub V);
285 
286 #[cfg(feature = "v4l2")]
287 impl<V: VideoFrame> From<V> for V4l2VideoFrame<V> {
from(value: V) -> Self288     fn from(value: V) -> Self {
289         Self(value)
290     }
291 }
292 
293 #[cfg(feature = "v4l2")]
294 impl<V: VideoFrame> BufferHandles for V4l2VideoFrame<V> {
295     type SupportedMemoryType = MemoryType;
296 
len(&self) -> usize297     fn len(&self) -> usize {
298         self.0.num_planes()
299     }
300 
fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane)301     fn fill_v4l2_plane(&self, index: usize, plane: &mut v4l2_plane) {
302         self.0.fill_v4l2_plane(index, plane)
303     }
304 }
305 
306 #[cfg(feature = "v4l2")]
307 impl<V: VideoFrame> PrimitiveBufferHandles for V4l2VideoFrame<V> {
308     type HandleType = V::NativeHandle;
309     const MEMORY_TYPE: Self::SupportedMemoryType =
310         <V::NativeHandle as PlaneHandle>::Memory::MEMORY_TYPE;
311 }
312