Skip to main content

rtp_engine/codec/
opus_codec.rs

1//! Opus codec implementation.
2//!
3//! Opus is a modern, high-quality codec optimized for voice and music.
4//! It operates at 48kHz internally, so resampling is required for 8kHz VoIP.
5
6use super::{AudioDecoder, AudioEncoder, CodecType};
7use crate::error::{Error, Result};
8
9/// Opus encoder with integrated 8kHz→48kHz resampling.
10pub struct OpusEncoder {
11    encoder: audiopus::coder::Encoder,
12    resample_buf: Vec<i16>,
13}
14
15impl OpusEncoder {
16    /// Create a new Opus encoder.
17    pub fn new() -> Result<Self> {
18        let encoder = audiopus::coder::Encoder::new(
19            audiopus::SampleRate::Hz48000,
20            audiopus::Channels::Mono,
21            audiopus::Application::Voip,
22        )
23        .map_err(|e| Error::codec(format!("Opus encoder init: {}", e)))?;
24
25        Ok(Self {
26            encoder,
27            resample_buf: Vec::with_capacity(960),
28        })
29    }
30}
31
32impl AudioEncoder for OpusEncoder {
33    fn encode(&mut self, pcm: &[i16], output: &mut Vec<u8>) -> usize {
34        // Resample 8kHz → 48kHz (6x linear interpolation)
35        self.resample_buf.clear();
36        for i in 0..pcm.len() {
37            let current = pcm[i] as f64;
38            let next = if i + 1 < pcm.len() {
39                pcm[i + 1] as f64
40            } else {
41                current
42            };
43            for j in 0..6 {
44                let t = j as f64 / 6.0;
45                self.resample_buf
46                    .push((current + (next - current) * t) as i16);
47            }
48        }
49
50        let mut encoded = [0u8; 4000];
51        match self.encoder.encode(&self.resample_buf, &mut encoded) {
52            Ok(len) => {
53                output.extend_from_slice(&encoded[..len]);
54                pcm.len()
55            }
56            Err(e) => {
57                log::error!("Opus encode error: {}", e);
58                0
59            }
60        }
61    }
62
63    fn payload_type(&self) -> u8 {
64        111
65    }
66
67    fn codec_type(&self) -> CodecType {
68        CodecType::Opus
69    }
70}
71
72impl std::fmt::Debug for OpusEncoder {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        f.debug_struct("OpusEncoder")
75            .field("resample_buf_capacity", &self.resample_buf.capacity())
76            .finish()
77    }
78}
79
80/// Opus decoder with integrated 48kHz→8kHz resampling.
81pub struct OpusDecoder {
82    decoder: audiopus::coder::Decoder,
83}
84
85impl OpusDecoder {
86    /// Create a new Opus decoder.
87    pub fn new() -> Result<Self> {
88        let decoder =
89            audiopus::coder::Decoder::new(audiopus::SampleRate::Hz48000, audiopus::Channels::Mono)
90                .map_err(|e| Error::codec(format!("Opus decoder init: {}", e)))?;
91
92        Ok(Self { decoder })
93    }
94}
95
96impl AudioDecoder for OpusDecoder {
97    fn decode(&mut self, encoded: &[u8], output: &mut Vec<i16>) {
98        let mut decoded = [0i16; 5760]; // Max opus frame
99
100        let packet: audiopus::packet::Packet<'_> = match encoded.try_into() {
101            Ok(p) => p,
102            Err(e) => {
103                log::error!("Opus packet error: {}", e);
104                return;
105            }
106        };
107        let out_signals: audiopus::MutSignals<'_, _> = match (&mut decoded[..]).try_into() {
108            Ok(s) => s,
109            Err(e) => {
110                log::error!("Opus signals error: {}", e);
111                return;
112            }
113        };
114
115        match self.decoder.decode(Some(packet), out_signals, false) {
116            Ok(samples) => {
117                // Downsample 48kHz → 8kHz (take every 6th sample)
118                for i in (0..samples).step_by(6) {
119                    output.push(decoded[i]);
120                }
121            }
122            Err(e) => {
123                log::error!("Opus decode error: {}", e);
124            }
125        }
126    }
127
128    fn codec_type(&self) -> CodecType {
129        CodecType::Opus
130    }
131}
132
133impl std::fmt::Debug for OpusDecoder {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("OpusDecoder").finish()
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_opus_encoder_creation() {
145        let encoder = OpusEncoder::new();
146        assert!(encoder.is_ok());
147    }
148
149    #[test]
150    fn test_opus_decoder_creation() {
151        let decoder = OpusDecoder::new();
152        assert!(decoder.is_ok());
153    }
154
155    #[test]
156    fn test_opus_roundtrip() {
157        let mut encoder = OpusEncoder::new().unwrap();
158        let mut decoder = OpusDecoder::new().unwrap();
159
160        // Create a 20ms frame of 440Hz sine wave at 8kHz
161        let input: Vec<i16> = (0..160)
162            .map(|i| {
163                let t = i as f64 / 8000.0;
164                (f64::sin(2.0 * std::f64::consts::PI * 440.0 * t) * 16000.0) as i16
165            })
166            .collect();
167
168        let mut encoded = Vec::new();
169        let consumed = encoder.encode(&input, &mut encoded);
170        assert_eq!(consumed, 160);
171        assert!(!encoded.is_empty());
172
173        let mut decoded = Vec::new();
174        decoder.decode(&encoded, &mut decoded);
175        // Opus output should be roughly the same length after resampling
176        assert!(decoded.len() >= 100 && decoded.len() <= 200);
177    }
178}