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