audio_codec/
opus.rs

1use super::{Decoder, Encoder, PcmBuf, Sample};
2
3use opusic_sys::{
4    OPUS_APPLICATION_VOIP, OPUS_OK, OpusDecoder as OpusDecoderRaw, OpusEncoder as OpusEncoderRaw,
5    opus_decode, opus_decoder_create, opus_decoder_destroy, opus_encode, opus_encoder_create,
6    opus_encoder_destroy, opus_strerror,
7};
8use std::{ffi::CStr, os::raw::c_int, ptr::NonNull};
9
10fn opus_error_message(code: c_int) -> String {
11    if code == OPUS_OK {
12        return "ok".to_string();
13    }
14
15    unsafe {
16        let ptr = opus_strerror(code);
17        if ptr.is_null() {
18            format!("error code {code}")
19        } else {
20            CStr::from_ptr(ptr).to_string_lossy().into_owned()
21        }
22    }
23}
24
25/// Opus audio decoder backed by opusic-sys
26pub struct OpusDecoder {
27    decoder: NonNull<OpusDecoderRaw>,
28    sample_rate: u32,
29    channels: u16,
30}
31
32impl OpusDecoder {
33    /// Create a new Opus decoder instance
34    pub fn new(sample_rate: u32, channels: u16) -> Self {
35        let channel_count: c_int = if channels == 1 { 1 } else { 2 };
36        let mut error: c_int = 0;
37        let ptr = unsafe {
38            opus_decoder_create(
39                sample_rate as c_int,
40                channel_count,
41                &mut error as *mut c_int,
42            )
43        };
44
45        if error != OPUS_OK {
46            unsafe {
47                if !ptr.is_null() {
48                    opus_decoder_destroy(ptr);
49                }
50            }
51            panic!(
52                "Failed to create Opus decoder: {}",
53                opus_error_message(error)
54            );
55        }
56
57        let decoder = NonNull::new(ptr).unwrap_or_else(|| {
58            panic!("Failed to create Opus decoder: null pointer returned");
59        });
60
61        Self {
62            decoder,
63            sample_rate,
64            channels: if channel_count == 1 { 1 } else { 2 },
65        }
66    }
67
68    /// Create a default Opus decoder (48kHz, stereo)
69    pub fn new_default() -> Self {
70        Self::new(48000, 2)
71    }
72}
73
74impl Drop for OpusDecoder {
75    fn drop(&mut self) {
76        unsafe {
77            opus_decoder_destroy(self.decoder.as_ptr());
78        }
79    }
80}
81
82unsafe impl Send for OpusDecoder {}
83unsafe impl Sync for OpusDecoder {}
84
85impl Decoder for OpusDecoder {
86    fn decode(&mut self, data: &[u8]) -> PcmBuf {
87        let channels = usize::from(self.channels);
88        if channels == 0 {
89            return Vec::new();
90        }
91
92        // Allow up to 120ms of audio as before: 48kHz * 0.12s * 2 channels = 11520 samples
93        let max_samples = 11520;
94        let mut output = vec![0i16; max_samples];
95        let frame_size = (max_samples / channels) as c_int;
96
97        let data_ptr = if data.is_empty() {
98            std::ptr::null()
99        } else {
100            data.as_ptr()
101        };
102
103        let len = unsafe {
104            opus_decode(
105                self.decoder.as_ptr(),
106                data_ptr.cast(),
107                data.len() as c_int,
108                output.as_mut_ptr().cast(),
109                frame_size,
110                0,
111            )
112        };
113
114        if len < 0 {
115            return Vec::new();
116        }
117
118        let total_samples = (len as usize) * channels;
119        output.truncate(total_samples);
120
121        if channels == 2 {
122            output = output
123                .chunks_exact(2)
124                .map(|chunk| ((chunk[0] as i32 + chunk[1] as i32) / 2) as i16)
125                .collect();
126        }
127
128        output
129    }
130
131    fn sample_rate(&self) -> u32 {
132        self.sample_rate
133    }
134
135    fn channels(&self) -> u16 {
136        self.channels
137    }
138}
139
140/// Opus audio encoder backed by opusic-sys
141pub struct OpusEncoder {
142    encoder: NonNull<OpusEncoderRaw>,
143    sample_rate: u32,
144    channels: u16,
145}
146
147impl OpusEncoder {
148    /// Create a new Opus encoder instance
149    pub fn new(sample_rate: u32, channels: u16) -> Self {
150        let channel_count: c_int = if channels == 1 { 1 } else { 2 };
151        let mut error: c_int = 0;
152        let ptr = unsafe {
153            opus_encoder_create(
154                sample_rate as c_int,
155                channel_count,
156                OPUS_APPLICATION_VOIP,
157                &mut error as *mut c_int,
158            )
159        };
160
161        if error != OPUS_OK {
162            unsafe {
163                if !ptr.is_null() {
164                    opus_encoder_destroy(ptr);
165                }
166            }
167            panic!(
168                "Failed to create Opus encoder: {}",
169                opus_error_message(error)
170            );
171        }
172
173        let encoder = NonNull::new(ptr).unwrap_or_else(|| {
174            panic!("Failed to create Opus encoder: null pointer returned");
175        });
176
177        Self {
178            encoder,
179            sample_rate,
180            channels: if channel_count == 1 { 1 } else { 2 },
181        }
182    }
183
184    /// Create a default Opus encoder (48kHz, stereo)
185    pub fn new_default() -> Self {
186        Self::new(48000, 2)
187    }
188
189    fn encode_raw(&mut self, samples: &[Sample]) -> Vec<u8> {
190        let channels = usize::from(self.channels);
191        if samples.is_empty() || channels == 0 || samples.len() % channels != 0 {
192            return Vec::new();
193        }
194
195        let frame_size = (samples.len() / channels) as c_int;
196        let mut output = vec![0u8; samples.len()];
197        let len = unsafe {
198            opus_encode(
199                self.encoder.as_ptr(),
200                samples.as_ptr().cast(),
201                frame_size,
202                output.as_mut_ptr(),
203                output.len() as c_int,
204            )
205        };
206
207        if len < 0 {
208            Vec::new()
209        } else {
210            output.truncate(len as usize);
211            output
212        }
213    }
214}
215
216impl Drop for OpusEncoder {
217    fn drop(&mut self) {
218        unsafe {
219            opus_encoder_destroy(self.encoder.as_ptr());
220        }
221    }
222}
223
224unsafe impl Send for OpusEncoder {}
225unsafe impl Sync for OpusEncoder {}
226
227impl Encoder for OpusEncoder {
228    fn encode(&mut self, samples: &[Sample]) -> Vec<u8> {
229        if self.channels == 2 {
230            let mut stereo_samples = Vec::with_capacity(samples.len() * 2);
231            for &sample in samples {
232                stereo_samples.push(sample);
233                stereo_samples.push(sample);
234            }
235            return self.encode_raw(&stereo_samples);
236        }
237
238        self.encode_raw(samples)
239    }
240
241    fn sample_rate(&self) -> u32 {
242        self.sample_rate
243    }
244
245    fn channels(&self) -> u16 {
246        self.channels
247    }
248}