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