Skip to main content

rusty_opus/
multistream.rs

1//! Opus multistream (surround) — port of the core of
2//! `src/opus_multistream_{encoder,decoder}.c`. Wraps N mono/coupled Opus
3//! coders behind a channel-mapping layout so >2-channel audio (quad, 5.1,
4//! 7.1) can be coded as a set of standard Opus streams concatenated with the
5//! self-delimited framing.
6//!
7//! The channel bitrate allocation here is a simple even split across streams
8//! (coupled streams get 2x a mono stream's share) — libopus adds a
9//! surround-masking analysis on top, a quality refinement, not a conformance
10//! requirement. The bitstream layout, mapping, and per-stream Opus coding are
11//! standard, so streams interoperate with libopus.
12
13use crate::repacketizer::{parse_packet, Repacketizer};
14use crate::{Application, Bandwidth, OpusDecoder, OpusEncoder};
15
16/// Vorbis channel layout for mapping family 1, channels 1..=8:
17/// (nb_streams, nb_coupled_streams, channel_mapping).
18const VORBIS_MAPPINGS: [(usize, usize, &[u8]); 8] = [
19    (1, 0, &[0]),                      // mono
20    (1, 1, &[0, 1]),                   // stereo
21    (2, 1, &[0, 2, 1]),                // 1-d (3.0)
22    (2, 2, &[0, 1, 2, 3]),             // quad
23    (3, 2, &[0, 4, 1, 2, 3]),          // 5.0
24    (4, 2, &[0, 4, 1, 2, 3, 5]),       // 5.1
25    (4, 3, &[0, 4, 1, 2, 3, 5, 6]),    // 6.1
26    (5, 3, &[0, 6, 1, 2, 3, 4, 5, 7]), // 7.1
27];
28
29#[derive(Clone)]
30pub struct ChannelLayout {
31    pub nb_channels: usize,
32    pub nb_streams: usize,
33    pub nb_coupled_streams: usize,
34    pub mapping: Vec<u8>,
35}
36
37impl ChannelLayout {
38    /// Standard layout for a channel count + mapping family (0 = mono/stereo,
39    /// 1 = Vorbis surround for 1..=8 channels).
40    pub fn surround(channels: usize, mapping_family: i32) -> Result<Self, &'static str> {
41        match mapping_family {
42            0 => {
43                if channels == 1 {
44                    Ok(ChannelLayout {
45                        nb_channels: 1,
46                        nb_streams: 1,
47                        nb_coupled_streams: 0,
48                        mapping: vec![0],
49                    })
50                } else if channels == 2 {
51                    Ok(ChannelLayout {
52                        nb_channels: 2,
53                        nb_streams: 1,
54                        nb_coupled_streams: 1,
55                        mapping: vec![0, 1],
56                    })
57                } else {
58                    Err("family 0 supports only 1-2 channels")
59                }
60            }
61            1 => {
62                if !(1..=8).contains(&channels) {
63                    return Err("family 1 supports 1-8 channels");
64                }
65                let (ns, nc, m) = VORBIS_MAPPINGS[channels - 1];
66                Ok(ChannelLayout {
67                    nb_channels: channels,
68                    nb_streams: ns,
69                    nb_coupled_streams: nc,
70                    mapping: m.to_vec(),
71                })
72            }
73            _ => Err("unsupported mapping family"),
74        }
75    }
76
77    fn left_channel(&self, stream_id: usize, prev: i32) -> i32 {
78        let start = if prev < 0 { 0 } else { prev as usize + 1 };
79        for (i, &m) in self.mapping.iter().enumerate().skip(start) {
80            if m as usize == stream_id * 2 {
81                return i as i32;
82            }
83        }
84        -1
85    }
86    fn right_channel(&self, stream_id: usize, prev: i32) -> i32 {
87        let start = if prev < 0 { 0 } else { prev as usize + 1 };
88        for (i, &m) in self.mapping.iter().enumerate().skip(start) {
89            if m as usize == stream_id * 2 + 1 {
90                return i as i32;
91            }
92        }
93        -1
94    }
95    fn mono_channel(&self, stream_id: usize, prev: i32) -> i32 {
96        let start = if prev < 0 { 0 } else { prev as usize + 1 };
97        for (i, &m) in self.mapping.iter().enumerate().skip(start) {
98            if m as usize == stream_id + self.nb_coupled_streams {
99                return i as i32;
100            }
101        }
102        -1
103    }
104}
105
106/// Multistream encoder: one Opus encoder per stream (coupled = stereo, the
107/// rest mono), coded per the channel layout and concatenated self-delimited.
108pub struct OpusMSEncoder {
109    layout: ChannelLayout,
110    encoders: Vec<OpusEncoder>,
111    sample_rate: i32,
112    /// Total target bitrate across all streams (split evenly, coupled=2x mono).
113    pub bitrate_bps: i32,
114}
115
116impl OpusMSEncoder {
117    pub fn new(
118        sample_rate: i32,
119        channels: usize,
120        mapping_family: i32,
121        application: Application,
122    ) -> Result<Self, &'static str> {
123        let layout = ChannelLayout::surround(channels, mapping_family)?;
124        let mut encoders = Vec::with_capacity(layout.nb_streams);
125        for s in 0..layout.nb_streams {
126            let ch = if s < layout.nb_coupled_streams { 2 } else { 1 };
127            encoders.push(OpusEncoder::new(sample_rate, ch, application)?);
128        }
129        let mut enc = OpusMSEncoder {
130            layout,
131            encoders,
132            sample_rate,
133            bitrate_bps: 64000 * channels as i32,
134        };
135        enc.set_bitrate(enc.bitrate_bps);
136        Ok(enc)
137    }
138
139    /// Split the total bitrate across streams (each coupled stream gets 2x a
140    /// mono stream's share, matching its 2 channels).
141    pub fn set_bitrate(&mut self, total: i32) {
142        self.bitrate_bps = total;
143        let units = self.layout.nb_coupled_streams * 2
144            + (self.layout.nb_streams - self.layout.nb_coupled_streams);
145        let per_unit = if units > 0 { total / units as i32 } else { total };
146        for (s, e) in self.encoders.iter_mut().enumerate() {
147            e.bitrate_bps = if s < self.layout.nb_coupled_streams {
148                per_unit * 2
149            } else {
150                per_unit
151            };
152        }
153    }
154
155    pub fn nb_streams(&self) -> usize {
156        self.layout.nb_streams
157    }
158
159    /// Encode one frame of interleaved `input` (nb_channels per sample) into a
160    /// multistream packet. `scratch` output is returned as a Vec.
161    pub fn encode(&mut self, input: &[f32], frame_size: usize) -> Result<Vec<u8>, &'static str> {
162        let nch = self.layout.nb_channels;
163        let mut out: Vec<u8> = Vec::new();
164        let mut stream_buf = vec![0f32; frame_size * 2];
165        let mut pkt = vec![0u8; 1500 + frame_size];
166
167        for s in 0..self.layout.nb_streams {
168            let coupled = s < self.layout.nb_coupled_streams;
169            let sch = if coupled { 2 } else { 1 };
170            // Gather this stream's channels from the interleaved input.
171            if coupled {
172                let l = self.layout.left_channel(s, -1);
173                let r = self.layout.right_channel(s, -1);
174                for i in 0..frame_size {
175                    stream_buf[i * 2] = if l >= 0 { input[i * nch + l as usize] } else { 0.0 };
176                    stream_buf[i * 2 + 1] =
177                        if r >= 0 { input[i * nch + r as usize] } else { 0.0 };
178                }
179            } else {
180                let m = self.layout.mono_channel(s, -1);
181                for i in 0..frame_size {
182                    stream_buf[i] = if m >= 0 { input[i * nch + m as usize] } else { 0.0 };
183                }
184            }
185            let n = self.encoders[s].encode(&stream_buf[..frame_size * sch], frame_size, &mut pkt)?;
186            // All streams but the last are self-delimited so the decoder can
187            // find each stream's boundary.
188            if s != self.layout.nb_streams - 1 {
189                let mut rp = Repacketizer::new();
190                rp.cat(&pkt[..n])?;
191                out.extend_from_slice(&rp.out_self_delimited()?);
192            } else {
193                out.extend_from_slice(&pkt[..n]);
194            }
195        }
196        Ok(out)
197    }
198
199    pub fn sample_rate(&self) -> i32 {
200        self.sample_rate
201    }
202}
203
204/// Multistream decoder: decode each stream and remux to the output channels.
205pub struct OpusMSDecoder {
206    layout: ChannelLayout,
207    decoders: Vec<OpusDecoder>,
208}
209
210impl OpusMSDecoder {
211    pub fn new(
212        sample_rate: i32,
213        channels: usize,
214        mapping_family: i32,
215    ) -> Result<Self, &'static str> {
216        let layout = ChannelLayout::surround(channels, mapping_family)?;
217        let mut decoders = Vec::with_capacity(layout.nb_streams);
218        for s in 0..layout.nb_streams {
219            let ch = if s < layout.nb_coupled_streams { 2 } else { 1 };
220            decoders.push(OpusDecoder::new(sample_rate, ch)?);
221        }
222        Ok(OpusMSDecoder { layout, decoders })
223    }
224
225    /// Decode a multistream packet into interleaved `output` (nb_channels per
226    /// sample). Returns the number of samples per channel.
227    pub fn decode(
228        &mut self,
229        packet: &[u8],
230        frame_size: usize,
231        output: &mut [f32],
232    ) -> Result<usize, &'static str> {
233        let nch = self.layout.nb_channels;
234        let mut buf = vec![0f32; frame_size * 2];
235        let mut data = packet;
236        let mut produced = frame_size;
237
238        for s in 0..self.layout.nb_streams {
239            let coupled = s < self.layout.nb_coupled_streams;
240            let last = s == self.layout.nb_streams - 1;
241            // Determine this stream's byte slice.
242            let (stream_slice, advance) = if last {
243                (data, data.len())
244            } else {
245                let (_toc, _frames, off) = parse_packet(data, true)?;
246                (&data[..off], off)
247            };
248            let n = self.decoders[s].decode(stream_slice, frame_size, &mut buf)?;
249            produced = n;
250            // Remux this stream's channel(s) to the output.
251            if coupled {
252                let mut prev = -1;
253                loop {
254                    let chan = self.layout.left_channel(s, prev);
255                    if chan == -1 {
256                        break;
257                    }
258                    for i in 0..n {
259                        output[i * nch + chan as usize] = buf[i * 2];
260                    }
261                    prev = chan;
262                }
263                let mut prev = -1;
264                loop {
265                    let chan = self.layout.right_channel(s, prev);
266                    if chan == -1 {
267                        break;
268                    }
269                    for i in 0..n {
270                        output[i * nch + chan as usize] = buf[i * 2 + 1];
271                    }
272                    prev = chan;
273                }
274            } else {
275                let mut prev = -1;
276                loop {
277                    let chan = self.layout.mono_channel(s, prev);
278                    if chan == -1 {
279                        break;
280                    }
281                    for i in 0..n {
282                        output[i * nch + chan as usize] = buf[i];
283                    }
284                    prev = chan;
285                }
286            }
287            if !last {
288                data = &data[advance..];
289            }
290        }
291        // Unmapped channels (mapping == 255) are silenced.
292        for c in 0..nch {
293            if self.layout.mapping.get(c).copied() == Some(255) {
294                for i in 0..produced {
295                    output[i * nch + c] = 0.0;
296                }
297            }
298        }
299        Ok(produced)
300    }
301}
302
303/// Bandwidth passthrough helper (so callers can cap all streams at once).
304impl OpusMSEncoder {
305    pub fn set_max_bandwidth(&mut self, bw: Bandwidth) {
306        for e in &mut self.encoders {
307            e.max_bandwidth = bw;
308        }
309    }
310}