pub(crate) struct StaticPayloadType {
pub encoding_name: &'static str,
pub clock_rate: u32,
pub channels: Option<u8>,
}
pub(crate) fn rfc3551_payload_type(pt: u8) -> Option<StaticPayloadType> {
macro_rules! pt {
($name:expr, $rate:expr, $ch:expr) => {
Some(StaticPayloadType {
encoding_name: $name,
clock_rate: $rate,
channels: Some($ch),
})
};
($name:expr, $rate:expr) => {
Some(StaticPayloadType {
encoding_name: $name,
clock_rate: $rate,
channels: None,
})
};
}
match pt {
0 => pt!("PCMU", 8000, 1),
3 => pt!("GSM", 8000, 1),
4 => pt!("G723", 8000, 1),
5 => pt!("DVI4", 8000, 1),
6 => pt!("DVI4", 16000, 1),
7 => pt!("LPC", 8000, 1),
8 => pt!("PCMA", 8000, 1),
9 => pt!("G722", 8000, 1),
10 => pt!("L16", 44100, 2),
11 => pt!("L16", 44100, 1),
12 => pt!("QCELP", 8000, 1),
13 => pt!("CN", 8000, 1),
14 => pt!("MPA", 90000),
15 => pt!("G728", 8000, 1),
16 => pt!("DVI4", 11025, 1),
17 => pt!("DVI4", 22050, 1),
18 => pt!("G729", 8000, 1),
25 => pt!("CelB", 90000),
26 => pt!("JPEG", 90000),
28 => pt!("nv", 90000),
31 => pt!("H261", 90000),
32 => pt!("MPV", 90000),
33 => pt!("MP2T", 90000),
34 => pt!("H263", 90000),
_ => None,
}
}
static CANONICAL_NAMES: &[(&str, &str)] = &[
("pcmu", "PCMU"),
("pcma", "PCMA"),
("g722", "G722"),
("gsm", "GSM"),
("lpc", "LPC"),
("dvi4", "DVI4"),
("g723", "G723"),
("g729", "G729"),
("opus", "opus"),
("ilbc", "iLBC"),
("amr", "AMR"),
("amr-wb", "AMR-WB"),
("silk", "SILK"),
("bv16", "BV16"),
("bv32", "BV32"),
("g7221", "G7221"),
("g726-16", "G726-16"),
("g726-24", "G726-24"),
("g726-32", "G726-32"),
("g726-40", "G726-40"),
("aal2-g726-16", "AAL2-G726-16"),
("aal2-g726-24", "AAL2-G726-24"),
("aal2-g726-32", "AAL2-G726-32"),
("aal2-g726-40", "AAL2-G726-40"),
("codec2", "CODEC2"),
("l16", "L16"),
("speex", "SPEEX"),
("vp8", "VP8"),
("vp9", "VP9"),
("h264", "H264"),
("h263", "H263"),
("h263-1998", "H263-1998"),
("b64", "b64"),
("i420", "I420"),
];
pub(crate) fn canonical_iananame(name: &str) -> &str {
CANONICAL_NAMES
.iter()
.find(|&&(key, _)| name.eq_ignore_ascii_case(key))
.map(|&(_, canonical)| canonical)
.unwrap_or(name)
}
pub fn default_rate(name: &str) -> u32 {
if name.eq_ignore_ascii_case("opus") {
48000
} else if name
.get(..3)
.is_some_and(|pfx| pfx.eq_ignore_ascii_case("h26"))
|| name
.get(..2)
.is_some_and(|pfx| pfx.eq_ignore_ascii_case("vp"))
{
90000
} else {
8000
}
}
pub fn default_ptime(name: &str) -> u32 {
if name.eq_ignore_ascii_case("ilbc")
|| name.eq_ignore_ascii_case("isac")
|| name.eq_ignore_ascii_case("G723")
{
30
} else {
20
}
}
pub(crate) fn known_bitrate(pt: u8) -> Option<u32> {
match pt {
0 => Some(64000), 3 => Some(13200), 4 => Some(6300), 7 => Some(2400), 8 => Some(64000), 9 => Some(64000), 18 => Some(8000), _ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pcmu_resolves() {
let pt = rfc3551_payload_type(0).unwrap();
assert_eq!(pt.encoding_name, "PCMU");
assert_eq!(pt.clock_rate, 8000);
assert_eq!(pt.channels, Some(1));
}
#[test]
fn g722_clock_rate_is_8000() {
let pt = rfc3551_payload_type(9).unwrap();
assert_eq!(pt.encoding_name, "G722");
assert_eq!(pt.clock_rate, 8000);
}
#[test]
fn g729_resolves() {
let pt = rfc3551_payload_type(18).unwrap();
assert_eq!(pt.encoding_name, "G729");
assert_eq!(pt.clock_rate, 8000);
assert_eq!(pt.channels, Some(1));
}
#[test]
fn video_type_channels_is_none() {
let h261 = rfc3551_payload_type(31).unwrap();
assert_eq!(h261.encoding_name, "H261");
assert!(h261
.channels
.is_none());
let h263 = rfc3551_payload_type(34).unwrap();
assert_eq!(h263.encoding_name, "H263");
assert!(h263
.channels
.is_none());
let jpeg = rfc3551_payload_type(26).unwrap();
assert_eq!(jpeg.encoding_name, "JPEG");
assert!(jpeg
.channels
.is_none());
}
#[test]
fn mp2t_channels_is_none() {
let mp2t = rfc3551_payload_type(33).unwrap();
assert_eq!(mp2t.encoding_name, "MP2T");
assert!(mp2t
.channels
.is_none());
}
#[test]
fn mpa_channels_none() {
let mpa = rfc3551_payload_type(14).unwrap();
assert_eq!(mpa.encoding_name, "MPA");
assert!(mpa
.channels
.is_none());
}
#[test]
fn reserved_pt1_is_none() {
assert!(rfc3551_payload_type(1).is_none());
}
#[test]
fn reserved_pt19_is_none() {
assert!(rfc3551_payload_type(19).is_none());
}
#[test]
fn unassigned_pt21_is_none() {
assert!(rfc3551_payload_type(21).is_none());
}
#[test]
fn dynamic_range_is_none() {
assert!(rfc3551_payload_type(96).is_none());
assert!(rfc3551_payload_type(255).is_none());
}
#[test]
fn canonical_name_opus_is_lowercase() {
assert_eq!(canonical_iananame("OPUS"), "opus");
assert_eq!(canonical_iananame("Opus"), "opus");
assert_eq!(canonical_iananame("opus"), "opus");
}
#[test]
fn canonical_name_ilbc_casing() {
assert_eq!(canonical_iananame("ILBC"), "iLBC");
assert_eq!(canonical_iananame("ilbc"), "iLBC");
assert_eq!(canonical_iananame("iLBC"), "iLBC");
}
#[test]
fn canonical_name_speex_is_all_caps() {
assert_eq!(canonical_iananame("speex"), "SPEEX");
assert_eq!(canonical_iananame("Speex"), "SPEEX");
assert_eq!(canonical_iananame("SPEEX"), "SPEEX");
}
#[test]
fn canonical_name_l16() {
assert_eq!(canonical_iananame("l16"), "L16");
assert_eq!(canonical_iananame("L16"), "L16");
}
#[test]
fn canonical_name_video() {
assert_eq!(canonical_iananame("vp8"), "VP8");
assert_eq!(canonical_iananame("VP8"), "VP8");
assert_eq!(canonical_iananame("vp9"), "VP9");
assert_eq!(canonical_iananame("h264"), "H264");
assert_eq!(canonical_iananame("H264"), "H264");
assert_eq!(canonical_iananame("h263"), "H263");
assert_eq!(canonical_iananame("H263-1998"), "H263-1998");
assert_eq!(canonical_iananame("h263-1998"), "H263-1998");
}
#[test]
fn canonical_name_b64_is_lowercase() {
assert_eq!(canonical_iananame("B64"), "b64");
assert_eq!(canonical_iananame("b64"), "b64");
}
#[test]
fn canonical_name_unknown_passthrough() {
assert_eq!(canonical_iananame("EVS"), "EVS");
assert_eq!(canonical_iananame("unknown-codec"), "unknown-codec");
}
#[test]
fn bitrate_pcmu() {
assert_eq!(known_bitrate(0), Some(64000));
}
#[test]
fn bitrate_g729() {
assert_eq!(known_bitrate(18), Some(8000));
}
#[test]
fn bitrate_absent_for_l16() {
assert_eq!(known_bitrate(10), None);
}
#[test]
fn bitrate_absent_for_dynamic() {
assert_eq!(known_bitrate(96), None);
}
#[test]
fn default_rate_opus_is_48k() {
assert_eq!(default_rate("opus"), 48000);
assert_eq!(default_rate("OPUS"), 48000);
}
#[test]
fn default_rate_h26x_is_90k() {
assert_eq!(default_rate("H264"), 90000);
assert_eq!(default_rate("h264"), 90000);
assert_eq!(default_rate("H263"), 90000);
assert_eq!(default_rate("H263-1998"), 90000);
}
#[test]
fn default_rate_vp_is_90k() {
assert_eq!(default_rate("VP8"), 90000);
assert_eq!(default_rate("vp8"), 90000);
assert_eq!(default_rate("VP9"), 90000);
}
#[test]
fn default_rate_audio_is_8k() {
assert_eq!(default_rate("PCMU"), 8000);
assert_eq!(default_rate("G722"), 8000);
assert_eq!(default_rate("AMR"), 8000);
assert_eq!(default_rate("iLBC"), 8000);
assert_eq!(default_rate("unknown"), 8000);
}
#[test]
fn default_ptime_30ms_codecs() {
assert_eq!(default_ptime("iLBC"), 30);
assert_eq!(default_ptime("ilbc"), 30);
assert_eq!(default_ptime("ILBC"), 30);
assert_eq!(default_ptime("isac"), 30);
assert_eq!(default_ptime("ISAC"), 30);
assert_eq!(default_ptime("G723"), 30);
assert_eq!(default_ptime("g723"), 30);
}
#[test]
fn default_ptime_20ms_for_others() {
assert_eq!(default_ptime("PCMU"), 20);
assert_eq!(default_ptime("opus"), 20);
assert_eq!(default_ptime("G722"), 20);
assert_eq!(default_ptime("AMR"), 20);
}
}