Skip to main content

opus_codec/
multistream.rs

1//! Safe wrappers for the Opus Multistream API (surround and channel-mapped streams)
2
3use crate::bindings::{
4    OPUS_AUTO, OPUS_BANDWIDTH_FULLBAND, OPUS_BANDWIDTH_MEDIUMBAND, OPUS_BANDWIDTH_NARROWBAND,
5    OPUS_BANDWIDTH_SUPERWIDEBAND, OPUS_BANDWIDTH_WIDEBAND, OPUS_BITRATE_MAX,
6    OPUS_GET_BANDWIDTH_REQUEST, OPUS_GET_BITRATE_REQUEST, OPUS_GET_COMPLEXITY_REQUEST,
7    OPUS_GET_DTX_REQUEST, OPUS_GET_FINAL_RANGE_REQUEST, OPUS_GET_FORCE_CHANNELS_REQUEST,
8    OPUS_GET_GAIN_REQUEST, OPUS_GET_IN_DTX_REQUEST, OPUS_GET_INBAND_FEC_REQUEST,
9    OPUS_GET_LAST_PACKET_DURATION_REQUEST, OPUS_GET_LOOKAHEAD_REQUEST, OPUS_GET_LSB_DEPTH_REQUEST,
10    OPUS_GET_MAX_BANDWIDTH_REQUEST, OPUS_GET_PACKET_LOSS_PERC_REQUEST,
11    OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, OPUS_GET_PITCH_REQUEST,
12    OPUS_GET_SAMPLE_RATE_REQUEST, OPUS_GET_SIGNAL_REQUEST, OPUS_GET_VBR_CONSTRAINT_REQUEST,
13    OPUS_GET_VBR_REQUEST, OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST,
14    OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST, OPUS_RESET_STATE, OPUS_SET_BANDWIDTH_REQUEST,
15    OPUS_SET_BITRATE_REQUEST, OPUS_SET_COMPLEXITY_REQUEST, OPUS_SET_DTX_REQUEST,
16    OPUS_SET_FORCE_CHANNELS_REQUEST, OPUS_SET_GAIN_REQUEST, OPUS_SET_INBAND_FEC_REQUEST,
17    OPUS_SET_LSB_DEPTH_REQUEST, OPUS_SET_MAX_BANDWIDTH_REQUEST, OPUS_SET_PACKET_LOSS_PERC_REQUEST,
18    OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, OPUS_SET_SIGNAL_REQUEST,
19    OPUS_SET_VBR_CONSTRAINT_REQUEST, OPUS_SET_VBR_REQUEST, OPUS_SIGNAL_MUSIC, OPUS_SIGNAL_VOICE,
20    OpusDecoder, OpusEncoder, OpusMSDecoder, OpusMSEncoder, opus_decoder_ctl, opus_encoder_ctl,
21    opus_multistream_decode, opus_multistream_decode_float, opus_multistream_decoder_create,
22    opus_multistream_decoder_ctl, opus_multistream_decoder_destroy,
23    opus_multistream_decoder_get_size, opus_multistream_decoder_init, opus_multistream_encode,
24    opus_multistream_encode_float, opus_multistream_encoder_create, opus_multistream_encoder_ctl,
25    opus_multistream_encoder_destroy, opus_multistream_encoder_get_size,
26    opus_multistream_encoder_init, opus_multistream_surround_encoder_create,
27    opus_multistream_surround_encoder_get_size, opus_multistream_surround_encoder_init,
28};
29use crate::constants::{is_frame_size_2_5ms_aligned, max_frame_samples_for};
30use crate::error::{Error, Result};
31use crate::types::{Application, Bandwidth, Bitrate, Channels, Complexity, SampleRate, Signal};
32use crate::{AlignedBuffer, Ownership, RawHandle};
33use std::marker::PhantomData;
34use std::num::{NonZeroU8, NonZeroUsize};
35use std::ops::Deref;
36use std::ptr::NonNull;
37
38/// Describes the multistream mapping configuration.
39#[derive(Debug, Clone, Copy)]
40pub struct Mapping<'a> {
41    /// Total input/output channels.
42    pub channels: u8,
43    /// Total number of streams (coupled + uncoupled).
44    pub streams: u8,
45    /// Number of coupled stereo streams (each counts as 2 channels).
46    pub coupled_streams: u8,
47    /// Channel-to-stream mapping table (length == channels).
48    pub mapping: &'a [u8],
49}
50
51impl Mapping<'_> {
52    fn validate_common(&self) -> Result<(usize, usize, usize)> {
53        let channel_count = NonZeroU8::new(self.channels).ok_or(Error::BadArg)?;
54        let channel_count = usize::from(channel_count.get());
55        if self.mapping.len() != channel_count {
56            return Err(Error::BadArg);
57        }
58
59        let streams = NonZeroU8::new(self.streams).ok_or(Error::BadArg)?;
60        let streams = usize::from(streams.get());
61        let coupled = usize::from(self.coupled_streams);
62        if coupled > streams {
63            return Err(Error::BadArg);
64        }
65        if streams + coupled > u8::MAX as usize {
66            return Err(Error::BadArg);
67        }
68        let total_streams = streams + coupled;
69        for &entry in self.mapping {
70            if entry == u8::MAX {
71                continue;
72            }
73            if usize::from(entry) >= total_streams {
74                return Err(Error::BadArg);
75            }
76        }
77        Ok((channel_count, streams, coupled))
78    }
79
80    /// Validate mapping for use with libopus multistream decoder.
81    fn validate_for_decoder(&self) -> Result<()> {
82        self.validate_common()?;
83        Ok(())
84    }
85
86    /// Validate mapping for use with libopus multistream encoder.
87    fn validate_for_encoder(&self) -> Result<()> {
88        let (channel_count, streams, coupled) = self.validate_common()?;
89        if streams + coupled > channel_count {
90            return Err(Error::BadArg);
91        }
92
93        let mut has_left = [false; u8::MAX as usize];
94        let mut has_right = [false; u8::MAX as usize];
95        let mut has_mono = [false; u8::MAX as usize];
96        for &entry in self.mapping {
97            if entry == u8::MAX {
98                continue;
99            }
100            let idx = usize::from(entry);
101            if idx < 2 * coupled {
102                let stream = idx / 2;
103                if idx % 2 == 0 {
104                    has_left[stream] = true;
105                } else {
106                    has_right[stream] = true;
107                }
108            } else {
109                let stream = idx - coupled;
110                if stream >= coupled && stream < streams {
111                    has_mono[stream - coupled] = true;
112                }
113            }
114        }
115
116        if has_left[..coupled].iter().any(|has| !has) || has_right[..coupled].iter().any(|has| !has)
117        {
118            return Err(Error::BadArg);
119        }
120        if has_mono[..streams.saturating_sub(coupled)]
121            .iter()
122            .any(|has| !has)
123        {
124            return Err(Error::BadArg);
125        }
126        Ok(())
127    }
128}
129
130// Keep these rules synchronized with `vorbis_mappings`,
131// `validate_ambisonics()`, and `opus_multistream_surround_encoder_init()` in
132// opus/src/opus_multistream_encoder.c. Tests compare every valid family 1 and
133// family 2 configuration against the linked libopus implementation.
134const VORBIS_SURROUND_MAPPINGS: [(&[u8], u8, u8); 8] = [
135    (&[0], 1, 0),
136    (&[0, 1], 1, 1),
137    (&[0, 2, 1], 2, 1),
138    (&[0, 1, 2, 3], 2, 2),
139    (&[0, 4, 1, 2, 3], 3, 2),
140    (&[0, 4, 1, 2, 3, 5], 4, 2),
141    (&[0, 4, 1, 2, 3, 5, 6], 4, 3),
142    (&[0, 6, 1, 2, 3, 4, 5, 7], 5, 3),
143];
144
145fn surround_mapping(channels: u8, mapping_family: i32) -> Result<(Vec<u8>, u8, u8)> {
146    let channel_count = usize::from(NonZeroU8::new(channels).ok_or(Error::BadArg)?.get());
147    match mapping_family {
148        0 => match channels {
149            1 => Ok((vec![0], 1, 0)),
150            2 => Ok((vec![0, 1], 1, 1)),
151            _ => Err(Error::BadArg),
152        },
153        1 if channel_count <= VORBIS_SURROUND_MAPPINGS.len() => {
154            let (mapping, streams, coupled) = VORBIS_SURROUND_MAPPINGS[channel_count - 1];
155            Ok((mapping.to_vec(), streams, coupled))
156        }
157        2 => {
158            if channels > 227 {
159                return Err(Error::BadArg);
160            }
161            let order_plus_one = (1usize..=15)
162                .take_while(|value| value * value <= channel_count)
163                .last()
164                .ok_or(Error::BadArg)?;
165            let ambisonic_channels = order_plus_one * order_plus_one;
166            let nondiegetic_channels = channel_count - ambisonic_channels;
167            if nondiegetic_channels != 0 && nondiegetic_channels != 2 {
168                return Err(Error::BadArg);
169            }
170
171            let coupled = u8::from(nondiegetic_channels != 0);
172            let streams = u8::try_from(ambisonic_channels + usize::from(coupled))
173                .map_err(|_| Error::BadArg)?;
174            let mut mapping = Vec::with_capacity(channel_count);
175            for channel in 0..ambisonic_channels {
176                mapping.push(
177                    u8::try_from(channel + 2 * usize::from(coupled)).map_err(|_| Error::BadArg)?,
178                );
179            }
180            mapping.extend(0..2 * coupled);
181            Ok((mapping, streams, coupled))
182        }
183        255 => Ok(((0..channels).collect(), channels, 0)),
184        _ => Err(Error::BadArg),
185    }
186}
187
188fn validate_stream_counts(streams: u8, coupled_streams: u8) -> Result<()> {
189    let streams = NonZeroU8::new(streams).ok_or(Error::BadArg)?;
190    if coupled_streams > streams.get() {
191        return Err(Error::BadArg);
192    }
193    if usize::from(streams.get()) + usize::from(coupled_streams) > u8::MAX as usize {
194        return Err(Error::BadArg);
195    }
196    Ok(())
197}
198
199fn bandwidth_from_ctl(value: i32) -> Result<Bandwidth> {
200    let value = u32::try_from(value).map_err(|_| Error::InternalError)?;
201    match value {
202        x if x == OPUS_BANDWIDTH_NARROWBAND => Ok(Bandwidth::Narrowband),
203        x if x == OPUS_BANDWIDTH_MEDIUMBAND => Ok(Bandwidth::Mediumband),
204        x if x == OPUS_BANDWIDTH_WIDEBAND => Ok(Bandwidth::Wideband),
205        x if x == OPUS_BANDWIDTH_SUPERWIDEBAND => Ok(Bandwidth::SuperWideband),
206        x if x == OPUS_BANDWIDTH_FULLBAND => Ok(Bandwidth::Fullband),
207        _ => Err(Error::InternalError),
208    }
209}
210
211/// Safe wrapper around `OpusMSEncoder`.
212pub struct MultistreamEncoder {
213    raw: RawHandle<OpusMSEncoder>,
214    sample_rate: SampleRate,
215    channels: u8,
216    streams: u8,
217    coupled_streams: u8,
218}
219
220unsafe impl Send for MultistreamEncoder {}
221
222/// Borrowed wrapper around a multistream encoder.
223///
224/// The owning handle cannot be moved out of this borrowed wrapper:
225///
226/// ```compile_fail
227/// use opus_codec::multistream::{MultistreamEncoder, MultistreamEncoderRef};
228/// fn extract<'a>(
229///     state: &mut MultistreamEncoderRef<'a>,
230///     replacement: MultistreamEncoder,
231/// ) -> MultistreamEncoder {
232///     std::mem::replace(&mut **state, replacement)
233/// }
234/// ```
235pub struct MultistreamEncoderRef<'a> {
236    inner: MultistreamEncoder,
237    _marker: PhantomData<&'a mut OpusMSEncoder>,
238}
239
240unsafe impl Send for MultistreamEncoderRef<'_> {}
241
242impl MultistreamEncoder {
243    fn from_raw(
244        ptr: NonNull<OpusMSEncoder>,
245        sample_rate: SampleRate,
246        channels: u8,
247        streams: u8,
248        coupled_streams: u8,
249        ownership: Ownership,
250    ) -> Self {
251        Self {
252            raw: RawHandle::new(ptr, ownership, opus_multistream_encoder_destroy),
253            sample_rate,
254            channels,
255            streams,
256            coupled_streams,
257        }
258    }
259
260    /// Size in bytes of a multistream encoder state for external allocation.
261    ///
262    /// # Errors
263    /// Returns [`Error::BadArg`] if the stream counts are invalid or libopus reports
264    /// an impossible size.
265    pub fn size(streams: u8, coupled_streams: u8) -> Result<usize> {
266        validate_stream_counts(streams, coupled_streams)?;
267        let raw = unsafe {
268            opus_multistream_encoder_get_size(i32::from(streams), i32::from(coupled_streams))
269        };
270        if raw <= 0 {
271            return Err(Error::BadArg);
272        }
273        usize::try_from(raw).map_err(|_| Error::InternalError)
274    }
275
276    /// Size in bytes of a surround multistream encoder state for external allocation.
277    ///
278    /// # Errors
279    /// Returns [`Error::BadArg`] if the channel/mapping configuration is invalid.
280    pub fn surround_size(channels: u8, mapping_family: i32) -> Result<usize> {
281        if channels == 0 {
282            return Err(Error::BadArg);
283        }
284        let raw = unsafe {
285            opus_multistream_surround_encoder_get_size(i32::from(channels), mapping_family)
286        };
287        if raw <= 0 {
288            return Err(Error::BadArg);
289        }
290        usize::try_from(raw).map_err(|_| Error::InternalError)
291    }
292
293    /// Return the standard channel mapping and stream counts for a surround family.
294    ///
295    /// This mirrors the family 0, 1, 2, and 255 rules used by
296    /// `opus_multistream_surround_encoder_init()`, without allocating an
297    /// encoder state.
298    ///
299    /// # Errors
300    /// Returns [`Error::BadArg`] if the channel count or mapping family is invalid.
301    pub fn surround_mapping(channels: u8, mapping_family: i32) -> Result<(Vec<u8>, u8, u8)> {
302        surround_mapping(channels, mapping_family)
303    }
304
305    /// Initialize a previously allocated multistream encoder state.
306    ///
307    /// # Safety
308    /// The caller must provide a valid pointer to `MultistreamEncoder::size()` bytes,
309    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
310    ///
311    /// # Errors
312    /// Returns [`Error::BadArg`] if the mapping is invalid or `ptr` is null, or a
313    /// mapped libopus error on failure.
314    pub unsafe fn init_in_place(
315        ptr: *mut OpusMSEncoder,
316        sr: SampleRate,
317        app: Application,
318        mapping: Mapping<'_>,
319    ) -> Result<()> {
320        if ptr.is_null() {
321            return Err(Error::BadArg);
322        }
323        if !crate::opus_ptr_is_aligned(ptr.cast()) {
324            return Err(Error::BadArg);
325        }
326        mapping.validate_for_encoder()?;
327        let r = unsafe {
328            opus_multistream_encoder_init(
329                ptr,
330                sr as i32,
331                i32::from(mapping.channels),
332                i32::from(mapping.streams),
333                i32::from(mapping.coupled_streams),
334                mapping.mapping.as_ptr(),
335                app as i32,
336            )
337        };
338        if r != 0 {
339            return Err(Error::from_code(r));
340        }
341        Ok(())
342    }
343
344    /// Initialize a previously allocated surround multistream encoder state.
345    ///
346    /// # Safety
347    /// The caller must provide a valid pointer to `MultistreamEncoder::surround_size()` bytes,
348    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
349    ///
350    /// # Errors
351    /// Returns [`Error::BadArg`] for invalid channel counts or a mapped libopus error.
352    pub unsafe fn init_surround_in_place(
353        ptr: *mut OpusMSEncoder,
354        sr: SampleRate,
355        channels: u8,
356        mapping_family: i32,
357        app: Application,
358    ) -> Result<(u8, u8, Vec<u8>)> {
359        if ptr.is_null() || channels == 0 {
360            return Err(Error::BadArg);
361        }
362        if !crate::opus_ptr_is_aligned(ptr.cast()) {
363            return Err(Error::BadArg);
364        }
365        Self::surround_size(channels, mapping_family)?;
366        let mut streams = 0i32;
367        let mut coupled = 0i32;
368        let mut mapping = vec![0u8; channels as usize];
369        let r = unsafe {
370            opus_multistream_surround_encoder_init(
371                ptr,
372                sr as i32,
373                i32::from(channels),
374                mapping_family,
375                std::ptr::addr_of_mut!(streams),
376                std::ptr::addr_of_mut!(coupled),
377                mapping.as_mut_ptr(),
378                app as i32,
379            )
380        };
381        if r != 0 {
382            return Err(Error::from_code(r));
383        }
384        Ok((
385            u8::try_from(streams).map_err(|_| Error::BadArg)?,
386            u8::try_from(coupled).map_err(|_| Error::BadArg)?,
387            mapping,
388        ))
389    }
390
391    /// Create a new multistream encoder.
392    ///
393    /// The `mapping.mapping` array describes how input channels are assigned to streams.
394    /// See libopus docs for standard surround layouts.
395    ///
396    /// # Errors
397    /// Returns [`Error::BadArg`] when the mapping dimensions are inconsistent, or
398    /// propagates allocation/configuration failures from libopus.
399    pub fn new(sr: SampleRate, app: Application, mapping: Mapping<'_>) -> Result<Self> {
400        mapping.validate_for_encoder()?;
401        let mut err = 0i32;
402        let enc = unsafe {
403            opus_multistream_encoder_create(
404                sr as i32,
405                i32::from(mapping.channels),
406                i32::from(mapping.streams),
407                i32::from(mapping.coupled_streams),
408                mapping.mapping.as_ptr(),
409                app as i32,
410                std::ptr::addr_of_mut!(err),
411            )
412        };
413        if err != 0 {
414            return Err(Error::from_code(err));
415        }
416        let enc = NonNull::new(enc).ok_or(Error::AllocFail)?;
417        Ok(Self::from_raw(
418            enc,
419            sr,
420            mapping.channels,
421            mapping.streams,
422            mapping.coupled_streams,
423            Ownership::Owned,
424        ))
425    }
426
427    /// Encode interleaved i16 PCM into a multistream Opus packet.
428    ///
429    /// # Errors
430    /// Returns [`Error::InvalidState`] if the encoder handle is invalid, [`Error::BadArg`]
431    /// for buffer mismatches, or the mapped libopus error code.
432    #[allow(clippy::missing_panics_doc)]
433    pub fn encode(
434        &mut self,
435        pcm: &[i16],
436        frame_size_per_ch: usize,
437        out: &mut [u8],
438    ) -> Result<usize> {
439        let frame_size_per_ch = NonZeroUsize::new(frame_size_per_ch).ok_or(Error::BadArg)?;
440        if frame_size_per_ch.get() > max_frame_samples_for(self.sample_rate) {
441            return Err(Error::BadArg);
442        }
443        if pcm.len() != frame_size_per_ch.get() * self.channels as usize {
444            return Err(Error::BadArg);
445        }
446        if out.is_empty() || out.len() > i32::MAX as usize {
447            return Err(Error::BadArg);
448        }
449        let n = unsafe {
450            opus_multistream_encode(
451                self.raw.as_ptr(),
452                pcm.as_ptr(),
453                i32::try_from(frame_size_per_ch.get()).map_err(|_| Error::BadArg)?,
454                out.as_mut_ptr(),
455                i32::try_from(out.len()).map_err(|_| Error::BadArg)?,
456            )
457        };
458        if n < 0 {
459            return Err(Error::from_code(n));
460        }
461        usize::try_from(n).map_err(|_| Error::InternalError)
462    }
463
464    /// Encode interleaved f32 PCM into a multistream Opus packet.
465    ///
466    /// # Errors
467    /// Returns [`Error::InvalidState`] if the encoder handle is invalid, [`Error::BadArg`]
468    /// for buffer mismatches, or the mapped libopus error code.
469    pub fn encode_float(
470        &mut self,
471        pcm: &[f32],
472        frame_size_per_ch: usize,
473        out: &mut [u8],
474    ) -> Result<usize> {
475        let frame_size_per_ch = NonZeroUsize::new(frame_size_per_ch).ok_or(Error::BadArg)?;
476        if frame_size_per_ch.get() > max_frame_samples_for(self.sample_rate) {
477            return Err(Error::BadArg);
478        }
479        if pcm.len() != frame_size_per_ch.get() * self.channels as usize {
480            return Err(Error::BadArg);
481        }
482        if out.is_empty() || out.len() > i32::MAX as usize {
483            return Err(Error::BadArg);
484        }
485        let n = unsafe {
486            opus_multistream_encode_float(
487                self.raw.as_ptr(),
488                pcm.as_ptr(),
489                i32::try_from(frame_size_per_ch.get()).map_err(|_| Error::BadArg)?,
490                out.as_mut_ptr(),
491                i32::try_from(out.len()).map_err(|_| Error::BadArg)?,
492            )
493        };
494        if n < 0 {
495            return Err(Error::from_code(n));
496        }
497        usize::try_from(n).map_err(|_| Error::InternalError)
498    }
499
500    /// Final RNG state from the last encode.
501    ///
502    /// # Errors
503    /// Returns [`Error::InvalidState`] when the encoder handle is null or
504    /// propagates the libopus error.
505    pub fn final_range(&mut self) -> Result<u32> {
506        let mut v: u32 = 0;
507        let r = unsafe {
508            opus_multistream_encoder_ctl(
509                self.raw.as_ptr(),
510                OPUS_GET_FINAL_RANGE_REQUEST as i32,
511                &mut v,
512            )
513        };
514        if r != 0 {
515            return Err(Error::from_code(r));
516        }
517        Ok(v)
518    }
519
520    /// Set target bitrate for the encoder.
521    ///
522    /// # Errors
523    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
524    /// reported by libopus.
525    pub fn set_bitrate(&mut self, bitrate: Bitrate) -> Result<()> {
526        self.simple_ctl(OPUS_SET_BITRATE_REQUEST as i32, bitrate.value())
527    }
528
529    /// Query the current bitrate target.
530    ///
531    /// # Errors
532    /// Returns [`Error::InvalidState`] if the encoder handle is null, [`Error::InternalError`]
533    /// if the returned value cannot be represented, or propagates any error reported by
534    /// libopus.
535    pub fn bitrate(&mut self) -> Result<Bitrate> {
536        let v = self.get_int_ctl(OPUS_GET_BITRATE_REQUEST as i32)?;
537        Ok(match v {
538            x if x == OPUS_AUTO => Bitrate::Auto,
539            x if x == OPUS_BITRATE_MAX => Bitrate::Max,
540            other => Bitrate::Custom(other),
541        })
542    }
543
544    /// Set encoder complexity in the range 0..=10.
545    ///
546    /// # Errors
547    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
548    /// reported by libopus.
549    pub fn set_complexity(&mut self, complexity: Complexity) -> Result<()> {
550        self.simple_ctl(
551            OPUS_SET_COMPLEXITY_REQUEST as i32,
552            complexity.value() as i32,
553        )
554    }
555
556    /// Query encoder complexity.
557    ///
558    /// # Errors
559    /// Returns [`Error::InvalidState`] if the encoder handle is null, [`Error::InternalError`]
560    /// if the response is outside the valid range, or propagates any error reported by libopus.
561    pub fn complexity(&mut self) -> Result<Complexity> {
562        let v = self.get_int_ctl(OPUS_GET_COMPLEXITY_REQUEST as i32)?;
563        let complexity = u32::try_from(v).map_err(|_| Error::InternalError)?;
564        Complexity::try_new(complexity).ok_or(Error::InternalError)
565    }
566
567    /// Enable/disable discontinuous transmission (DTX).
568    ///
569    /// # Errors
570    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
571    /// reported by libopus.
572    pub fn set_dtx(&mut self, enabled: bool) -> Result<()> {
573        self.simple_ctl(OPUS_SET_DTX_REQUEST as i32, i32::from(enabled))
574    }
575
576    /// Query whether DTX is enabled.
577    ///
578    /// # Errors
579    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
580    /// reported by libopus.
581    pub fn dtx(&mut self) -> Result<bool> {
582        self.get_bool_ctl(OPUS_GET_DTX_REQUEST as i32)
583    }
584
585    /// Query whether every coded stream is currently in DTX.
586    ///
587    /// Libopus does not forward `OPUS_GET_IN_DTX` through its multistream dispatcher, so this
588    /// queries each underlying encoder state and returns true only when all streams report DTX.
589    ///
590    /// # Errors
591    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
592    /// reported by libopus.
593    pub fn in_dtx(&mut self) -> Result<bool> {
594        for stream_index in 0..i32::from(self.streams) {
595            if self.encoder_int_ctl(stream_index, OPUS_GET_IN_DTX_REQUEST as i32)? == 0 {
596                return Ok(false);
597            }
598        }
599        Ok(true)
600    }
601
602    /// Enable/disable in-band FEC generation.
603    ///
604    /// # Errors
605    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
606    /// reported by libopus.
607    pub fn set_inband_fec(&mut self, enabled: bool) -> Result<()> {
608        self.simple_ctl(OPUS_SET_INBAND_FEC_REQUEST as i32, i32::from(enabled))
609    }
610
611    /// Query whether in-band FEC is enabled.
612    ///
613    /// # Errors
614    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
615    /// reported by libopus.
616    pub fn inband_fec(&mut self) -> Result<bool> {
617        self.get_bool_ctl(OPUS_GET_INBAND_FEC_REQUEST as i32)
618    }
619
620    /// Set expected packet loss percentage (0..=100).
621    ///
622    /// # Errors
623    /// Returns [`Error::BadArg`] when `perc` is outside `0..=100`, [`Error::InvalidState`] if
624    /// the encoder handle is null, or propagates any error reported by libopus.
625    pub fn set_packet_loss_perc(&mut self, perc: i32) -> Result<()> {
626        if !(0..=100).contains(&perc) {
627            return Err(Error::BadArg);
628        }
629        self.simple_ctl(OPUS_SET_PACKET_LOSS_PERC_REQUEST as i32, perc)
630    }
631
632    /// Query expected packet loss percentage.
633    ///
634    /// # Errors
635    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
636    /// reported by libopus.
637    pub fn packet_loss_perc(&mut self) -> Result<i32> {
638        self.get_int_ctl(OPUS_GET_PACKET_LOSS_PERC_REQUEST as i32)
639    }
640
641    /// Enable/disable variable bitrate.
642    ///
643    /// # Errors
644    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
645    /// reported by libopus.
646    pub fn set_vbr(&mut self, enabled: bool) -> Result<()> {
647        self.simple_ctl(OPUS_SET_VBR_REQUEST as i32, i32::from(enabled))
648    }
649
650    /// Query VBR status.
651    ///
652    /// # Errors
653    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
654    /// reported by libopus.
655    pub fn vbr(&mut self) -> Result<bool> {
656        self.get_bool_ctl(OPUS_GET_VBR_REQUEST as i32)
657    }
658
659    /// Constrain VBR to reduce instantaneous bitrate swings.
660    ///
661    /// # Errors
662    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
663    /// reported by libopus.
664    pub fn set_vbr_constraint(&mut self, constrained: bool) -> Result<()> {
665        self.simple_ctl(
666            OPUS_SET_VBR_CONSTRAINT_REQUEST as i32,
667            i32::from(constrained),
668        )
669    }
670
671    /// Query VBR constraint flag.
672    ///
673    /// # Errors
674    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
675    /// reported by libopus.
676    pub fn vbr_constraint(&mut self) -> Result<bool> {
677        self.get_bool_ctl(OPUS_GET_VBR_CONSTRAINT_REQUEST as i32)
678    }
679
680    /// Set the maximum bandwidth the encoder may use.
681    ///
682    /// # Errors
683    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
684    /// reported by libopus.
685    pub fn set_max_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
686        self.simple_ctl(OPUS_SET_MAX_BANDWIDTH_REQUEST as i32, bw as i32)
687    }
688
689    /// Query the configured maximum bandwidth of the first coded stream.
690    ///
691    /// [`Self::set_max_bandwidth`] applies the value to every stream. Direct mutation through
692    /// [`Self::encoder_state_ptr`] can make per-stream values differ; this getter then reports
693    /// stream zero, matching libopus' convention for supported multistream integer getters.
694    ///
695    /// # Errors
696    /// Returns [`Error::InvalidState`] if the encoder handle is null, [`Error::InternalError`]
697    /// if the value cannot be represented, or propagates any error reported by libopus.
698    pub fn max_bandwidth(&mut self) -> Result<Bandwidth> {
699        bandwidth_from_ctl(self.encoder_int_ctl(0, OPUS_GET_MAX_BANDWIDTH_REQUEST as i32)?)
700    }
701
702    /// Force a specific output bandwidth (overrides automatic selection).
703    ///
704    /// # Errors
705    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
706    /// reported by libopus.
707    pub fn set_bandwidth(&mut self, bw: Bandwidth) -> Result<()> {
708        self.simple_ctl(OPUS_SET_BANDWIDTH_REQUEST as i32, bw as i32)
709    }
710
711    /// Query the current forced bandwidth, if any.
712    ///
713    /// # Errors
714    /// Returns [`Error::InvalidState`] if the encoder handle is null or [`Error::InternalError`]
715    /// if the value is outside the known set, and propagates any error reported by libopus.
716    pub fn bandwidth(&mut self) -> Result<Bandwidth> {
717        self.get_bandwidth_ctl(OPUS_GET_BANDWIDTH_REQUEST as i32)
718    }
719
720    /// Force mono/stereo output for coupled streams, or `None` for automatic.
721    ///
722    /// # Errors
723    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
724    /// reported by libopus.
725    pub fn set_force_channels(&mut self, channels: Option<Channels>) -> Result<()> {
726        let value = match channels {
727            Some(Channels::Mono) => 1,
728            Some(Channels::Stereo) => 2,
729            None => OPUS_AUTO,
730        };
731        self.simple_ctl(OPUS_SET_FORCE_CHANNELS_REQUEST as i32, value)
732    }
733
734    /// Query forced channel configuration (if any).
735    ///
736    /// # Errors
737    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
738    /// reported by libopus.
739    pub fn force_channels(&mut self) -> Result<Option<Channels>> {
740        let v = self.get_int_ctl(OPUS_GET_FORCE_CHANNELS_REQUEST as i32)?;
741        Ok(match v {
742            1 => Some(Channels::Mono),
743            2 => Some(Channels::Stereo),
744            x if x == OPUS_AUTO => None,
745            _ => None,
746        })
747    }
748
749    /// Hint the type of content being encoded (voice/music).
750    ///
751    /// # Errors
752    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
753    /// reported by libopus.
754    pub fn set_signal(&mut self, signal: Signal) -> Result<()> {
755        self.simple_ctl(OPUS_SET_SIGNAL_REQUEST as i32, signal as i32)
756    }
757
758    /// Query the current signal hint.
759    ///
760    /// # Errors
761    /// Returns [`Error::InvalidState`] if the encoder handle is null, [`Error::InternalError`]
762    /// if the response is not recognized, or propagates any error reported by libopus.
763    pub fn signal(&mut self) -> Result<Signal> {
764        let v = self.get_int_ctl(OPUS_GET_SIGNAL_REQUEST as i32)?;
765        match v {
766            x if x == OPUS_AUTO => Ok(Signal::Auto),
767            x if x == OPUS_SIGNAL_VOICE as i32 => Ok(Signal::Voice),
768            x if x == OPUS_SIGNAL_MUSIC as i32 => Ok(Signal::Music),
769            _ => Err(Error::InternalError),
770        }
771    }
772
773    /// Query the algorithmic lookahead in samples at this encoder's configured sample rate.
774    ///
775    /// # Errors
776    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
777    /// reported by libopus.
778    pub fn lookahead(&mut self) -> Result<i32> {
779        self.get_int_ctl(OPUS_GET_LOOKAHEAD_REQUEST as i32)
780    }
781
782    /// Set the effective input signal depth in bits.
783    ///
784    /// # Errors
785    /// Returns [`Error::BadArg`] when `bits` is outside `8..=24`,
786    /// [`Error::InvalidState`] if the encoder handle is null, or a mapped libopus error.
787    pub fn set_lsb_depth(&mut self, bits: i32) -> Result<()> {
788        if !(8..=24).contains(&bits) {
789            return Err(Error::BadArg);
790        }
791        self.simple_ctl(OPUS_SET_LSB_DEPTH_REQUEST as i32, bits)
792    }
793
794    /// Query the effective input signal depth in bits.
795    ///
796    /// # Errors
797    /// Returns [`Error::InvalidState`] if the encoder handle is null or a mapped libopus error.
798    pub fn lsb_depth(&mut self) -> Result<i32> {
799        self.get_int_ctl(OPUS_GET_LSB_DEPTH_REQUEST as i32)
800    }
801
802    /// Reset the encoder state (retaining configuration).
803    ///
804    /// # Errors
805    /// Returns [`Error::InvalidState`] if the encoder handle is null or propagates any error
806    /// reported by libopus.
807    pub fn reset(&mut self) -> Result<()> {
808        let r = unsafe { opus_multistream_encoder_ctl(self.raw.as_ptr(), OPUS_RESET_STATE as i32) };
809        if r != 0 {
810            return Err(Error::from_code(r));
811        }
812        Ok(())
813    }
814
815    /// Channels of this encoder (interleaved input).
816    #[must_use]
817    pub const fn channels(&self) -> u8 {
818        self.channels
819    }
820    /// Input sampling rate.
821    #[must_use]
822    pub const fn sample_rate(&self) -> SampleRate {
823        self.sample_rate
824    }
825    /// Total number of coded streams, including coupled streams.
826    #[must_use]
827    pub const fn streams(&self) -> u8 {
828        self.streams
829    }
830    /// Number of coupled streams.
831    #[must_use]
832    pub const fn coupled_streams(&self) -> u8 {
833        self.coupled_streams
834    }
835
836    /// Create a multistream encoder using libopus surround mapping helpers.
837    ///
838    /// # Errors
839    /// Returns [`Error::BadArg`] for invalid channel counts or the mapped libopus
840    /// error when surround initialisation fails.
841    pub fn new_surround(
842        sr: SampleRate,
843        channels: u8,
844        mapping_family: i32,
845        app: Application,
846    ) -> Result<(Self, Vec<u8>)> {
847        Self::surround_size(channels, mapping_family)?;
848        let mut err = 0i32;
849        let mut streams = 0i32;
850        let mut coupled = 0i32;
851        let mut mapping = vec![0u8; channels as usize];
852        let enc = unsafe {
853            opus_multistream_surround_encoder_create(
854                sr as i32,
855                i32::from(channels),
856                mapping_family,
857                std::ptr::addr_of_mut!(streams),
858                std::ptr::addr_of_mut!(coupled),
859                mapping.as_mut_ptr(),
860                app as i32,
861                std::ptr::addr_of_mut!(err),
862            )
863        };
864        if err != 0 {
865            return Err(Error::from_code(err));
866        }
867        let enc = NonNull::new(enc).ok_or(Error::AllocFail)?;
868        let streams_u8 = u8::try_from(streams).map_err(|_| Error::BadArg)?;
869        let coupled_u8 = u8::try_from(coupled).map_err(|_| Error::BadArg)?;
870        Ok((
871            Self::from_raw(enc, sr, channels, streams_u8, coupled_u8, Ownership::Owned),
872            mapping,
873        ))
874    }
875
876    /// Borrow a pointer to an individual underlying encoder state for CTLs.
877    ///
878    /// # Safety
879    /// Caller must not outlive the multistream encoder and must ensure the
880    /// returned pointer is only used for immediate FFI calls.
881    ///
882    /// # Errors
883    /// Returns [`Error::InvalidState`] if the encoder handle is invalid or propagates the
884    /// libopus error if retrieving the state fails.
885    pub unsafe fn encoder_state_ptr(&mut self, stream_index: i32) -> Result<*mut OpusEncoder> {
886        Ok(self.encoder_state(stream_index)?.as_ptr())
887    }
888
889    fn encoder_state(&mut self, stream_index: i32) -> Result<NonNull<OpusEncoder>> {
890        if stream_index < 0 || stream_index >= i32::from(self.streams) {
891            return Err(Error::BadArg);
892        }
893        let mut state: *mut OpusEncoder = std::ptr::null_mut();
894        let r = unsafe {
895            opus_multistream_encoder_ctl(
896                self.raw.as_ptr(),
897                OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST as i32,
898                stream_index,
899                &mut state,
900            )
901        };
902        if r != 0 {
903            return Err(Error::from_code(r));
904        }
905        NonNull::new(state).ok_or(Error::InternalError)
906    }
907
908    fn encoder_int_ctl(&mut self, stream_index: i32, req: i32) -> Result<i32> {
909        let state = self.encoder_state(stream_index)?;
910        let mut value = 0i32;
911        let r = unsafe { opus_encoder_ctl(state.as_ptr(), req, &mut value) };
912        if r != 0 {
913            return Err(Error::from_code(r));
914        }
915        Ok(value)
916    }
917
918    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
919        let r = unsafe { opus_multistream_encoder_ctl(self.raw.as_ptr(), req, val) };
920        if r != 0 {
921            return Err(Error::from_code(r));
922        }
923        Ok(())
924    }
925
926    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
927        let mut v: i32 = 0;
928        let r = unsafe { opus_multistream_encoder_ctl(self.raw.as_ptr(), req, &mut v) };
929        if r != 0 {
930            return Err(Error::from_code(r));
931        }
932        Ok(v)
933    }
934
935    fn get_bool_ctl(&mut self, req: i32) -> Result<bool> {
936        Ok(self.get_int_ctl(req)? != 0)
937    }
938
939    fn get_bandwidth_ctl(&mut self, req: i32) -> Result<Bandwidth> {
940        bandwidth_from_ctl(self.get_int_ctl(req)?)
941    }
942}
943
944impl<'a> MultistreamEncoderRef<'a> {
945    unsafe fn from_raw_parts(
946        ptr: *mut OpusMSEncoder,
947        sr: SampleRate,
948        channels: u8,
949        streams: u8,
950        coupled_streams: u8,
951        caller: &str,
952    ) -> Self {
953        assert!(channels != 0, "{caller} called with zero channels");
954        assert!(
955            validate_stream_counts(streams, coupled_streams).is_ok(),
956            "{caller} called with invalid stream counts"
957        );
958        let encoder = MultistreamEncoder::from_raw(
959            crate::checked_non_null(ptr, caller),
960            sr,
961            channels,
962            streams,
963            coupled_streams,
964            Ownership::Borrowed,
965        );
966        Self {
967            inner: encoder,
968            _marker: PhantomData,
969        }
970    }
971
972    /// Wrap an externally-initialized multistream encoder without taking ownership.
973    ///
974    /// # Safety
975    /// - `ptr` must point to valid, initialized memory of at least [`MultistreamEncoder::size()`] bytes
976    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
977    /// - `sr` and `mapping` must exactly match the encoder state already stored at `ptr`
978    /// - The memory must remain valid for the lifetime `'a`
979    /// - Caller is responsible for freeing the memory after this wrapper is dropped
980    ///
981    /// Passing mismatched metadata is undefined behavior: later safe methods may validate buffer
982    /// sizes with the wrong layout and then call libopus with out-of-bounds buffers.
983    ///
984    /// # Panics
985    /// Panics if `ptr` is null, not pointer-aligned, or `mapping` is invalid.
986    ///
987    /// Use [`MultistreamEncoder::init_in_place`] to initialize the memory before calling this.
988    #[must_use]
989    pub unsafe fn from_raw(ptr: *mut OpusMSEncoder, sr: SampleRate, mapping: Mapping<'_>) -> Self {
990        assert!(
991            mapping.validate_for_encoder().is_ok(),
992            "MultistreamEncoderRef::from_raw called with invalid mapping"
993        );
994        unsafe {
995            Self::from_raw_parts(
996                ptr,
997                sr,
998                mapping.channels,
999                mapping.streams,
1000                mapping.coupled_streams,
1001                "MultistreamEncoderRef::from_raw",
1002            )
1003        }
1004    }
1005
1006    /// Initialize and wrap an externally allocated buffer.
1007    ///
1008    /// # Errors
1009    /// Returns [`Error::BadArg`] if the buffer is too small, or a mapped libopus error.
1010    pub fn init_in(
1011        buf: &'a mut AlignedBuffer,
1012        sr: SampleRate,
1013        app: Application,
1014        mapping: Mapping<'_>,
1015    ) -> Result<Self> {
1016        let required = MultistreamEncoder::size(mapping.streams, mapping.coupled_streams)?;
1017        if buf.capacity_bytes() < required {
1018            return Err(Error::BadArg);
1019        }
1020        let ptr = buf.as_mut_ptr::<OpusMSEncoder>();
1021        unsafe { MultistreamEncoder::init_in_place(ptr, sr, app, mapping)? };
1022        Ok(unsafe { Self::from_raw(ptr, sr, mapping) })
1023    }
1024
1025    /// Initialize a surround encoder in an externally allocated buffer.
1026    ///
1027    /// # Errors
1028    /// Returns [`Error::BadArg`] if the buffer is too small, or a mapped libopus error.
1029    pub fn init_in_surround(
1030        buf: &'a mut AlignedBuffer,
1031        sr: SampleRate,
1032        channels: u8,
1033        mapping_family: i32,
1034        app: Application,
1035    ) -> Result<(Self, Vec<u8>)> {
1036        let required = MultistreamEncoder::surround_size(channels, mapping_family)?;
1037        if buf.capacity_bytes() < required {
1038            return Err(Error::BadArg);
1039        }
1040        let ptr = buf.as_mut_ptr::<OpusMSEncoder>();
1041        let (streams, coupled, mapping) = unsafe {
1042            MultistreamEncoder::init_surround_in_place(ptr, sr, channels, mapping_family, app)?
1043        };
1044        let encoder = unsafe {
1045            Self::from_raw_parts(
1046                ptr,
1047                sr,
1048                channels,
1049                streams,
1050                coupled,
1051                "MultistreamEncoderRef::init_in_surround",
1052            )
1053        };
1054        Ok((encoder, mapping))
1055    }
1056
1057    delegate_ref_mut_methods! {
1058        fn encode(pcm: &[i16], frame_size_per_ch: usize, out: &mut [u8]) -> Result<usize>;
1059        fn encode_float(pcm: &[f32], frame_size_per_ch: usize, out: &mut [u8]) -> Result<usize>;
1060        fn final_range() -> Result<u32>;
1061        fn set_bitrate(bitrate: Bitrate) -> Result<()>;
1062        fn bitrate() -> Result<Bitrate>;
1063        fn set_complexity(complexity: Complexity) -> Result<()>;
1064        fn complexity() -> Result<Complexity>;
1065        fn set_dtx(enabled: bool) -> Result<()>;
1066        fn dtx() -> Result<bool>;
1067        fn in_dtx() -> Result<bool>;
1068        fn set_inband_fec(enabled: bool) -> Result<()>;
1069        fn inband_fec() -> Result<bool>;
1070        fn set_packet_loss_perc(perc: i32) -> Result<()>;
1071        fn packet_loss_perc() -> Result<i32>;
1072        fn set_vbr(enabled: bool) -> Result<()>;
1073        fn vbr() -> Result<bool>;
1074        fn set_vbr_constraint(constrained: bool) -> Result<()>;
1075        fn vbr_constraint() -> Result<bool>;
1076        fn set_max_bandwidth(bw: Bandwidth) -> Result<()>;
1077        fn max_bandwidth() -> Result<Bandwidth>;
1078        fn set_bandwidth(bw: Bandwidth) -> Result<()>;
1079        fn bandwidth() -> Result<Bandwidth>;
1080        fn set_force_channels(channels: Option<Channels>) -> Result<()>;
1081        fn force_channels() -> Result<Option<Channels>>;
1082        fn set_signal(signal: Signal) -> Result<()>;
1083        fn signal() -> Result<Signal>;
1084        fn lookahead() -> Result<i32>;
1085        fn set_lsb_depth(bits: i32) -> Result<()>;
1086        fn lsb_depth() -> Result<i32>;
1087        fn reset() -> Result<()>;
1088    }
1089
1090    delegate_ref_unsafe_mut_methods! {
1091        unsafe fn encoder_state_ptr(stream_index: i32) -> Result<*mut OpusEncoder>;
1092    }
1093}
1094
1095impl Deref for MultistreamEncoderRef<'_> {
1096    type Target = MultistreamEncoder;
1097
1098    fn deref(&self) -> &Self::Target {
1099        &self.inner
1100    }
1101}
1102
1103/// Safe wrapper around `OpusMSDecoder`.
1104pub struct MultistreamDecoder {
1105    raw: RawHandle<OpusMSDecoder>,
1106    sample_rate: SampleRate,
1107    channels: u8,
1108}
1109
1110unsafe impl Send for MultistreamDecoder {}
1111
1112/// Borrowed wrapper around a multistream decoder.
1113///
1114/// The owning handle cannot be moved out of this borrowed wrapper:
1115///
1116/// ```compile_fail
1117/// use opus_codec::multistream::{MultistreamDecoder, MultistreamDecoderRef};
1118/// fn extract<'a>(
1119///     state: &mut MultistreamDecoderRef<'a>,
1120///     replacement: MultistreamDecoder,
1121/// ) -> MultistreamDecoder {
1122///     std::mem::replace(&mut **state, replacement)
1123/// }
1124/// ```
1125pub struct MultistreamDecoderRef<'a> {
1126    inner: MultistreamDecoder,
1127    _marker: PhantomData<&'a mut OpusMSDecoder>,
1128}
1129
1130unsafe impl Send for MultistreamDecoderRef<'_> {}
1131
1132impl MultistreamDecoder {
1133    fn from_raw(
1134        ptr: NonNull<OpusMSDecoder>,
1135        sample_rate: SampleRate,
1136        channels: u8,
1137        ownership: Ownership,
1138    ) -> Self {
1139        Self {
1140            raw: RawHandle::new(ptr, ownership, opus_multistream_decoder_destroy),
1141            sample_rate,
1142            channels,
1143        }
1144    }
1145
1146    /// Size in bytes of a multistream decoder state for external allocation.
1147    ///
1148    /// # Errors
1149    /// Returns [`Error::BadArg`] if the stream counts are invalid or libopus reports
1150    /// an impossible size.
1151    pub fn size(streams: u8, coupled_streams: u8) -> Result<usize> {
1152        validate_stream_counts(streams, coupled_streams)?;
1153        let raw = unsafe {
1154            opus_multistream_decoder_get_size(i32::from(streams), i32::from(coupled_streams))
1155        };
1156        if raw <= 0 {
1157            return Err(Error::BadArg);
1158        }
1159        usize::try_from(raw).map_err(|_| Error::InternalError)
1160    }
1161
1162    /// Initialize a previously allocated multistream decoder state.
1163    ///
1164    /// # Safety
1165    /// The caller must provide a valid pointer to `MultistreamDecoder::size()` bytes,
1166    /// aligned to at least `align_of::<usize>()` (malloc-style alignment).
1167    ///
1168    /// # Errors
1169    /// Returns [`Error::BadArg`] if the mapping is invalid or `ptr` is null, or a
1170    /// mapped libopus error on failure.
1171    pub unsafe fn init_in_place(
1172        ptr: *mut OpusMSDecoder,
1173        sr: SampleRate,
1174        mapping: Mapping<'_>,
1175    ) -> Result<()> {
1176        if ptr.is_null() {
1177            return Err(Error::BadArg);
1178        }
1179        if !crate::opus_ptr_is_aligned(ptr.cast()) {
1180            return Err(Error::BadArg);
1181        }
1182        mapping.validate_for_decoder()?;
1183        let r = unsafe {
1184            opus_multistream_decoder_init(
1185                ptr,
1186                sr as i32,
1187                i32::from(mapping.channels),
1188                i32::from(mapping.streams),
1189                i32::from(mapping.coupled_streams),
1190                mapping.mapping.as_ptr(),
1191            )
1192        };
1193        if r != 0 {
1194            return Err(Error::from_code(r));
1195        }
1196        Ok(())
1197    }
1198
1199    /// Create a new multistream decoder.
1200    ///
1201    /// # Errors
1202    /// Returns [`Error::BadArg`] when the mapping dimensions are inconsistent, or
1203    /// propagates allocation/configuration failures from libopus.
1204    pub fn new(sr: SampleRate, mapping: Mapping<'_>) -> Result<Self> {
1205        mapping.validate_for_decoder()?;
1206        let mut err = 0i32;
1207        let dec = unsafe {
1208            opus_multistream_decoder_create(
1209                sr as i32,
1210                i32::from(mapping.channels),
1211                i32::from(mapping.streams),
1212                i32::from(mapping.coupled_streams),
1213                mapping.mapping.as_ptr(),
1214                std::ptr::addr_of_mut!(err),
1215            )
1216        };
1217        if err != 0 {
1218            return Err(Error::from_code(err));
1219        }
1220        let dec = NonNull::new(dec).ok_or(Error::AllocFail)?;
1221        Ok(Self::from_raw(dec, sr, mapping.channels, Ownership::Owned))
1222    }
1223
1224    /// Decode into interleaved i16 PCM (`frame_size` is per-channel).
1225    ///
1226    /// # Errors
1227    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
1228    /// for buffer mismatches, or the mapped libopus error code.
1229    pub fn decode(
1230        &mut self,
1231        packet: &[u8],
1232        out: &mut [i16],
1233        frame_size_per_ch: usize,
1234        fec: bool,
1235    ) -> Result<usize> {
1236        let frame_size_per_ch = NonZeroUsize::new(frame_size_per_ch).ok_or(Error::BadArg)?;
1237        if frame_size_per_ch.get() > max_frame_samples_for(self.sample_rate) {
1238            return Err(Error::BadArg);
1239        }
1240        if out.len() != frame_size_per_ch.get() * self.channels as usize {
1241            return Err(Error::BadArg);
1242        }
1243        // libopus requires PLC/FEC frame sizes to be multiples of 2.5 ms.
1244        if (packet.is_empty() || fec)
1245            && !is_frame_size_2_5ms_aligned(frame_size_per_ch.get(), self.sample_rate)
1246        {
1247            return Err(Error::BadArg);
1248        }
1249        let n = unsafe {
1250            opus_multistream_decode(
1251                self.raw.as_ptr(),
1252                if packet.is_empty() {
1253                    std::ptr::null()
1254                } else {
1255                    packet.as_ptr()
1256                },
1257                if packet.is_empty() {
1258                    0
1259                } else {
1260                    i32::try_from(packet.len()).map_err(|_| Error::BadArg)?
1261                },
1262                out.as_mut_ptr(),
1263                i32::try_from(frame_size_per_ch.get()).map_err(|_| Error::BadArg)?,
1264                i32::from(fec),
1265            )
1266        };
1267        if n < 0 {
1268            return Err(Error::from_code(n));
1269        }
1270        usize::try_from(n).map_err(|_| Error::InternalError)
1271    }
1272
1273    /// Decode into interleaved f32 PCM (`frame_size` is per-channel).
1274    ///
1275    /// # Errors
1276    /// Returns [`Error::InvalidState`] if the decoder handle is invalid, [`Error::BadArg`]
1277    /// for buffer mismatches, or the mapped libopus error code.
1278    pub fn decode_float(
1279        &mut self,
1280        packet: &[u8],
1281        out: &mut [f32],
1282        frame_size_per_ch: usize,
1283        fec: bool,
1284    ) -> Result<usize> {
1285        let frame_size_per_ch = NonZeroUsize::new(frame_size_per_ch).ok_or(Error::BadArg)?;
1286        if frame_size_per_ch.get() > max_frame_samples_for(self.sample_rate) {
1287            return Err(Error::BadArg);
1288        }
1289        if out.len() != frame_size_per_ch.get() * self.channels as usize {
1290            return Err(Error::BadArg);
1291        }
1292        // libopus requires PLC/FEC frame sizes to be multiples of 2.5 ms.
1293        if (packet.is_empty() || fec)
1294            && !is_frame_size_2_5ms_aligned(frame_size_per_ch.get(), self.sample_rate)
1295        {
1296            return Err(Error::BadArg);
1297        }
1298        let n = unsafe {
1299            opus_multistream_decode_float(
1300                self.raw.as_ptr(),
1301                if packet.is_empty() {
1302                    std::ptr::null()
1303                } else {
1304                    packet.as_ptr()
1305                },
1306                if packet.is_empty() {
1307                    0
1308                } else {
1309                    i32::try_from(packet.len()).map_err(|_| Error::BadArg)?
1310                },
1311                out.as_mut_ptr(),
1312                i32::try_from(frame_size_per_ch.get()).map_err(|_| Error::BadArg)?,
1313                i32::from(fec),
1314            )
1315        };
1316        if n < 0 {
1317            return Err(Error::from_code(n));
1318        }
1319        usize::try_from(n).map_err(|_| Error::InternalError)
1320    }
1321
1322    /// Final RNG state from the last decode.
1323    ///
1324    /// # Errors
1325    /// Returns [`Error::InvalidState`] when the decoder handle is null or
1326    /// propagates the libopus error.
1327    pub fn final_range(&mut self) -> Result<u32> {
1328        let mut v: u32 = 0;
1329        let r = unsafe {
1330            opus_multistream_decoder_ctl(
1331                self.raw.as_ptr(),
1332                OPUS_GET_FINAL_RANGE_REQUEST as i32,
1333                &mut v,
1334            )
1335        };
1336        if r != 0 {
1337            return Err(Error::from_code(r));
1338        }
1339        Ok(v)
1340    }
1341
1342    /// Reset the decoder to its initial state.
1343    ///
1344    /// # Errors
1345    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1346    /// reported by libopus.
1347    pub fn reset(&mut self) -> Result<()> {
1348        let r = unsafe { opus_multistream_decoder_ctl(self.raw.as_ptr(), OPUS_RESET_STATE as i32) };
1349        if r != 0 {
1350            return Err(Error::from_code(r));
1351        }
1352        Ok(())
1353    }
1354
1355    /// Set post-decode gain in Q8 dB units.
1356    ///
1357    /// # Errors
1358    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1359    /// reported by libopus.
1360    pub fn set_gain(&mut self, q8_db: i32) -> Result<()> {
1361        self.simple_ctl(OPUS_SET_GAIN_REQUEST as i32, q8_db)
1362    }
1363
1364    /// Query post-decode gain in Q8 dB units.
1365    ///
1366    /// # Errors
1367    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1368    /// reported by libopus.
1369    pub fn gain(&mut self) -> Result<i32> {
1370        self.get_int_ctl(OPUS_GET_GAIN_REQUEST as i32)
1371    }
1372
1373    /// Disable or enable phase inversion (CELT stereo decorrelation).
1374    ///
1375    /// # Errors
1376    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1377    /// reported by libopus.
1378    pub fn set_phase_inversion_disabled(&mut self, disabled: bool) -> Result<()> {
1379        self.simple_ctl(
1380            OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST as i32,
1381            i32::from(disabled),
1382        )
1383    }
1384
1385    /// Query the phase inversion disabled flag.
1386    ///
1387    /// # Errors
1388    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1389    /// reported by libopus.
1390    pub fn phase_inversion_disabled(&mut self) -> Result<bool> {
1391        self.get_bool_ctl(OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST as i32)
1392    }
1393
1394    /// Query decoder output sample rate.
1395    ///
1396    /// # Errors
1397    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1398    /// reported by libopus.
1399    pub fn get_sample_rate(&mut self) -> Result<i32> {
1400        self.get_int_ctl(OPUS_GET_SAMPLE_RATE_REQUEST as i32)
1401    }
1402
1403    /// Query the pitch of the first coded stream's last decoded frame.
1404    ///
1405    /// The value is the fundamental period in samples at 48 kHz. Libopus does not forward
1406    /// `OPUS_GET_PITCH` through its multistream dispatcher, so this follows the dispatcher's
1407    /// convention for scalar getters and queries stream zero explicitly.
1408    ///
1409    /// # Errors
1410    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1411    /// reported by libopus.
1412    pub fn get_pitch(&mut self) -> Result<i32> {
1413        self.decoder_int_ctl(0, OPUS_GET_PITCH_REQUEST as i32)
1414    }
1415
1416    /// Query the duration (per channel) of the last decoded packet.
1417    ///
1418    /// # Errors
1419    /// Returns [`Error::InvalidState`] if the decoder handle is null or propagates any error
1420    /// reported by libopus.
1421    pub fn get_last_packet_duration(&mut self) -> Result<i32> {
1422        self.get_int_ctl(OPUS_GET_LAST_PACKET_DURATION_REQUEST as i32)
1423    }
1424
1425    /// Output channels (interleaved).
1426    #[must_use]
1427    pub const fn channels(&self) -> u8 {
1428        self.channels
1429    }
1430    /// Output sample rate.
1431    #[must_use]
1432    pub const fn sample_rate(&self) -> SampleRate {
1433        self.sample_rate
1434    }
1435
1436    /// Create a multistream decoder using the standard libopus surround mapping.
1437    ///
1438    /// # Errors
1439    /// Returns [`Error::BadArg`] for invalid channel counts or the mapped libopus
1440    /// error when decoder initialisation fails.
1441    pub fn new_surround(
1442        sr: SampleRate,
1443        channels: u8,
1444        mapping_family: i32,
1445    ) -> Result<(Self, Vec<u8>, u8, u8)> {
1446        MultistreamEncoder::surround_size(channels, mapping_family)?;
1447        let (mapping, streams, coupled) =
1448            MultistreamEncoder::surround_mapping(channels, mapping_family)?;
1449        let mut err = 0i32;
1450        let dec = unsafe {
1451            opus_multistream_decoder_create(
1452                sr as i32,
1453                i32::from(channels),
1454                i32::from(streams),
1455                i32::from(coupled),
1456                mapping.as_ptr(),
1457                std::ptr::addr_of_mut!(err),
1458            )
1459        };
1460        if err != 0 {
1461            return Err(Error::from_code(err));
1462        }
1463        let dec = NonNull::new(dec).ok_or(Error::AllocFail)?;
1464        Ok((
1465            Self::from_raw(dec, sr, channels, Ownership::Owned),
1466            mapping,
1467            streams,
1468            coupled,
1469        ))
1470    }
1471
1472    /// Borrow a pointer to an individual underlying decoder state for CTLs.
1473    ///
1474    /// # Safety
1475    /// Caller must not outlive the multistream decoder and must ensure the
1476    /// returned pointer is only used for immediate FFI calls.
1477    ///
1478    /// # Errors
1479    /// Returns [`Error::InvalidState`] if the decoder handle is invalid or propagates the
1480    /// libopus error when retrieving the per-stream state fails.
1481    pub unsafe fn decoder_state_ptr(&mut self, stream_index: i32) -> Result<*mut OpusDecoder> {
1482        Ok(self.decoder_state(stream_index)?.as_ptr())
1483    }
1484
1485    fn decoder_state(&mut self, stream_index: i32) -> Result<NonNull<OpusDecoder>> {
1486        let mut state: *mut OpusDecoder = std::ptr::null_mut();
1487        let r = unsafe {
1488            opus_multistream_decoder_ctl(
1489                self.raw.as_ptr(),
1490                OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST as i32,
1491                stream_index,
1492                &mut state,
1493            )
1494        };
1495        if r != 0 {
1496            return Err(Error::from_code(r));
1497        }
1498        NonNull::new(state).ok_or(Error::InternalError)
1499    }
1500
1501    fn decoder_int_ctl(&mut self, stream_index: i32, req: i32) -> Result<i32> {
1502        let state = self.decoder_state(stream_index)?;
1503        let mut value = 0i32;
1504        let r = unsafe { opus_decoder_ctl(state.as_ptr(), req, &mut value) };
1505        if r != 0 {
1506            return Err(Error::from_code(r));
1507        }
1508        Ok(value)
1509    }
1510
1511    fn simple_ctl(&mut self, req: i32, val: i32) -> Result<()> {
1512        let r = unsafe { opus_multistream_decoder_ctl(self.raw.as_ptr(), req, val) };
1513        if r != 0 {
1514            return Err(Error::from_code(r));
1515        }
1516        Ok(())
1517    }
1518
1519    fn get_int_ctl(&mut self, req: i32) -> Result<i32> {
1520        let mut v: i32 = 0;
1521        let r = unsafe { opus_multistream_decoder_ctl(self.raw.as_ptr(), req, &mut v) };
1522        if r != 0 {
1523            return Err(Error::from_code(r));
1524        }
1525        Ok(v)
1526    }
1527
1528    fn get_bool_ctl(&mut self, req: i32) -> Result<bool> {
1529        Ok(self.get_int_ctl(req)? != 0)
1530    }
1531}
1532
1533impl<'a> MultistreamDecoderRef<'a> {
1534    /// Wrap an externally-initialized multistream decoder without taking ownership.
1535    ///
1536    /// # Safety
1537    /// - `ptr` must point to valid, initialized memory of at least [`MultistreamDecoder::size()`] bytes
1538    /// - `ptr` must be aligned to at least `align_of::<usize>()` (malloc-style alignment)
1539    /// - `sr` and `mapping` must exactly match the decoder state already stored at `ptr`
1540    /// - The memory must remain valid for the lifetime `'a`
1541    /// - Caller is responsible for freeing the memory after this wrapper is dropped
1542    ///
1543    /// Passing mismatched metadata is undefined behavior: later safe methods may validate buffer
1544    /// sizes with the wrong layout and then call libopus with out-of-bounds buffers.
1545    ///
1546    /// # Panics
1547    /// Panics if `ptr` is null, not pointer-aligned, or `mapping` is invalid.
1548    ///
1549    /// Use [`MultistreamDecoder::init_in_place`] to initialize the memory before calling this.
1550    #[must_use]
1551    pub unsafe fn from_raw(ptr: *mut OpusMSDecoder, sr: SampleRate, mapping: Mapping<'_>) -> Self {
1552        assert!(
1553            mapping.validate_for_decoder().is_ok(),
1554            "MultistreamDecoderRef::from_raw called with invalid mapping"
1555        );
1556        let decoder = MultistreamDecoder::from_raw(
1557            crate::checked_non_null(ptr, "MultistreamDecoderRef::from_raw"),
1558            sr,
1559            mapping.channels,
1560            Ownership::Borrowed,
1561        );
1562        Self {
1563            inner: decoder,
1564            _marker: PhantomData,
1565        }
1566    }
1567
1568    /// Initialize and wrap an externally allocated buffer.
1569    ///
1570    /// # Errors
1571    /// Returns [`Error::BadArg`] if the buffer is too small, or a mapped libopus error.
1572    pub fn init_in(
1573        buf: &'a mut AlignedBuffer,
1574        sr: SampleRate,
1575        mapping: Mapping<'_>,
1576    ) -> Result<Self> {
1577        let required = MultistreamDecoder::size(mapping.streams, mapping.coupled_streams)?;
1578        if buf.capacity_bytes() < required {
1579            return Err(Error::BadArg);
1580        }
1581        let ptr = buf.as_mut_ptr::<OpusMSDecoder>();
1582        unsafe { MultistreamDecoder::init_in_place(ptr, sr, mapping)? };
1583        Ok(unsafe { Self::from_raw(ptr, sr, mapping) })
1584    }
1585
1586    delegate_ref_mut_methods! {
1587        fn decode(packet: &[u8], out: &mut [i16], frame_size_per_ch: usize, fec: bool) -> Result<usize>;
1588        fn decode_float(packet: &[u8], out: &mut [f32], frame_size_per_ch: usize, fec: bool) -> Result<usize>;
1589        fn final_range() -> Result<u32>;
1590        fn reset() -> Result<()>;
1591        fn set_gain(q8_db: i32) -> Result<()>;
1592        fn gain() -> Result<i32>;
1593        fn set_phase_inversion_disabled(disabled: bool) -> Result<()>;
1594        fn phase_inversion_disabled() -> Result<bool>;
1595        fn get_sample_rate() -> Result<i32>;
1596        fn get_pitch() -> Result<i32>;
1597        fn get_last_packet_duration() -> Result<i32>;
1598    }
1599
1600    delegate_ref_unsafe_mut_methods! {
1601        unsafe fn decoder_state_ptr(stream_index: i32) -> Result<*mut OpusDecoder>;
1602    }
1603}
1604
1605impl Deref for MultistreamDecoderRef<'_> {
1606    type Target = MultistreamDecoder;
1607
1608    fn deref(&self) -> &Self::Target {
1609        &self.inner
1610    }
1611}
1612
1613#[cfg(test)]
1614mod tests {
1615    use super::*;
1616
1617    #[test]
1618    fn mapping_allows_dropped_channels() {
1619        let mapping = Mapping {
1620            channels: 6,
1621            streams: 2,
1622            coupled_streams: 1,
1623            mapping: &[0, 1, 2, u8::MAX, u8::MAX, u8::MAX],
1624        };
1625        assert!(mapping.validate_for_encoder().is_ok());
1626    }
1627
1628    #[test]
1629    fn mapping_requires_encoder_stream_coverage() {
1630        let mapping = Mapping {
1631            channels: 2,
1632            streams: 1,
1633            coupled_streams: 1,
1634            mapping: &[0, 0],
1635        };
1636        assert!(mapping.validate_for_decoder().is_ok());
1637        assert!(mapping.validate_for_encoder().is_err());
1638    }
1639}