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    opus_decoder_get_size, opus_decoder_init,
14};
15use crate::constants::max_frame_samples_for;
16use crate::error::{Error, Result};
17use crate::packet;
18use crate::types::{Bandwidth, Channels, SampleRate};
19use crate::{AlignedBuffer, Ownership, RawHandle};
20use std::marker::PhantomData;
21use std::num::NonZeroUsize;
22use std::ops::{Deref, DerefMut};
23use std::ptr::{self, NonNull};
24
25/// Safe wrapper around a libopus `OpusDecoder`.
26pub struct Decoder {
27    raw: RawHandle<OpusDecoder>,
28    sample_rate: SampleRate,
29    channels: Channels,
30}
31
32unsafe impl Send for Decoder {}
33unsafe impl Sync for Decoder {}
34
35/// Borrowed wrapper around a decoder state.
36pub struct DecoderRef<'a> {
37    inner: Decoder,
38    _marker: PhantomData<&'a mut OpusDecoder>,
39}
40
41unsafe impl Send for DecoderRef<'_> {}
42unsafe impl Sync for DecoderRef<'_> {}
43
44impl Decoder {
45    fn from_raw(
46        ptr: NonNull<OpusDecoder>,
47        sample_rate: SampleRate,
48        channels: Channels,
49        ownership: Ownership,
50    ) -> Self {
51        Self {
52            raw: RawHandle::new(ptr, ownership, opus_decoder_destroy),
53            sample_rate,
54            channels,
55        }
56    }
57
58    /// Size in bytes of a decoder state for external allocation.
59    ///
60    /// # Errors
61    /// Returns [`Error::BadArg`] if the channel count is invalid or libopus reports
62    /// an impossible size.
63    pub fn size(channels: Channels) -> Result<usize> {
64        let raw = unsafe { opus_decoder_get_size(channels.as_i32()) };
65        if raw <= 0 {
66            return Err(Error::BadArg);
67        }
68        usize::try_from(raw).map_err(|_| Error::InternalError)
69    }
70
71    /// Initialize a previously allocated decoder state.
72    ///
73    /// # Safety
74    /// The caller must provide a valid pointer to `Decoder::size()` bytes,
75    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
76    ///
77    /// # Errors
78    /// Returns [`Error::BadArg`] if `ptr` is null, or a mapped libopus error.
79    pub unsafe fn init_in_place(
80        ptr: *mut OpusDecoder,
81        sample_rate: SampleRate,
82        channels: Channels,
83    ) -> Result<()> {
84        if ptr.is_null() {
85            return Err(Error::BadArg);
86        }
87        if !crate::opus_ptr_is_aligned(ptr.cast()) {
88            return Err(Error::BadArg);
89        }
90        let r = unsafe { opus_decoder_init(ptr, sample_rate.as_i32(), channels.as_i32()) };
91        if r != 0 {
92            return Err(Error::from_code(r));
93        }
94        Ok(())
95    }
96
97    /// Create a new decoder for a given sample rate and channel layout.
98    ///
99    /// # Errors
100    /// Returns an error if allocation fails or arguments are invalid.
101    pub fn new(sample_rate: SampleRate, channels: Channels) -> Result<Self> {
102        // Validate sample rate
103        if !sample_rate.is_valid() {
104            return Err(Error::BadArg);
105        }
106
107        let mut error = 0i32;
108        let decoder = unsafe {
109            opus_decoder_create(
110                sample_rate.as_i32(),
111                channels.as_i32(),
112                std::ptr::addr_of_mut!(error),
113            )
114        };
115
116        if error != 0 {
117            return Err(Error::from_code(error));
118        }
119
120        let decoder = NonNull::new(decoder).ok_or(Error::AllocFail)?;
121
122        Ok(Self::from_raw(
123            decoder,
124            sample_rate,
125            channels,
126            Ownership::Owned,
127        ))
128    }
129
130    /// Decode a packet into 16-bit PCM.
131    ///
132    /// - `input`: Opus packet bytes. Pass empty slice to invoke PLC.
133    /// - `output`: Interleaved output buffer sized to `frame_size * channels`.
134    /// - `fec`: Enable in-band FEC if available.
135    ///
136    /// # Errors
137    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
138    /// for invalid buffer sizes or frame sizes, or a mapped libopus error via
139    /// [`Error::from_code`].
140    pub fn decode(&mut self, input: &[u8], output: &mut [i16], fec: bool) -> Result<usize> {
141        // Errors: InvalidState, BadArg, or libopus error mapped.
142        // Validate buffer sizes up-front
143        if !input.is_empty() && input.len() > i32::MAX as usize {
144            return Err(Error::BadArg);
145        }
146        if output.is_empty() {
147            return Err(Error::BadArg);
148        }
149        if !output.len().is_multiple_of(self.channels.as_usize()) {
150            return Err(Error::BadArg);
151        }
152        let frame_size = output.len() / self.channels.as_usize();
153        let frame_size = NonZeroUsize::new(frame_size).ok_or(Error::BadArg)?;
154        let max_frame = max_frame_samples_for(self.sample_rate);
155        if frame_size.get() > max_frame {
156            return Err(Error::BadArg);
157        }
158
159        let input_len_i32 = if input.is_empty() {
160            0
161        } else {
162            i32::try_from(input.len()).map_err(|_| Error::BadArg)?
163        };
164        let frame_size_i32 = i32::try_from(frame_size.get()).map_err(|_| Error::BadArg)?;
165
166        let result = unsafe {
167            opus_decode(
168                self.raw.as_ptr(),
169                if input.is_empty() {
170                    ptr::null()
171                } else {
172                    input.as_ptr()
173                },
174                input_len_i32,
175                output.as_mut_ptr(),
176                frame_size_i32,
177                i32::from(fec),
178            )
179        };
180
181        if result < 0 {
182            return Err(Error::from_code(result));
183        }
184
185        usize::try_from(result).map_err(|_| Error::InternalError)
186    }
187
188    /// Decode a packet into `f32` PCM.
189    ///
190    /// See [`Self::decode`] for parameter semantics.
191    ///
192    /// # Errors
193    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
194    /// for invalid buffer sizes or frame sizes, or a mapped libopus error via
195    /// [`Error::from_code`].
196    pub fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> Result<usize> {
197        // Validate buffer sizes up-front
198        if !input.is_empty() && input.len() > i32::MAX as usize {
199            return Err(Error::BadArg);
200        }
201        if output.is_empty() {
202            return Err(Error::BadArg);
203        }
204        if !output.len().is_multiple_of(self.channels.as_usize()) {
205            return Err(Error::BadArg);
206        }
207        let frame_size = output.len() / self.channels.as_usize();
208        let frame_size = NonZeroUsize::new(frame_size).ok_or(Error::BadArg)?;
209        let max_frame = max_frame_samples_for(self.sample_rate);
210        if frame_size.get() > max_frame {
211            return Err(Error::BadArg);
212        }
213
214        let input_len_i32 = if input.is_empty() {
215            0
216        } else {
217            i32::try_from(input.len()).map_err(|_| Error::BadArg)?
218        };
219        let frame_size_i32 = i32::try_from(frame_size.get()).map_err(|_| Error::BadArg)?;
220
221        let result = unsafe {
222            opus_decode_float(
223                self.raw.as_ptr(),
224                if input.is_empty() {
225                    ptr::null()
226                } else {
227                    input.as_ptr()
228                },
229                input_len_i32,
230                output.as_mut_ptr(),
231                frame_size_i32,
232                i32::from(fec),
233            )
234        };
235
236        if result < 0 {
237            return Err(Error::from_code(result));
238        }
239
240        usize::try_from(result).map_err(|_| Error::InternalError)
241    }
242
243    /// Return the number of samples (per channel) in an Opus `packet` at this decoder's rate.
244    ///
245    /// # Errors
246    /// Returns [`Error::InvalidState`] if the decoder is invalid, [`Error::BadArg`] for
247    /// overlong input, or a mapped libopus error.
248    pub fn packet_samples(&self, packet: &[u8]) -> Result<usize> {
249        // Errors: InvalidState or libopus error mapped.
250        if packet.len() > i32::MAX as usize {
251            return Err(Error::BadArg);
252        }
253        let len_i32 = i32::try_from(packet.len()).map_err(|_| Error::BadArg)?;
254        let result =
255            unsafe { opus_decoder_get_nb_samples(self.raw.as_ptr(), packet.as_ptr(), len_i32) };
256
257        if result < 0 {
258            return Err(Error::from_code(result));
259        }
260
261        usize::try_from(result).map_err(|_| Error::InternalError)
262    }
263
264    /// Return the bandwidth encoded in an Opus `packet`.
265    ///
266    /// # Errors
267    /// Returns [`Error::InvalidState`] if the decoder is invalid, or [`Error::InvalidPacket`]
268    /// if the packet cannot be parsed.
269    pub fn packet_bandwidth(&self, packet: &[u8]) -> Result<Bandwidth> {
270        // Errors: InvalidState or InvalidPacket.
271        packet::packet_bandwidth(packet)
272    }
273
274    /// Return the number of channels described by an Opus `packet`.
275    ///
276    /// # Errors
277    /// Returns [`Error::InvalidState`] if the decoder is invalid, or [`Error::InvalidPacket`]
278    /// if the packet cannot be parsed.
279    pub fn packet_channels(&self, packet: &[u8]) -> Result<Channels> {
280        // Errors: InvalidState or InvalidPacket.
281        packet::packet_channels(packet)
282    }
283
284    /// Reset the decoder to its initial state.
285    ///
286    /// # Errors
287    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error
288    /// if resetting fails.
289    pub fn reset(&mut self) -> Result<()> {
290        // Errors: InvalidState or request failure.
291        // OPUS_RESET_STATE takes no additional argument. Passing extras is undefined behavior.
292        let result = unsafe { opus_decoder_ctl(self.raw.as_ptr(), OPUS_RESET_STATE as i32) };
293
294        if result != 0 {
295            return Err(Error::from_code(result));
296        }
297
298        Ok(())
299    }
300
301    /// The decoder's configured sample rate.
302    #[must_use]
303    pub const fn sample_rate(&self) -> SampleRate {
304        self.sample_rate
305    }
306
307    /// The decoder's channel configuration.
308    #[must_use]
309    pub const fn channels(&self) -> Channels {
310        self.channels
311    }
312
313    #[cfg_attr(not(feature = "dred"), allow(dead_code))]
314    pub(crate) fn as_mut_ptr(&mut self) -> *mut OpusDecoder {
315        self.raw.as_ptr()
316    }
317
318    /// Query decoder output sample rate.
319    ///
320    /// # Errors
321    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
322    pub fn get_sample_rate(&mut self) -> Result<i32> {
323        self.get_int_ctl(OPUS_GET_SAMPLE_RATE_REQUEST as i32)
324    }
325
326    /// Query pitch (fundamental period) of the last decoded frame (in samples at 48 kHz domain).
327    ///
328    /// # Errors
329    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
330    pub fn get_pitch(&mut self) -> Result<i32> {
331        self.get_int_ctl(OPUS_GET_PITCH_REQUEST as i32)
332    }
333
334    /// Duration (per channel) of the last decoded packet.
335    ///
336    /// # Errors
337    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
338    pub fn get_last_packet_duration(&mut self) -> Result<i32> {
339        self.get_int_ctl(OPUS_GET_LAST_PACKET_DURATION_REQUEST as i32)
340    }
341
342    /// Final RNG state after the last decode.
343    ///
344    /// # Errors
345    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
346    pub fn final_range(&mut self) -> Result<u32> {
347        let mut v: u32 = 0;
348        let r = unsafe {
349            opus_decoder_ctl(
350                self.raw.as_ptr(),
351                OPUS_GET_FINAL_RANGE_REQUEST as i32,
352                &mut v,
353            )
354        };
355        if r != 0 {
356            return Err(Error::from_code(r));
357        }
358        Ok(v)
359    }
360
361    /// Set post-decode gain in Q8 dB units.
362    ///
363    /// # Errors
364    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
365    pub fn set_gain(&mut self, q8_db: i32) -> Result<()> {
366        self.simple_ctl(OPUS_SET_GAIN_REQUEST as i32, q8_db)
367    }
368    /// Query post-decode gain in Q8 dB units.
369    ///
370    /// # Errors
371    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
372    pub fn gain(&mut self) -> Result<i32> {
373        self.get_int_ctl(OPUS_GET_GAIN_REQUEST as i32)
374    }
375
376    /// Returns true if phase inversion is disabled (CELT stereo decorrelation).
377    ///
378    /// # Errors
379    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
380    pub fn phase_inversion_disabled(&mut self) -> Result<bool> {
381        Ok(self.get_int_ctl(OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST as i32)? != 0)
382    }
383
384    /// Disable/enable phase inversion (CELT stereo decorrelation).
385    ///
386    /// # Errors
387    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
388    pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
389        self.simple_ctl(
390            OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST as i32,
391            i32::from(disabled),
392        )
393    }
394
395    #[cfg(feature = "dred")]
396    /// Set DRED duration in ms (if libopus built with DRED).
397    ///
398    /// # Errors
399    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
400    pub fn set_dred_duration(&mut self, ms: i32) -> Result<()> {
401        self.simple_ctl(OPUS_SET_DRED_DURATION_REQUEST as i32, ms)
402    }
403    #[cfg(feature = "dred")]
404    /// Query DRED duration in ms.
405    ///
406    /// # Errors
407    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
408    pub fn dred_duration(&mut self) -> Result<i32> {
409        self.get_int_ctl(OPUS_GET_DRED_DURATION_REQUEST as i32)
410    }
411    #[cfg(feature = "dred")]
412    /// Set DNN blob for DRED (feature-gated; will error if unsupported).
413    ///
414    /// # Safety
415    /// Caller must ensure `ptr` is valid for reads as expected by libopus for the duration of the call
416    /// and points to a properly formatted DNN blob. Passing an invalid or dangling pointer is UB.
417    ///
418    /// # Errors
419    /// Returns [`Error::InvalidState`] if the decoder is invalid, or a mapped libopus error.
420    pub unsafe fn set_dnn_blob(&mut self, ptr: *const u8, len: i32) -> Result<()> {
421        if ptr.is_null() || len <= 0 {
422            return Err(Error::BadArg);
423        }
424        let r = unsafe {
425            opus_decoder_ctl(
426                self.raw.as_ptr(),
427                OPUS_SET_DNN_BLOB_REQUEST as i32,
428                ptr,
429                len,
430            )
431        };
432        if r != 0 {
433            return Err(Error::from_code(r));
434        }
435        Ok(())
436    }
437
438    // --- internal helpers for CTLs ---
439    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
440        let r = unsafe { opus_decoder_ctl(self.raw.as_ptr(), req, val) };
441        if r != 0 {
442            return Err(Error::from_code(r));
443        }
444        Ok(())
445    }
446    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
447        let mut v: i32 = 0;
448        let r = unsafe { opus_decoder_ctl(self.raw.as_ptr(), req, &mut v) };
449        if r != 0 {
450            return Err(Error::from_code(r));
451        }
452        Ok(v)
453    }
454}
455
456impl<'a> DecoderRef<'a> {
457    /// Wrap an externally-initialized decoder without taking ownership.
458    ///
459    /// # Safety
460    /// - `ptr` must point to valid, initialized memory of at least [`Decoder::size()`] bytes
461    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
462    /// - The memory must remain valid for the lifetime `'a`
463    /// - Caller is responsible for freeing the memory after this wrapper is dropped
464    ///
465    /// Use [`Decoder::init_in_place`] to initialize the memory before calling this.
466    #[must_use]
467    pub unsafe fn from_raw(
468        ptr: *mut OpusDecoder,
469        sample_rate: SampleRate,
470        channels: Channels,
471    ) -> Self {
472        debug_assert!(!ptr.is_null(), "from_raw called with null ptr");
473        debug_assert!(crate::opus_ptr_is_aligned(ptr.cast()));
474        let decoder = Decoder::from_raw(
475            unsafe { NonNull::new_unchecked(ptr) },
476            sample_rate,
477            channels,
478            Ownership::Borrowed,
479        );
480        Self {
481            inner: decoder,
482            _marker: PhantomData,
483        }
484    }
485
486    /// Initialize and wrap an externally allocated buffer.
487    ///
488    /// # Errors
489    /// Returns [`Error::BadArg`] if the buffer is too small, or a mapped libopus error.
490    pub fn init_in(
491        buf: &'a mut AlignedBuffer,
492        sample_rate: SampleRate,
493        channels: Channels,
494    ) -> Result<Self> {
495        let required = Decoder::size(channels)?;
496        if buf.capacity_bytes() < required {
497            return Err(Error::BadArg);
498        }
499        let ptr = buf.as_mut_ptr::<OpusDecoder>();
500        unsafe { Decoder::init_in_place(ptr, sample_rate, channels)? };
501        Ok(unsafe { Self::from_raw(ptr, sample_rate, channels) })
502    }
503}
504
505impl Deref for DecoderRef<'_> {
506    type Target = Decoder;
507
508    fn deref(&self) -> &Self::Target {
509        &self.inner
510    }
511}
512
513impl DerefMut for DecoderRef<'_> {
514    fn deref_mut(&mut self) -> &mut Self::Target {
515        &mut self.inner
516    }
517}