Skip to main content

audio_codec/
opus.rs

1use super::{Decoder, Encoder, PcmBuf, Sample};
2pub use opus_rs::Application as OpusApplication;
3use opus_rs::{Application, OpusDecoder as OpusDecoderRaw, OpusEncoder as OpusEncoderRaw};
4
5pub struct OpusDecoder {
6    decoder: OpusDecoderRaw,
7    sample_rate: u32,
8    channels: u16,
9    w_output_f32: Vec<f32>,
10    w_pcm_i16: Vec<i16>,
11}
12
13impl OpusDecoder {
14    /// Create a new Opus decoder instance
15    pub fn new(sample_rate: u32, channels: u16) -> Self {
16        let decoder = OpusDecoderRaw::new(sample_rate as i32, channels as usize)
17            .expect("Failed to create Opus decoder");
18
19        Self {
20            decoder,
21            sample_rate,
22            channels,
23            w_output_f32: Vec::new(),
24            w_pcm_i16: Vec::new(),
25        }
26    }
27
28    /// Create a default Opus decoder (48kHz, stereo)
29    pub fn new_default() -> Self {
30        Self::new(48000, 2)
31    }
32
33    pub fn decode_into(&mut self, data: &[u8], output: &mut [i16]) -> usize {
34        if data.is_empty() {
35            return 0;
36        }
37
38        // Detect the actual channel count from the Opus packet's TOC byte
39        // (bit 2 is the stereo flag per RFC 6716). opus-rs 0.1.19+ rejects
40        // decoding when the packet channel count doesn't match the decoder's,
41        // so we adapt the decoder here to avoid returning empty PCM.
42        let packet_channels = if data[0] & 0x04 != 0 { 2usize } else { 1 };
43        if self.channels as usize != packet_channels {
44            self.channels = packet_channels as u16;
45            self.decoder = OpusDecoderRaw::new(self.sample_rate as i32, packet_channels)
46                .expect("Failed to create Opus decoder");
47        }
48
49        let channels = usize::from(self.channels);
50        let frame_size = (self.sample_rate as usize * 20) / 1000;
51        let max_samples = frame_size * channels;
52        if self.w_output_f32.len() < max_samples {
53            self.w_output_f32.resize(max_samples, 0.0);
54        }
55
56        match self
57            .decoder
58            .decode(data, frame_size, &mut self.w_output_f32[..max_samples])
59        {
60            Ok(len) => {
61                let total_samples = len * channels;
62                if total_samples == 0 {
63                    return 0;
64                }
65
66                if self.w_pcm_i16.len() < total_samples {
67                    self.w_pcm_i16.resize(total_samples, 0);
68                }
69
70                for i in 0..total_samples {
71                    self.w_pcm_i16[i] =
72                        (self.w_output_f32[i] * 32768.0).clamp(-32768.0, 32767.0) as i16;
73                }
74
75                let n = total_samples.min(output.len());
76                output[..n].copy_from_slice(&self.w_pcm_i16[..n]);
77                n
78            }
79            Err(_) => 0,
80        }
81    }
82}
83
84impl Decoder for OpusDecoder {
85    fn decode(&mut self, data: &[u8]) -> PcmBuf {
86        if data.is_empty() {
87            return Vec::new();
88        }
89        let packet_channels = if data[0] & 0x04 != 0 { 2usize } else { 1 };
90
91        let frame_size = (self.sample_rate as usize * 20) / 1000;
92        let max_samples = frame_size * packet_channels;
93        let mut pcm = vec![0i16; max_samples];
94        let n = self.decode_into(data, &mut pcm);
95        pcm.truncate(n);
96        if usize::from(self.channels) == 2 {
97            pcm = pcm
98                .chunks_exact(2)
99                .map(|chunk| ((chunk[0] as i32 + chunk[1] as i32) / 2) as i16)
100                .collect();
101        }
102        pcm
103    }
104
105    fn sample_rate(&self) -> u32 {
106        self.sample_rate
107    }
108
109    fn channels(&self) -> u16 {
110        self.channels
111    }
112}
113
114pub struct OpusEncoder {
115    encoder: OpusEncoderRaw,
116    sample_rate: u32,
117    channels: u16,
118    w_input_f32: Vec<f32>,
119    w_packet: Vec<u8>,
120    /// Reusable buffer for mono→stereo upmix.  Avoids a per-frame allocation
121    /// in `encode()` when `channels == 2`.
122    w_stereo: Vec<i16>,
123}
124
125impl OpusEncoder {
126    pub fn new_with_application(sample_rate: u32, channels: u16, application: Application) -> Self {
127        let encoder = OpusEncoderRaw::new(sample_rate as i32, channels as usize, application)
128            .expect("Failed to create Opus encoder");
129
130        Self {
131            encoder,
132            sample_rate,
133            channels,
134            w_input_f32: Vec::new(),
135            w_packet: vec![0u8; 1275],
136            w_stereo: Vec::new(),
137        }
138    }
139
140    /// Create a new Opus encoder instance.
141    ///
142    /// Keep backward-compatible defaults with pre-0.3.31 behavior:
143    /// - VoIP application
144    /// - caller can provide mono PCM even when encoder is configured as stereo;
145    ///   `encode()` duplicates mono samples to stereo.
146    pub fn new(sample_rate: u32, channels: u16) -> Self {
147        let mut enc = Self::new_with_application(sample_rate, channels, Application::Voip);
148        enc.encoder.bitrate_bps = if channels == 2 { 64000 } else { 48000 };
149        enc.encoder.complexity = 5;
150        enc.encoder.use_cbr = true;
151        enc
152    }
153
154    /// Create a default Opus encoder (48kHz, stereo)
155    pub fn new_default() -> Self {
156        Self::new(48000, 2)
157    }
158
159    /// Set the encoder bitrate in bits per second.
160    pub fn set_bitrate(&mut self, bitrate_bps: i32) {
161        self.encoder.bitrate_bps = bitrate_bps;
162    }
163
164    /// Set the encoder complexity (0-10).
165    pub fn set_complexity(&mut self, complexity: i32) {
166        self.encoder.complexity = complexity;
167    }
168
169    /// Enable or disable constant bitrate (CBR) mode.
170    pub fn set_cbr(&mut self, cbr: bool) {
171        self.encoder.use_cbr = cbr;
172    }
173
174    /// Encode into a caller-provided packet buffer.
175    ///
176    /// Returns `Some(bytes_written)` on success.
177    pub fn encode_into(&mut self, samples: &[Sample], output: &mut [u8]) -> Option<usize> {
178        let channels = usize::from(self.channels);
179        if samples.is_empty() || channels == 0 || samples.len() % channels != 0 {
180            return None;
181        }
182
183        let frame_size = samples.len() / channels;
184
185        if self.w_input_f32.len() < samples.len() {
186            self.w_input_f32.resize(samples.len(), 0.0);
187        }
188
189        for (dst, &s) in self.w_input_f32[..samples.len()]
190            .iter_mut()
191            .zip(samples.iter())
192        {
193            *dst = s as f32 / 32768.0;
194        }
195
196        self.encoder
197            .encode(&self.w_input_f32[..samples.len()], frame_size, output)
198            .ok()
199    }
200
201    fn encode_raw(&mut self, samples: &[Sample]) -> Vec<u8> {
202        let channels = usize::from(self.channels);
203        if samples.is_empty() || channels == 0 || samples.len() % channels != 0 {
204            return Vec::new();
205        }
206
207        let frame_size = samples.len() / channels;
208
209        if self.w_input_f32.len() < samples.len() {
210            self.w_input_f32.resize(samples.len(), 0.0);
211        }
212
213        for (dst, &s) in self.w_input_f32[..samples.len()]
214            .iter_mut()
215            .zip(samples.iter())
216        {
217            *dst = s as f32 / 32768.0;
218        }
219
220        match self.encoder.encode(
221            &self.w_input_f32[..samples.len()],
222            frame_size,
223            &mut self.w_packet,
224        ) {
225            Ok(len) => {
226                let mut out = Vec::with_capacity(len);
227                out.extend_from_slice(&self.w_packet[..len]);
228                out
229            }
230            Err(_) => Vec::new(),
231        }
232    }
233}
234
235impl Encoder for OpusEncoder {
236    fn encode(&mut self, samples: &[Sample]) -> Vec<u8> {
237        if self.channels == 2 {
238            let mut stereo = std::mem::take(&mut self.w_stereo);
239            stereo.resize(samples.len() * 2, 0);
240            for (i, &sample) in samples.iter().enumerate() {
241                stereo[2 * i] = sample;
242                stereo[2 * i + 1] = sample;
243            }
244            let out = self.encode_raw(&stereo);
245            self.w_stereo = stereo;
246            return out;
247        }
248        self.encode_raw(samples)
249    }
250
251    fn sample_rate(&self) -> u32 {
252        self.sample_rate
253    }
254
255    fn channels(&self) -> u16 {
256        self.channels
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263
264    fn make_mono_pcm_20ms() -> Vec<i16> {
265        (0..960).map(|i| ((i * 100) % 32767) as i16).collect()
266    }
267
268    #[test]
269    fn test_opus_encode_decode_20ms_produces_960_mono_samples() {
270        let mut enc = OpusEncoder::new_default();
271        let pcm = make_mono_pcm_20ms();
272        let opus_pkt = enc.encode(&pcm);
273        assert!(!opus_pkt.is_empty(), "encoder should produce output");
274
275        // TOC byte bit 2 should be set (stereo flag from mono→stereo upmix)
276        assert!(opus_pkt[0] & 0x04 != 0, "packet should be stereo");
277
278        let mut dec = OpusDecoder::new_default();
279        let decoded = dec.decode(&opus_pkt);
280
281        // 20ms at 48kHz mono = 960 samples
282        assert_eq!(
283            decoded.len(),
284            960,
285            "decoder should downmix stereo→mono and output 960 samples, got {}",
286            decoded.len()
287        );
288    }
289
290    #[test]
291    fn test_opus_consecutive_frames_all_produce_960() {
292        let mut enc = OpusEncoder::new_default();
293        let pcm = make_mono_pcm_20ms();
294        let opus_pkt = enc.encode(&pcm);
295
296        let mut dec = OpusDecoder::new_default();
297        for i in 0..5 {
298            let decoded = dec.decode(&opus_pkt);
299            assert_eq!(
300                decoded.len(),
301                960,
302                "frame {} should produce 960 mono samples, got {}",
303                i,
304                decoded.len()
305            );
306        }
307    }
308
309    #[test]
310    fn test_opus_decoder_output_has_reasonable_energy() {
311        let mut enc = OpusEncoder::new_default();
312        // 440Hz sine at 48kHz, 20ms
313        let pcm: Vec<i16> = (0..960)
314            .map(|i| {
315                let t = i as f64 / 48000.0;
316                (16384.0 * (2.0 * std::f64::consts::PI * 440.0 * t).sin()) as i16
317            })
318            .collect();
319        let opus_pkt = enc.encode(&pcm);
320
321        let mut dec = OpusDecoder::new_default();
322        let decoded = dec.decode(&opus_pkt);
323        assert_eq!(decoded.len(), 960);
324
325        let energy: f64 =
326            decoded.iter().map(|&s| (s as f64).powi(2)).sum::<f64>() / decoded.len() as f64;
327        let rms = energy.sqrt();
328        assert!(
329            rms > 100.0 && rms < 20000.0,
330            "decoded audio RMS {} should be in reasonable range",
331            rms
332        );
333    }
334
335    #[test]
336    fn test_opus_decoder_handles_mono_packet_gracefully() {
337        // Create a mono Opus encoder, encode PCM → mono Opus packet
338        let mut mono_enc = OpusEncoder::new(48000, 1);
339        let pcm = make_mono_pcm_20ms();
340        let mono_pkt = mono_enc.encode(&pcm);
341        assert!(!mono_pkt.is_empty());
342        // Mono packet TOC bit 2 should be 0
343        assert!(
344            mono_pkt[0] & 0x04 == 0,
345            "mono encoder should produce mono packet"
346        );
347
348        // Decode with default stereo decoder — should handle mono→stereo transition
349        let mut dec = OpusDecoder::new_default();
350        let decoded = dec.decode(&mono_pkt);
351        assert_eq!(
352            decoded.len(),
353            960,
354            "first mono packet should produce exactly 960 mono samples, got {}",
355            decoded.len()
356        );
357
358        // Second mono packet should also be 960
359        let decoded2 = dec.decode(&mono_pkt);
360        assert_eq!(
361            decoded2.len(),
362            960,
363            "second mono packet should also produce 960 mono samples, got {}",
364            decoded2.len()
365        );
366    }
367
368    #[test]
369    fn test_opus_stereo_packet_downmix_produces_960() {
370        let mut enc = OpusEncoder::new_default();
371        let pcm = make_mono_pcm_20ms();
372        let stereo_pkt = enc.encode(&pcm);
373        assert!(stereo_pkt[0] & 0x04 != 0);
374
375        let mut dec = OpusDecoder::new_default();
376        let decoded = dec.decode(&stereo_pkt);
377        assert_eq!(
378            decoded.len(),
379            960,
380            "stereo packet should downmix to 960 mono samples"
381        );
382    }
383}