1use crate::error::{CodecError, Result};
68use crate::types::{AudioCodec, CodecConfig, CodecInfo, CodecType};
69use std::collections::HashMap;
70
71#[cfg(feature = "g711")]
73pub mod g711;
74
75#[cfg(feature = "g729")]
76pub mod g729;
77
78#[cfg(any(feature = "opus", feature = "opus-sim"))]
79pub mod opus;
80
81pub struct CodecFactory;
83
84impl CodecFactory {
85 pub fn create(config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
87 config.validate()?;
89
90 match config.codec_type {
91 #[cfg(feature = "g711")]
92 CodecType::G711Pcmu => {
93 let codec = g711::G711Codec::new_pcmu(config)?;
94 Ok(Box::new(codec))
95 }
96
97 #[cfg(feature = "g711")]
98 CodecType::G711Pcma => {
99 let codec = g711::G711Codec::new_pcma(config)?;
100 Ok(Box::new(codec))
101 }
102
103 #[cfg(feature = "g729")]
104 CodecType::G729 | CodecType::G729A | CodecType::G729BA => {
105 let codec = g729::G729Codec::new(config)?;
106 Ok(Box::new(codec))
107 }
108
109 #[cfg(any(feature = "opus", feature = "opus-sim"))]
110 CodecType::Opus => {
111 let codec = opus::OpusCodec::new(config)?;
112 Ok(Box::new(codec))
113 }
114
115 codec_type => Err(CodecError::feature_not_enabled(format!(
116 "Codec {} not enabled in build features",
117 codec_type.name()
118 ))),
119 }
120 }
121
122 pub fn create_by_name(name: &str, config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
124 let codec_type = match normalize_codec_name(name).as_str() {
125 "PCMU" => CodecType::G711Pcmu,
126 "PCMA" => CodecType::G711Pcma,
127 "G729" => CodecType::G729,
128 "G729A" => CodecType::G729A,
129 "G729AB" | "G729BA" => CodecType::G729BA,
130 "OPUS" => CodecType::Opus,
131 _ => return Err(CodecError::unsupported_codec(name)),
132 };
133
134 let config = CodecConfig {
135 codec_type,
136 ..config
137 };
138
139 Self::create(config)
140 }
141
142 pub fn create_by_payload_type(
144 payload_type: u8,
145 config: CodecConfig,
146 ) -> Result<Box<dyn AudioCodec>> {
147 let codec_type = match payload_type {
148 0 => CodecType::G711Pcmu,
149 8 => CodecType::G711Pcma,
150 18 => CodecType::G729,
151
152 _ => return Err(CodecError::unsupported_codec(format!("PT{}", payload_type))),
153 };
154
155 let config = CodecConfig {
156 codec_type,
157 ..config
158 };
159
160 Self::create(config)
161 }
162
163 pub fn supported_codecs() -> Vec<&'static str> {
165 vec![
166 #[cfg(feature = "g711")]
167 "PCMU",
168 #[cfg(feature = "g711")]
169 "PCMA",
170 #[cfg(feature = "g729")]
171 "G729",
172 #[cfg(feature = "g729")]
173 "G729A",
174 #[cfg(feature = "g729")]
175 "G729BA",
176 #[cfg(any(feature = "opus", feature = "opus-sim"))]
177 "OPUS",
178 ]
179 }
180
181 pub fn is_supported(name: &str) -> bool {
183 let normalized = normalize_codec_name(name);
184 match normalized.as_str() {
185 #[cfg(feature = "g711")]
186 "PCMU" | "PCMA" => true,
187 #[cfg(feature = "g729")]
188 "G729" | "G729A" | "G729AB" | "G729BA" => true,
189 #[cfg(any(feature = "opus", feature = "opus-sim"))]
190 "OPUS" => true,
191 _ => false,
192 }
193 }
194}
195
196fn normalize_codec_name(name: &str) -> String {
197 name.to_ascii_uppercase().replace('.', "")
198}
199
200pub struct CodecRegistry {
202 codecs: HashMap<String, Box<dyn AudioCodec>>,
203}
204
205impl CodecRegistry {
206 pub fn new() -> Self {
208 Self {
209 codecs: HashMap::new(),
210 }
211 }
212
213 pub fn register(&mut self, name: String, codec: Box<dyn AudioCodec>) {
215 self.codecs.insert(name, codec);
216 }
217
218 pub fn get(&self, name: &str) -> Option<&dyn AudioCodec> {
220 self.codecs.get(name).map(|codec| codec.as_ref())
221 }
222
223 pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioCodec>> {
225 self.codecs.get_mut(name)
226 }
227
228 pub fn remove(&mut self, name: &str) -> Option<Box<dyn AudioCodec>> {
230 self.codecs.remove(name)
231 }
232
233 pub fn list_codecs(&self) -> Vec<&String> {
235 self.codecs.keys().collect()
236 }
237
238 pub fn len(&self) -> usize {
240 self.codecs.len()
241 }
242
243 pub fn is_empty(&self) -> bool {
245 self.codecs.is_empty()
246 }
247
248 pub fn clear(&mut self) {
250 self.codecs.clear();
251 }
252}
253
254impl Default for CodecRegistry {
255 fn default() -> Self {
256 Self::new()
257 }
258}
259
260#[derive(Debug, Clone)]
262pub struct CodecCapabilities {
263 pub codec_types: Vec<CodecType>,
265 pub codec_info: HashMap<CodecType, CodecInfo>,
267}
268
269impl CodecCapabilities {
270 pub fn get_all() -> Self {
272 let mut codec_types = Vec::new();
273 let mut codec_info = HashMap::new();
274
275 #[cfg(feature = "g711")]
276 {
277 codec_types.push(CodecType::G711Pcmu);
278 codec_types.push(CodecType::G711Pcma);
279
280 codec_info.insert(
281 CodecType::G711Pcmu,
282 CodecInfo {
283 name: "PCMU",
284 sample_rate: 8000,
285 channels: 1,
286 bitrate: 64000,
287 frame_size: 160,
288 payload_type: Some(0),
289 },
290 );
291
292 codec_info.insert(
293 CodecType::G711Pcma,
294 CodecInfo {
295 name: "PCMA",
296 sample_rate: 8000,
297 channels: 1,
298 bitrate: 64000,
299 frame_size: 160,
300 payload_type: Some(8),
301 },
302 );
303 }
304
305 #[cfg(any(feature = "opus", feature = "opus-sim"))]
306 {
307 codec_types.push(CodecType::Opus);
308 codec_info.insert(
309 CodecType::Opus,
310 CodecInfo {
311 name: "opus",
312 sample_rate: 48000,
313 channels: 1,
314 bitrate: 64000,
315 frame_size: 960,
316 payload_type: None,
317 },
318 );
319 }
320
321 #[cfg(feature = "g729")]
322 {
323 codec_types.push(CodecType::G729);
324 codec_types.push(CodecType::G729A);
325 codec_types.push(CodecType::G729BA);
326
327 codec_info.insert(
328 CodecType::G729,
329 CodecInfo {
330 name: "G729",
331 sample_rate: 8000,
332 channels: 1,
333 bitrate: 8000,
334 frame_size: 80,
335 payload_type: Some(18),
336 },
337 );
338 codec_info.insert(
339 CodecType::G729A,
340 CodecInfo {
341 name: "G729A",
342 sample_rate: 8000,
343 channels: 1,
344 bitrate: 8000,
345 frame_size: 80,
346 payload_type: Some(18),
347 },
348 );
349 codec_info.insert(
350 CodecType::G729BA,
351 CodecInfo {
352 name: "G729BA",
353 sample_rate: 8000,
354 channels: 1,
355 bitrate: 8000,
356 frame_size: 80,
357 payload_type: Some(18),
358 },
359 );
360 }
361
362 Self {
363 codec_types,
364 codec_info,
365 }
366 }
367
368 pub fn is_supported(&self, codec_type: CodecType) -> bool {
370 self.codec_types.contains(&codec_type)
371 }
372
373 pub fn get_info(&self, codec_type: CodecType) -> Option<&CodecInfo> {
375 self.codec_info.get(&codec_type)
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_codec_factory_supported_codecs() {
385 let supported = CodecFactory::supported_codecs();
386 assert!(!supported.is_empty());
387
388 #[cfg(feature = "g711")]
389 {
390 assert!(supported.contains(&"PCMU"));
391 assert!(supported.contains(&"PCMA"));
392 }
393 }
394
395 #[test]
396 fn test_codec_factory_is_supported() {
397 #[cfg(feature = "g711")]
398 {
399 assert!(CodecFactory::is_supported("PCMU"));
400 assert!(CodecFactory::is_supported("pcmu"));
401 assert!(CodecFactory::is_supported("PCMA"));
402 }
403
404 assert!(!CodecFactory::is_supported("UNSUPPORTED"));
405 }
406
407 #[test]
408 fn test_codec_registry() {
409 let mut registry = CodecRegistry::new();
410 assert!(registry.is_empty());
411 assert_eq!(registry.len(), 0);
412
413 #[cfg(feature = "g711")]
414 {
415 let config = CodecConfig::g711_pcmu();
416 let codec = CodecFactory::create(config).unwrap();
417 registry.register("test_pcmu".to_string(), codec);
418
419 assert_eq!(registry.len(), 1);
420 assert!(!registry.is_empty());
421 assert!(registry.get("test_pcmu").is_some());
422 }
423
424 registry.clear();
425 assert!(registry.is_empty());
426 }
427
428 #[test]
429 fn test_codec_capabilities() {
430 let caps = CodecCapabilities::get_all();
431 assert!(!caps.codec_types.is_empty());
432 assert!(!caps.codec_info.is_empty());
433
434 #[cfg(feature = "g711")]
435 {
436 assert!(caps.is_supported(CodecType::G711Pcmu));
437 assert!(caps.get_info(CodecType::G711Pcmu).is_some());
438 }
439 }
440
441 #[test]
442 #[cfg(feature = "g711")]
443 fn test_codec_creation() {
444 let config = CodecConfig::g711_pcmu();
445 let codec = CodecFactory::create(config);
446 assert!(codec.is_ok());
447
448 let codec = codec.unwrap();
449 let info = codec.info();
450 assert_eq!(info.name, "PCMU");
451 assert_eq!(info.sample_rate, 8000);
452 }
453
454 #[test]
455 #[cfg(feature = "g711")]
456 fn test_codec_creation_by_name() {
457 let config = CodecConfig::new(CodecType::G711Pcmu);
458 let codec = CodecFactory::create_by_name("PCMU", config.clone());
459 assert!(codec.is_ok());
460
461 let codec = CodecFactory::create_by_name("UNKNOWN", config);
462 assert!(codec.is_err());
463 }
464
465 #[test]
466 #[cfg(feature = "g711")]
467 fn test_codec_creation_by_payload_type() {
468 let config = CodecConfig::new(CodecType::G711Pcmu);
469 let codec = CodecFactory::create_by_payload_type(0, config.clone());
470 assert!(codec.is_ok());
471
472 let codec = CodecFactory::create_by_payload_type(255, config);
473 assert!(codec.is_err());
474 }
475}