Skip to main content

opus_decoder/
multistream.rs

1//! Opus multistream decoder โ€” wraps multiple `OpusDecoder` instances
2//! and demultiplexes channel mapping per RFC 7845 ยง5.1.
3//!
4//! Mirrors the wrapper logic from libopus `opus_multistream_decoder.c`.
5
6use crate::packet;
7use crate::{OpusDecoder, OpusError};
8
9/// Opus multistream decoder.
10///
11/// Holds one `OpusDecoder` per stream. Coupled streams use stereo decoders and
12/// remaining streams use mono decoders.
13pub struct OpusMultistreamDecoder {
14    /// One decoder per stream. Coupled streams are stereo (2ch), rest mono.
15    decoders: Vec<OpusDecoder>,
16    /// Total output channels (1โ€“255).
17    nb_channels: usize,
18    /// Total streams in packet (coupled + mono).
19    nb_streams: usize,
20    /// Streams that are stereo-coupled (always the first `nb_coupled_streams`).
21    nb_coupled_streams: usize,
22    /// `mapping[output_channel] = index into interleaved decoder outputs`.
23    /// `255` means "silence this output channel".
24    mapping: Vec<u8>,
25    /// Output sample rate (same for all streams).
26    sample_rate: u32,
27}
28
29impl OpusMultistreamDecoder {
30    /// Create a new multistream decoder.
31    ///
32    /// Parameters: `sample_rate`, total output `nb_channels`, `nb_streams`,
33    /// `nb_coupled_streams`, and per-output-channel `mapping`.
34    /// Returns: initialized multistream decoder on success.
35    ///
36    /// # Examples
37    ///
38    /// ```rust
39    /// use opus_decoder::OpusMultistreamDecoder;
40    ///
41    /// let decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?;
42    /// # let _ = decoder;
43    /// # Ok::<(), opus_decoder::OpusError>(())
44    /// ```
45    pub fn new(
46        sample_rate: u32,
47        nb_channels: usize,
48        nb_streams: usize,
49        nb_coupled_streams: usize,
50        mapping: &[u8],
51    ) -> Result<Self, OpusError> {
52        if nb_channels == 0 || nb_channels > 255 {
53            return Err(OpusError::InvalidArgument("nb_channels"));
54        }
55        if nb_streams == 0 {
56            return Err(OpusError::InvalidArgument("nb_streams"));
57        }
58        if nb_coupled_streams > nb_streams {
59            return Err(OpusError::InvalidArgument("nb_coupled_streams"));
60        }
61        if nb_streams > 255usize.saturating_sub(nb_coupled_streams) {
62            return Err(OpusError::InvalidArgument("nb_streams"));
63        }
64        if mapping.len() != nb_channels {
65            return Err(OpusError::InvalidArgument("mapping"));
66        }
67
68        let total_decoded_channels = (2 * nb_coupled_streams) + (nb_streams - nb_coupled_streams);
69        for &slot in mapping {
70            if slot != 255 && usize::from(slot) >= total_decoded_channels {
71                return Err(OpusError::InvalidArgument("mapping"));
72            }
73        }
74
75        let mut decoders = Vec::with_capacity(nb_streams);
76        for stream_idx in 0..nb_streams {
77            let channels = if stream_idx < nb_coupled_streams {
78                2
79            } else {
80                1
81            };
82            decoders.push(OpusDecoder::new(sample_rate, channels)?);
83        }
84
85        Ok(Self {
86            decoders,
87            nb_channels,
88            nb_streams,
89            nb_coupled_streams,
90            mapping: mapping.to_vec(),
91            sample_rate,
92        })
93    }
94
95    /// Decode a multistream packet into interleaved i16 PCM.
96    ///
97    /// Parameters: multistream `packet`, writable interleaved `pcm`, and `fec`.
98    /// - `fec`: reserved for future in-band FEC support. Currently treated as
99    ///   packet loss concealment (PLC) when `true`. Pass `false` for normal decode.
100    ///
101    /// Returns: decoded samples per output channel.
102    ///
103    /// # Examples
104    ///
105    /// ```rust,no_run
106    /// use opus_decoder::OpusMultistreamDecoder;
107    ///
108    /// let mut decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?;
109    /// let packet = std::fs::read("multistream-frame.opus")?;
110    /// let mut pcm = vec![0i16; 960 * 2];
111    /// let samples = decoder.decode(&packet, &mut pcm, false)?;
112    /// # let _ = samples;
113    /// # Ok::<(), Box<dyn std::error::Error>>(())
114    /// ```
115    pub fn decode(
116        &mut self,
117        packet: &[u8],
118        pcm: &mut [i16],
119        fec: bool,
120    ) -> Result<usize, OpusError> {
121        let sub_packets = if packet.is_empty() || fec {
122            vec![&[][..]; self.nb_streams]
123        } else {
124            split_multistream_packet(packet, self.nb_streams)?
125        };
126        let frame_size = self.expected_frame_size(&sub_packets, fec)?;
127        let needed = frame_size * self.nb_channels;
128        if pcm.len() < needed {
129            return Err(OpusError::BufferTooSmall);
130        }
131        if frame_size == 0 {
132            return Ok(0);
133        }
134
135        let mut stream_pcm = Vec::with_capacity(self.nb_streams);
136        for (stream_idx, sub_packet) in sub_packets.iter().enumerate() {
137            let channels = self.stream_channels(stream_idx);
138            let mut buf = vec![0i16; frame_size * channels];
139            let written = self.decoders[stream_idx].decode(sub_packet, &mut buf, fec)?;
140            if written != frame_size {
141                return Err(OpusError::InvalidPacket);
142            }
143            stream_pcm.push(buf);
144        }
145
146        for (channel_idx, &slot) in self.mapping.iter().enumerate() {
147            if slot == 255 {
148                zero_output_channel_i16(pcm, self.nb_channels, channel_idx, frame_size);
149                continue;
150            }
151
152            let (stream_idx, source_channel) = self.slot_to_stream_channel(usize::from(slot));
153            copy_output_channel_i16(
154                pcm,
155                self.nb_channels,
156                channel_idx,
157                &stream_pcm[stream_idx],
158                self.stream_channels(stream_idx),
159                source_channel,
160                frame_size,
161            );
162        }
163
164        Ok(frame_size)
165    }
166
167    /// Decode a multistream packet into interleaved f32 PCM.
168    ///
169    /// Parameters: multistream `packet`, writable interleaved `pcm`, and `fec`.
170    /// - `fec`: reserved for future in-band FEC support. Currently treated as
171    ///   packet loss concealment (PLC) when `true`. Pass `false` for normal decode.
172    ///
173    /// Returns: decoded samples per output channel.
174    ///
175    /// # Examples
176    ///
177    /// ```rust,no_run
178    /// use opus_decoder::OpusMultistreamDecoder;
179    ///
180    /// let mut decoder = OpusMultistreamDecoder::new(48_000, 2, 2, 0, &[0, 1])?;
181    /// let packet = std::fs::read("multistream-frame.opus")?;
182    /// let mut pcm = vec![0.0f32; 960 * 2];
183    /// let samples = decoder.decode_float(&packet, &mut pcm, false)?;
184    /// # let _ = samples;
185    /// # Ok::<(), Box<dyn std::error::Error>>(())
186    /// ```
187    pub fn decode_float(
188        &mut self,
189        packet: &[u8],
190        pcm: &mut [f32],
191        fec: bool,
192    ) -> Result<usize, OpusError> {
193        let sub_packets = if packet.is_empty() || fec {
194            vec![&[][..]; self.nb_streams]
195        } else {
196            split_multistream_packet(packet, self.nb_streams)?
197        };
198        let frame_size = self.expected_frame_size(&sub_packets, fec)?;
199        let needed = frame_size * self.nb_channels;
200        if pcm.len() < needed {
201            return Err(OpusError::BufferTooSmall);
202        }
203        if frame_size == 0 {
204            return Ok(0);
205        }
206
207        let mut stream_pcm = Vec::with_capacity(self.nb_streams);
208        for (stream_idx, sub_packet) in sub_packets.iter().enumerate() {
209            let channels = self.stream_channels(stream_idx);
210            let mut buf = vec![0.0f32; frame_size * channels];
211            let written = self.decoders[stream_idx].decode_float(sub_packet, &mut buf, fec)?;
212            if written != frame_size {
213                return Err(OpusError::InvalidPacket);
214            }
215            stream_pcm.push(buf);
216        }
217
218        for (channel_idx, &slot) in self.mapping.iter().enumerate() {
219            if slot == 255 {
220                zero_output_channel_f32(pcm, self.nb_channels, channel_idx, frame_size);
221                continue;
222            }
223
224            let (stream_idx, source_channel) = self.slot_to_stream_channel(usize::from(slot));
225            copy_output_channel_f32(
226                pcm,
227                self.nb_channels,
228                channel_idx,
229                &stream_pcm[stream_idx],
230                self.stream_channels(stream_idx),
231                source_channel,
232                frame_size,
233            );
234        }
235
236        Ok(frame_size)
237    }
238
239    /// Reset all internal decoders.
240    ///
241    /// Parameters: none.
242    /// Returns: nothing.
243    pub fn reset(&mut self) {
244        for decoder in &mut self.decoders {
245            decoder.reset();
246        }
247    }
248
249    /// Compute the expected frame size for one multistream decode call.
250    ///
251    /// Parameters: per-stream `sub_packets` and FEC flag `fec`.
252    /// Returns: decoded samples per output channel.
253    fn expected_frame_size(&self, sub_packets: &[&[u8]], fec: bool) -> Result<usize, OpusError> {
254        if fec || sub_packets.iter().all(|packet| packet.is_empty()) {
255            return Ok(self
256                .decoders
257                .first()
258                .map(|decoder| decoder.last_packet_duration)
259                .unwrap_or(0));
260        }
261
262        let mut frame_size = None;
263        for sub_packet in sub_packets.iter().filter(|packet| !packet.is_empty()) {
264            let samples = packet::parse_packet(sub_packet)
265                .map_err(OpusError::from)?
266                .samples_per_channel(self.sample_rate);
267            if let Some(expected) = frame_size {
268                if expected != samples {
269                    return Err(OpusError::InvalidPacket);
270                }
271            } else {
272                frame_size = Some(samples);
273            }
274        }
275
276        Ok(frame_size.unwrap_or(0))
277    }
278
279    /// Return the decoded channel count for one elementary stream.
280    ///
281    /// Parameters: `stream_idx` elementary stream index.
282    /// Returns: `2` for coupled streams and `1` for mono streams.
283    fn stream_channels(&self, stream_idx: usize) -> usize {
284        if stream_idx < self.nb_coupled_streams {
285            2
286        } else {
287            1
288        }
289    }
290
291    /// Map one decoded channel slot to `(stream_idx, channel_idx)`.
292    ///
293    /// Parameters: flattened decoded `slot`.
294    /// Returns: elementary stream index and channel index within that stream.
295    fn slot_to_stream_channel(&self, slot: usize) -> (usize, usize) {
296        let coupled_slots = 2 * self.nb_coupled_streams;
297        if slot < coupled_slots {
298            (slot / 2, slot % 2)
299        } else {
300            (self.nb_coupled_streams + (slot - coupled_slots), 0)
301        }
302    }
303}
304
305/// Split a multistream packet into per-stream sub-packets.
306///
307/// Parameters: full multistream `packet` and total `nb_streams`.
308/// Returns: borrowed sub-packet slices in stream order.
309fn split_multistream_packet(packet: &[u8], nb_streams: usize) -> Result<Vec<&[u8]>, OpusError> {
310    if nb_streams == 0 {
311        return Err(OpusError::InvalidArgument("nb_streams"));
312    }
313    if packet.is_empty() {
314        return Ok(vec![packet; nb_streams]);
315    }
316
317    let mut out = Vec::with_capacity(nb_streams);
318    let mut offset = 0usize;
319    for _stream_idx in 0..nb_streams.saturating_sub(1) {
320        let (packet_len, used) = parse_self_delimited_size(&packet[offset..])?;
321        offset += used;
322        if offset + packet_len > packet.len() {
323            return Err(OpusError::InvalidPacket);
324        }
325        out.push(&packet[offset..offset + packet_len]);
326        offset += packet_len;
327    }
328
329    if offset > packet.len() {
330        return Err(OpusError::InvalidPacket);
331    }
332    out.push(&packet[offset..]);
333    Ok(out)
334}
335
336/// Parse one self-delimited multistream sub-packet length.
337///
338/// Parameters: encoded length `data`.
339/// Returns: tuple `(packet_len, bytes_used)`.
340fn parse_self_delimited_size(data: &[u8]) -> Result<(usize, usize), OpusError> {
341    if data.is_empty() {
342        return Err(OpusError::InvalidPacket);
343    }
344
345    let first = usize::from(data[0]);
346    if first < 252 {
347        Ok((first, 1))
348    } else {
349        if data.len() < 2 {
350            return Err(OpusError::InvalidPacket);
351        }
352        Ok((first + (4 * usize::from(data[1])), 2))
353    }
354}
355
356/// Copy one decoded i16 channel into the interleaved multistream output buffer.
357///
358/// Parameters: mutable destination `pcm`, destination `dst_stride`, output
359/// channel index `dst_channel`, source `src`, source `src_stride`, source
360/// channel index `src_channel`, and `frame_size`.
361/// Returns: nothing; destination channel is overwritten.
362fn copy_output_channel_i16(
363    pcm: &mut [i16],
364    dst_stride: usize,
365    dst_channel: usize,
366    src: &[i16],
367    src_stride: usize,
368    src_channel: usize,
369    frame_size: usize,
370) {
371    for sample_idx in 0..frame_size {
372        pcm[sample_idx * dst_stride + dst_channel] = src[sample_idx * src_stride + src_channel];
373    }
374}
375
376/// Zero one interleaved i16 output channel.
377///
378/// Parameters: mutable destination `pcm`, destination `dst_stride`, output
379/// channel index `dst_channel`, and `frame_size`.
380/// Returns: nothing; destination channel is zero-filled.
381fn zero_output_channel_i16(
382    pcm: &mut [i16],
383    dst_stride: usize,
384    dst_channel: usize,
385    frame_size: usize,
386) {
387    for sample_idx in 0..frame_size {
388        pcm[sample_idx * dst_stride + dst_channel] = 0;
389    }
390}
391
392/// Copy one decoded f32 channel into the interleaved multistream output buffer.
393///
394/// Parameters: mutable destination `pcm`, destination `dst_stride`, output
395/// channel index `dst_channel`, source `src`, source `src_stride`, source
396/// channel index `src_channel`, and `frame_size`.
397/// Returns: nothing; destination channel is overwritten.
398fn copy_output_channel_f32(
399    pcm: &mut [f32],
400    dst_stride: usize,
401    dst_channel: usize,
402    src: &[f32],
403    src_stride: usize,
404    src_channel: usize,
405    frame_size: usize,
406) {
407    for sample_idx in 0..frame_size {
408        pcm[sample_idx * dst_stride + dst_channel] = src[sample_idx * src_stride + src_channel];
409    }
410}
411
412/// Zero one interleaved f32 output channel.
413///
414/// Parameters: mutable destination `pcm`, destination `dst_stride`, output
415/// channel index `dst_channel`, and `frame_size`.
416/// Returns: nothing; destination channel is zero-filled.
417fn zero_output_channel_f32(
418    pcm: &mut [f32],
419    dst_stride: usize,
420    dst_channel: usize,
421    frame_size: usize,
422) {
423    for sample_idx in 0..frame_size {
424        pcm[sample_idx * dst_stride + dst_channel] = 0.0;
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    /// Split one two-stream packet with a one-byte length prefix.
433    ///
434    /// Parameters: none.
435    /// Returns: nothing; panics on mismatch.
436    #[test]
437    fn split_two_stream_packet_with_short_prefix() {
438        let packet = [3u8, 10, 11, 12, 20, 21];
439        let sub_packets = split_multistream_packet(&packet, 2).unwrap();
440        assert_eq!(sub_packets, vec![&[10, 11, 12][..], &[20, 21][..]]);
441    }
442
443    /// Split one empty packet into PLC sub-packets for all streams.
444    ///
445    /// Parameters: none.
446    /// Returns: nothing; panics on mismatch.
447    #[test]
448    fn split_empty_packet_for_plc() {
449        let sub_packets = split_multistream_packet(&[], 3).unwrap();
450        assert_eq!(sub_packets, vec![&[][..], &[][..], &[][..]]);
451    }
452}