#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum HwAccelType {
None,
Cuda,
Qsv,
Vaapi,
VideoToolbox,
Amf,
}
impl HwAccelType {
pub fn is_available_stub(self) -> bool {
!matches!(self, HwAccelType::None)
}
pub fn name(self) -> &'static str {
match self {
HwAccelType::None => "Software",
HwAccelType::Cuda => "CUDA",
HwAccelType::Qsv => "Intel QSV",
HwAccelType::Vaapi => "VA-API",
HwAccelType::VideoToolbox => "VideoToolbox",
HwAccelType::Amf => "AMD AMF",
}
}
}
#[derive(Debug, Clone)]
pub struct CodecCaps {
pub codec_id: String,
pub max_width: u32,
pub max_height: u32,
pub hw_accels: Vec<HwAccelType>,
pub b_frames: bool,
pub lossless: bool,
}
impl CodecCaps {
pub fn new(codec_id: impl Into<String>) -> Self {
Self {
codec_id: codec_id.into(),
max_width: 7680,
max_height: 4320,
hw_accels: Vec::new(),
b_frames: false,
lossless: false,
}
}
pub fn with_max_resolution(mut self, w: u32, h: u32) -> Self {
self.max_width = w;
self.max_height = h;
self
}
pub fn with_hw_accel(mut self, accel: HwAccelType) -> Self {
self.hw_accels.push(accel);
self
}
pub fn with_b_frames(mut self) -> Self {
self.b_frames = true;
self
}
pub fn with_lossless(mut self) -> Self {
self.lossless = true;
self
}
pub fn supports_hw_accel(&self, accel: HwAccelType) -> bool {
self.hw_accels.contains(&accel)
}
pub fn supports_resolution(&self, width: u32, height: u32) -> bool {
width <= self.max_width && height <= self.max_height
}
}
#[derive(Debug, Default)]
pub struct CodecCapsRegistry {
entries: Vec<CodecCaps>,
}
impl CodecCapsRegistry {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn register(&mut self, caps: CodecCaps) {
if let Some(existing) = self
.entries
.iter_mut()
.find(|c| c.codec_id == caps.codec_id)
{
*existing = caps;
} else {
self.entries.push(caps);
}
}
pub fn find(&self, codec_id: &str) -> Option<&CodecCaps> {
self.entries.iter().find(|c| c.codec_id == codec_id)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn codec_ids(&self) -> Vec<&str> {
self.entries.iter().map(|c| c.codec_id.as_str()).collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hw_accel_none_not_available() {
assert!(!HwAccelType::None.is_available_stub());
}
#[test]
fn test_hw_accel_cuda_available() {
assert!(HwAccelType::Cuda.is_available_stub());
}
#[test]
fn test_hw_accel_vaapi_available() {
assert!(HwAccelType::Vaapi.is_available_stub());
}
#[test]
fn test_hw_accel_name() {
assert_eq!(HwAccelType::Cuda.name(), "CUDA");
assert_eq!(HwAccelType::Qsv.name(), "Intel QSV");
assert_eq!(HwAccelType::VideoToolbox.name(), "VideoToolbox");
}
#[test]
fn test_codec_caps_default_max_resolution() {
let caps = CodecCaps::new("av1");
assert_eq!(caps.max_width, 7680);
assert_eq!(caps.max_height, 4320);
}
#[test]
fn test_codec_caps_supports_hw_accel_true() {
let caps = CodecCaps::new("h264").with_hw_accel(HwAccelType::Cuda);
assert!(caps.supports_hw_accel(HwAccelType::Cuda));
}
#[test]
fn test_codec_caps_supports_hw_accel_false() {
let caps = CodecCaps::new("av1");
assert!(!caps.supports_hw_accel(HwAccelType::Cuda));
}
#[test]
fn test_codec_caps_supports_resolution_within() {
let caps = CodecCaps::new("vp9").with_max_resolution(3840, 2160);
assert!(caps.supports_resolution(1920, 1080));
}
#[test]
fn test_codec_caps_supports_resolution_too_large() {
let caps = CodecCaps::new("vp9").with_max_resolution(3840, 2160);
assert!(!caps.supports_resolution(7680, 4320));
}
#[test]
fn test_codec_caps_lossless_flag() {
let caps = CodecCaps::new("av1").with_lossless();
assert!(caps.lossless);
}
#[test]
fn test_registry_register_and_find() {
let mut reg = CodecCapsRegistry::new();
reg.register(CodecCaps::new("av1"));
assert!(reg.find("av1").is_some());
}
#[test]
fn test_registry_find_missing() {
let reg = CodecCapsRegistry::new();
assert!(reg.find("h264").is_none());
}
#[test]
fn test_registry_replaces_existing() {
let mut reg = CodecCapsRegistry::new();
reg.register(CodecCaps::new("av1").with_max_resolution(1920, 1080));
reg.register(CodecCaps::new("av1").with_max_resolution(3840, 2160));
let caps = reg.find("av1").expect("should succeed");
assert_eq!(caps.max_width, 3840);
}
#[test]
fn test_registry_len_and_is_empty() {
let mut reg = CodecCapsRegistry::new();
assert!(reg.is_empty());
reg.register(CodecCaps::new("av1"));
assert_eq!(reg.len(), 1);
assert!(!reg.is_empty());
}
#[test]
fn test_registry_codec_ids() {
let mut reg = CodecCapsRegistry::new();
reg.register(CodecCaps::new("av1"));
reg.register(CodecCaps::new("vp9"));
let ids = reg.codec_ids();
assert!(ids.contains(&"av1"));
assert!(ids.contains(&"vp9"));
}
}