opus_codec/
decoder.rs

1//! Opus decoder implementation with safe wrappers
2
3#[cfg(feature = "dred")]
4use crate::bindings::{
5    OPUS_GET_DRED_DURATION_REQUEST, OPUS_SET_DNN_BLOB_REQUEST, OPUS_SET_DRED_DURATION_REQUEST,
6};
7use crate::bindings::{
8    OPUS_GET_FINAL_RANGE_REQUEST, OPUS_GET_GAIN_REQUEST, OPUS_GET_LAST_PACKET_DURATION_REQUEST,
9    OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, OPUS_GET_PITCH_REQUEST,
10    OPUS_GET_SAMPLE_RATE_REQUEST, OPUS_RESET_STATE, OPUS_SET_GAIN_REQUEST,
11    OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, OpusDecoder, opus_decode, opus_decode_float,
12    opus_decoder_create, opus_decoder_ctl, opus_decoder_destroy, opus_decoder_get_nb_samples,
13};
14use crate::constants::max_frame_samples_for;
15use crate::error::{Error, Result};
16use crate::packet;
17use crate::types::{Bandwidth, Channels, SampleRate};
18use std::ptr;
19
20/// Safe wrapper around a libopus `OpusDecoder`.
21pub struct Decoder {
22    raw: *mut OpusDecoder,
23    sample_rate: SampleRate,
24    channels: Channels,
25}
26
27unsafe impl Send for Decoder {}
28unsafe impl Sync for Decoder {}
29
30impl Decoder {
31    /// Create a new decoder for a given sample rate and channel layout.
32    ///
33    /// # Errors
34    /// Returns an error if allocation fails or arguments are invalid.
35    pub fn new(sample_rate: SampleRate, channels: Channels) -> Result<Self> {
36        // Validate sample rate
37        if !sample_rate.is_valid() {
38            return Err(Error::BadArg);
39        }
40
41        let mut error = 0i32;
42        let decoder = unsafe {
43            opus_decoder_create(
44                sample_rate.as_i32(),
45                channels.as_i32(),
46                std::ptr::addr_of_mut!(error),
47            )
48        };
49
50        if error != 0 {
51            return Err(Error::from_code(error));
52        }
53
54        if decoder.is_null() {
55            return Err(Error::AllocFail);
56        }
57
58        Ok(Self {
59            raw: decoder,
60            sample_rate,
61            channels,
62        })
63    }
64
65    /// Decode a packet into 16-bit PCM.
66    ///
67    /// - `input`: Opus packet bytes. Pass empty slice to invoke PLC.
68    /// - `output`: Interleaved output buffer sized to `frame_size * channels`.
69    /// - `fec`: Enable in-band FEC if available.
70    ///
71    /// # Errors
72    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
73    /// for invalid buffer sizes or frame sizes, or a mapped libopus error via
74    /// [`Error::from_code`].
75    pub fn decode(&mut self, input: &[u8], output: &mut [i16], fec: bool) -> Result<usize> {
76        // Errors: InvalidState, BadArg, or libopus error mapped.
77        if self.raw.is_null() {
78            return Err(Error::InvalidState);
79        }
80
81        // Validate buffer sizes up-front
82        if !input.is_empty() && input.len() > i32::MAX as usize {
83            return Err(Error::BadArg);
84        }
85        if output.is_empty() {
86            return Err(Error::BadArg);
87        }
88        if !output.len().is_multiple_of(self.channels.as_usize()) {
89            return Err(Error::BadArg);
90        }
91        let frame_size = output.len() / self.channels.as_usize();
92        let max_frame = max_frame_samples_for(self.sample_rate);
93        if frame_size == 0 || frame_size > max_frame {
94            return Err(Error::BadArg);
95        }
96
97        let input_len_i32 = if input.is_empty() {
98            0
99        } else {
100            i32::try_from(input.len()).map_err(|_| Error::BadArg)?
101        };
102        let frame_size_i32 = i32::try_from(frame_size).map_err(|_| Error::BadArg)?;
103
104        let result = unsafe {
105            opus_decode(
106                self.raw,
107                if input.is_empty() {
108                    ptr::null()
109                } else {
110                    input.as_ptr()
111                },
112                input_len_i32,
113                output.as_mut_ptr(),
114                frame_size_i32,
115                i32::from(fec),
116            )
117        };
118
119        if result < 0 {
120            return Err(Error::from_code(result));
121        }
122
123        usize::try_from(result).map_err(|_| Error::InternalError)
124    }
125
126    /// Decode a packet into `f32` PCM.
127    ///
128    /// See [`Self::decode`] for parameter semantics.
129    ///
130    /// # Errors
131    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
132    /// for invalid buffer sizes or frame sizes, or a mapped libopus error via
133    /// [`Error::from_code`].
134    pub fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> Result<usize> {
135        if self.raw.is_null() {
136            return Err(Error::InvalidState);
137        }
138
139        // Validate buffer sizes up-front
140        if !input.is_empty() && input.len() > i32::MAX as usize {
141            return Err(Error::BadArg);
142        }
143        if output.is_empty() {
144            return Err(Error::BadArg);
145        }
146        if !output.len().is_multiple_of(self.channels.as_usize()) {
147            return Err(Error::BadArg);
148        }
149        let frame_size = output.len() / self.channels.as_usize();
150        let max_frame = max_frame_samples_for(self.sample_rate);
151        if frame_size == 0 || frame_size > max_frame {
152            return Err(Error::BadArg);
153        }
154
155        let input_len_i32 = if input.is_empty() {
156            0
157        } else {
158            i32::try_from(input.len()).map_err(|_| Error::BadArg)?
159        };
160        let frame_size_i32 = i32::try_from(frame_size).map_err(|_| Error::BadArg)?;
161
162        let result = unsafe {
163            opus_decode_float(
164                self.raw,
165                if input.is_empty() {
166                    ptr::null()
167                } else {
168                    input.as_ptr()
169                },
170                input_len_i32,
171                output.as_mut_ptr(),
172                frame_size_i32,
173                i32::from(fec),
174            )
175        };
176
177        if result < 0 {
178            return Err(Error::from_code(result));
179        }
180
181        usize::try_from(result).map_err(|_| Error::InternalError)
182    }
183
184    /// Return the number of samples (per channel) in an Opus `packet` at this decoder's rate.
185    ///
186    /// # Errors
187    /// Returns [`Error::InvalidState`] if the decoder is invalid, [`Error::BadArg`] for
188    /// overlong input, or a mapped libopus error.
189    pub fn packet_samples(&self, packet: &[u8]) -> Result<usize> {
190        // Errors: InvalidState or libopus error mapped.
191        if self.raw.is_null() {
192            return Err(Error::InvalidState);
193        }
194
195        if packet.len() > i32::MAX as usize {
196            return Err(Error::BadArg);
197        }
198        let len_i32 = i32::try_from(packet.len()).map_err(|_| Error::BadArg)?;
199        let result = unsafe { opus_decoder_get_nb_samples(self.raw, packet.as_ptr(), len_i32) };
200
201        if result < 0 {
202            return Err(Error::from_code(result));
203        }
204
205        usize::try_from(result).map_err(|_| Error::InternalError)
206    }
207
208    /// Return the bandwidth encoded in an Opus `packet`.
209    ///
210    /// # Errors
211    /// Returns [`Error::InvalidState`] if the decoder is invalid, or [`Error::InvalidPacket`]
212    /// if the packet cannot be parsed.
213    pub fn packet_bandwidth(&self, packet: &[u8]) -> Result<Bandwidth> {
214        // Errors: InvalidState or InvalidPacket.
215        if self.raw.is_null() {
216            return Err(Error::InvalidState);
217        }
218
219        packet::packet_bandwidth(packet)
220    }
221
222    /// Return the number of channels described by an Opus `packet`.
223    ///
224    /// # Errors
225    /// Returns [`Error::InvalidState`] if the decoder is invalid, or [`Error::InvalidPacket`]
226    /// if the packet cannot be parsed.
227    pub fn packet_channels(&self, packet: &[u8]) -> Result<Channels> {
228        // Errors: InvalidState or InvalidPacket.
229        if self.raw.is_null() {
230            return Err(Error::InvalidState);
231        }
232
233        packet::packet_channels(packet)
234    }
235
236    /// Reset the decoder to its initial state.
237    ///
238    /// # Errors
239    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error
240    /// if resetting fails.
241    pub fn reset(&mut self) -> Result<()> {
242        // Errors: InvalidState or request failure.
243        if self.raw.is_null() {
244            return Err(Error::InvalidState);
245        }
246
247        // OPUS_RESET_STATE takes no additional argument. Passing extras is undefined behavior.
248        let result = unsafe { opus_decoder_ctl(self.raw, OPUS_RESET_STATE as i32) };
249
250        if result != 0 {
251            return Err(Error::from_code(result));
252        }
253
254        Ok(())
255    }
256
257    /// The decoder's configured sample rate.
258    #[must_use]
259    pub const fn sample_rate(&self) -> SampleRate {
260        self.sample_rate
261    }
262
263    /// The decoder's channel configuration.
264    #[must_use]
265    pub const fn channels(&self) -> Channels {
266        self.channels
267    }
268
269    #[cfg_attr(not(feature = "dred"), allow(dead_code))]
270    pub(crate) fn as_mut_ptr(&mut self) -> *mut OpusDecoder {
271        self.raw
272    }
273
274    /// Query decoder output sample rate.
275    ///
276    /// # Errors
277    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
278    pub fn get_sample_rate(&mut self) -> Result<i32> {
279        self.get_int_ctl(OPUS_GET_SAMPLE_RATE_REQUEST as i32)
280    }
281
282    /// Query pitch (fundamental period) of the last decoded frame (in samples at 48 kHz domain).
283    ///
284    /// # Errors
285    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
286    pub fn get_pitch(&mut self) -> Result<i32> {
287        self.get_int_ctl(OPUS_GET_PITCH_REQUEST as i32)
288    }
289
290    /// Duration (per channel) of the last decoded packet.
291    ///
292    /// # Errors
293    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
294    pub fn get_last_packet_duration(&mut self) -> Result<i32> {
295        self.get_int_ctl(OPUS_GET_LAST_PACKET_DURATION_REQUEST as i32)
296    }
297
298    /// Final RNG state after the last decode.
299    ///
300    /// # Errors
301    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
302    pub fn final_range(&mut self) -> Result<u32> {
303        if self.raw.is_null() {
304            return Err(Error::InvalidState);
305        }
306        let mut v: u32 = 0;
307        let r = unsafe { opus_decoder_ctl(self.raw, OPUS_GET_FINAL_RANGE_REQUEST as i32, &mut v) };
308        if r != 0 {
309            return Err(Error::from_code(r));
310        }
311        Ok(v)
312    }
313
314    /// Set post-decode gain in Q8 dB units.
315    ///
316    /// # Errors
317    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
318    pub fn set_gain(&mut self, q8_db: i32) -> Result<()> {
319        self.simple_ctl(OPUS_SET_GAIN_REQUEST as i32, q8_db)
320    }
321    /// Query post-decode gain in Q8 dB units.
322    ///
323    /// # Errors
324    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
325    pub fn gain(&mut self) -> Result<i32> {
326        self.get_int_ctl(OPUS_GET_GAIN_REQUEST as i32)
327    }
328
329    /// Returns true if phase inversion is disabled (CELT stereo decorrelation).
330    ///
331    /// # Errors
332    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
333    pub fn phase_inversion_disabled(&mut self) -> Result<bool> {
334        Ok(self.get_int_ctl(OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST as i32)? != 0)
335    }
336
337    /// Disable/enable phase inversion (CELT stereo decorrelation).
338    ///
339    /// # Errors
340    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
341    pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
342        self.simple_ctl(
343            OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST as i32,
344            i32::from(disabled),
345        )
346    }
347
348    #[cfg(feature = "dred")]
349    /// Set DRED duration in ms (if libopus built with DRED).
350    ///
351    /// # Errors
352    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
353    pub fn set_dred_duration(&mut self, ms: i32) -> Result<()> {
354        self.simple_ctl(OPUS_SET_DRED_DURATION_REQUEST as i32, ms)
355    }
356    #[cfg(feature = "dred")]
357    /// Query DRED duration in ms.
358    ///
359    /// # Errors
360    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
361    pub fn dred_duration(&mut self) -> Result<i32> {
362        self.get_int_ctl(OPUS_GET_DRED_DURATION_REQUEST as i32)
363    }
364    #[cfg(feature = "dred")]
365    /// Set DNN blob for DRED (feature-gated; will error if unsupported).
366    ///
367    /// # Safety
368    /// Caller must ensure `ptr` is valid for reads as expected by libopus for the duration of the call
369    /// and points to a properly formatted DNN blob. Passing an invalid or dangling pointer is UB.
370    ///
371    /// # Errors
372    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
373    pub unsafe fn set_dnn_blob(&mut self, ptr: *const u8, len: i32) -> Result<()> {
374        if self.raw.is_null() {
375            return Err(Error::InvalidState);
376        }
377        if ptr.is_null() || len <= 0 {
378            return Err(Error::BadArg);
379        }
380        let r = unsafe { opus_decoder_ctl(self.raw, OPUS_SET_DNN_BLOB_REQUEST as i32, ptr, len) };
381        if r != 0 {
382            return Err(Error::from_code(r));
383        }
384        Ok(())
385    }
386
387    // --- internal helpers for CTLs ---
388    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
389        if self.raw.is_null() {
390            return Err(Error::InvalidState);
391        }
392        let r = unsafe { opus_decoder_ctl(self.raw, req, val) };
393        if r != 0 {
394            return Err(Error::from_code(r));
395        }
396        Ok(())
397    }
398    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
399        if self.raw.is_null() {
400            return Err(Error::InvalidState);
401        }
402        let mut v: i32 = 0;
403        let r = unsafe { opus_decoder_ctl(self.raw, req, &mut v) };
404        if r != 0 {
405            return Err(Error::from_code(r));
406        }
407        Ok(v)
408    }
409}
410
411impl Drop for Decoder {
412    fn drop(&mut self) {
413        unsafe {
414            opus_decoder_destroy(self.raw);
415        }
416    }
417}