1use crate::error::{CodecError, Result};
74use crate::types::{AudioCodec, CodecConfig, CodecInfo, CodecType};
75use std::collections::HashMap;
76
77#[cfg(feature = "g711")]
79pub mod g711;
80
81#[cfg(feature = "g729")]
82pub mod g729;
83
84#[cfg(feature = "opus")]
85pub mod opus;
86
87#[cfg(any(feature = "amr-nb", feature = "amr-wb"))]
88pub mod amr;
89
90pub struct CodecFactory;
92
93impl CodecFactory {
94 #[cfg_attr(
104 not(any(feature = "g711", feature = "g729", feature = "opus")),
105 allow(clippy::needless_pass_by_value)
106 )]
107 pub fn create(config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
108 config.validate()?;
110
111 match config.codec_type {
112 #[cfg(feature = "g711")]
113 CodecType::G711Pcmu => {
114 let codec = g711::G711Codec::new_pcmu(config)?;
115 Ok(Box::new(codec))
116 }
117
118 #[cfg(feature = "g711")]
119 CodecType::G711Pcma => {
120 let codec = g711::G711Codec::new_pcma(config)?;
121 Ok(Box::new(codec))
122 }
123
124 #[cfg(feature = "g729")]
125 CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
126 let codec = g729::G729Codec::new(config)?;
127 Ok(Box::new(codec))
128 }
129
130 #[cfg(feature = "opus")]
131 CodecType::Opus => {
132 let codec = opus::OpusCodec::new(config)?;
133 Ok(Box::new(codec))
134 }
135
136 #[cfg(feature = "amr-nb")]
137 CodecType::AmrNb => {
138 let codec = amr::AmrCodec::new(&config)?;
139 Ok(Box::new(codec))
140 }
141
142 #[cfg(feature = "amr-wb")]
143 CodecType::AmrWb => {
144 let codec = amr::AmrCodec::new(&config)?;
145 Ok(Box::new(codec))
146 }
147
148 codec_type => Err(CodecError::feature_not_enabled(format!(
149 "Codec {codec_type} not enabled in build features"
150 ))),
151 }
152 }
153
154 pub fn create_by_name(name: &str, config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
161 let codec_type = match normalize_codec_name(name).as_str() {
162 "PCMU" => CodecType::G711Pcmu,
163 "PCMA" => CodecType::G711Pcma,
164 "G729" => CodecType::G729,
165 "G729A" => CodecType::G729A,
166 "G729AB" | "G729BA" => CodecType::G729BA,
167 "OPUS" => CodecType::Opus,
168 "AMR" => CodecType::AmrNb,
169 "AMR-WB" | "AMRWB" => CodecType::AmrWb,
170 _ => return Err(CodecError::unsupported_codec(name)),
171 };
172
173 let config = CodecConfig {
174 codec_type,
175 ..config
176 };
177
178 Self::create(config)
179 }
180
181 pub fn create_by_payload_type(
188 payload_type: u8,
189 config: CodecConfig,
190 ) -> Result<Box<dyn AudioCodec>> {
191 let codec_type = match payload_type {
192 0 => CodecType::G711Pcmu,
193 8 => CodecType::G711Pcma,
194 18 => CodecType::G729,
195
196 _ => return Err(CodecError::unsupported_codec(format!("PT{payload_type}"))),
197 };
198
199 let config = CodecConfig {
200 codec_type,
201 ..config
202 };
203
204 Self::create(config)
205 }
206
207 #[must_use]
209 pub fn supported_codecs() -> Vec<&'static str> {
210 vec![
211 #[cfg(feature = "g711")]
212 "PCMU",
213 #[cfg(feature = "g711")]
214 "PCMA",
215 #[cfg(feature = "g729")]
216 "G729",
217 #[cfg(feature = "g729")]
218 "G729A",
219 #[cfg(feature = "g729")]
220 "G729BA",
221 #[cfg(feature = "opus")]
222 "OPUS",
223 #[cfg(feature = "amr-nb")]
224 "AMR",
225 #[cfg(feature = "amr-wb")]
226 "AMR-WB",
227 ]
228 }
229
230 #[must_use]
232 pub fn is_supported(name: &str) -> bool {
233 let normalized = normalize_codec_name(name);
234 match normalized.as_str() {
235 #[cfg(feature = "g711")]
236 "PCMU" | "PCMA" => true,
237 #[cfg(feature = "g729")]
238 "G729" | "G729A" | "G729AB" | "G729BA" => true,
239 #[cfg(feature = "opus")]
240 "OPUS" => true,
241 #[cfg(feature = "amr-nb")]
242 "AMR" => true,
243 #[cfg(feature = "amr-wb")]
244 "AMR-WB" | "AMRWB" => true,
245 _ => false,
246 }
247 }
248}
249
250fn normalize_codec_name(name: &str) -> String {
251 name.to_ascii_uppercase().replace('.', "")
252}
253
254pub struct CodecRegistry {
256 codecs: HashMap<String, Box<dyn AudioCodec>>,
257}
258
259impl CodecRegistry {
260 #[must_use]
262 pub fn new() -> Self {
263 Self {
264 codecs: HashMap::new(),
265 }
266 }
267
268 pub fn register(&mut self, name: String, codec: Box<dyn AudioCodec>) {
270 self.codecs.insert(name, codec);
271 }
272
273 #[must_use]
275 pub fn get(&self, name: &str) -> Option<&dyn AudioCodec> {
276 self.codecs.get(name).map(std::convert::AsRef::as_ref)
277 }
278
279 pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioCodec>> {
281 self.codecs.get_mut(name)
282 }
283
284 pub fn remove(&mut self, name: &str) -> Option<Box<dyn AudioCodec>> {
286 self.codecs.remove(name)
287 }
288
289 #[must_use]
291 pub fn list_codecs(&self) -> Vec<&String> {
292 self.codecs.keys().collect()
293 }
294
295 #[must_use]
297 pub fn len(&self) -> usize {
298 self.codecs.len()
299 }
300
301 #[must_use]
303 pub fn is_empty(&self) -> bool {
304 self.codecs.is_empty()
305 }
306
307 pub fn clear(&mut self) {
309 self.codecs.clear();
310 }
311}
312
313impl Default for CodecRegistry {
314 fn default() -> Self {
315 Self::new()
316 }
317}
318
319#[derive(Debug, Clone)]
321pub struct CodecCapabilities {
322 pub codec_types: Vec<CodecType>,
324 pub codec_info: HashMap<CodecType, CodecInfo>,
326}
327
328#[allow(unused_variables)]
334fn add_amr_capabilities(
335 codec_types: &mut Vec<CodecType>,
336 codec_info: &mut HashMap<CodecType, CodecInfo>,
337) {
338 #[cfg(feature = "amr-nb")]
339 {
340 codec_types.push(CodecType::AmrNb);
341 codec_info.insert(
342 CodecType::AmrNb,
343 CodecInfo {
344 name: "AMR",
345 sample_rate: 8000,
346 channels: 1,
347 bitrate: CodecType::AmrNb.default_bitrate(),
348 frame_size: 160,
349 payload_type: None,
350 },
351 );
352 }
353
354 #[cfg(feature = "amr-wb")]
355 {
356 codec_types.push(CodecType::AmrWb);
357 codec_info.insert(
358 CodecType::AmrWb,
359 CodecInfo {
360 name: "AMR-WB",
361 sample_rate: 16000,
362 channels: 1,
363 bitrate: CodecType::AmrWb.default_bitrate(),
364 frame_size: 320,
365 payload_type: None,
366 },
367 );
368 }
369}
370
371impl CodecCapabilities {
372 #[must_use]
374 pub fn get_all() -> Self {
375 #[allow(unused_mut)]
378 let mut codec_types = Vec::new();
379 #[allow(unused_mut)]
380 let mut codec_info = HashMap::new();
381
382 #[cfg(feature = "g711")]
383 {
384 codec_types.push(CodecType::G711Pcmu);
385 codec_types.push(CodecType::G711Pcma);
386
387 codec_info.insert(
388 CodecType::G711Pcmu,
389 CodecInfo {
390 name: "PCMU",
391 sample_rate: 8000,
392 channels: 1,
393 bitrate: 64000,
394 frame_size: 160,
395 payload_type: Some(0),
396 },
397 );
398
399 codec_info.insert(
400 CodecType::G711Pcma,
401 CodecInfo {
402 name: "PCMA",
403 sample_rate: 8000,
404 channels: 1,
405 bitrate: 64000,
406 frame_size: 160,
407 payload_type: Some(8),
408 },
409 );
410 }
411
412 #[cfg(feature = "opus")]
413 {
414 codec_types.push(CodecType::Opus);
415 codec_info.insert(
416 CodecType::Opus,
417 CodecInfo {
418 name: "opus",
419 sample_rate: 48000,
420 channels: 1,
421 bitrate: 64000,
422 frame_size: 960,
423 payload_type: None,
424 },
425 );
426 }
427
428 add_amr_capabilities(&mut codec_types, &mut codec_info);
429
430 #[cfg(feature = "g729")]
431 {
432 codec_types.push(CodecType::G729);
433 codec_types.push(CodecType::G729A);
434 codec_types.push(CodecType::G729BA);
435
436 codec_info.insert(
437 CodecType::G729,
438 CodecInfo {
439 name: "G729",
440 sample_rate: 8000,
441 channels: 1,
442 bitrate: 8000,
443 frame_size: 80,
444 payload_type: Some(18),
445 },
446 );
447 codec_info.insert(
448 CodecType::G729A,
449 CodecInfo {
450 name: "G729A",
451 sample_rate: 8000,
452 channels: 1,
453 bitrate: 8000,
454 frame_size: 80,
455 payload_type: Some(18),
456 },
457 );
458 codec_info.insert(
459 CodecType::G729BA,
460 CodecInfo {
461 name: "G729BA",
462 sample_rate: 8000,
463 channels: 1,
464 bitrate: 8000,
465 frame_size: 80,
466 payload_type: Some(18),
467 },
468 );
469 }
470
471 Self {
472 codec_types,
473 codec_info,
474 }
475 }
476
477 #[must_use]
479 pub fn is_supported(&self, codec_type: CodecType) -> bool {
480 self.codec_types.contains(&codec_type)
481 }
482
483 #[must_use]
485 pub fn get_info(&self, codec_type: CodecType) -> Option<&CodecInfo> {
486 self.codec_info.get(&codec_type)
487 }
488}
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493
494 #[test]
495 fn test_codec_factory_supported_codecs() {
496 let supported = CodecFactory::supported_codecs();
497
498 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
499 assert!(!supported.is_empty());
500
501 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
502 assert!(supported.is_empty());
503
504 #[cfg(feature = "g711")]
505 {
506 assert!(supported.contains(&"PCMU"));
507 assert!(supported.contains(&"PCMA"));
508 }
509 }
510
511 #[test]
512 fn test_codec_factory_is_supported() {
513 #[cfg(feature = "g711")]
514 {
515 assert!(CodecFactory::is_supported("PCMU"));
516 assert!(CodecFactory::is_supported("pcmu"));
517 assert!(CodecFactory::is_supported("PCMA"));
518 }
519
520 assert!(!CodecFactory::is_supported("UNSUPPORTED"));
521 assert!(!CodecFactory::is_supported("G722"));
522
523 #[cfg(feature = "opus")]
524 for name in ["opus", "Opus", "OPUS"] {
525 assert!(CodecFactory::is_supported(name));
526 }
527 #[cfg(not(feature = "opus"))]
528 for name in ["opus", "Opus", "OPUS"] {
529 assert!(!CodecFactory::is_supported(name));
530 }
531 }
532
533 #[test]
534 fn test_codec_registry() {
535 let mut registry = CodecRegistry::new();
536 assert!(registry.is_empty());
537 assert_eq!(registry.len(), 0);
538
539 #[cfg(feature = "g711")]
540 {
541 let config = CodecConfig::g711_pcmu();
542 let codec = CodecFactory::create(config).unwrap();
543 registry.register("test_pcmu".to_string(), codec);
544
545 assert_eq!(registry.len(), 1);
546 assert!(!registry.is_empty());
547 assert!(registry.get("test_pcmu").is_some());
548 }
549
550 registry.clear();
551 assert!(registry.is_empty());
552 }
553
554 #[test]
555 fn test_codec_capabilities() {
556 let caps = CodecCapabilities::get_all();
557
558 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
559 {
560 assert!(!caps.codec_types.is_empty());
561 assert!(!caps.codec_info.is_empty());
562 }
563
564 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
565 {
566 assert!(caps.codec_types.is_empty());
567 assert!(caps.codec_info.is_empty());
568 }
569
570 #[cfg(feature = "g711")]
571 {
572 assert!(caps.is_supported(CodecType::G711Pcmu));
573 assert!(caps.get_info(CodecType::G711Pcmu).is_some());
574 }
575 }
576
577 #[test]
578 #[cfg(feature = "g711")]
579 fn test_codec_creation() {
580 let config = CodecConfig::g711_pcmu();
581 let codec = CodecFactory::create(config);
582 assert!(codec.is_ok());
583
584 let codec = codec.unwrap();
585 let info = codec.info();
586 assert_eq!(info.name, "PCMU");
587 assert_eq!(info.sample_rate, 8000);
588 }
589
590 #[test]
591 #[cfg(feature = "g711")]
592 fn test_codec_creation_by_name() {
593 let config = CodecConfig::new(CodecType::G711Pcmu);
594 let codec = CodecFactory::create_by_name("PCMU", config.clone());
595 assert!(codec.is_ok());
596
597 let codec = CodecFactory::create_by_name("UNKNOWN", config);
598 assert!(codec.is_err());
599 }
600
601 #[test]
602 #[cfg(feature = "g711")]
603 fn test_codec_creation_by_payload_type() {
604 let config = CodecConfig::new(CodecType::G711Pcmu);
605 let codec = CodecFactory::create_by_payload_type(0, config.clone());
606 assert!(codec.is_ok());
607
608 let codec = CodecFactory::create_by_payload_type(255, config);
609 assert!(codec.is_err());
610 }
611}