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