use serde::{Deserialize, Serialize};
use std::fmt;
use crate::packet::MediaKind;
use crate::rtp_::Frequency;
use super::format_params::FormatParams;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CodecSpec {
pub codec: Codec,
pub clock_rate: Frequency,
pub channels: Option<u8>,
pub format: FormatParams,
}
impl CodecSpec {
pub(crate) fn rtp_clock_rate(&self) -> Frequency {
if self.codec == Codec::G722 {
Frequency::EIGHT_KHZ
} else {
self.clock_rate
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum Codec {
Opus,
PCMU,
PCMA,
G722,
CN,
H264,
#[doc(hidden)]
H265,
H266,
Vp8,
Vp9,
#[doc(hidden)]
Av1,
#[doc(hidden)]
Rtx,
#[doc(hidden)]
Null,
#[doc(hidden)]
Unknown,
}
#[cfg(feature = "drv")]
crate::drv_identity_copy!(Codec, CodecSpec);
impl Codec {
pub fn is_audio(&self) -> bool {
use Codec::*;
matches!(self, Opus | PCMU | PCMA | G722 | CN)
}
pub fn is_video(&self) -> bool {
use Codec::*;
matches!(self, H266 | H265 | H264 | Vp8 | Vp9 | Av1)
}
pub fn kind(&self) -> MediaKind {
if self.is_audio() {
MediaKind::Audio
} else {
MediaKind::Video
}
}
}
impl<'a> From<&'a str> for Codec {
fn from(v: &'a str) -> Self {
let lc = v.to_ascii_lowercase();
match &lc[..] {
"opus" => Codec::Opus,
"pcmu" => Codec::PCMU,
"pcma" => Codec::PCMA,
"g722" => Codec::G722,
"cn" => Codec::CN,
"h264" => Codec::H264,
"h265" => Codec::H265,
"h266" => Codec::H266,
"vp8" => Codec::Vp8,
"vp9" => Codec::Vp9,
"av1" => Codec::Av1,
"rtx" => Codec::Rtx, _ => Codec::Unknown,
}
}
}
impl fmt::Display for Codec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Codec::Opus => write!(f, "opus"),
Codec::PCMU => write!(f, "PCMU"),
Codec::PCMA => write!(f, "PCMA"),
Codec::G722 => write!(f, "G722"),
Codec::CN => write!(f, "CN"),
Codec::H264 => write!(f, "H264"),
Codec::H265 => write!(f, "H265"),
Codec::H266 => write!(f, "H266"),
Codec::Vp8 => write!(f, "VP8"),
Codec::Vp9 => write!(f, "VP9"),
Codec::Av1 => write!(f, "AV1"),
Codec::Rtx => write!(f, "rtx"),
Codec::Null => write!(f, "null"),
Codec::Unknown => write!(f, "unknown"),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::format::FormatParams;
use crate::rtp_::{Frequency, MediaTime};
fn g722_spec() -> CodecSpec {
CodecSpec {
codec: Codec::G722,
clock_rate: Frequency::SIXTEEN_KHZ,
channels: None,
format: FormatParams::default(),
}
}
#[test]
fn g722_is_audio_and_parses() {
assert!(Codec::G722.is_audio());
assert!(!Codec::G722.is_video());
assert_eq!(Codec::from("G722"), Codec::G722);
assert_eq!(Codec::from("g722"), Codec::G722);
assert_eq!(Codec::G722.to_string(), "G722");
}
#[test]
fn comfort_noise_is_audio_and_parses() {
assert!(Codec::CN.is_audio());
assert!(!Codec::CN.is_video());
assert_eq!(Codec::from("CN"), Codec::CN);
assert_eq!(Codec::from("cn"), Codec::CN);
assert_eq!(Codec::CN.to_string(), "CN");
}
#[test]
fn g722_rtp_clock_rate_is_8khz() {
let spec = g722_spec();
assert_eq!(spec.clock_rate, Frequency::SIXTEEN_KHZ);
assert_eq!(spec.rtp_clock_rate(), Frequency::EIGHT_KHZ);
}
#[test]
fn g722_16khz_media_time_halves_on_the_wire() {
let spec = g722_spec();
let media_time = MediaTime::new(320, spec.clock_rate);
let wire = media_time.rebase(spec.rtp_clock_rate());
assert_eq!(wire.numer(), 160);
}
#[test]
fn non_g722_rtp_clock_rate_equals_clock_rate() {
let spec = CodecSpec {
codec: Codec::Opus,
clock_rate: Frequency::FORTY_EIGHT_KHZ,
channels: Some(2),
format: FormatParams::default(),
};
assert_eq!(spec.rtp_clock_rate(), Frequency::FORTY_EIGHT_KHZ);
}
}