1 // Copyright 2022 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 //! VAAPI backend for both stateless decoders and encoders.
6
7 use std::collections::HashSet;
8 use std::fmt::Debug;
9 use std::os::fd::AsRawFd;
10
11 use anyhow::anyhow;
12 use libva::Display;
13 use libva::VAConfigAttrib;
14 use libva::VAConfigAttribType;
15
16 use crate::utils::DmabufFrame;
17 use crate::utils::UserPtrFrame;
18 use crate::DecodedFormat;
19
20 pub mod decoder;
21 pub mod encoder;
22 pub mod surface_pool;
23
24 #[allow(dead_code)]
va_rt_format_to_string(va_rt_format: u32) -> String25 fn va_rt_format_to_string(va_rt_format: u32) -> String {
26 String::from(match va_rt_format {
27 libva::VA_RT_FORMAT_YUV420 => "YUV420",
28 libva::VA_RT_FORMAT_YUV422 => "YUV422",
29 libva::VA_RT_FORMAT_YUV444 => "YUV444",
30 libva::VA_RT_FORMAT_YUV420_10 => "YUV420_10",
31 libva::VA_RT_FORMAT_YUV420_12 => "YUV420_12",
32 libva::VA_RT_FORMAT_YUV422_10 => "YUV422_10",
33 libva::VA_RT_FORMAT_YUV422_12 => "YUV422_12",
34 libva::VA_RT_FORMAT_YUV444_10 => "YUV444_10",
35 libva::VA_RT_FORMAT_YUV444_12 => "YUV444_12",
36 other => return format!("unknown VA rt_format {}", other),
37 })
38 }
39
40 #[allow(dead_code)]
41 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
42 struct FormatMap {
43 pub rt_format: u32,
44 pub va_fourcc: u32,
45 pub decoded_format: DecodedFormat,
46 }
47
48 /// Maps a given VA_RT_FORMAT to a compatible decoded format in an arbitrary
49 /// preferred order.
50 #[allow(dead_code)]
51 const FORMAT_MAP: [FormatMap; 10] = [
52 FormatMap {
53 rt_format: libva::VA_RT_FORMAT_YUV420,
54 va_fourcc: libva::VA_FOURCC_NV12,
55 decoded_format: DecodedFormat::NV12,
56 },
57 FormatMap {
58 rt_format: libva::VA_RT_FORMAT_YUV420,
59 va_fourcc: libva::VA_FOURCC_I420,
60 decoded_format: DecodedFormat::I420,
61 },
62 FormatMap {
63 rt_format: libva::VA_RT_FORMAT_YUV422,
64 va_fourcc: libva::VA_FOURCC_422H,
65 decoded_format: DecodedFormat::I422,
66 },
67 FormatMap {
68 rt_format: libva::VA_RT_FORMAT_YUV444,
69 va_fourcc: libva::VA_FOURCC_444P,
70 decoded_format: DecodedFormat::I444,
71 },
72 FormatMap {
73 rt_format: libva::VA_RT_FORMAT_YUV420_10,
74 va_fourcc: libva::VA_FOURCC_P010,
75 decoded_format: DecodedFormat::I010,
76 },
77 FormatMap {
78 rt_format: libva::VA_RT_FORMAT_YUV420_12,
79 va_fourcc: libva::VA_FOURCC_P012,
80 decoded_format: DecodedFormat::I012,
81 },
82 FormatMap {
83 rt_format: libva::VA_RT_FORMAT_YUV422_10,
84 va_fourcc: libva::VA_FOURCC_Y210,
85 decoded_format: DecodedFormat::I210,
86 },
87 FormatMap {
88 rt_format: libva::VA_RT_FORMAT_YUV422_12,
89 va_fourcc: libva::VA_FOURCC_Y212,
90 decoded_format: DecodedFormat::I212,
91 },
92 FormatMap {
93 rt_format: libva::VA_RT_FORMAT_YUV444_10,
94 va_fourcc: libva::VA_FOURCC_Y410,
95 decoded_format: DecodedFormat::I410,
96 },
97 FormatMap {
98 rt_format: libva::VA_RT_FORMAT_YUV444_12,
99 va_fourcc: libva::VA_FOURCC_Y412,
100 decoded_format: DecodedFormat::I412,
101 },
102 ];
103
104 /// Returns a set of supported decoded formats given `rt_format`
105 #[allow(dead_code)]
supported_formats_for_rt_format( display: &Display, rt_format: u32, profile: i32, entrypoint: u32, image_formats: &[libva::VAImageFormat], ) -> anyhow::Result<HashSet<FormatMap>>106 fn supported_formats_for_rt_format(
107 display: &Display,
108 rt_format: u32,
109 profile: i32,
110 entrypoint: u32,
111 image_formats: &[libva::VAImageFormat],
112 ) -> anyhow::Result<HashSet<FormatMap>> {
113 let mut attrs =
114 vec![VAConfigAttrib { type_: VAConfigAttribType::VAConfigAttribRTFormat, value: 0 }];
115
116 display.get_config_attributes(profile, entrypoint, &mut attrs)?;
117
118 // See whether this RT_FORMAT is supported by the given VAProfile and
119 // VAEntrypoint pair.
120 if attrs[0].value == libva::VA_ATTRIB_NOT_SUPPORTED || attrs[0].value & rt_format == 0 {
121 return Err(anyhow!(
122 "rt_format {:?} not supported for profile {:?} and entrypoint {:?}",
123 rt_format,
124 profile,
125 entrypoint
126 ));
127 }
128
129 let mut supported_formats = HashSet::new();
130
131 for format in FORMAT_MAP {
132 if format.rt_format == rt_format {
133 supported_formats.insert(format);
134 }
135 }
136
137 // Only retain those that the hardware can actually map into.
138 supported_formats
139 .retain(|&entry| image_formats.iter().any(|fmt| fmt.fourcc == entry.va_fourcc));
140
141 Ok(supported_formats)
142 }
143
144 impl TryFrom<&libva::VAImageFormat> for DecodedFormat {
145 type Error = anyhow::Error;
146
try_from(value: &libva::VAImageFormat) -> Result<Self, Self::Error>147 fn try_from(value: &libva::VAImageFormat) -> Result<Self, Self::Error> {
148 match value.fourcc {
149 libva::VA_FOURCC_I420 => Ok(DecodedFormat::I420),
150 libva::VA_FOURCC_NV12 => Ok(DecodedFormat::NV12),
151 libva::VA_FOURCC_P010 => Ok(DecodedFormat::I010),
152 libva::VA_FOURCC_P012 => Ok(DecodedFormat::I012),
153 libva::VA_FOURCC_Y210 => Ok(DecodedFormat::I210),
154 libva::VA_FOURCC_Y212 => Ok(DecodedFormat::I212),
155 libva::VA_FOURCC_Y410 => Ok(DecodedFormat::I410),
156 libva::VA_FOURCC_Y412 => Ok(DecodedFormat::I412),
157 _ => Err(anyhow!("Unsupported format")),
158 }
159 }
160 }
161
162 impl libva::ExternalBufferDescriptor for UserPtrFrame {
163 const MEMORY_TYPE: libva::MemoryType = libva::MemoryType::UserPtr;
164 type DescriptorAttribute = libva::VASurfaceAttribExternalBuffers;
165
va_surface_attribute(&mut self) -> Self::DescriptorAttribute166 fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute {
167 let pitches = self
168 .layout
169 .planes
170 .iter()
171 .map(|p| p.stride as u32)
172 .chain(std::iter::repeat(0))
173 .take(4)
174 .collect::<Vec<_>>()
175 .try_into()
176 .unwrap();
177 let offsets = self
178 .layout
179 .planes
180 .iter()
181 .map(|p| p.offset as u32)
182 .chain(std::iter::repeat(0))
183 .take(4)
184 .collect::<Vec<_>>()
185 .try_into()
186 .unwrap();
187
188 libva::VASurfaceAttribExternalBuffers {
189 pixel_format: self.layout.format.0.into(),
190 width: self.layout.size.width,
191 height: self.layout.size.height,
192 data_size: self.mem_layout.size() as u32,
193 num_planes: self.layout.planes.len() as u32,
194 pitches,
195 offsets,
196 buffers: self.buffers.as_mut_ptr() as *mut _,
197 num_buffers: self.buffers.len() as u32,
198 flags: 0,
199 private_data: std::ptr::null_mut(),
200 }
201 }
202 }
203
204 impl libva::ExternalBufferDescriptor for DmabufFrame {
205 const MEMORY_TYPE: libva::MemoryType = libva::MemoryType::DrmPrime2;
206 type DescriptorAttribute = libva::VADRMPRIMESurfaceDescriptor;
207
va_surface_attribute(&mut self) -> Self::DescriptorAttribute208 fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute {
209 let objects = self
210 .fds
211 .iter()
212 .map(|fd| libva::VADRMPRIMESurfaceDescriptorObject {
213 fd: fd.as_raw_fd(),
214 size: nix::sys::stat::fstat(fd.as_raw_fd())
215 .map(|stat| stat.st_size as u32)
216 // If we don't have the information about the plane fd size, fallback to 0.
217 // Libva seems to be *sometimes* "happy" with zero.
218 .unwrap_or(0),
219 // TODO should the descriptor be moved to individual objects?
220 drm_format_modifier: self.layout.format.1,
221 })
222 .chain(std::iter::repeat(Default::default()))
223 .take(4)
224 .collect::<Vec<_>>()
225 .try_into()
226 .unwrap();
227
228 let layers = [
229 libva::VADRMPRIMESurfaceDescriptorLayer {
230 drm_format: self.layout.format.0.into(),
231 num_planes: self.layout.planes.len() as u32,
232 object_index: [0, 0, 0, 0],
233 offset: self
234 .layout
235 .planes
236 .iter()
237 .map(|p| p.offset as u32)
238 .chain(std::iter::repeat(0))
239 .take(4)
240 .collect::<Vec<_>>()
241 .try_into()
242 .unwrap(),
243 pitch: self
244 .layout
245 .planes
246 .iter()
247 .map(|p| p.stride as u32)
248 .chain(std::iter::repeat(0))
249 .take(4)
250 .collect::<Vec<_>>()
251 .try_into()
252 .unwrap(),
253 },
254 Default::default(),
255 Default::default(),
256 Default::default(),
257 ];
258
259 libva::VADRMPRIMESurfaceDescriptor {
260 // TODO should we match and use VA_FOURCC_* here?
261 fourcc: self.layout.format.0.into(),
262 width: self.layout.size.width,
263 height: self.layout.size.height,
264 num_objects: 1,
265 objects,
266 num_layers: 1,
267 layers,
268 }
269 }
270 }
271