mod decoder;
mod encoder;
mod layout;
pub(crate) mod rate_alloc;
pub(crate) mod surround;
pub use decoder::MultistreamDecoder;
pub use encoder::MultistreamEncoder;
pub use layout::{ChannelLayout, MappingType, SILENT_CHANNEL, validate_ambisonics};
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::Error;
use crate::{float2int16, float2int24, int16_to_res, int24_to_res, packet};
pub(crate) trait ChannelSource {
fn copy(&self, dst: &mut [f32], channel: usize, frame_size: usize);
}
pub(crate) trait Sample: Copy {
const ZERO: Self;
const LSB_DEPTH: i32;
const FLOAT_API: bool;
fn analysis_pcm(pcm: &[Self]) -> crate::analysis::PcmRef<'_>;
fn to_f32(self) -> f32;
fn from_f32(v: f32) -> Self;
}
impl Sample for f32 {
const ZERO: f32 = 0.0;
const LSB_DEPTH: i32 = 24;
const FLOAT_API: bool = true;
#[inline(always)]
fn analysis_pcm(pcm: &[f32]) -> crate::analysis::PcmRef<'_> {
crate::analysis::PcmRef::F32(pcm)
}
#[inline(always)]
fn to_f32(self) -> f32 {
self
}
#[inline(always)]
fn from_f32(v: f32) -> f32 {
v
}
}
impl Sample for i16 {
const ZERO: i16 = 0;
const LSB_DEPTH: i32 = 16;
const FLOAT_API: bool = false;
#[inline(always)]
fn analysis_pcm(pcm: &[i16]) -> crate::analysis::PcmRef<'_> {
crate::analysis::PcmRef::I16(pcm)
}
#[inline(always)]
fn to_f32(self) -> f32 {
int16_to_res(self)
}
#[inline(always)]
fn from_f32(v: f32) -> i16 {
float2int16(v)
}
}
impl Sample for i32 {
const ZERO: i32 = 0;
const LSB_DEPTH: i32 = 24;
const FLOAT_API: bool = false;
#[inline(always)]
fn analysis_pcm(pcm: &[i32]) -> crate::analysis::PcmRef<'_> {
crate::analysis::PcmRef::I32(pcm)
}
#[inline(always)]
fn to_f32(self) -> f32 {
int24_to_res(self)
}
#[inline(always)]
fn from_f32(v: f32) -> i32 {
float2int24(v)
}
}
pub(crate) struct Interleaved<'a, T> {
pub(crate) pcm: &'a [T],
pub(crate) channels: usize,
}
impl<T: Sample> Interleaved<'_, T> {
#[inline]
pub(crate) fn copy_pair(&self, dst: &mut [f32], left: usize, right: usize, frame_size: usize) {
if left >= self.channels || right >= self.channels {
return;
}
let pairs = dst.as_chunks_mut::<2>().0.iter_mut();
let frames = self.pcm.chunks_exact(self.channels);
for (d, frame) in pairs.zip(frames).take(frame_size) {
*d = [frame[left].to_f32(), frame[right].to_f32()];
}
}
}
impl<T: Sample> ChannelSource for Interleaved<'_, T> {
#[inline]
fn copy(&self, dst: &mut [f32], channel: usize, frame_size: usize) {
if channel >= self.channels {
return;
}
let frames = self.pcm.chunks_exact(self.channels);
for (d, frame) in dst.iter_mut().zip(frames).take(frame_size) {
*d = frame[channel].to_f32();
}
}
}
pub(crate) trait ChannelSink {
fn put(&mut self, dst_channel: usize, src: Option<(&[f32], usize, usize)>, frame_size: usize);
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct ChannelMappingTable {
pub channels: u8,
pub stream_count: u8,
pub coupled_count: u8,
pub mapping: Vec<u8>,
}
impl ChannelMappingTable {
pub fn new(channels: u8, stream_count: u8, coupled_count: u8, mapping: &[u8]) -> Self {
Self {
channels,
stream_count,
coupled_count,
mapping: mapping.to_vec(),
}
}
pub fn parse(mapping_family: u8, channels: u8, tail: &[u8]) -> Option<Self> {
if mapping_family == 0 || tail.len() < 2 + channels as usize {
return None;
}
Some(Self {
channels,
stream_count: tail[0],
coupled_count: tail[1],
mapping: tail[2..2 + channels as usize].to_vec(),
})
}
}
pub fn split_self_delimited<'a>(
data: &[u8],
scratch: &'a mut Vec<u8>,
) -> Result<(&'a [u8], usize), Error> {
const INVALID: Error = Error::INVALID_PACKET;
let parsed = packet::parse(data, packet::Framing::SelfDelimited)?;
let last_size = parsed.frame_sizes().last().copied().ok_or(INVALID)?;
let field_len = packet::size_field_len(last_size);
let header_end = parsed.payload_offset().checked_sub(field_len).ok_or(INVALID)?;
let header = data.get(..header_end).ok_or(INVALID)?;
let frames_and_padding = data.get(parsed.payload_offset()..parsed.packet_offset()).ok_or(INVALID)?;
scratch.clear();
scratch.extend_from_slice(header);
scratch.extend_from_slice(frames_and_padding);
Ok((scratch.as_slice(), parsed.packet_offset()))
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::packet::{Framing, Padding};
fn toc_byte(config: u8, stereo: bool, code: u8) -> u8 {
(config << 3) | ((stereo as u8) << 2) | (code & 0x03)
}
#[test]
fn interleaved_input_gives_one_channel_or_a_pair() {
let pcm: Vec<f32> = (0..12).map(|k| (10 * (k % 3) + k / 3) as f32).collect();
let src = Interleaved {
pcm: &pcm,
channels: 3,
};
let mut mono = [-1.0f32; 6];
src.copy(&mut mono, 2, 4);
assert_eq!(mono, [20.0, 21.0, 22.0, 23.0, -1.0, -1.0]);
let mut stereo = [-1.0f32; 10];
src.copy_pair(&mut stereo, 0, 1, 4);
assert_eq!(stereo, [0.0, 10.0, 1.0, 11.0, 2.0, 12.0, 3.0, 13.0, -1.0, -1.0]);
src.copy_pair(&mut stereo, 2, 0, 2);
assert_eq!(&stereo[..4], [20.0, 0.0, 21.0, 1.0]);
}
#[test]
fn interleaved_input_never_reads_or_writes_out_of_range() {
let pcm = [1.0f32; 7];
let src = Interleaved {
pcm: &pcm,
channels: 2,
};
let mut dst = [0.0f32; 2];
src.copy(&mut dst, 1, 100);
assert_eq!(dst, [1.0, 1.0]);
let mut dst = [0.0f32; 5];
src.copy_pair(&mut dst, 0, 1, 100);
assert_eq!(dst, [1.0, 1.0, 1.0, 1.0, 0.0]);
let mut dst = [0.0f32; 4];
src.copy(&mut dst, 2, 2);
src.copy_pair(&mut dst, 0, 2, 2);
src.copy_pair(&mut dst, 2, 1, 2);
assert_eq!(dst, [0.0; 4]);
}
#[test]
fn sixteen_bit_input_is_scaled_by_32768() {
let pcm = [i16::MIN, -1, 0, 1, 16384, i16::MAX];
let src = Interleaved {
pcm: &pcm,
channels: 1,
};
let mut dst = [9.0f32; 6];
src.copy(&mut dst, 0, 6);
assert_eq!(
dst,
[
-1.0,
-1.0 / 32768.0,
0.0,
1.0 / 32768.0,
0.5,
32767.0 / 32768.0
]
);
}
#[test]
fn mapping_table_parses_5_1_shape() {
let tail = [4u8, 2, 0, 1, 4, 5, 2, 3];
let table = ChannelMappingTable::parse(1, 6, &tail).unwrap();
assert_eq!(table.stream_count, 4);
assert_eq!(table.coupled_count, 2);
assert_eq!(table.mapping, vec![0, 1, 4, 5, 2, 3]);
}
#[test]
fn mapping_family_zero_has_no_table() {
assert!(ChannelMappingTable::parse(0, 2, &[9, 9, 9, 9]).is_none());
}
#[test]
fn mapping_table_rejects_truncated_tail() {
assert!(ChannelMappingTable::parse(1, 6, &[4, 2, 0, 1]).is_none());
}
#[test]
fn split_code0_single_frame() {
let bytes = [toc_byte(0, false, 0), 3, 0xAA, 0xBB, 0xCC, 0xFF];
let mut scratch = Vec::new();
let (pkt, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 1 + 3);
assert_eq!(pkt, &[toc_byte(0, false, 0), 0xAA, 0xBB, 0xCC][..]);
}
#[test]
fn split_code0_two_byte_length() {
let mut bytes = vec![toc_byte(0, false, 0), 252, 0];
bytes.extend(std::iter::repeat_n(0x7Fu8, 252));
let mut scratch = Vec::new();
let (_, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 2 + 252);
}
#[test]
fn split_code1_equal_frames() {
let bytes = [toc_byte(0, false, 1), 2, 0xAA, 0xBB, 0xCC, 0xDD, 0x11];
let mut scratch = Vec::new();
let (_, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 1 + 4);
}
#[test]
fn split_code2_two_lengths() {
let bytes = [toc_byte(0, false, 2), 3, 2, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE];
let mut scratch = Vec::new();
let (pkt, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 1 + 1 + 3 + 2);
assert_eq!(pkt, &[toc_byte(0, false, 2), 3, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..]);
}
#[test]
fn split_code3_cbr_three_frames() {
let bytes = [toc_byte(0, false, 3), 3, 2, 0x10, 0x11, 0x20, 0x21, 0x30, 0x31];
let mut scratch = Vec::new();
let (_, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 1 + 1 + 6);
}
#[test]
fn split_code3_cbr_with_padding() {
let fc = 0x40 | 2u8;
let bytes = [
toc_byte(0, false, 3), fc, 2, 3,
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x55, 0x66,
];
let mut scratch = Vec::new();
let (_, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 12);
}
#[test]
fn split_code3_vbr_three_frames() {
let fc = 0x80 | 3u8;
let bytes = [
toc_byte(0, false, 3), fc, 2, 3, 1,
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
];
let mut scratch = Vec::new();
let (pkt, consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(consumed, 1 + 1 + 1 + 1 + 1 + 2 + 3 + 1);
assert_eq!(pkt, &[toc_byte(0, false, 3), fc, 2, 3, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF][..]);
}
#[test]
fn split_two_streams_chained() {
let bytes = [
toc_byte(0, false, 0), 2, 0xA0, 0xA1,
toc_byte(0, false, 1), 1, 0xB0, 0xB1,
];
let mut scratch = Vec::new();
let a_consumed = {
let (a, a_consumed) = split_self_delimited(&bytes, &mut scratch).unwrap();
assert_eq!(a, &[toc_byte(0, false, 0), 0xA0, 0xA1][..]);
a_consumed
};
assert_eq!(a_consumed, 4);
let (_, b_consumed) = split_self_delimited(&bytes[a_consumed..], &mut scratch).unwrap();
assert_eq!(b_consumed, 4);
}
#[test]
fn split_truncated_frame_is_error() {
let bytes = [toc_byte(0, false, 0), 4, 0xAA, 0xBB];
let mut scratch = Vec::new();
assert!(split_self_delimited(&bytes, &mut scratch).is_err());
}
#[test]
fn split_code3_zero_frames_is_error() {
let bytes = [toc_byte(0, false, 3), 0, 1, 0xAA];
let mut scratch = Vec::new();
assert!(split_self_delimited(&bytes, &mut scratch).is_err());
}
#[test]
fn split_gives_the_packet_that_packet_write_gives() {
let big = [0x5Au8; 600];
let cases: [(&[&[u8]], Padding); 9] = [
(&[&big[..0]], Padding::Shortest),
(&[&big[..1]], Padding::Shortest),
(&[&big[..251]], Padding::Shortest),
(&[&big[..252]], Padding::Shortest),
(&[&big[..600]], Padding::Shortest),
(&[&big[..300], &big[..300]], Padding::Shortest),
(&[&big[..7], &big[..300]], Padding::Shortest),
(&[&big[..3], &big[..3], &big[..3]], Padding::FillBuffer),
(&[&big[..10], &big[..253], &big[..0], &big[..40]], Padding::FillBuffer),
];
let toc = toc_byte(0, false, 0);
for (frames, pad) in cases {
let mut delimited = [0u8; 2000];
let mut ordinary = [0u8; 2000];
let (len_d, len_o) = if pad == Padding::FillBuffer {
let total: usize = frames.iter().map(|f| f.len()).sum();
(total + 40, total + 37)
} else {
(2000, 2000)
};
let n_d = packet::write(toc, frames, &mut delimited[..len_d], Framing::SelfDelimited, pad).unwrap();
let n_o = packet::write(toc, frames, &mut ordinary[..len_o], Framing::Normal, pad).unwrap();
delimited[n_d..n_d + 3].copy_from_slice(&[0xEE; 3]);
let mut scratch = vec![1, 2, 3];
let (pkt, consumed) = split_self_delimited(&delimited[..n_d + 3], &mut scratch).unwrap();
assert_eq!(consumed, n_d, "{} frames", frames.len());
let parsed = packet::parse(pkt, Framing::Normal).unwrap();
assert_eq!(parsed.nb_frames(), frames.len());
for (i, frame) in frames.iter().enumerate() {
assert_eq!(parsed.frame(pkt, i), *frame, "frame {i}");
}
if pad == Padding::Shortest {
assert_eq!(pkt, &ordinary[..n_o], "{} frames", frames.len());
}
}
}
#[test]
fn split_rejects_what_libopus_rejects() {
let mut scratch = Vec::new();
assert!(split_self_delimited(&[], &mut scratch).is_err());
assert!(split_self_delimited(&[toc_byte(0, false, 0)], &mut scratch).is_err());
assert!(split_self_delimited(&[toc_byte(0, false, 0), 252], &mut scratch).is_err());
let mut long = vec![toc_byte(0, false, 3), 13, 1];
long.extend_from_slice(&[0u8; 13]);
assert!(split_self_delimited(&long, &mut scratch).is_err());
}
}