• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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