nihed-cros-libva: use simple error enum instead of anyhow crate
[nihav-player.git] / nihed-cros-libva / src / display.rs
CommitLineData
68362724
KS
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
5use std::ffi::CStr;
6use std::fs::File;
7use std::os::unix::io::AsRawFd;
8use std::path::Path;
9use std::path::PathBuf;
10use std::rc::Rc;
11
68362724
KS
12use crate::bindings;
13use crate::config::Config;
14use crate::context::Context;
69e6ce02 15use crate::status::*;
68362724
KS
16use crate::surface::Surface;
17use crate::UsageHint;
18
19/// Iterates over existing DRM devices.
20///
21/// DRM devices can be passed to [`Display::open_drm_display`] in order to create a `Display` on
22/// that device.
23pub struct DrmDeviceIterator {
24 cur_idx: usize,
25}
26
27const DRM_NODE_DEFAULT_PREFIX: &str = "/dev/dri/renderD";
28const DRM_NUM_NODES: usize = 64;
29const DRM_RENDER_NODE_START: usize = 128;
30
31impl Default for DrmDeviceIterator {
32 fn default() -> Self {
33 Self {
34 cur_idx: DRM_RENDER_NODE_START,
35 }
36 }
37}
38
39impl Iterator for DrmDeviceIterator {
40 type Item = PathBuf;
41
42 fn next(&mut self) -> Option<Self::Item> {
43 match self.cur_idx {
44 idx if idx >= DRM_RENDER_NODE_START + DRM_NUM_NODES => None,
45 idx => {
46 let path = PathBuf::from(format!("{}{}", DRM_NODE_DEFAULT_PREFIX, idx));
47 if !path.exists() {
48 None
49 } else {
50 self.cur_idx += 1;
51 Some(path)
52 }
53 }
54 }
55 }
56}
57
58/// A VADisplay opened over DRM.
59///
60/// A Display is the starting point to using libva. This struct is essentially a safe wrapper over
61/// `VADisplay`, from which [`Surface`]s and [`Context`]s can be allocated in order to perform
62/// actual work using [`Display::create_surfaces`] and [`Display::create_context`], respectively.
63///
64/// Although libva offers several ways to create a display, this struct currently only supports
65/// opening through DRM. It may be extended to support other display types (X11, Wayland) in the
66/// future.
67pub struct Display {
68 /// Handle to interact with the underlying `VADisplay`.
69 handle: bindings::VADisplay,
70 /// DRM file that must be kept open while the display is in use.
71 #[allow(dead_code)]
72 drm_file: File,
73}
74
75impl Display {
76 /// Opens and initializes a specific DRM `Display`.
77 ///
78 /// `path` is the path to a DRM device that supports VAAPI, e.g. `/dev/dri/renderD128`.
69e6ce02 79 pub fn open_drm_display<P: AsRef<Path>>(path: P) -> VAResult<Rc<Self>> {
68362724
KS
80 let file = std::fs::File::options()
81 .read(true)
82 .write(true)
83 .open(path.as_ref())
69e6ce02 84 .map_err(|_| VAError::InvalidValue)?;
68362724
KS
85
86 // Safe because fd represents a valid file descriptor and the pointer is checked for
87 // NULL afterwards.
88 let display = unsafe { bindings::vaGetDisplayDRM(file.as_raw_fd()) };
89 if display.is_null() {
90 // The File will close the DRM fd on drop.
69e6ce02 91 return Err(VAError::InvalidDisplay);
68362724
KS
92 }
93
94 let mut major = 0i32;
95 let mut minor = 0i32;
96 // Safe because we ensure that the display is valid (i.e not NULL) before calling
97 // vaInitialize. The File will close the DRM fd on drop.
69e6ce02 98 (unsafe { bindings::vaInitialize(display, &mut major, &mut minor) }).check()?;
68362724
KS
99
100 Ok(Rc::new(Self {
101 handle: display,
102 drm_file: file,
103 }))
104 }
105
106 /// Opens the first device that succeeds and returns its `Display`.
107 ///
108 /// If an error occurs on a given device, it is ignored and the next one is tried until one
109 /// succeeds or we reach the end of the iterator.
110 pub fn open() -> Option<Rc<Self>> {
111 let devices = DrmDeviceIterator::default();
112
113 // Try all the DRM devices until one succeeds.
114 for device in devices {
115 if let Ok(display) = Self::open_drm_display(device) {
116 return Some(display);
117 }
118 }
119
120 None
121 }
122
123 /// Returns the handle of this display.
124 pub(crate) fn handle(&self) -> bindings::VADisplay {
125 self.handle
126 }
127
128 /// Queries supported profiles by this display.
69e6ce02 129 pub fn query_config_profiles(&self) -> VAResult<Vec<bindings::VAProfile::Type>> {
68362724
KS
130 // Safe because `self` represents a valid VADisplay.
131 let mut max_num_profiles = unsafe { bindings::vaMaxNumProfiles(self.handle) };
132 let mut profiles = Vec::with_capacity(max_num_profiles as usize);
133
134 // Safe because `self` represents a valid `VADisplay` and the vector has `max_num_profiles`
135 // as capacity.
69e6ce02 136 (unsafe {
68362724
KS
137 bindings::vaQueryConfigProfiles(
138 self.handle,
139 profiles.as_mut_ptr(),
140 &mut max_num_profiles,
141 )
142 })
143 .check()?;
144
145 // Safe because `profiles` is allocated with a `max_num_profiles` capacity and
146 // `vaQueryConfigProfiles` wrote the actual number of profiles to `max_num_entrypoints`.
147 unsafe {
148 profiles.set_len(max_num_profiles as usize);
149 };
150
151 Ok(profiles)
152 }
153
154 /// Returns a string describing some aspects of the VA implemenation on the specific hardware
155 /// accelerator used by this display.
156 ///
157 /// The format of the returned string is vendor specific and at the discretion of the
158 /// implementer. e.g. for the Intel GMA500 implementation, an example would be: `Intel GMA500 -
159 /// 2.0.0.32L.0005`.
160 pub fn query_vendor_string(&self) -> std::result::Result<String, &'static str> {
161 // Safe because `self` represents a valid VADisplay.
162 let vendor_string = unsafe { bindings::vaQueryVendorString(self.handle) };
163
164 if vendor_string.is_null() {
165 return Err("vaQueryVendorString() returned NULL");
166 }
167
168 // Safe because we check the whether the vendor_String pointer is NULL
169 Ok(unsafe { CStr::from_ptr(vendor_string) }
170 .to_string_lossy()
171 .to_string())
172 }
173
174 /// Query supported entrypoints for a given profile.
175 pub fn query_config_entrypoints(
176 &self,
177 profile: bindings::VAProfile::Type,
69e6ce02 178 ) -> VAResult<Vec<bindings::VAEntrypoint::Type>> {
68362724
KS
179 // Safe because `self` represents a valid VADisplay.
180 let mut max_num_entrypoints = unsafe { bindings::vaMaxNumEntrypoints(self.handle) };
181 let mut entrypoints = Vec::with_capacity(max_num_entrypoints as usize);
182
183 // Safe because `self` represents a valid VADisplay and the vector has `max_num_entrypoints`
184 // as capacity.
69e6ce02 185 (unsafe {
68362724
KS
186 bindings::vaQueryConfigEntrypoints(
187 self.handle,
188 profile,
189 entrypoints.as_mut_ptr(),
190 &mut max_num_entrypoints,
191 )
192 })
193 .check()?;
194
195 // Safe because `entrypoints` is allocated with a `max_num_entrypoints` capacity, and
196 // `vaQueryConfigEntrypoints` wrote the actual number of entrypoints to
197 // `max_num_entrypoints`
198 unsafe {
199 entrypoints.set_len(max_num_entrypoints as usize);
200 }
201
202 Ok(entrypoints)
203 }
204
205 /// Writes attributes for a given `profile`/`entrypoint` pair into `attributes`.
206 ///
207 /// Entries of `attributes` must have their `type_` member initialized to the desired attribute
208 /// to retrieve.
209 pub fn get_config_attributes(
210 &self,
211 profile: bindings::VAProfile::Type,
212 entrypoint: bindings::VAEntrypoint::Type,
213 attributes: &mut [bindings::VAConfigAttrib],
69e6ce02 214 ) -> VAResult<()> {
68362724
KS
215 // Safe because `self` represents a valid VADisplay. The slice length is passed to the C
216 // function, so it is impossible to write past the end of the slice's storage by mistake.
69e6ce02 217 (unsafe {
68362724
KS
218 bindings::vaGetConfigAttributes(
219 self.handle,
220 profile,
221 entrypoint,
222 attributes.as_mut_ptr(),
223 attributes.len() as i32,
224 )
225 })
226 .check()
227 }
228
229 /// Creates `Surface`s by wrapping around a `vaCreateSurfaces` call.
230 ///
231 /// # Arguments
232 ///
233 /// * `rt_format` - The desired surface format. See `VA_RT_FORMAT_*`
234 /// * `va_fourcc` - The desired pixel format (optional). See `VA_FOURCC_*`
235 /// * `width` - Width for the create surfaces
236 /// * `height` - Height for the created surfaces
237 /// * `usage_hint` - Optional hint of intended usage to optimize allocation (e.g. tiling)
238 /// * `num_surfaces` - Number of surfaces to create
239 pub fn create_surfaces(
240 self: &Rc<Self>,
241 rt_format: u32,
242 va_fourcc: Option<u32>,
243 width: u32,
244 height: u32,
245 usage_hint: Option<UsageHint>,
246 num_surfaces: u32,
69e6ce02 247 ) -> VAResult<Vec<Surface>> {
68362724
KS
248 Surface::new(
249 Rc::clone(self),
250 rt_format,
251 va_fourcc,
252 width,
253 height,
254 usage_hint,
255 num_surfaces,
256 )
257 }
258
259 /// Creates a `Context` by wrapping around a `vaCreateContext` call.
260 ///
261 /// # Arguments
262 ///
263 /// * `config` - The configuration for the context
264 /// * `coded_width` - The coded picture width
265 /// * `coded_height` - The coded picture height
266 /// * `surfaces` - Optional hint for the amount of surfaces tied to the context
267 /// * `progressive` - Whether only progressive frame pictures are present in the sequence
268 pub fn create_context(
269 self: &Rc<Self>,
270 config: &Config,
271 coded_width: i32,
272 coded_height: i32,
273 surfaces: Option<&Vec<Surface>>,
274 progressive: bool,
69e6ce02 275 ) -> VAResult<Rc<Context>> {
68362724
KS
276 Context::new(
277 Rc::clone(self),
278 config,
279 coded_width,
280 coded_height,
281 surfaces,
282 progressive,
283 )
284 }
285
286 /// Creates a `Config` by wrapping around the `vaCreateConfig` call.
287 ///
288 /// `attrs` describe the attributes to set for this config. A list of the supported attributes
289 /// for a given profile/entrypoint pair can be retrieved using
290 /// [`Display::get_config_attributes`]. Other attributes will take their default values, and
291 /// `attrs` can be empty in order to obtain a default configuration.
292 pub fn create_config(
293 self: &Rc<Self>,
294 attrs: Vec<bindings::VAConfigAttrib>,
295 profile: bindings::VAProfile::Type,
296 entrypoint: bindings::VAEntrypoint::Type,
69e6ce02 297 ) -> VAResult<Config> {
68362724
KS
298 Config::new(Rc::clone(self), attrs, profile, entrypoint)
299 }
300
301 /// Returns available image formats for this display by wrapping around `vaQueryImageFormats`.
69e6ce02 302 pub fn query_image_formats(&self) -> VAResult<Vec<bindings::VAImageFormat>> {
68362724
KS
303 // Safe because `self` represents a valid VADisplay.
304 let mut num_image_formats = unsafe { bindings::vaMaxNumImageFormats(self.handle) };
305 let mut image_formats = Vec::with_capacity(num_image_formats as usize);
306
307 // Safe because `self` represents a valid VADisplay. The `image_formats` vector is properly
308 // initialized and a valid size is passed to the C function, so it is impossible to write
309 // past the end of their storage by mistake.
69e6ce02 310 (unsafe {
68362724
KS
311 bindings::vaQueryImageFormats(
312 self.handle,
313 image_formats.as_mut_ptr(),
314 &mut num_image_formats,
315 )
316 })
317 .check()?;
318
319 // Safe because the C function will have written exactly `num_image_format` entries, which
320 // is known to be within the vector's capacity.
321 unsafe {
322 image_formats.set_len(num_image_formats as usize);
323 }
324
325 Ok(image_formats)
326 }
327}
328
329impl Drop for Display {
330 fn drop(&mut self) {
331 // Safe because `self` represents a valid VADisplay.
332 unsafe {
333 bindings::vaTerminate(self.handle);
334 // The File will close the DRM fd on drop.
335 }
336 }
337}