#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CodecCapability {
pub name: String,
pub profiles: Vec<String>,
pub max_level: u32,
pub hardware_accelerated: bool,
}
impl CodecCapability {
#[must_use]
pub fn new(
name: impl Into<String>,
profiles: Vec<String>,
max_level: u32,
hardware_accelerated: bool,
) -> Self {
Self {
name: name.into(),
profiles,
max_level,
hardware_accelerated,
}
}
#[must_use]
pub fn supports_profile(&self, profile: &str) -> bool {
self.profiles.iter().any(|p| p == profile)
}
#[must_use]
pub fn is_hw_accelerated(&self) -> bool {
self.hardware_accelerated
}
}
#[derive(Debug, Default)]
pub struct CodecNegotiator {
pub local_caps: Vec<CodecCapability>,
pub remote_caps: Vec<CodecCapability>,
}
impl CodecNegotiator {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add_local(&mut self, cap: CodecCapability) {
self.local_caps.push(cap);
}
pub fn add_remote(&mut self, cap: CodecCapability) {
self.remote_caps.push(cap);
}
#[must_use]
pub fn common_codecs(&self) -> Vec<&str> {
self.local_caps
.iter()
.filter(|l| self.remote_caps.iter().any(|r| r.name == l.name))
.map(|l| l.name.as_str())
.collect()
}
#[must_use]
pub fn preferred_codec(&self) -> Option<&str> {
let common = self.common_codecs();
if common.is_empty() {
return None;
}
for name in &common {
if let Some(cap) = self.local_caps.iter().find(|c| &c.name.as_str() == name) {
if cap.hardware_accelerated {
return Some(name);
}
}
}
common.into_iter().next()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NegotiationResult {
pub selected_codec: String,
pub profile: String,
pub level: u32,
pub hardware_accelerated: bool,
}
impl NegotiationResult {
#[must_use]
pub fn is_hardware(&self) -> bool {
self.hardware_accelerated
}
}
#[must_use]
pub fn negotiate(
local: &[CodecCapability],
remote: &[CodecCapability],
) -> Option<NegotiationResult> {
let mut ordered: Vec<&CodecCapability> = local.iter().collect();
ordered.sort_by_key(|c| u8::from(!c.hardware_accelerated));
for local_cap in ordered {
if let Some(remote_cap) = remote.iter().find(|r| r.name == local_cap.name) {
let common_profile = local_cap
.profiles
.iter()
.find(|p| remote_cap.profiles.contains(p));
if let Some(profile) = common_profile {
let level = local_cap.max_level.min(remote_cap.max_level);
return Some(NegotiationResult {
selected_codec: local_cap.name.clone(),
profile: profile.clone(),
level,
hardware_accelerated: local_cap.hardware_accelerated,
});
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn av1_cap(hw: bool) -> CodecCapability {
CodecCapability::new("av1", vec!["main".to_string(), "high".to_string()], 40, hw)
}
fn vp9_cap() -> CodecCapability {
CodecCapability::new("vp9", vec!["profile0".to_string()], 50, false)
}
#[test]
fn test_supports_profile_positive() {
let cap = av1_cap(false);
assert!(cap.supports_profile("main"));
assert!(cap.supports_profile("high"));
}
#[test]
fn test_supports_profile_negative() {
let cap = av1_cap(false);
assert!(!cap.supports_profile("baseline"));
}
#[test]
fn test_is_hw_accelerated() {
assert!(av1_cap(true).is_hw_accelerated());
assert!(!av1_cap(false).is_hw_accelerated());
}
#[test]
fn test_common_codecs_overlap() {
let mut neg = CodecNegotiator::new();
neg.add_local(av1_cap(false));
neg.add_local(vp9_cap());
neg.add_remote(av1_cap(false));
let common = neg.common_codecs();
assert_eq!(common, vec!["av1"]);
}
#[test]
fn test_common_codecs_no_overlap() {
let mut neg = CodecNegotiator::new();
neg.add_local(vp9_cap());
neg.add_remote(av1_cap(false));
assert!(neg.common_codecs().is_empty());
}
#[test]
fn test_preferred_codec_hw_first() {
let mut neg = CodecNegotiator::new();
neg.add_local(vp9_cap()); neg.add_local(av1_cap(true)); neg.add_remote(vp9_cap());
neg.add_remote(av1_cap(true));
assert_eq!(neg.preferred_codec(), Some("av1"));
}
#[test]
fn test_preferred_codec_none() {
let mut neg = CodecNegotiator::new();
neg.add_local(vp9_cap());
neg.add_remote(av1_cap(false));
assert!(neg.preferred_codec().is_none());
}
#[test]
fn test_preferred_codec_fallback() {
let mut neg = CodecNegotiator::new();
neg.add_local(av1_cap(false));
neg.add_local(vp9_cap());
neg.add_remote(av1_cap(false));
neg.add_remote(vp9_cap());
let pref = neg.preferred_codec();
assert!(pref.is_some());
}
#[test]
fn test_negotiate_success() {
let local = vec![av1_cap(false)];
let remote = vec![av1_cap(false)];
let result = negotiate(&local, &remote).expect("negotiation should succeed");
assert_eq!(result.selected_codec, "av1");
assert!(result.profile == "main" || result.profile == "high");
assert_eq!(result.level, 40);
assert!(!result.is_hardware());
}
#[test]
fn test_negotiate_prefers_hw() {
let local = vec![vp9_cap(), av1_cap(true)];
let remote = vec![vp9_cap(), av1_cap(true)];
let result = negotiate(&local, &remote).expect("negotiation should succeed");
assert_eq!(result.selected_codec, "av1");
assert!(result.is_hardware());
}
#[test]
fn test_negotiate_level_min() {
let local = vec![CodecCapability::new(
"av1",
vec!["main".to_string()],
50,
false,
)];
let remote = vec![CodecCapability::new(
"av1",
vec!["main".to_string()],
30,
false,
)];
let result = negotiate(&local, &remote).expect("negotiation should succeed");
assert_eq!(result.level, 30);
}
#[test]
fn test_negotiate_no_common() {
let local = vec![av1_cap(false)];
let remote = vec![vp9_cap()];
assert!(negotiate(&local, &remote).is_none());
}
#[test]
fn test_negotiate_profile_mismatch() {
let local = vec![CodecCapability::new(
"av1",
vec!["high".to_string()],
40,
false,
)];
let remote = vec![CodecCapability::new(
"av1",
vec!["baseline".to_string()],
40,
false,
)];
assert!(negotiate(&local, &remote).is_none());
}
#[test]
fn test_negotiation_result_is_hardware() {
let r = NegotiationResult {
selected_codec: "av1".to_string(),
profile: "main".to_string(),
level: 40,
hardware_accelerated: true,
};
assert!(r.is_hardware());
let r2 = NegotiationResult {
hardware_accelerated: false,
..r
};
assert!(!r2.is_hardware());
}
}