use core::fmt;
use std::{
ffi::{c_char, c_int, c_void, CStr},
mem,
panic::catch_unwind,
ptr::{self, NonNull},
sync::Arc,
vec,
};
use raw_window_handle::{HasDisplayHandle, RawDisplayHandle};
use crate::{
check, check_log,
dlopen::{libva, libva_drm, libva_wayland, libva_win32, libva_x11},
image::{ImageFormat, ImageFormats},
raw::{VADisplay, VA_PADDING_LOW},
subpicture::{SubpictureFlags, SubpictureFormats},
Entrypoint, Entrypoints, Error, Profile, Profiles, Result,
};
ffi_enum! {
pub enum DisplayAttribType: c_int {
Brightness = 0,
Contrast = 1,
Hue = 2,
Saturation = 3,
BackgroundColor = 4,
DirectSurface = 5,
Rotation = 6,
OutofLoopDeblock = 7,
BLEBlackMode = 8,
BLEWhiteMode = 9,
BlueStretch = 10,
SkinColorCorrection = 11,
CSCMatrix = 12,
BlendColor = 13,
OverlayAutoPaintColorKey = 14,
OverlayColorKey = 15,
RenderMode = 16,
RenderDevice = 17,
RenderRect = 18,
SubDevice = 19,
Copy = 20,
PCIID = 21,
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DisplayAttribFlags: u32 {
const GETTABLE = 0x0001;
const SETTABLE = 0x0002;
}
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DisplayAttribute {
pub(crate) type_: DisplayAttribType,
pub(crate) min_value: i32,
pub(crate) max_value: i32,
pub(crate) value: i32,
pub(crate) flags: DisplayAttribFlags,
va_reserved: [u32; VA_PADDING_LOW],
}
impl DisplayAttribute {
pub(crate) fn zeroed() -> Self {
unsafe { mem::zeroed() }
}
pub fn new(ty: DisplayAttribType, value: i32) -> Self {
let mut this: Self = unsafe { std::mem::zeroed() };
this.type_ = ty;
this.value = value;
this
}
pub fn ty(&self) -> DisplayAttribType {
self.type_
}
pub fn min_value(&self) -> i32 {
self.min_value
}
pub fn max_value(&self) -> i32 {
self.max_value
}
pub fn value(&self) -> i32 {
self.value
}
pub fn flags(&self) -> DisplayAttribFlags {
self.flags
}
}
#[derive(Clone)]
pub struct DisplayAttributes {
pub(crate) vec: Vec<DisplayAttribute>,
}
impl DisplayAttributes {
pub fn len(&self) -> usize {
self.vec.len()
}
pub fn is_empty(&self) -> bool {
self.vec.is_empty()
}
}
impl IntoIterator for DisplayAttributes {
type Item = DisplayAttribute;
type IntoIter = vec::IntoIter<DisplayAttribute>;
fn into_iter(self) -> Self::IntoIter {
self.vec.into_iter()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DisplayApi {
Xlib,
Wayland,
Drm,
Win32,
}
pub(crate) struct DisplayOwner {
pub(crate) raw: VADisplay,
pub(crate) libva: &'static libva,
#[allow(dead_code)]
display_handle_owner: Option<Box<dyn HasDisplayHandle>>,
}
unsafe impl Send for DisplayOwner {}
unsafe impl Sync for DisplayOwner {}
impl fmt::Debug for DisplayOwner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DisplayOwner")
.field("raw", &self.raw)
.finish()
}
}
impl Drop for DisplayOwner {
fn drop(&mut self) {
unsafe {
check_log("vaTerminate", self.libva.vaTerminate(self.raw));
}
}
}
pub struct Display {
pub(crate) d: Arc<DisplayOwner>,
api: DisplayApi,
major: u32,
minor: u32,
}
impl Display {
pub fn new<H: HasDisplayHandle + 'static>(handle: H) -> Result<Self> {
Self::new_impl(
handle.display_handle().map_err(Error::from)?.as_raw(),
Some(Box::new(handle)),
)
}
pub unsafe fn new_unmanaged<H: HasDisplayHandle>(handle: &H) -> Result<Self> {
Self::new_impl(handle.display_handle().map_err(Error::from)?.as_raw(), None)
}
fn new_impl(
handle: RawDisplayHandle,
display_handle_owner: Option<Box<dyn HasDisplayHandle>>,
) -> Result<Self> {
unsafe {
let raw: VADisplay;
let api = match handle {
RawDisplayHandle::Xlib(d) => {
let display = d.display.map_or(ptr::null_mut(), NonNull::as_ptr);
raw = libva_x11::get()?.vaGetDisplay(display.cast());
DisplayApi::Xlib
}
RawDisplayHandle::Wayland(d) => {
raw = libva_wayland::get()?.vaGetDisplayWl(d.display.as_ptr().cast());
DisplayApi::Wayland
}
RawDisplayHandle::Drm(d) => {
raw = libva_drm::get()?.vaGetDisplayDRM(d.fd);
DisplayApi::Drm
}
RawDisplayHandle::Windows(_) => {
raw = libva_win32::get()?.vaGetDisplayWin32(ptr::null());
DisplayApi::Win32
}
_ => {
return Err(Error::from(format!(
"unsupported display handle type: {:?}",
handle
)));
}
};
let libva = libva::get()?;
let valid = libva.vaDisplayIsValid(raw);
if valid == 0 {
return Err(Error::from(format!(
"failed to create VADisplay from window handle {:?}",
handle
)));
}
libva.vaSetErrorCallback(raw, error_callback, ptr::null_mut());
libva.vaSetInfoCallback(raw, info_callback, ptr::null_mut());
let mut major = 0;
let mut minor = 0;
check(
"vaInitialize",
libva.vaInitialize(raw, &mut major, &mut minor),
)?;
log::info!("initialized libva {major}.{minor}");
let this = Self {
d: Arc::new(DisplayOwner {
raw,
libva,
display_handle_owner,
}),
api,
major: major as _,
minor: minor as _,
};
let vendor = this.query_vendor_string()?;
log::info!("VA-API vendor: {vendor}");
Ok(this)
}
}
#[inline]
pub fn version_major(&self) -> u32 {
self.major
}
#[inline]
pub fn version_minor(&self) -> u32 {
self.minor
}
#[inline]
pub fn display_api(&self) -> DisplayApi {
self.api
}
pub fn query_vendor_string(&self) -> Result<&str> {
unsafe {
let cstr = CStr::from_ptr(self.d.libva.vaQueryVendorString(self.d.raw));
cstr.to_str().map_err(Error::from)
}
}
pub fn query_profiles(&self) -> Result<Profiles> {
let max = unsafe { self.d.libva.vaMaxNumProfiles(self.d.raw) as usize };
let mut profiles = vec![Profile(0); max];
let mut num = 0;
unsafe {
check(
"vaQueryConfigProfiles",
self.d
.libva
.vaQueryConfigProfiles(self.d.raw, profiles.as_mut_ptr(), &mut num),
)?;
}
profiles.truncate(num as usize);
Ok(Profiles { vec: profiles })
}
pub fn query_entrypoints(&self, profile: Profile) -> Result<Entrypoints> {
let max = unsafe { self.d.libva.vaMaxNumEntrypoints(self.d.raw) as usize };
let mut entrypoints = vec![Entrypoint(0); max];
let mut num = 0;
unsafe {
check(
"vaQueryConfigEntrypoints",
self.d.libva.vaQueryConfigEntrypoints(
self.d.raw,
profile,
entrypoints.as_mut_ptr(),
&mut num,
),
)?;
}
entrypoints.truncate(num as usize);
Ok(Entrypoints { vec: entrypoints })
}
pub fn query_image_formats(&self) -> Result<ImageFormats> {
unsafe {
let max = self.d.libva.vaMaxNumImageFormats(self.d.raw) as usize;
let mut formats = vec![ImageFormat::zeroed(); max];
let mut num = 0;
check(
"vaQueryImageFormats",
self.d
.libva
.vaQueryImageFormats(self.d.raw, formats.as_mut_ptr(), &mut num),
)?;
formats.truncate(num as usize);
Ok(ImageFormats { vec: formats })
}
}
pub fn query_subpicture_format(&self) -> Result<SubpictureFormats> {
unsafe {
let max = self.d.libva.vaMaxNumSubpictureFormats(self.d.raw) as usize;
let mut formats = vec![ImageFormat::zeroed(); max];
let mut flags: Vec<SubpictureFlags> = vec![SubpictureFlags::empty(); max];
let mut num = 0;
check(
"vaQuerySubpictureFormats",
self.d.libva.vaQuerySubpictureFormats(
self.d.raw,
formats.as_mut_ptr(),
flags.as_mut_ptr().cast(),
&mut num,
),
)?;
formats.truncate(num as usize);
flags.truncate(num as usize);
Ok(SubpictureFormats { formats, flags })
}
}
pub fn query_display_attributes(&self) -> Result<DisplayAttributes> {
let max = unsafe { self.d.libva.vaMaxNumDisplayAttributes(self.d.raw) as usize };
let mut attribs = vec![DisplayAttribute::zeroed(); max];
let mut num = 0;
unsafe {
check(
"vaQueryDisplayAttributes",
self.d
.libva
.vaQueryDisplayAttributes(self.d.raw, attribs.as_mut_ptr(), &mut num),
)?;
}
attribs.truncate(num as usize);
Ok(DisplayAttributes { vec: attribs })
}
pub fn set_driver_name(&mut self, name: &str) -> Result<()> {
let mut buf;
let mut name = name.as_bytes();
if name.last() != Some(&0) {
buf = Vec::with_capacity(name.len() + 1);
buf.extend_from_slice(name);
buf.push(0);
name = &buf;
}
unsafe {
check(
"vaSetDriverName",
self.d
.libva
.vaSetDriverName(self.d.raw, name.as_ptr() as *mut c_char),
)
}
}
pub fn set_attributes(&mut self, attr_list: &mut [DisplayAttribute]) -> Result<()> {
unsafe {
check(
"vaSetDisplayAttributes",
self.d.libva.vaSetDisplayAttributes(
self.d.raw,
attr_list.as_mut_ptr(),
attr_list.len().try_into().unwrap(),
),
)?;
Ok(())
}
}
}
extern "C" fn error_callback(_ctx: *mut c_void, message: *const c_char) {
catch_unwind(|| unsafe {
let cstr = CStr::from_ptr(message);
match cstr.to_str() {
Ok(s) => {
log::error!("libva: {}", s.trim());
}
Err(e) => {
log::error!("failed to decode libva error: {e}");
}
}
})
.ok();
}
extern "C" fn info_callback(_ctx: *mut c_void, message: *const c_char) {
catch_unwind(|| unsafe {
let cstr = CStr::from_ptr(message);
match cstr.to_str() {
Ok(s) => {
log::info!("libva: {}", s.trim());
}
Err(e) => {
log::error!("failed to decode libva info message: {e}");
}
}
})
.ok();
}