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
87pub struct CodecFactory;
89
90impl CodecFactory {
91 #[cfg_attr(
101 not(any(feature = "g711", feature = "g729", feature = "opus")),
102 allow(clippy::needless_pass_by_value)
103 )]
104 pub fn create(config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
105 config.validate()?;
107
108 match config.codec_type {
109 #[cfg(feature = "g711")]
110 CodecType::G711Pcmu => {
111 let codec = g711::G711Codec::new_pcmu(config)?;
112 Ok(Box::new(codec))
113 }
114
115 #[cfg(feature = "g711")]
116 CodecType::G711Pcma => {
117 let codec = g711::G711Codec::new_pcma(config)?;
118 Ok(Box::new(codec))
119 }
120
121 #[cfg(feature = "g729")]
122 CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
123 let codec = g729::G729Codec::new(config)?;
124 Ok(Box::new(codec))
125 }
126
127 #[cfg(feature = "opus")]
128 CodecType::Opus => {
129 let codec = opus::OpusCodec::new(config)?;
130 Ok(Box::new(codec))
131 }
132
133 codec_type => Err(CodecError::feature_not_enabled(format!(
134 "Codec {codec_type} not enabled in build features"
135 ))),
136 }
137 }
138
139 pub fn create_by_name(name: &str, config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
146 let codec_type = match normalize_codec_name(name).as_str() {
147 "PCMU" => CodecType::G711Pcmu,
148 "PCMA" => CodecType::G711Pcma,
149 "G729" => CodecType::G729,
150 "G729A" => CodecType::G729A,
151 "G729AB" | "G729BA" => CodecType::G729BA,
152 "OPUS" => CodecType::Opus,
153 _ => return Err(CodecError::unsupported_codec(name)),
154 };
155
156 let config = CodecConfig {
157 codec_type,
158 ..config
159 };
160
161 Self::create(config)
162 }
163
164 pub fn create_by_payload_type(
171 payload_type: u8,
172 config: CodecConfig,
173 ) -> Result<Box<dyn AudioCodec>> {
174 let codec_type = match payload_type {
175 0 => CodecType::G711Pcmu,
176 8 => CodecType::G711Pcma,
177 18 => CodecType::G729,
178
179 _ => return Err(CodecError::unsupported_codec(format!("PT{payload_type}"))),
180 };
181
182 let config = CodecConfig {
183 codec_type,
184 ..config
185 };
186
187 Self::create(config)
188 }
189
190 #[must_use]
192 pub fn supported_codecs() -> Vec<&'static str> {
193 vec![
194 #[cfg(feature = "g711")]
195 "PCMU",
196 #[cfg(feature = "g711")]
197 "PCMA",
198 #[cfg(feature = "g729")]
199 "G729",
200 #[cfg(feature = "g729")]
201 "G729A",
202 #[cfg(feature = "g729")]
203 "G729BA",
204 #[cfg(feature = "opus")]
205 "OPUS",
206 ]
207 }
208
209 #[must_use]
211 pub fn is_supported(name: &str) -> bool {
212 let normalized = normalize_codec_name(name);
213 match normalized.as_str() {
214 #[cfg(feature = "g711")]
215 "PCMU" | "PCMA" => true,
216 #[cfg(feature = "g729")]
217 "G729" | "G729A" | "G729AB" | "G729BA" => true,
218 #[cfg(feature = "opus")]
219 "OPUS" => true,
220 _ => false,
221 }
222 }
223}
224
225fn normalize_codec_name(name: &str) -> String {
226 name.to_ascii_uppercase().replace('.', "")
227}
228
229pub struct CodecRegistry {
231 codecs: HashMap<String, Box<dyn AudioCodec>>,
232}
233
234impl CodecRegistry {
235 #[must_use]
237 pub fn new() -> Self {
238 Self {
239 codecs: HashMap::new(),
240 }
241 }
242
243 pub fn register(&mut self, name: String, codec: Box<dyn AudioCodec>) {
245 self.codecs.insert(name, codec);
246 }
247
248 #[must_use]
250 pub fn get(&self, name: &str) -> Option<&dyn AudioCodec> {
251 self.codecs.get(name).map(std::convert::AsRef::as_ref)
252 }
253
254 pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioCodec>> {
256 self.codecs.get_mut(name)
257 }
258
259 pub fn remove(&mut self, name: &str) -> Option<Box<dyn AudioCodec>> {
261 self.codecs.remove(name)
262 }
263
264 #[must_use]
266 pub fn list_codecs(&self) -> Vec<&String> {
267 self.codecs.keys().collect()
268 }
269
270 #[must_use]
272 pub fn len(&self) -> usize {
273 self.codecs.len()
274 }
275
276 #[must_use]
278 pub fn is_empty(&self) -> bool {
279 self.codecs.is_empty()
280 }
281
282 pub fn clear(&mut self) {
284 self.codecs.clear();
285 }
286}
287
288impl Default for CodecRegistry {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294#[derive(Debug, Clone)]
296pub struct CodecCapabilities {
297 pub codec_types: Vec<CodecType>,
299 pub codec_info: HashMap<CodecType, CodecInfo>,
301}
302
303impl CodecCapabilities {
304 #[must_use]
306 pub fn get_all() -> Self {
307 #[allow(unused_mut)]
310 let mut codec_types = Vec::new();
311 #[allow(unused_mut)]
312 let mut codec_info = HashMap::new();
313
314 #[cfg(feature = "g711")]
315 {
316 codec_types.push(CodecType::G711Pcmu);
317 codec_types.push(CodecType::G711Pcma);
318
319 codec_info.insert(
320 CodecType::G711Pcmu,
321 CodecInfo {
322 name: "PCMU",
323 sample_rate: 8000,
324 channels: 1,
325 bitrate: 64000,
326 frame_size: 160,
327 payload_type: Some(0),
328 },
329 );
330
331 codec_info.insert(
332 CodecType::G711Pcma,
333 CodecInfo {
334 name: "PCMA",
335 sample_rate: 8000,
336 channels: 1,
337 bitrate: 64000,
338 frame_size: 160,
339 payload_type: Some(8),
340 },
341 );
342 }
343
344 #[cfg(feature = "opus")]
345 {
346 codec_types.push(CodecType::Opus);
347 codec_info.insert(
348 CodecType::Opus,
349 CodecInfo {
350 name: "opus",
351 sample_rate: 48000,
352 channels: 1,
353 bitrate: 64000,
354 frame_size: 960,
355 payload_type: None,
356 },
357 );
358 }
359
360 #[cfg(feature = "g729")]
361 {
362 codec_types.push(CodecType::G729);
363 codec_types.push(CodecType::G729A);
364 codec_types.push(CodecType::G729BA);
365
366 codec_info.insert(
367 CodecType::G729,
368 CodecInfo {
369 name: "G729",
370 sample_rate: 8000,
371 channels: 1,
372 bitrate: 8000,
373 frame_size: 80,
374 payload_type: Some(18),
375 },
376 );
377 codec_info.insert(
378 CodecType::G729A,
379 CodecInfo {
380 name: "G729A",
381 sample_rate: 8000,
382 channels: 1,
383 bitrate: 8000,
384 frame_size: 80,
385 payload_type: Some(18),
386 },
387 );
388 codec_info.insert(
389 CodecType::G729BA,
390 CodecInfo {
391 name: "G729BA",
392 sample_rate: 8000,
393 channels: 1,
394 bitrate: 8000,
395 frame_size: 80,
396 payload_type: Some(18),
397 },
398 );
399 }
400
401 Self {
402 codec_types,
403 codec_info,
404 }
405 }
406
407 #[must_use]
409 pub fn is_supported(&self, codec_type: CodecType) -> bool {
410 self.codec_types.contains(&codec_type)
411 }
412
413 #[must_use]
415 pub fn get_info(&self, codec_type: CodecType) -> Option<&CodecInfo> {
416 self.codec_info.get(&codec_type)
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 #[test]
425 fn test_codec_factory_supported_codecs() {
426 let supported = CodecFactory::supported_codecs();
427
428 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
429 assert!(!supported.is_empty());
430
431 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
432 assert!(supported.is_empty());
433
434 #[cfg(feature = "g711")]
435 {
436 assert!(supported.contains(&"PCMU"));
437 assert!(supported.contains(&"PCMA"));
438 }
439 }
440
441 #[test]
442 fn test_codec_factory_is_supported() {
443 #[cfg(feature = "g711")]
444 {
445 assert!(CodecFactory::is_supported("PCMU"));
446 assert!(CodecFactory::is_supported("pcmu"));
447 assert!(CodecFactory::is_supported("PCMA"));
448 }
449
450 assert!(!CodecFactory::is_supported("UNSUPPORTED"));
451 assert!(!CodecFactory::is_supported("G722"));
452
453 #[cfg(feature = "opus")]
454 for name in ["opus", "Opus", "OPUS"] {
455 assert!(CodecFactory::is_supported(name));
456 }
457 #[cfg(not(feature = "opus"))]
458 for name in ["opus", "Opus", "OPUS"] {
459 assert!(!CodecFactory::is_supported(name));
460 }
461 }
462
463 #[test]
464 fn test_codec_registry() {
465 let mut registry = CodecRegistry::new();
466 assert!(registry.is_empty());
467 assert_eq!(registry.len(), 0);
468
469 #[cfg(feature = "g711")]
470 {
471 let config = CodecConfig::g711_pcmu();
472 let codec = CodecFactory::create(config).unwrap();
473 registry.register("test_pcmu".to_string(), codec);
474
475 assert_eq!(registry.len(), 1);
476 assert!(!registry.is_empty());
477 assert!(registry.get("test_pcmu").is_some());
478 }
479
480 registry.clear();
481 assert!(registry.is_empty());
482 }
483
484 #[test]
485 fn test_codec_capabilities() {
486 let caps = CodecCapabilities::get_all();
487
488 #[cfg(any(feature = "g711", feature = "g729", feature = "opus"))]
489 {
490 assert!(!caps.codec_types.is_empty());
491 assert!(!caps.codec_info.is_empty());
492 }
493
494 #[cfg(not(any(feature = "g711", feature = "g729", feature = "opus")))]
495 {
496 assert!(caps.codec_types.is_empty());
497 assert!(caps.codec_info.is_empty());
498 }
499
500 #[cfg(feature = "g711")]
501 {
502 assert!(caps.is_supported(CodecType::G711Pcmu));
503 assert!(caps.get_info(CodecType::G711Pcmu).is_some());
504 }
505 }
506
507 #[test]
508 #[cfg(feature = "g711")]
509 fn test_codec_creation() {
510 let config = CodecConfig::g711_pcmu();
511 let codec = CodecFactory::create(config);
512 assert!(codec.is_ok());
513
514 let codec = codec.unwrap();
515 let info = codec.info();
516 assert_eq!(info.name, "PCMU");
517 assert_eq!(info.sample_rate, 8000);
518 }
519
520 #[test]
521 #[cfg(feature = "g711")]
522 fn test_codec_creation_by_name() {
523 let config = CodecConfig::new(CodecType::G711Pcmu);
524 let codec = CodecFactory::create_by_name("PCMU", config.clone());
525 assert!(codec.is_ok());
526
527 let codec = CodecFactory::create_by_name("UNKNOWN", config);
528 assert!(codec.is_err());
529 }
530
531 #[test]
532 #[cfg(feature = "g711")]
533 fn test_codec_creation_by_payload_type() {
534 let config = CodecConfig::new(CodecType::G711Pcmu);
535 let codec = CodecFactory::create_by_payload_type(0, config.clone());
536 assert!(codec.is_ok());
537
538 let codec = CodecFactory::create_by_payload_type(255, config);
539 assert!(codec.is_err());
540 }
541}