use libva::VAProfile::Type as VAProfileType;
use libva::{Config, Display};
use std::rc::Rc;
use vacc_core::{
codec::VideoCodec as CoreVideoCodec,
device::{DecodeCapabilities, DecoderDevice},
format::{ChromaSubsampling, ComponentBitDepth, VideoFormat},
session::Extent2D,
};
use super::{Error, Result};
pub struct VaapiDecoderDevice {
display: Rc<Display>,
}
impl VaapiDecoderDevice {
pub fn new() -> Result<Self> {
let display = match std::env::var("NVD_GPU")
.ok()
.and_then(|v| v.parse::<u32>().ok())
{
Some(idx) => {
let path = format!("/dev/dri/renderD{}", 128 + idx);
Display::open_drm_display(&path).map_err(|e| {
Error::VaApi(format!("Failed to open VA display on {}: {}", path, e))
})?
}
None => Display::open()
.ok_or_else(|| Error::VaApi("No VA display available".to_string()))?,
};
Ok(Self { display })
}
pub fn display(&self) -> &Display {
&self.display
}
fn supports_profile(&self, profile: VAProfileType) -> bool {
match self.display.query_config_entrypoints(profile) {
Ok(entrypoints) => entrypoints.contains(&libva::VAEntrypoint::VAEntrypointVLD),
Err(_) => false,
}
}
fn codec_profiles(codec: CoreVideoCodec) -> Vec<VAProfileType> {
match codec {
CoreVideoCodec::DecodeH264 => vec![
libva::VAProfile::VAProfileH264ConstrainedBaseline,
libva::VAProfile::VAProfileH264Main,
libva::VAProfile::VAProfileH264High,
],
CoreVideoCodec::DecodeH265 => vec![
libva::VAProfile::VAProfileHEVCMain,
libva::VAProfile::VAProfileHEVCMain10,
],
CoreVideoCodec::DecodeVp9 => vec![
libva::VAProfile::VAProfileVP9Profile0,
libva::VAProfile::VAProfileVP9Profile2,
],
CoreVideoCodec::DecodeAv1 => vec![libva::VAProfile::VAProfileAV1Profile0],
_ => vec![],
}
}
fn try_create_config(&self, codec: CoreVideoCodec) -> Result<Config> {
let profiles = Self::codec_profiles(codec);
for profile in profiles {
if self.supports_profile(profile) {
let config = self.display.create_config(
Vec::new(),
profile,
libva::VAEntrypoint::VAEntrypointVLD,
);
if let Ok(config) = config {
return Ok(config);
}
}
}
Err(Error::CodecNotSupported(format!(
"No supported VA profile for {:?}",
codec
)))
}
}
impl DecoderDevice for VaapiDecoderDevice {
type Error = Error;
fn backend_name(&self) -> &str {
"vaapi"
}
fn supports_codec(&self, codec: CoreVideoCodec) -> bool {
self.try_create_config(codec).is_ok()
}
fn supported_codecs(&self) -> Vec<CoreVideoCodec> {
let mut codecs = Vec::new();
for codec in [
CoreVideoCodec::DecodeH264,
CoreVideoCodec::DecodeH265,
CoreVideoCodec::DecodeVp9,
CoreVideoCodec::DecodeAv1,
] {
if self.supports_codec(codec) {
codecs.push(codec);
}
}
codecs
}
fn query_capabilities(
&self,
codec: CoreVideoCodec,
_chroma_subsampling: ChromaSubsampling,
_luma_bit_depth: ComponentBitDepth,
_chroma_bit_depth: ComponentBitDepth,
_profile_idc: Option<u32>,
) -> Result<DecodeCapabilities> {
let _config = self.try_create_config(codec)?;
Ok(DecodeCapabilities {
codec_operations: codec,
min_bitstream_buffer_offset_alignment: 1,
min_bitstream_buffer_size_alignment: 16,
picture_access_granularity: Extent2D::new(1, 1),
min_coded_extent: Extent2D::new(16, 16),
max_coded_extent: Extent2D::new(4096, 2160),
max_dpb_slots: 16,
max_active_reference_pictures: 16,
supported_formats: vec![
VideoFormat::new(
codec,
ChromaSubsampling::_420,
ComponentBitDepth::Bit8,
ComponentBitDepth::Bit8,
),
VideoFormat::new(
codec,
ChromaSubsampling::_420,
ComponentBitDepth::Bit10,
ComponentBitDepth::Bit10,
),
],
})
}
fn query_supported_formats(&self, codec: CoreVideoCodec) -> Result<Vec<VideoFormat>> {
let _config = self.try_create_config(codec)?;
Ok(vec![
VideoFormat::new(
codec,
ChromaSubsampling::_420,
ComponentBitDepth::Bit8,
ComponentBitDepth::Bit8,
),
VideoFormat::new(
codec,
ChromaSubsampling::_420,
ComponentBitDepth::Bit10,
ComponentBitDepth::Bit10,
),
])
}
}