codec_core/codecs/
mod.rs

1//! # Audio Codec Implementations
2//!
3//! This module contains G.711 audio codec implementation for VoIP applications.
4//!
5//! ## Available Codecs
6//!
7//! ### G.711 (PCMU/PCMA) - [`g711`]
8//! - **Standard**: ITU-T G.711 
9//! - **Sample Rate**: 8 kHz
10//! - **Bitrate**: 64 kbps
11//! - **Quality**: ~37 dB SNR
12//! - **Use Case**: Standard telephony
13//! - **Variants**: μ-law (PCMU), A-law (PCMA)
14//!
15//! ## Testing
16//!
17//! G.711 is validated with real speech samples through WAV roundtrip tests:
18//! - Downloads reference audio samples
19//! - Round-trip encoding and decoding validation
20//! - Signal-to-Noise Ratio (SNR) measurement
21//!
22//! ## Usage Examples
23//!
24//! ### Using the Codec Factory
25//! ```rust
26//! use codec_core::codecs::CodecFactory;
27//! use codec_core::types::{CodecConfig, CodecType, SampleRate};
28//!
29//! // Create any codec through the factory
30//! let config = CodecConfig::new(CodecType::G711Pcmu)
31//!     .with_sample_rate(SampleRate::Rate8000);
32//! let mut codec = CodecFactory::create(config)?;
33//!
34//! // Use unified interface
35//! let samples = vec![0i16; 160];
36//! let encoded = codec.encode(&samples)?;
37//! let decoded = codec.decode(&encoded)?;
38//! # Ok::<(), Box<dyn std::error::Error>>(())
39//! ```
40//!
41//! ### Direct Codec Access  
42//! ```rust
43//! use codec_core::codecs::g711::{G711Codec, G711Variant};
44//!
45//! // Direct instantiation  
46//! let mut g711_ulaw = G711Codec::new(G711Variant::MuLaw);
47//! let mut g711_alaw = G711Codec::new(G711Variant::ALaw);
48//! # Ok::<(), Box<dyn std::error::Error>>(())
49//! ```
50//!
51//! ## Testing & Validation
52//!
53//! All codecs include comprehensive test suites:
54//! - ITU-T compliance validation
55//! - Real audio roundtrip tests
56//! - Performance benchmarks
57//! - Quality measurements (SNR)
58//!
59//! ```bash
60//! # Test all codecs
61//! cargo test
62//!
63//! # Test with real audio (downloads speech samples)
64//! cargo test wav_roundtrip_test -- --nocapture
65//! ```
66
67use crate::error::{CodecError, Result};
68use crate::types::{AudioCodec, CodecConfig, CodecInfo, CodecType};
69use std::collections::HashMap;
70
71// Codec implementations
72#[cfg(feature = "g711")]
73pub mod g711;
74
75
76
77#[cfg(any(feature = "opus", feature = "opus-sim"))]
78pub mod opus;
79
80/// Codec factory for creating codec instances
81pub struct CodecFactory;
82
83impl CodecFactory {
84    /// Create a codec instance from configuration
85    pub fn create(config: CodecConfig) -> Result<Box<dyn AudioCodec>> {
86        // Validate configuration first
87        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    /// Create a codec by name
118    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    /// Create a codec by RTP payload type
136    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    /// Get all supported codec names
153    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    /// Check if a codec is supported
166    pub fn is_supported(name: &str) -> bool {
167        Self::supported_codecs().contains(&name.to_uppercase().as_str())
168    }
169}
170
171/// Codec registry for managing multiple codec instances
172pub struct CodecRegistry {
173    codecs: HashMap<String, Box<dyn AudioCodec>>,
174}
175
176impl CodecRegistry {
177    /// Create a new empty registry
178    pub fn new() -> Self {
179        Self {
180            codecs: HashMap::new(),
181        }
182    }
183    
184    /// Register a codec with a name
185    pub fn register(&mut self, name: String, codec: Box<dyn AudioCodec>) {
186        self.codecs.insert(name, codec);
187    }
188    
189    /// Get a codec by name
190    pub fn get(&self, name: &str) -> Option<&dyn AudioCodec> {
191        self.codecs.get(name).map(|codec| codec.as_ref())
192    }
193    
194    /// Get a mutable codec by name
195    pub fn get_mut(&mut self, name: &str) -> Option<&mut Box<dyn AudioCodec>> {
196        self.codecs.get_mut(name)
197    }
198    
199    /// Remove a codec by name
200    pub fn remove(&mut self, name: &str) -> Option<Box<dyn AudioCodec>> {
201        self.codecs.remove(name)
202    }
203    
204    /// List all registered codec names
205    pub fn list_codecs(&self) -> Vec<&String> {
206        self.codecs.keys().collect()
207    }
208    
209    /// Get the count of registered codecs
210    pub fn len(&self) -> usize {
211        self.codecs.len()
212    }
213    
214    /// Check if the registry is empty
215    pub fn is_empty(&self) -> bool {
216        self.codecs.is_empty()
217    }
218    
219    /// Clear all registered codecs
220    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/// Codec capability information
232#[derive(Debug, Clone)]
233pub struct CodecCapabilities {
234    /// Available codec types
235    pub codec_types: Vec<CodecType>,
236    /// Codec information
237    pub codec_info: HashMap<CodecType, CodecInfo>,
238}
239
240impl CodecCapabilities {
241    /// Get capabilities for all supported codecs
242    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    /// Check if a codec type is supported
292    pub fn is_supported(&self, codec_type: CodecType) -> bool {
293        self.codec_types.contains(&codec_type)
294    }
295    
296    /// Get information for a specific codec type
297    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}