Skip to main content

audio_codec/
lib.rs

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