use crate::framing_self_delim::parse_self_delimited;
use crate::opus_head::{ChannelMappingTable, OpusHead};
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamPacket<'a> {
pub bytes: &'a [u8],
pub self_delimited: bool,
}
pub fn split_multistream_packet(
packet: &[u8],
stream_count: u8,
) -> Result<Vec<StreamPacket<'_>>, Error> {
if stream_count == 0 {
return Err(Error::MalformedPacket);
}
let n = stream_count as usize;
let mut streams = Vec::with_capacity(n);
let mut offset = 0usize;
for _ in 0..(n - 1) {
if offset >= packet.len() {
return Err(Error::MalformedPacket);
}
let parsed = parse_self_delimited(&packet[offset..])?;
let consumed = parsed.consumed;
streams.push(StreamPacket {
bytes: &packet[offset..offset + consumed],
self_delimited: true,
});
offset += consumed;
}
if offset >= packet.len() {
return Err(Error::MalformedPacket);
}
streams.push(StreamPacket {
bytes: &packet[offset..],
self_delimited: false,
});
Ok(streams)
}
pub fn assemble_multistream_packet(packets: &[&[u8]]) -> Result<Vec<u8>, Error> {
use crate::frames::OpusPacket;
use crate::packet_compose::compose_self_delimited;
let n = packets.len();
if n == 0 {
return Err(Error::MalformedPacket);
}
let mut shape: Option<(u8, usize)> = None;
let mut out = Vec::new();
for (idx, &packet) in packets.iter().enumerate() {
let parsed = OpusPacket::parse(packet)?;
let config = packet[0] >> 3;
let count = parsed.frame_count();
match shape {
None => shape = Some((config, count)),
Some(s) => {
if s != (config, count) {
return Err(Error::MalformedPacket);
}
}
}
if idx + 1 < n {
let frames = parsed.frames();
let (vbr, padding) =
if parsed.toc.frame_count_code == crate::toc::FrameCountCode::Arbitrary {
(
frames.iter().any(|f| f.len() != frames[0].len()),
parsed.padding,
)
} else {
(false, 0)
};
let sd = compose_self_delimited(packet[0], frames, vbr, padding)?;
out.extend_from_slice(&sd);
} else {
out.extend_from_slice(packet);
}
}
Ok(out)
}
#[derive(Debug)]
pub struct MultistreamDecoder {
mapping: ChannelMappingTable,
decoders: Vec<crate::decoder::OpusDecoder>,
}
impl MultistreamDecoder {
pub fn new(mapping: ChannelMappingTable) -> Self {
let n = mapping.stream_count as usize;
let decoders = (0..n).map(|_| crate::decoder::OpusDecoder::new()).collect();
MultistreamDecoder { mapping, decoders }
}
pub fn from_head(head: &OpusHead) -> Self {
Self::new(head.mapping.clone())
}
pub fn mapping(&self) -> &ChannelMappingTable {
&self.mapping
}
pub fn output_channels(&self) -> u8 {
self.mapping.output_channels()
}
pub fn reset(&mut self) {
for d in &mut self.decoders {
d.reset();
}
}
pub fn decode_packet(&mut self, packet: &[u8]) -> Result<MultistreamAudio, Error> {
let streams = split_multistream_packet(packet, self.mapping.stream_count)?;
let coupled = self.mapping.coupled_count as usize;
let mut decoded: Vec<(Vec<i16>, u8)> = Vec::with_capacity(streams.len());
for (s, stream) in streams.iter().enumerate() {
let dec = &mut self.decoders[s];
let audio = if stream.self_delimited {
dec.decode_self_delimited_packet(stream.bytes)
} else {
dec.decode_packet(stream.bytes)
}
.map_err(|_| Error::MalformedPacket)?;
decoded.push((audio.pcm, audio.channels));
}
let samples_per_channel = decoded
.first()
.map(|(pcm, ch)| pcm.len() / (*ch).max(1) as usize)
.unwrap_or(0);
for (pcm, ch) in &decoded[1..] {
let spc = pcm.len() / (*ch).max(1) as usize;
if spc != samples_per_channel {
return Err(Error::MalformedPacket);
}
}
let c = self.mapping.output_channels() as usize;
let mut out = vec![0i16; samples_per_channel * c];
for (out_ch, &index) in self.mapping.mapping.iter().enumerate() {
if index == 255 {
continue;
}
let (stream_idx, chan_in_stream) = if (index as usize) < 2 * coupled {
(index as usize / 2, index as usize % 2)
} else {
((index as usize) - coupled, 0usize)
};
let (pcm, ch) = &decoded[stream_idx];
let src_channels = (*ch as usize).max(1);
let src_chan = if chan_in_stream < src_channels {
chan_in_stream
} else {
0
};
for sample in 0..samples_per_channel {
let src_idx = sample * src_channels + src_chan;
let v = pcm.get(src_idx).copied().unwrap_or(0);
out[sample * c + out_ch] = v;
}
}
Ok(MultistreamAudio {
pcm: out,
channels: self.mapping.output_channels(),
sample_rate_hz: crate::decoder::OUTPUT_SAMPLE_RATE_HZ,
samples_per_channel,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MultistreamAudio {
pub pcm: Vec<i16>,
pub channels: u8,
pub sample_rate_hz: u32,
pub samples_per_channel: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::toc::OpusTocByte;
fn sd_code0(config: u8, frame: &[u8]) -> Vec<u8> {
let toc = (config << 3) & 0xF8; let mut v = vec![toc];
assert!(frame.len() < 252, "test helper only emits 1-byte lengths");
v.push(frame.len() as u8);
v.extend_from_slice(frame);
v
}
fn regular_code0(config: u8, frame: &[u8]) -> Vec<u8> {
let toc = (config << 3) & 0xF8;
let mut v = vec![toc];
v.extend_from_slice(frame);
v
}
#[test]
fn single_stream_is_whole_packet() {
let pkt = regular_code0(1, &[1, 2, 3, 4]);
let streams = split_multistream_packet(&pkt, 1).unwrap();
assert_eq!(streams.len(), 1);
assert!(!streams[0].self_delimited);
assert_eq!(streams[0].bytes, pkt.as_slice());
}
#[test]
fn two_streams_split_at_self_delim_boundary() {
let s0 = sd_code0(1, &[0xAA, 0xBB]);
let s1 = regular_code0(1, &[0xCC, 0xDD, 0xEE]);
let mut pkt = s0.clone();
pkt.extend_from_slice(&s1);
let streams = split_multistream_packet(&pkt, 2).unwrap();
assert_eq!(streams.len(), 2);
assert!(streams[0].self_delimited);
assert_eq!(streams[0].bytes, s0.as_slice());
assert!(!streams[1].self_delimited);
assert_eq!(streams[1].bytes, s1.as_slice());
assert_eq!(
OpusTocByte::from_byte(streams[0].bytes[0]).frame_size_tenths_ms,
OpusTocByte::from_byte(streams[1].bytes[0]).frame_size_tenths_ms
);
}
#[test]
fn four_streams_5_1_layout() {
let s0 = sd_code0(1, &[1]);
let s1 = sd_code0(1, &[2, 2]);
let s2 = sd_code0(1, &[3, 3, 3]);
let s3 = regular_code0(1, &[4, 4, 4, 4]);
let mut pkt = Vec::new();
for s in [&s0, &s1, &s2] {
pkt.extend_from_slice(s);
}
pkt.extend_from_slice(&s3);
let streams = split_multistream_packet(&pkt, 4).unwrap();
assert_eq!(streams.len(), 4);
assert_eq!(streams[0].bytes, s0.as_slice());
assert_eq!(streams[1].bytes, s1.as_slice());
assert_eq!(streams[2].bytes, s2.as_slice());
assert_eq!(streams[3].bytes, s3.as_slice());
assert!(streams[3].bytes.starts_with(&[(1u8 << 3) & 0xF8]));
}
#[test]
fn zero_stream_count_rejected() {
assert_eq!(
split_multistream_packet(&[0x08, 1, 2], 0),
Err(Error::MalformedPacket)
);
}
#[test]
fn missing_final_stream_rejected() {
let s0 = sd_code0(1, &[0xAA, 0xBB]);
assert_eq!(
split_multistream_packet(&s0, 2),
Err(Error::MalformedPacket)
);
}
#[test]
fn truncated_self_delim_prefix_rejected() {
let bad = vec![(1u8 << 3) & 0xF8, 200, 1, 2]; assert_eq!(
split_multistream_packet(&bad, 2),
Err(Error::MalformedPacket)
);
}
#[test]
fn assemble_split_roundtrip_mixed_codes() {
use crate::frames::OpusPacket;
use crate::framing_self_delim::parse_self_delimited;
use crate::packet_compose::{compose_packet, compose_packet_code3};
use crate::toc::{Bandwidth, FrameCountCode, Mode};
let toc2 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::TwoUnequal,
)
.unwrap();
let toc3 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
true,
FrameCountCode::Arbitrary,
)
.unwrap();
let fa: &[u8] = &[1, 2, 3];
let fb: &[u8] = &[4, 5, 6, 7, 8];
let p0 = compose_packet(toc2, &[fa, fb]).unwrap();
let p1 = compose_packet_code3(toc3, &[fb, fa], true, 300).unwrap();
let p2 = compose_packet_code3(toc3, &[fa, fa], false, 0).unwrap();
let assembled = assemble_multistream_packet(&[&p0, &p1, &p2]).unwrap();
let streams = split_multistream_packet(&assembled, 3).unwrap();
assert_eq!(streams.len(), 3);
assert!(streams[0].self_delimited && streams[1].self_delimited);
assert!(!streams[2].self_delimited);
let s0 = parse_self_delimited(streams[0].bytes).unwrap();
assert_eq!(s0.packet.frames(), &[fa, fb]);
assert_eq!(s0.packet.padding, 0);
let s1 = parse_self_delimited(streams[1].bytes).unwrap();
assert_eq!(s1.packet.frames(), &[fb, fa]);
assert_eq!(s1.packet.padding, 300);
assert_eq!(streams[2].bytes, p2.as_slice());
let s2 = OpusPacket::parse(streams[2].bytes).unwrap();
assert_eq!(s2.frames(), &[fa, fa]);
}
#[test]
fn assemble_rejects_mismatch_and_garbage() {
use crate::packet_compose::compose_packet;
use crate::toc::{Bandwidth, FrameCountCode, Mode};
assert_eq!(
assemble_multistream_packet(&[]),
Err(Error::MalformedPacket)
);
let toc_20 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::One,
)
.unwrap();
let toc_40 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
400,
false,
FrameCountCode::One,
)
.unwrap();
let a = compose_packet(toc_20, &[&[1, 2][..]]).unwrap();
let b = compose_packet(toc_40, &[&[3, 4][..]]).unwrap();
assert_eq!(
assemble_multistream_packet(&[&a, &b]),
Err(Error::MalformedPacket)
);
let toc_c1 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::TwoEqual,
)
.unwrap();
let c = compose_packet(toc_c1, &[&[1, 2][..], &[3, 4][..]]).unwrap();
assert_eq!(
assemble_multistream_packet(&[&a, &c]),
Err(Error::MalformedPacket)
);
assert_eq!(
assemble_multistream_packet(&[&a, &[][..]]),
Err(Error::EmptyPacket)
);
}
}