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(any(feature = "opus", feature = "opus-sim"))]
76pub mod opus;
77
78pub struct CodecFactory;
80
81impl CodecFactory {
82 pub fn create(config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
84 config.validate()?;
86
87 match config.codec_type {
88 #[cfg(feature = "g711")]
89 CodecType::G711Pcmu => {
90 let codec = g711::G711Codec::new_pcmu(config)?;
91 Ok(Box::new(codec))
92 }
93
94 #[cfg(feature = "g711")]
95 CodecType::G711Pcma => {
96 let codec = g711::G711Codec::new_pcma(config)?;
97 Ok(Box::new(codec))
98 }
99
100 #[cfg(any(feature = "opus", feature = "opus-sim"))]
101 CodecType::Opus => {
102 let codec = opus::OpusCodec::new(config)?;
103 Ok(Box::new(codec))
104 }
105
106 codec_type => Err(CodecError::feature_not_enabled(format!(
107 "Codec {} not enabled in build features",
108 codec_type.name()
109 ))),
110 }
111 }
112
113 pub fn create_by_name(name: &str, config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
115 let codec_type = match name.to_uppercase().as_str() {
116 "PCMU" => CodecType::G711Pcmu,
117 "PCMA" => CodecType::G711Pcma,
118
119 "OPUS" => CodecType::Opus,
120 _ => return Err(CodecError::unsupported_codec(name)),
121 };
122
123 let config = CodecConfig {
124 codec_type,
125 ..config
126 };
127
128 Self::create(config)
129 }
130
131 pub fn create_by_payload_type(
133 payload_type: u8,
134 config: CodecConfig,
135 ) -> Result<Box<dyn AudioCodec>> {
136 let codec_type = match payload_type {
137 0 => CodecType::G711Pcmu,
138 8 => CodecType::G711Pcma,
139
140 _ => return Err(CodecError::unsupported_codec(format!("PT{}", payload_type))),
141 };
142
143 let config = CodecConfig {
144 codec_type,
145 ..config
146 };
147
148 Self::create(config)
149 }
150
151 pub fn supported_codecs() -> Vec<&'static str> {
153 vec![
154 #[cfg(feature = "g711")]
155 "PCMU",
156 #[cfg(feature = "g711")]
157 "PCMA",
158 #[cfg(any(feature = "opus", feature = "opus-sim"))]
159 "OPUS",
160 ]
161 }
162
163 pub fn is_supported(name: &str) -> bool {
165 Self::supported_codecs().contains(&name.to_uppercase().as_str())
166 }
167}
168
169pub struct CodecRegistry {
171 codecs: HashMap<String, Box<dyn AudioCodec>>,
172}
173
174impl CodecRegistry {
175 pub fn new() -> Self {
177 Self {
178 codecs: HashMap::new(),
179 }
180 }
181
182 pub fn register(&mut self, name: String, codec: Box<dyn AudioCodec>) {
184 self.codecs.insert(name, codec);
185 }
186
187 pub fn get(&self, name: &str) -> Option<&dyn AudioCodec> {
189 self.codecs.get(name).map(|codec| codec.as_ref())
190 }
191
192 pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioCodec>> {
194 self.codecs.get_mut(name)
195 }
196
197 pub fn remove(&mut self, name: &str) -> Option<Box<dyn AudioCodec>> {
199 self.codecs.remove(name)
200 }
201
202 pub fn list_codecs(&self) -> Vec<&String> {
204 self.codecs.keys().collect()
205 }
206
207 pub fn len(&self) -> usize {
209 self.codecs.len()
210 }
211
212 pub fn is_empty(&self) -> bool {
214 self.codecs.is_empty()
215 }
216
217 pub fn clear(&mut self) {
219 self.codecs.clear();
220 }
221}
222
223impl Default for CodecRegistry {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229#[derive(Debug, Clone)]
231pub struct CodecCapabilities {
232 pub codec_types: Vec<CodecType>,
234 pub codec_info: HashMap<CodecType, CodecInfo>,
236}
237
238impl CodecCapabilities {
239 pub fn get_all() -> Self {
241 let mut codec_types = Vec::new();
242 let mut codec_info = HashMap::new();
243
244 #[cfg(feature = "g711")]
245 {
246 codec_types.push(CodecType::G711Pcmu);
247 codec_types.push(CodecType::G711Pcma);
248
249 codec_info.insert(
250 CodecType::G711Pcmu,
251 CodecInfo {
252 name: "PCMU",
253 sample_rate: 8000,
254 channels: 1,
255 bitrate: 64000,
256 frame_size: 160,
257 payload_type: Some(0),
258 },
259 );
260
261 codec_info.insert(
262 CodecType::G711Pcma,
263 CodecInfo {
264 name: "PCMA",
265 sample_rate: 8000,
266 channels: 1,
267 bitrate: 64000,
268 frame_size: 160,
269 payload_type: Some(8),
270 },
271 );
272 }
273
274 #[cfg(any(feature = "opus", feature = "opus-sim"))]
275 {
276 codec_types.push(CodecType::Opus);
277 codec_info.insert(
278 CodecType::Opus,
279 CodecInfo {
280 name: "opus",
281 sample_rate: 48000,
282 channels: 1,
283 bitrate: 64000,
284 frame_size: 960,
285 payload_type: None,
286 },
287 );
288 }
289
290 Self {
291 codec_types,
292 codec_info,
293 }
294 }
295
296 pub fn is_supported(&self, codec_type: CodecType) -> bool {
298 self.codec_types.contains(&codec_type)
299 }
300
301 pub fn get_info(&self, codec_type: CodecType) -> Option<&CodecInfo> {
303 self.codec_info.get(&codec_type)
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310
311
312 #[test]
313 fn test_codec_factory_supported_codecs() {
314 let supported = CodecFactory::supported_codecs();
315 assert!(!supported.is_empty());
316
317 #[cfg(feature = "g711")]
318 {
319 assert!(supported.contains(&"PCMU"));
320 assert!(supported.contains(&"PCMA"));
321 }
322 }
323
324 #[test]
325 fn test_codec_factory_is_supported() {
326 #[cfg(feature = "g711")]
327 {
328 assert!(CodecFactory::is_supported("PCMU"));
329 assert!(CodecFactory::is_supported("pcmu"));
330 assert!(CodecFactory::is_supported("PCMA"));
331 }
332
333 assert!(!CodecFactory::is_supported("UNSUPPORTED"));
334 }
335
336 #[test]
337 fn test_codec_registry() {
338 let mut registry = CodecRegistry::new();
339 assert!(registry.is_empty());
340 assert_eq!(registry.len(), 0);
341
342 #[cfg(feature = "g711")]
343 {
344 let config = CodecConfig::g711_pcmu();
345 let codec = CodecFactory::create(config).unwrap();
346 registry.register("test_pcmu".to_string(), codec);
347
348 assert_eq!(registry.len(), 1);
349 assert!(!registry.is_empty());
350 assert!(registry.get("test_pcmu").is_some());
351 }
352
353 registry.clear();
354 assert!(registry.is_empty());
355 }
356
357 #[test]
358 fn test_codec_capabilities() {
359 let caps = CodecCapabilities::get_all();
360 assert!(!caps.codec_types.is_empty());
361 assert!(!caps.codec_info.is_empty());
362
363 #[cfg(feature = "g711")]
364 {
365 assert!(caps.is_supported(CodecType::G711Pcmu));
366 assert!(caps.get_info(CodecType::G711Pcmu).is_some());
367 }
368 }
369
370 #[test]
371 #[cfg(feature = "g711")]
372 fn test_codec_creation() {
373 let config = CodecConfig::g711_pcmu();
374 let codec = CodecFactory::create(config);
375 assert!(codec.is_ok());
376
377 let codec = codec.unwrap();
378 let info = codec.info();
379 assert_eq!(info.name, "PCMU");
380 assert_eq!(info.sample_rate, 8000);
381 }
382
383 #[test]
384 #[cfg(feature = "g711")]
385 fn test_codec_creation_by_name() {
386 let config = CodecConfig::new(CodecType::G711Pcmu);
387 let codec = CodecFactory::create_by_name("PCMU", config.clone());
388 assert!(codec.is_ok());
389
390 let codec = CodecFactory::create_by_name("UNKNOWN", config);
391 assert!(codec.is_err());
392 }
393
394 #[test]
395 #[cfg(feature = "g711")]
396 fn test_codec_creation_by_payload_type() {
397 let config = CodecConfig::new(CodecType::G711Pcmu);
398 let codec = CodecFactory::create_by_payload_type(0, config.clone());
399 assert!(codec.is_ok());
400
401 let codec = CodecFactory::create_by_payload_type(255, config);
402 assert!(codec.is_err());
403 }
404}