Skip to main content

audio_codec/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3// Opus wraps the (large, ~260 KB) opus-rs encoder/decoder on the heap, so it
4// needs `alloc` even in `no_std`. The telephone-event payload layer (and any
5// other Vec-returning helper) uses it in BOTH std and no_std builds.
6extern crate alloc;
7
8pub use error::CodecError;
9
10pub mod error;
11pub mod g722;
12#[cfg(feature = "g729")]
13pub mod g729;
14#[cfg(feature = "opus")]
15pub mod opus;
16pub mod pcma;
17pub mod pcmu;
18/// Default polyphase resampler (f32 core, exact integer timing).
19pub mod resampler;
20/// Alternative polyphase resampler with a Q15 fixed-point core, for targets
21/// without an FPU. Not the default; use [`resampler`] unless you need it.
22pub mod resampler_fixed;
23pub mod telephone_event;
24
25#[cfg(feature = "std")]
26pub use resampler::Resampler;
27#[cfg(feature = "std")]
28pub use resampler::{BoxedResampler, resample};
29
30pub type Sample = i16;
31
32#[cfg(feature = "std")]
33pub type PcmBuf = Vec<Sample>;
34
35#[derive(Debug, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
36pub enum CodecType {
37    PCMU,
38    PCMA,
39    G722,
40    #[cfg(feature = "g729")]
41    G729,
42    #[cfg(feature = "opus")]
43    Opus,
44    TelephoneEvent,
45}
46
47/// Decoder trait: converts codec-specific bytes into PCM samples.
48///
49/// The slice-based `decode_into` is the primary, always-available method
50/// (works in `no_std`). The convenience `decode` returning a `Vec` is only
51/// available with the `std` feature and has a default implementation that
52/// delegates to `decode_into`.
53pub trait Decoder: Send + Sync {
54    /// Decode `data` into `out`, returning the number of samples written.
55    ///
56    /// Returns [`CodecError::BufferTooSmall`] if `out` cannot hold the
57    /// decoded samples (use `max_decode_samples` to size it).
58    fn decode_into(&mut self, data: &[u8], out: &mut [Sample]) -> Result<usize, CodecError>;
59
60    /// Upper bound on the number of samples that `decode_into` will write
61    /// for an input of `n_bytes` bytes.
62    fn max_decode_samples(&self, n_bytes: usize) -> usize;
63
64    /// Get the sample rate of the decoded audio.
65    fn sample_rate(&self) -> u32;
66
67    /// Get the number of channels.
68    fn channels(&self) -> u16;
69
70    /// Convenience wrapper that allocates a `Vec` and calls `decode_into`.
71    #[cfg(feature = "std")]
72    fn decode(&mut self, data: &[u8]) -> PcmBuf {
73        let max = self.max_decode_samples(data.len());
74        let mut buf = vec![0i16; max];
75        match self.decode_into(data, &mut buf) {
76            Ok(n) => {
77                buf.truncate(n);
78                buf
79            }
80            Err(_) => Vec::new(),
81        }
82    }
83}
84
85/// Encoder trait: converts PCM samples into codec-specific bytes.
86///
87/// The slice-based `encode_into` is the primary, always-available method
88/// (works in `no_std`). The convenience `encode` returning a `Vec` is only
89/// available with the `std` feature and has a default implementation that
90/// delegates to `encode_into`.
91pub trait Encoder: Send + Sync {
92    /// Encode `samples` into `out`, returning the number of bytes written.
93    ///
94    /// Returns [`CodecError::BufferTooSmall`] if `out` cannot hold the
95    /// encoded bytes (use `max_encode_bytes` to size it).
96    fn encode_into(&mut self, samples: &[Sample], out: &mut [u8]) -> Result<usize, CodecError>;
97
98    /// Upper bound on the number of bytes that `encode_into` will write
99    /// for an input of `n_samples` samples.
100    fn max_encode_bytes(&self, n_samples: usize) -> usize;
101
102    /// Get the sample rate expected for input samples.
103    fn sample_rate(&self) -> u32;
104
105    /// Get the number of channels expected for input.
106    fn channels(&self) -> u16;
107
108    /// Convenience wrapper that allocates a `Vec` and calls `encode_into`.
109    #[cfg(feature = "std")]
110    fn encode(&mut self, samples: &[Sample]) -> Vec<u8> {
111        let max = self.max_encode_bytes(samples.len());
112        let mut buf = vec![0u8; max];
113        match self.encode_into(samples, &mut buf) {
114            Ok(n) => {
115                buf.truncate(n);
116                buf
117            }
118            Err(_) => Vec::new(),
119        }
120    }
121}
122
123#[cfg(feature = "std")]
124pub fn create_decoder(codec: CodecType) -> Box<dyn Decoder> {
125    match codec {
126        CodecType::PCMU => Box::new(pcmu::PcmuDecoder::new()),
127        CodecType::PCMA => Box::new(pcma::PcmaDecoder::new()),
128        CodecType::G722 => Box::new(g722::G722Decoder::new()),
129        #[cfg(feature = "g729")]
130        CodecType::G729 => Box::new(g729::G729Decoder::new()),
131        #[cfg(feature = "opus")]
132        CodecType::Opus => Box::new(opus::OpusDecoder::new_default()),
133        CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventDecoder::new()),
134    }
135}
136
137#[cfg(feature = "std")]
138pub fn create_encoder(codec: CodecType) -> Box<dyn Encoder> {
139    match codec {
140        CodecType::PCMU => Box::new(pcmu::PcmuEncoder::new()),
141        CodecType::PCMA => Box::new(pcma::PcmaEncoder::new()),
142        CodecType::G722 => Box::new(g722::G722Encoder::new()),
143        #[cfg(feature = "g729")]
144        CodecType::G729 => Box::new(g729::G729Encoder::new()),
145        #[cfg(feature = "opus")]
146        CodecType::Opus => Box::new(opus::OpusEncoder::new_default()),
147        CodecType::TelephoneEvent => Box::new(telephone_event::TelephoneEventEncoder::new()),
148    }
149}
150
151#[cfg(all(feature = "std", feature = "opus"))]
152pub fn create_opus_encoder(
153    sample_rate: u32,
154    channels: u16,
155    application: opus::OpusApplication,
156) -> Box<dyn Encoder> {
157    Box::new(opus::OpusEncoder::new_with_application(
158        sample_rate,
159        channels,
160        application,
161    ))
162}
163
164#[cfg(all(feature = "std", feature = "opus"))]
165pub fn create_opus_decoder(sample_rate: u32, channels: u16) -> Box<dyn Decoder> {
166    Box::new(opus::OpusDecoder::new(sample_rate, channels))
167}
168
169impl CodecType {
170    pub fn mime_type(&self) -> &str {
171        match self {
172            CodecType::PCMU => "audio/PCMU",
173            CodecType::PCMA => "audio/PCMA",
174            CodecType::G722 => "audio/G722",
175            #[cfg(feature = "g729")]
176            CodecType::G729 => "audio/G729",
177            #[cfg(feature = "opus")]
178            CodecType::Opus => "audio/opus",
179            CodecType::TelephoneEvent => "audio/telephone-event",
180        }
181    }
182    pub fn rtpmap(&self) -> &str {
183        match self {
184            CodecType::PCMU => "PCMU/8000",
185            CodecType::PCMA => "PCMA/8000",
186            CodecType::G722 => "G722/8000",
187            #[cfg(feature = "g729")]
188            CodecType::G729 => "G729/8000",
189            #[cfg(feature = "opus")]
190            CodecType::Opus => "opus/48000/2",
191            CodecType::TelephoneEvent => "telephone-event/8000",
192        }
193    }
194    pub fn fmtp(&self) -> Option<&str> {
195        match self {
196            CodecType::PCMU => None,
197            CodecType::PCMA => None,
198            CodecType::G722 => None,
199            #[cfg(feature = "g729")]
200            CodecType::G729 => None,
201            #[cfg(feature = "opus")]
202            CodecType::Opus => Some("minptime=10;useinbandfec=1;stereo=1;sprop-stereo=1"),
203            CodecType::TelephoneEvent => Some("0-16"),
204        }
205    }
206
207    pub fn clock_rate(&self) -> u32 {
208        match self {
209            CodecType::PCMU => 8000,
210            CodecType::PCMA => 8000,
211            CodecType::G722 => 8000,
212            #[cfg(feature = "g729")]
213            CodecType::G729 => 8000,
214            #[cfg(feature = "opus")]
215            CodecType::Opus => 48000,
216            CodecType::TelephoneEvent => 8000,
217        }
218    }
219
220    pub fn channels(&self) -> u16 {
221        match self {
222            #[cfg(feature = "opus")]
223            CodecType::Opus => 2,
224            _ => 1,
225        }
226    }
227
228    pub fn payload_type(&self) -> u8 {
229        match self {
230            CodecType::PCMU => 0,
231            CodecType::PCMA => 8,
232            CodecType::G722 => 9,
233            #[cfg(feature = "g729")]
234            CodecType::G729 => 18,
235            #[cfg(feature = "opus")]
236            CodecType::Opus => 111,
237            CodecType::TelephoneEvent => 101,
238        }
239    }
240    pub fn samplerate(&self) -> u32 {
241        match self {
242            CodecType::PCMU => 8000,
243            CodecType::PCMA => 8000,
244            CodecType::G722 => 16000,
245            #[cfg(feature = "g729")]
246            CodecType::G729 => 8000,
247            #[cfg(feature = "opus")]
248            CodecType::Opus => 48000,
249            CodecType::TelephoneEvent => 8000,
250        }
251    }
252    pub fn is_audio(&self) -> bool {
253        match self {
254            CodecType::PCMU | CodecType::PCMA | CodecType::G722 => true,
255            #[cfg(feature = "g729")]
256            CodecType::G729 => true,
257            #[cfg(feature = "opus")]
258            CodecType::Opus => true,
259            _ => false,
260        }
261    }
262
263    pub fn is_dynamic(&self) -> bool {
264        match self {
265            #[cfg(feature = "opus")]
266            CodecType::Opus => true,
267            CodecType::TelephoneEvent => true,
268            _ => false,
269        }
270    }
271}
272
273impl TryFrom<u8> for CodecType {
274    type Error = CodecError;
275
276    fn try_from(value: u8) -> Result<Self, Self::Error> {
277        match value {
278            0 => Ok(CodecType::PCMU),
279            8 => Ok(CodecType::PCMA),
280            9 => Ok(CodecType::G722),
281            #[cfg(feature = "g729")]
282            18 => Ok(CodecType::G729), // Static payload type
283            // Dynamic payload type should get from the rtpmap in sdp offer, leave this for backward compatibility
284            101 => Ok(CodecType::TelephoneEvent),
285            #[cfg(feature = "opus")]
286            111 => Ok(CodecType::Opus), // Dynamic payload type
287            _ => Err(CodecError::InvalidCodecType),
288        }
289    }
290}
291
292impl TryFrom<&str> for CodecType {
293    type Error = CodecError;
294
295    fn try_from(name: &str) -> Result<Self, Self::Error> {
296        let b = name.as_bytes();
297        if b.eq_ignore_ascii_case(b"pcmu") || b.eq_ignore_ascii_case(b"ulaw") {
298            Ok(CodecType::PCMU)
299        } else if b.eq_ignore_ascii_case(b"pcma") || b.eq_ignore_ascii_case(b"alaw") {
300            Ok(CodecType::PCMA)
301        } else if b.eq_ignore_ascii_case(b"g722") {
302            Ok(CodecType::G722)
303        } else if cfg!(feature = "g729") && b.eq_ignore_ascii_case(b"g729") {
304            #[cfg(feature = "g729")]
305            {
306                Ok(CodecType::G729)
307            }
308            #[cfg(not(feature = "g729"))]
309            {
310                Err(CodecError::InvalidCodecName)
311            }
312        } else if cfg!(feature = "opus") && b.eq_ignore_ascii_case(b"opus") {
313            #[cfg(feature = "opus")]
314            {
315                Ok(CodecType::Opus)
316            }
317            #[cfg(not(feature = "opus"))]
318            {
319                Err(CodecError::InvalidCodecName)
320            }
321        } else if b.eq_ignore_ascii_case(b"telephone-event") {
322            Ok(CodecType::TelephoneEvent)
323        } else {
324            Err(CodecError::InvalidCodecName)
325        }
326    }
327}
328
329// ----------------------------------------------------------------------------
330// Byte <-> sample slice helpers
331// ----------------------------------------------------------------------------
332
333/// Write the little-endian byte representation of `samples` into `out`.
334///
335/// Returns the number of bytes written (`samples.len() * 2`). Returns
336/// [`CodecError::BufferTooSmall`] if `out` is too small.
337pub fn samples_to_bytes_into(samples: &[Sample], out: &mut [u8]) -> Result<usize, CodecError> {
338    let needed = core::mem::size_of_val(samples);
339    if out.len() < needed {
340        return Err(CodecError::BufferTooSmall);
341    }
342    if samples.is_empty() {
343        return Ok(0);
344    }
345    // A `&mut [u8]` is only guaranteed 1-byte aligned (and an empty buffer's
346    // dangling pointer is 1-aligned), so the i16 view is only sound when the
347    // pointer happens to be properly aligned; otherwise fall back to a
348    // bytewise copy.
349    let dst_aligned = out
350        .as_mut_ptr()
351        .addr()
352        .is_multiple_of(core::mem::align_of::<Sample>());
353    #[cfg(target_endian = "little")]
354    if dst_aligned {
355        // SAFETY: `out` is large enough (checked above) and its pointer is
356        // aligned to `Sample`; `[u8; N*2]` and `[i16; N]` then have the same
357        // layout.
358        let dst = unsafe {
359            core::slice::from_raw_parts_mut(out.as_mut_ptr() as *mut Sample, samples.len())
360        };
361        dst.copy_from_slice(samples);
362        return Ok(needed);
363    }
364    for (i, s) in samples.iter().enumerate() {
365        let b = s.to_le_bytes();
366        out[2 * i] = b[0];
367        out[2 * i + 1] = b[1];
368    }
369    Ok(needed)
370}
371
372/// Decode the little-endian bytes into `out` as `Sample` (i16) values.
373///
374/// Returns the number of samples written (`u8_data.len() / 2`). Returns
375/// [`CodecError::BufferTooSmall`] if `out` cannot hold them all.
376pub fn bytes_to_samples_into(u8_data: &[u8], out: &mut [Sample]) -> Result<usize, CodecError> {
377    let n = u8_data.len() / core::mem::size_of::<Sample>();
378    if out.len() < n {
379        return Err(CodecError::BufferTooSmall);
380    }
381    if n == 0 {
382        return Ok(0);
383    }
384    // See `samples_to_bytes_into`: a `&[u8]` is only guaranteed 1-byte
385    // aligned, so only build the i16 view when the pointer is aligned.
386    let src_aligned = u8_data
387        .as_ptr()
388        .addr()
389        .is_multiple_of(core::mem::align_of::<Sample>());
390    #[cfg(target_endian = "little")]
391    if src_aligned {
392        // SAFETY: see `samples_to_bytes_into`.
393        let src = unsafe { core::slice::from_raw_parts(u8_data.as_ptr() as *const Sample, n) };
394        out[..n].copy_from_slice(src);
395        return Ok(n);
396    }
397    for (i, chunk) in u8_data.chunks_exact(2).enumerate() {
398        out[i] = (chunk[0] as i16) | ((chunk[1] as i16) << 8);
399    }
400    Ok(n)
401}
402
403#[cfg(feature = "std")]
404pub fn samples_to_bytes(samples: &[Sample]) -> Vec<u8> {
405    let mut out = vec![0u8; core::mem::size_of_val(samples)];
406    let _ = samples_to_bytes_into(samples, &mut out);
407    out
408}
409
410#[cfg(feature = "std")]
411pub fn bytes_to_samples(u8_data: &[u8]) -> PcmBuf {
412    let n = u8_data.len() / core::mem::size_of::<Sample>();
413    let mut out = vec![0i16; n];
414    let _ = bytes_to_samples_into(u8_data, &mut out);
415    out
416}