Skip to main content

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
71#[cfg(feature = "opus")]
72pub fn create_opus_encoder(
73    sample_rate: u32,
74    channels: u16,
75    application: opus::OpusApplication,
76) -> Box<dyn Encoder> {
77    Box::new(opus::OpusEncoder::new_with_application(
78        sample_rate,
79        channels,
80        application,
81    ))
82}
83
84#[cfg(feature = "opus")]
85pub fn create_opus_decoder(sample_rate: u32, channels: u16) -> Box<dyn Decoder> {
86    Box::new(opus::OpusDecoder::new(sample_rate, channels))
87}
88
89impl CodecType {
90    pub fn mime_type(&self) -> &str {
91        match self {
92            CodecType::PCMU => "audio/PCMU",
93            CodecType::PCMA => "audio/PCMA",
94            CodecType::G722 => "audio/G722",
95            CodecType::G729 => "audio/G729",
96            #[cfg(feature = "opus")]
97            CodecType::Opus => "audio/opus",
98            CodecType::TelephoneEvent => "audio/telephone-event",
99        }
100    }
101    pub fn rtpmap(&self) -> &str {
102        match self {
103            CodecType::PCMU => "PCMU/8000",
104            CodecType::PCMA => "PCMA/8000",
105            CodecType::G722 => "G722/8000",
106            CodecType::G729 => "G729/8000",
107            #[cfg(feature = "opus")]
108            CodecType::Opus => "opus/48000/2",
109            CodecType::TelephoneEvent => "telephone-event/8000",
110        }
111    }
112    pub fn fmtp(&self) -> Option<&str> {
113        match self {
114            CodecType::PCMU => None,
115            CodecType::PCMA => None,
116            CodecType::G722 => None,
117            CodecType::G729 => None,
118            #[cfg(feature = "opus")]
119            CodecType::Opus => Some("minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1"),
120            CodecType::TelephoneEvent => Some("0-16"),
121        }
122    }
123
124    pub fn clock_rate(&self) -> u32 {
125        match self {
126            CodecType::PCMU => 8000,
127            CodecType::PCMA => 8000,
128            CodecType::G722 => 8000,
129            CodecType::G729 => 8000,
130            #[cfg(feature = "opus")]
131            CodecType::Opus => 48000,
132            CodecType::TelephoneEvent => 8000,
133        }
134    }
135
136    pub fn channels(&self) -> u16 {
137        match self {
138            #[cfg(feature = "opus")]
139            CodecType::Opus => 2,
140            _ => 1,
141        }
142    }
143
144    pub fn payload_type(&self) -> u8 {
145        match self {
146            CodecType::PCMU => 0,
147            CodecType::PCMA => 8,
148            CodecType::G722 => 9,
149            CodecType::G729 => 18,
150            #[cfg(feature = "opus")]
151            CodecType::Opus => 111,
152            CodecType::TelephoneEvent => 101,
153        }
154    }
155    pub fn samplerate(&self) -> u32 {
156        match self {
157            CodecType::PCMU => 8000,
158            CodecType::PCMA => 8000,
159            CodecType::G722 => 16000,
160            CodecType::G729 => 8000,
161            #[cfg(feature = "opus")]
162            CodecType::Opus => 48000,
163            CodecType::TelephoneEvent => 8000,
164        }
165    }
166    pub fn is_audio(&self) -> bool {
167        match self {
168            CodecType::PCMU | CodecType::PCMA | CodecType::G722 => true,
169            CodecType::G729 => true,
170            #[cfg(feature = "opus")]
171            CodecType::Opus => true,
172            _ => false,
173        }
174    }
175
176    pub fn is_dynamic(&self) -> bool {
177        match self {
178            #[cfg(feature = "opus")]
179            CodecType::Opus => true,
180            CodecType::TelephoneEvent => true,
181            _ => false,
182        }
183    }
184}
185
186impl TryFrom<u8> for CodecType {
187    type Error = anyhow::Error;
188
189    fn try_from(value: u8) -> Result<Self, Self::Error> {
190        match value {
191            0 => Ok(CodecType::PCMU),
192            8 => Ok(CodecType::PCMA),
193            9 => Ok(CodecType::G722),
194            18 => Ok(CodecType::G729), // Static payload type
195            // Dynamic payload type shoulw get from the rtpmap in sdp offer, leave this for backward compatibility
196            101 => Ok(CodecType::TelephoneEvent),
197            #[cfg(feature = "opus")]
198            111 => Ok(CodecType::Opus), // Dynamic payload type
199            _ => Err(anyhow::anyhow!("Invalid codec type: {}", value)),
200        }
201    }
202}
203
204impl TryFrom<&str> for CodecType {
205    type Error = anyhow::Error;
206
207    fn try_from(name: &str) -> Result<Self, Self::Error> {
208        match name.to_lowercase().as_str() {
209            "pcmu" | "ulaw" => Ok(CodecType::PCMU),
210            "pcma" | "alaw" => Ok(CodecType::PCMA),
211            "g722" => Ok(CodecType::G722),
212            "g729" => Ok(CodecType::G729),
213            #[cfg(feature = "opus")]
214            "opus" => Ok(CodecType::Opus),
215            "telephone-event" => Ok(CodecType::TelephoneEvent),
216            _ => Err(anyhow::anyhow!("Invalid codec name: {}", name)),
217        }
218    }
219}
220
221#[cfg(target_endian = "little")]
222pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
223    unsafe {
224        std::slice::from_raw_parts(
225            samples.as_ptr() as *const u8,
226            samples.len() * std::mem::size_of::<Sample>(),
227        )
228        .to_vec()
229    }
230}
231
232#[cfg(target_endian = "big")]
233pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
234    samples.iter().flat_map(|s| s.to_le_bytes()).collect()
235}
236
237#[cfg(target_endian = "little")]
238pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
239    unsafe {
240        std::slice::from_raw_parts(
241            u8_data.as_ptr() as *const Sample,
242            u8_data.len() / std::mem::size_of::<Sample>(),
243        )
244        .to_vec()
245    }
246}
247#[cfg(target_endian = "big")]
248pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
249    u8_data
250        .chunks(2)
251        .map(|chunk| (chunk[0] as i16) | ((chunk[1] as i16) << 8))
252        .collect()
253}