1mod g711;
28
29#[cfg(feature = "opus")]
30mod opus_codec;
31
32pub use g711::{PcmaDecoder, PcmaEncoder, PcmuDecoder, PcmuEncoder};
33
34#[cfg(feature = "opus")]
35pub use opus_codec::{OpusDecoder, OpusEncoder};
36
37use crate::error::Result;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum CodecType {
42 Pcmu,
44 Pcma,
46 #[cfg(feature = "opus")]
48 Opus,
49}
50
51impl CodecType {
52 pub fn payload_type(&self) -> u8 {
54 match self {
55 Self::Pcmu => 0,
56 Self::Pcma => 8,
57 #[cfg(feature = "opus")]
58 Self::Opus => 111,
59 }
60 }
61
62 pub fn name(&self) -> &'static str {
64 match self {
65 Self::Pcmu => "PCMU",
66 Self::Pcma => "PCMA",
67 #[cfg(feature = "opus")]
68 Self::Opus => "opus",
69 }
70 }
71
72 pub fn clock_rate(&self) -> u32 {
74 match self {
75 Self::Pcmu | Self::Pcma => 8000,
76 #[cfg(feature = "opus")]
77 Self::Opus => 48000,
78 }
79 }
80
81 pub fn channels(&self) -> u8 {
83 match self {
84 Self::Pcmu | Self::Pcma => 1,
85 #[cfg(feature = "opus")]
86 Self::Opus => 1, }
88 }
89
90 pub fn frame_duration_ms(&self) -> u32 {
92 20 }
94
95 pub fn samples_per_frame(&self) -> usize {
97 (self.clock_rate() * self.frame_duration_ms() / 1000) as usize
98 }
99
100 pub fn from_payload_type(pt: u8) -> Option<Self> {
102 match pt {
103 0 => Some(Self::Pcmu),
104 8 => Some(Self::Pcma),
105 #[cfg(feature = "opus")]
106 111 => Some(Self::Opus),
107 _ => None,
108 }
109 }
110
111 pub fn from_name(name: &str) -> Option<Self> {
113 match name.to_uppercase().as_str() {
114 "PCMU" | "G711U" => Some(Self::Pcmu),
115 "PCMA" | "G711A" => Some(Self::Pcma),
116 #[cfg(feature = "opus")]
117 "OPUS" => Some(Self::Opus),
118 _ => None,
119 }
120 }
121}
122
123impl std::fmt::Display for CodecType {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 write!(f, "{}", self.name())
126 }
127}
128
129pub trait AudioEncoder: Send {
133 fn encode(&mut self, pcm: &[i16], output: &mut Vec<u8>) -> usize;
142
143 fn payload_type(&self) -> u8;
145
146 fn codec_type(&self) -> CodecType;
148}
149
150pub trait AudioDecoder: Send {
154 fn decode(&mut self, encoded: &[u8], output: &mut Vec<i16>);
160
161 fn codec_type(&self) -> CodecType;
163}
164
165pub fn create_encoder(codec: CodecType) -> Result<Box<dyn AudioEncoder>> {
167 match codec {
168 CodecType::Pcmu => Ok(Box::new(PcmuEncoder::new())),
169 CodecType::Pcma => Ok(Box::new(PcmaEncoder::new())),
170 #[cfg(feature = "opus")]
171 CodecType::Opus => Ok(Box::new(OpusEncoder::new()?)),
172 }
173}
174
175pub fn create_decoder(codec: CodecType) -> Result<Box<dyn AudioDecoder>> {
177 match codec {
178 CodecType::Pcmu => Ok(Box::new(PcmuDecoder::new())),
179 CodecType::Pcma => Ok(Box::new(PcmaDecoder::new())),
180 #[cfg(feature = "opus")]
181 CodecType::Opus => Ok(Box::new(OpusDecoder::new()?)),
182 }
183}
184
185pub fn negotiate_codec(sdp: &str) -> CodecType {
189 for line in sdp.lines() {
190 let line = line.trim();
191 if line.starts_with("m=audio ") {
192 let parts: Vec<&str> = line.split_whitespace().collect();
193 for pt_str in parts.iter().skip(3) {
195 if let Ok(pt) = pt_str.parse::<u8>()
196 && let Some(codec) = CodecType::from_payload_type(pt)
197 {
198 return codec;
199 }
200 }
201 }
202 }
203 CodecType::Pcmu }
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_codec_type_properties() {
212 assert_eq!(CodecType::Pcmu.payload_type(), 0);
213 assert_eq!(CodecType::Pcma.payload_type(), 8);
214 assert_eq!(CodecType::Pcmu.clock_rate(), 8000);
215 assert_eq!(CodecType::Pcmu.samples_per_frame(), 160);
216 }
217
218 #[test]
219 fn test_codec_type_from_payload_type() {
220 assert_eq!(CodecType::from_payload_type(0), Some(CodecType::Pcmu));
221 assert_eq!(CodecType::from_payload_type(8), Some(CodecType::Pcma));
222 assert_eq!(CodecType::from_payload_type(99), None);
223 }
224
225 #[test]
226 fn test_codec_type_from_name() {
227 assert_eq!(CodecType::from_name("PCMU"), Some(CodecType::Pcmu));
228 assert_eq!(CodecType::from_name("pcma"), Some(CodecType::Pcma));
229 assert_eq!(CodecType::from_name("unknown"), None);
230 }
231
232 #[test]
233 fn test_negotiate_codec() {
234 let sdp = "v=0\r\n\
235 m=audio 5004 RTP/AVP 0 8\r\n";
236 assert_eq!(negotiate_codec(sdp), CodecType::Pcmu);
237
238 let sdp2 = "v=0\r\n\
239 m=audio 5004 RTP/AVP 8 0\r\n";
240 assert_eq!(negotiate_codec(sdp2), CodecType::Pcma);
241 }
242
243 #[test]
244 fn test_negotiate_codec_fallback() {
245 let sdp = "v=0\r\nm=audio 5004 RTP/AVP 96 97 98\r\n";
247 assert_eq!(negotiate_codec(sdp), CodecType::Pcmu);
248 }
249
250 #[test]
251 fn test_negotiate_codec_no_audio() {
252 let sdp = "v=0\r\nm=video 5004 RTP/AVP 96\r\n";
253 assert_eq!(negotiate_codec(sdp), CodecType::Pcmu); }
255
256 #[test]
257 fn test_codec_type_display() {
258 assert_eq!(format!("{}", CodecType::Pcmu), "PCMU");
259 assert_eq!(format!("{}", CodecType::Pcma), "PCMA");
260 }
261
262 #[test]
263 fn test_codec_type_name() {
264 assert_eq!(CodecType::Pcmu.name(), "PCMU");
265 assert_eq!(CodecType::Pcma.name(), "PCMA");
266 }
267
268 #[test]
269 fn test_codec_type_channels() {
270 assert_eq!(CodecType::Pcmu.channels(), 1);
271 assert_eq!(CodecType::Pcma.channels(), 1);
272 }
273
274 #[test]
275 fn test_codec_type_frame_duration() {
276 assert_eq!(CodecType::Pcmu.frame_duration_ms(), 20);
277 assert_eq!(CodecType::Pcma.frame_duration_ms(), 20);
278 }
279
280 #[test]
281 fn test_codec_from_name_aliases() {
282 assert_eq!(CodecType::from_name("G711U"), Some(CodecType::Pcmu));
283 assert_eq!(CodecType::from_name("G711A"), Some(CodecType::Pcma));
284 assert_eq!(CodecType::from_name("g711u"), Some(CodecType::Pcmu));
285 }
286
287 #[test]
288 fn test_create_encoder() {
289 let encoder = create_encoder(CodecType::Pcmu).unwrap();
290 assert_eq!(encoder.payload_type(), 0);
291 assert_eq!(encoder.codec_type(), CodecType::Pcmu);
292
293 let encoder = create_encoder(CodecType::Pcma).unwrap();
294 assert_eq!(encoder.payload_type(), 8);
295 assert_eq!(encoder.codec_type(), CodecType::Pcma);
296 }
297
298 #[test]
299 fn test_create_decoder() {
300 let decoder = create_decoder(CodecType::Pcmu).unwrap();
301 assert_eq!(decoder.codec_type(), CodecType::Pcmu);
302
303 let decoder = create_decoder(CodecType::Pcma).unwrap();
304 assert_eq!(decoder.codec_type(), CodecType::Pcma);
305 }
306
307 #[test]
308 fn test_encoder_empty_input() {
309 let mut encoder = create_encoder(CodecType::Pcmu).unwrap();
310 let mut output = Vec::new();
311 let consumed = encoder.encode(&[], &mut output);
312 assert_eq!(consumed, 0);
313 assert!(output.is_empty());
314 }
315
316 #[test]
317 fn test_decoder_empty_input() {
318 let mut decoder = create_decoder(CodecType::Pcmu).unwrap();
319 let mut output = Vec::new();
320 decoder.decode(&[], &mut output);
321 assert!(output.is_empty());
322 }
323
324 #[test]
325 fn test_codec_roundtrip_various_lengths() {
326 let mut encoder = create_encoder(CodecType::Pcmu).unwrap();
327 let mut decoder = create_decoder(CodecType::Pcmu).unwrap();
328
329 for len in [1, 10, 80, 160, 320, 480] {
330 let input: Vec<i16> = (0..len).map(|i| (i * 100) as i16).collect();
331 let mut encoded = Vec::new();
332 let consumed = encoder.encode(&input, &mut encoded);
333 assert_eq!(consumed, len);
334 assert_eq!(encoded.len(), len);
335
336 let mut decoded = Vec::new();
337 decoder.decode(&encoded, &mut decoded);
338 assert_eq!(decoded.len(), len);
339 }
340 }
341
342 #[test]
343 fn test_codec_type_hash_eq() {
344 use std::collections::HashSet;
345 let mut set = HashSet::new();
346 set.insert(CodecType::Pcmu);
347 set.insert(CodecType::Pcma);
348 set.insert(CodecType::Pcmu); assert_eq!(set.len(), 2);
351 assert!(set.contains(&CodecType::Pcmu));
352 assert!(set.contains(&CodecType::Pcma));
353 }
354
355 #[test]
356 fn test_codec_type_clone_copy() {
357 let codec = CodecType::Pcmu;
358 let copied = codec; let cloned = copied; assert_eq!(codec, cloned);
362 assert_eq!(codec, copied);
363 }
364}