audio_codec/
lib.rs

1pub type Sample = i16;
2pub type PcmBuf = Vec<Sample>;
3
4pub mod g722;
5pub mod g729;
6#[cfg(feature = "opus")]
7pub mod opus;
8pub mod pcma;
9pub mod pcmu;
10pub mod resampler;
11pub mod telephone_event;
12pub use resampler::{Resampler, resample};
13
14#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
15pub enum CodecType {
16    PCMU,
17    PCMA,
18    G722,
19    G729,
20    #[cfg(feature = "opus")]
21    Opus,
22    TelephoneEvent,
23}
24
25pub trait Decoder: Send + Sync {
26    /// Decode encoded audio data into PCM samples
27    fn decode(&mut self, data: &[u8]) -> PcmBuf;
28
29    /// Get the sample rate of the decoded audio
30    fn sample_rate(&self) -> u32;
31
32    /// Get the number of channels
33    fn channels(&self) -> u16;
34}
35
36pub trait Encoder: Send + Sync {
37    /// Encode PCM samples into codec-specific format
38    fn encode(&mut self, samples: &[Sample]) -> Vec<u8>;
39
40    /// Get the sample rate expected for input samples
41    fn sample_rate(&self) -> u32;
42
43    /// Get the number of channels expected for input
44    fn channels(&self) -> u16;
45}
46
47pub fn create_decoder(codec: CodecType) -> Box<dyn Decoder> {
48    match codec {
49        CodecType::PCMU => Box::new(pcmu::PcmuDecoder::new()),
50        CodecType::PCMA => Box::new(pcma::PcmaDecoder::new()),
51        CodecType::G722 => Box::new(g722::G722Decoder::new()),
52        CodecType::G729 => Box::new(g729::G729Decoder::new()),
53        #[cfg(feature = "opus")]
54        CodecType::Opus => Box::new(opus::OpusDecoder::new_default()),
55        CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventDecoder::new()),
56    }
57}
58
59pub fn create_encoder(codec: CodecType) -> Box<dyn Encoder> {
60    match codec {
61        CodecType::PCMU => Box::new(pcmu::PcmuEncoder::new()),
62        CodecType::PCMA => Box::new(pcma::PcmaEncoder::new()),
63        CodecType::G722 => Box::new(g722::G722Encoder::new()),
64        CodecType::G729 => Box::new(g729::G729Encoder::new()),
65        #[cfg(feature = "opus")]
66        CodecType::Opus => Box::new(opus::OpusEncoder::new_default()),
67        CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventEncoder::new()),
68    }
69}
70
71impl CodecType {
72    pub fn mime_type(&self) -> &str {
73        match self {
74            CodecType::PCMU => "audio/PCMU",
75            CodecType::PCMA => "audio/PCMA",
76            CodecType::G722 => "audio/G722",
77            CodecType::G729 => "audio/G729",
78            #[cfg(feature = "opus")]
79            CodecType::Opus => "audio/opus",
80            CodecType::TelephoneEvent => "audio/telephone-event",
81        }
82    }
83    pub fn rtpmap(&self) -> &str {
84        match self {
85            CodecType::PCMU => "PCMU/8000",
86            CodecType::PCMA => "PCMA/8000",
87            CodecType::G722 => "G722/8000",
88            CodecType::G729 => "G729/8000",
89            #[cfg(feature = "opus")]
90            CodecType::Opus => "opus/48000/2",
91            CodecType::TelephoneEvent => "telephone-event/8000",
92        }
93    }
94    pub fn fmtp(&self) -> Option<&str> {
95        match self {
96            CodecType::PCMU => None,
97            CodecType::PCMA => None,
98            CodecType::G722 => None,
99            CodecType::G729 => None,
100            #[cfg(feature = "opus")]
101            CodecType::Opus => Some("minptime=10;useinbandfec=1"),
102            CodecType::TelephoneEvent => Some("0-16"),
103        }
104    }
105
106    pub fn clock_rate(&self) -> u32 {
107        match self {
108            CodecType::PCMU => 8000,
109            CodecType::PCMA => 8000,
110            CodecType::G722 => 8000,
111            CodecType::G729 => 8000,
112            #[cfg(feature = "opus")]
113            CodecType::Opus => 48000,
114            CodecType::TelephoneEvent => 8000,
115        }
116    }
117
118    pub fn channels(&self) -> u16 {
119        match self {
120            #[cfg(feature = "opus")]
121            CodecType::Opus => 2,
122            _ => 1,
123        }
124    }
125
126    pub fn payload_type(&self) -> u8 {
127        match self {
128            CodecType::PCMU => 0,
129            CodecType::PCMA => 8,
130            CodecType::G722 => 9,
131            CodecType::G729 => 18,
132            #[cfg(feature = "opus")]
133            CodecType::Opus => 111,
134            CodecType::TelephoneEvent => 101,
135        }
136    }
137    pub fn samplerate(&self) -> u32 {
138        match self {
139            CodecType::PCMU => 8000,
140            CodecType::PCMA => 8000,
141            CodecType::G722 => 16000,
142            CodecType::G729 => 8000,
143            #[cfg(feature = "opus")]
144            CodecType::Opus => 48000,
145            CodecType::TelephoneEvent => 8000,
146        }
147    }
148    pub fn is_audio(&self) -> bool {
149        match self {
150            CodecType::PCMU | CodecType::PCMA | CodecType::G722 => true,
151            CodecType::G729 => true,
152            #[cfg(feature = "opus")]
153            CodecType::Opus => true,
154            _ => false,
155        }
156    }
157
158    pub fn is_dynamic(&self) -> bool {
159        match self {
160            #[cfg(feature = "opus")]
161            CodecType::Opus => true,
162            CodecType::TelephoneEvent => true,
163            _ => false,
164        }
165    }
166}
167
168impl TryFrom<u8> for CodecType {
169    type Error = anyhow::Error;
170
171    fn try_from(value: u8) -> Result<Self, Self::Error> {
172        match value {
173            0 => Ok(CodecType::PCMU),
174            8 => Ok(CodecType::PCMA),
175            9 => Ok(CodecType::G722),
176            18 => Ok(CodecType::G729), // Static payload type
177            // Dynamic payload type shoulw get from the rtpmap in sdp offer, leave this for backward compatibility
178            101 => Ok(CodecType::TelephoneEvent),
179            #[cfg(feature = "opus")]
180            111 => Ok(CodecType::Opus), // Dynamic payload type
181            _ => Err(anyhow::anyhow!("Invalid codec type: {}", value)),
182        }
183    }
184}
185
186impl TryFrom<&str> for CodecType {
187    type Error = anyhow::Error;
188
189    fn try_from(name: &str) -> Result<Self, Self::Error> {
190        match name.to_lowercase().as_str() {
191            "pcmu" | "ulaw" => Ok(CodecType::PCMU),
192            "pcma" | "alaw" => Ok(CodecType::PCMA),
193            "g722" => Ok(CodecType::G722),
194            "g729" => Ok(CodecType::G729),
195            #[cfg(feature = "opus")]
196            "opus" => Ok(CodecType::Opus),
197            "telephone-event" => Ok(CodecType::TelephoneEvent),
198            _ => Err(anyhow::anyhow!("Invalid codec name: {}", name)),
199        }
200    }
201}
202
203#[cfg(target_endian = "little")]
204pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
205    unsafe {
206        std::slice::from_raw_parts(
207            samples.as_ptr() as *const u8,
208            samples.len() * std::mem::size_of::<Sample>(),
209        )
210        .to_vec()
211    }
212}
213
214#[cfg(target_endian = "big")]
215pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
216    samples.iter().flat_map(|s| s.to_le_bytes()).collect()
217}
218
219#[cfg(target_endian = "little")]
220pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
221    unsafe {
222        std::slice::from_raw_parts(
223            u8_data.as_ptr() as *const Sample,
224            u8_data.len() / std::mem::size_of::<Sample>(),
225        )
226        .to_vec()
227    }
228}
229#[cfg(target_endian = "big")]
230pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
231    u8_data
232        .chunks(2)
233        .map(|chunk| (chunk[0] as i16) | ((chunk[1] as i16) << 8))
234        .collect()
235}