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