Skip to content
File

Blob: firmware/vendor/str0m/src/format/codec.rs

rust212 lines
1use serde::{Deserialize, Serialize};
2use std::fmt;
3 
4use crate::packet::MediaKind;
5use crate::rtp_::Frequency;
6 
7use super::format_params::FormatParams;
8 
9/// Codec specification
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
11pub struct CodecSpec {
12 /// The codec identifier.
13 pub codec: Codec,
14 
15 /// Clock rate of the codec.
16 pub clock_rate: Frequency,
17 
18 /// Number of audio channels (if any).
19 pub channels: Option<u8>,
20 
21 /// Codec specific format parameters. This might carry additional config for
22 /// things like h264.
23 pub format: FormatParams,
24}
25 
26impl CodecSpec {
27 /// The RTP clock rate used on the wire for this codec.
28 ///
29 /// For most codecs this is the same as [`CodecSpec::clock_rate`]. G722 is a
30 /// special case: although the codec samples audio at 16 kHz, its RTP timestamp
31 /// clock is 8000 Hz (to remain backwards compatible with RFC 1890, which
32 /// incorrectly used this value). str0m treats G722 as a 16 kHz codec everywhere
33 /// user facing, and only maps to 8 kHz when converting to and from RTP timestamps
34 /// (and in the SDP `a=rtpmap` line).
35 ///
36 /// See RFC 3551 §4.5.2 and
37 /// <https://en.wikipedia.org/wiki/RTP_payload_formats#cite_note-55>
38 pub(crate) fn rtp_clock_rate(&self) -> Frequency {
39 if self.codec == Codec::G722 {
40 Frequency::EIGHT_KHZ
41 } else {
42 self.clock_rate
43 }
44 }
45}
46 
47/// Known codecs.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
49#[non_exhaustive]
50#[allow(missing_docs)]
51pub enum Codec {
52 Opus,
53 PCMU,
54 PCMA,
55 G722,
56 /// Comfort Noise payload, per RFC 3389.
57 CN,
58 H264,
59 // TODO show this when we support h265.
60 #[doc(hidden)]
61 H265,
62 /// H266/VVC.
63 H266,
64 Vp8,
65 Vp9,
66 // TODO show this when we support Av1.
67 #[doc(hidden)]
68 Av1,
69 /// Technically not a codec, but used in places where codecs go
70 /// in `a=rtpmap` lines.
71 #[doc(hidden)]
72 Rtx,
73 /// For RTP mode. No codec.
74 #[doc(hidden)]
75 Null,
76 #[doc(hidden)]
77 Unknown,
78}
79 
80#[cfg(feature = "drv")]
81crate::drv_identity_copy!(Codec, CodecSpec);
82 
83impl Codec {
84 /// Tells if codec is audio.
85 pub fn is_audio(&self) -> bool {
86 use Codec::*;
87 matches!(self, Opus | PCMU | PCMA | G722 | CN)
88 }
89 
90 /// Tells if codec is video.
91 pub fn is_video(&self) -> bool {
92 use Codec::*;
93 matches!(self, H266 | H265 | H264 | Vp8 | Vp9 | Av1)
94 }
95 
96 /// Audio/Video.
97 pub fn kind(&self) -> MediaKind {
98 if self.is_audio() {
99 MediaKind::Audio
100 } else {
101 MediaKind::Video
102 }
103 }
104}
105 
106impl<'a> From<&'a str> for Codec {
107 fn from(v: &'a str) -> Self {
108 let lc = v.to_ascii_lowercase();
109 match &lc[..] {
110 "opus" => Codec::Opus,
111 "pcmu" => Codec::PCMU,
112 "pcma" => Codec::PCMA,
113 "g722" => Codec::G722,
114 "cn" => Codec::CN,
115 "h264" => Codec::H264,
116 "h265" => Codec::H265,
117 "h266" => Codec::H266,
118 "vp8" => Codec::Vp8,
119 "vp9" => Codec::Vp9,
120 "av1" => Codec::Av1,
121 "rtx" => Codec::Rtx, // resends
122 _ => Codec::Unknown,
123 }
124 }
125}
126 
127impl fmt::Display for Codec {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 match self {
130 Codec::Opus => write!(f, "opus"),
131 Codec::PCMU => write!(f, "PCMU"),
132 Codec::PCMA => write!(f, "PCMA"),
133 Codec::G722 => write!(f, "G722"),
134 Codec::CN => write!(f, "CN"),
135 Codec::H264 => write!(f, "H264"),
136 Codec::H265 => write!(f, "H265"),
137 Codec::H266 => write!(f, "H266"),
138 Codec::Vp8 => write!(f, "VP8"),
139 Codec::Vp9 => write!(f, "VP9"),
140 Codec::Av1 => write!(f, "AV1"),
141 Codec::Rtx => write!(f, "rtx"),
142 Codec::Null => write!(f, "null"),
143 Codec::Unknown => write!(f, "unknown"),
144 }
145 }
146}
147 
148#[cfg(test)]
149mod test {
150 use super::*;
151 use crate::format::FormatParams;
152 use crate::rtp_::{Frequency, MediaTime};
153 
154 fn g722_spec() -> CodecSpec {
155 CodecSpec {
156 codec: Codec::G722,
157 clock_rate: Frequency::SIXTEEN_KHZ,
158 channels: None,
159 format: FormatParams::default(),
160 }
161 }
162 
163 #[test]
164 fn g722_is_audio_and_parses() {
165 assert!(Codec::G722.is_audio());
166 assert!(!Codec::G722.is_video());
167 assert_eq!(Codec::from("G722"), Codec::G722);
168 assert_eq!(Codec::from("g722"), Codec::G722);
169 assert_eq!(Codec::G722.to_string(), "G722");
170 }
171 
172 #[test]
173 fn comfort_noise_is_audio_and_parses() {
174 assert!(Codec::CN.is_audio());
175 assert!(!Codec::CN.is_video());
176 assert_eq!(Codec::from("CN"), Codec::CN);
177 assert_eq!(Codec::from("cn"), Codec::CN);
178 assert_eq!(Codec::CN.to_string(), "CN");
179 }
180 
181 #[test]
182 fn g722_rtp_clock_rate_is_8khz() {
183 let spec = g722_spec();
184 // User facing clock rate is 16 kHz ...
185 assert_eq!(spec.clock_rate, Frequency::SIXTEEN_KHZ);
186 // ... but the RTP wire clock rate is 8 kHz (RFC 3551).
187 assert_eq!(spec.rtp_clock_rate(), Frequency::EIGHT_KHZ);
188 }
189 
190 #[test]
191 fn g722_16khz_media_time_halves_on_the_wire() {
192 // 20 ms of 16 kHz audio is 320 samples. On the wire the RTP timestamp must
193 // advance by 160 (8000 Hz clock), i.e. the 16 kHz media time is halved when
194 // converted to an RTP timestamp.
195 let spec = g722_spec();
196 let media_time = MediaTime::new(320, spec.clock_rate);
197 let wire = media_time.rebase(spec.rtp_clock_rate());
198 assert_eq!(wire.numer(), 160);
199 }
200 
201 #[test]
202 fn non_g722_rtp_clock_rate_equals_clock_rate() {
203 let spec = CodecSpec {
204 codec: Codec::Opus,
205 clock_rate: Frequency::FORTY_EIGHT_KHZ,
206 channels: Some(2),
207 format: FormatParams::default(),
208 };
209 assert_eq!(spec.rtp_clock_rate(), Frequency::FORTY_EIGHT_KHZ);
210 }
211}