use std::ffi::CStr;
use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use thiserror::Error;
use crate::bindings;
use crate::config::Config;
use crate::context::Context;
use crate::surface::Surface;
use crate::va_check;
use crate::SurfaceMemoryDescriptor;
use crate::UsageHint;
use crate::VaError;
pub struct DrmDeviceIterator {
cur_idx: usize,
}
const DRM_NODE_DEFAULT_PREFIX: &str = "/dev/dri/renderD";
const DRM_NUM_NODES: usize = 64;
const DRM_RENDER_NODE_START: usize = 128;
impl Default for DrmDeviceIterator {
fn default() -> Self {
Self {
cur_idx: DRM_RENDER_NODE_START,
}
}
}
impl Iterator for DrmDeviceIterator {
type Item = PathBuf;
fn next(&mut self) -> Option<Self::Item> {
match self.cur_idx {
idx if idx >= DRM_RENDER_NODE_START + DRM_NUM_NODES => None,
idx => {
let path = PathBuf::from(format!("{}{}", DRM_NODE_DEFAULT_PREFIX, idx));
if !path.exists() {
None
} else {
self.cur_idx += 1;
Some(path)
}
}
}
}
}
pub struct Display {
handle: bindings::VADisplay,
#[allow(dead_code)]
drm_file: File,
}
#[derive(Debug, Error)]
pub enum OpenDrmDisplayError {
#[error("cannot open DRM device: {0}")]
DeviceOpen(io::Error),
#[error("vaGetDisplayDRM returned NULL")]
VaGetDisplayDrm,
#[error("call to vaInitialize failed: {0}")]
VaInitialize(VaError),
}
impl Display {
pub fn open_drm_display<P: AsRef<Path>>(path: P) -> Result<Arc<Self>, OpenDrmDisplayError> {
let file = std::fs::File::options()
.read(true)
.write(true)
.open(path.as_ref())
.map_err(OpenDrmDisplayError::DeviceOpen)?;
let display = unsafe { bindings::vaGetDisplayDRM(file.as_raw_fd()) };
if display.is_null() {
return Err(OpenDrmDisplayError::VaGetDisplayDrm);
}
let mut major = 0i32;
let mut minor = 0i32;
va_check(unsafe { bindings::vaInitialize(display, &mut major, &mut minor) })
.map(|()| {
Arc::new(Self {
handle: display,
drm_file: file,
})
})
.map_err(OpenDrmDisplayError::VaInitialize)
}
pub fn open() -> Option<Arc<Self>> {
let devices = DrmDeviceIterator::default();
for device in devices {
if let Ok(display) = Self::open_drm_display(device) {
return Some(display);
}
}
None
}
pub fn handle(&self) -> bindings::VADisplay {
self.handle
}
pub fn query_config_profiles(&self) -> Result<Vec<bindings::VAProfile::Type>, VaError> {
let mut max_num_profiles = unsafe { bindings::vaMaxNumProfiles(self.handle) };
let mut profiles = Vec::with_capacity(max_num_profiles as usize);
va_check(unsafe {
bindings::vaQueryConfigProfiles(self.handle, profiles.as_mut_ptr(), &mut max_num_profiles)
})?;
unsafe {
profiles.set_len(max_num_profiles as usize);
};
Ok(profiles)
}
pub fn query_vendor_string(&self) -> std::result::Result<String, &'static str> {
let vendor_string = unsafe { bindings::vaQueryVendorString(self.handle) };
if vendor_string.is_null() {
return Err("vaQueryVendorString() returned NULL");
}
Ok(unsafe { CStr::from_ptr(vendor_string) }.to_string_lossy().to_string())
}
pub fn query_config_entrypoints(
&self,
profile: bindings::VAProfile::Type,
) -> Result<Vec<bindings::VAEntrypoint::Type>, VaError> {
let mut max_num_entrypoints = unsafe { bindings::vaMaxNumEntrypoints(self.handle) };
let mut entrypoints = Vec::with_capacity(max_num_entrypoints as usize);
va_check(unsafe {
bindings::vaQueryConfigEntrypoints(self.handle, profile, entrypoints.as_mut_ptr(), &mut max_num_entrypoints)
})?;
unsafe {
entrypoints.set_len(max_num_entrypoints as usize);
}
Ok(entrypoints)
}
pub fn get_config_attributes(
&self,
profile: bindings::VAProfile::Type,
entrypoint: bindings::VAEntrypoint::Type,
attributes: &mut [bindings::VAConfigAttrib],
) -> Result<(), VaError> {
va_check(unsafe {
bindings::vaGetConfigAttributes(
self.handle,
profile,
entrypoint,
attributes.as_mut_ptr(),
attributes.len() as i32,
)
})
}
pub fn create_surfaces<D: SurfaceMemoryDescriptor>(
self: &Arc<Self>,
rt_format: u32,
va_fourcc: Option<u32>,
width: u32,
height: u32,
usage_hint: Option<UsageHint>,
descriptors: Vec<D>,
) -> Result<Vec<Surface<D>>, VaError> {
Surface::new(
Arc::clone(self),
rt_format,
va_fourcc,
width,
height,
usage_hint,
descriptors,
)
}
pub fn create_context<D: SurfaceMemoryDescriptor>(
self: &Arc<Self>,
config: &Config,
coded_width: u32,
coded_height: u32,
surfaces: Option<&Vec<Surface<D>>>,
progressive: bool,
) -> Result<Rc<Context>, VaError> {
Context::new(
Arc::clone(self),
config,
coded_width,
coded_height,
surfaces,
progressive,
)
}
pub fn create_config(
self: &Arc<Self>,
attrs: Vec<bindings::VAConfigAttrib>,
profile: bindings::VAProfile::Type,
entrypoint: bindings::VAEntrypoint::Type,
) -> Result<Config, VaError> {
Config::new(Arc::clone(self), attrs, profile, entrypoint)
}
pub fn query_image_formats(&self) -> Result<Vec<bindings::VAImageFormat>, VaError> {
let mut num_image_formats = unsafe { bindings::vaMaxNumImageFormats(self.handle) };
let mut image_formats = Vec::with_capacity(num_image_formats as usize);
va_check(unsafe {
bindings::vaQueryImageFormats(self.handle, image_formats.as_mut_ptr(), &mut num_image_formats)
})?;
unsafe {
image_formats.set_len(num_image_formats as usize);
}
Ok(image_formats)
}
}
impl Drop for Display {
fn drop(&mut self) {
unsafe {
bindings::vaTerminate(self.handle);
}
}
}
unsafe impl Send for Display {}
unsafe impl Sync for Display {}