use crate::{Bandwidth, Channels, Error, SampleRate};
use crate::extensions::{self, Extension, ExtensionIterator, ExtensionList};
use crate::fixedvec::FixedVec;
pub const MAX_FRAMES: usize = 48;
pub const MAX_FRAME_BYTES: usize = 1275;
pub(crate) const MAX_PACKET_SAMPLES: usize = max_packet_samples(48000);
#[inline]
pub(crate) const fn max_packet_samples(fs: i32) -> usize {
fs as usize / 25 * 3
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Framing {
Normal,
SelfDelimited,
}
impl Framing {
pub(crate) const fn from_flag(self_delimited: bool) -> Self {
if self_delimited { Framing::SelfDelimited } else { Framing::Normal }
}
const fn is_self_delimited(self) -> bool {
matches!(self, Framing::SelfDelimited)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Padding {
Shortest,
FillBuffer,
}
impl Padding {
pub(crate) const fn from_flag(pad: bool) -> Self {
if pad { Padding::FillBuffer } else { Padding::Shortest }
}
pub(crate) const fn fills_buffer(self) -> bool {
matches!(self, Padding::FillBuffer)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ParsedPacket {
toc: u8,
count: usize,
sizes: [u16; MAX_FRAMES],
payload_offset: usize,
packet_offset: usize,
padding_len: usize,
}
impl ParsedPacket {
pub fn toc(&self) -> u8 {
self.toc
}
pub fn nb_frames(&self) -> usize {
self.count
}
pub fn frame_sizes(&self) -> &[u16] {
&self.sizes[..self.count]
}
pub fn payload_offset(&self) -> usize {
self.payload_offset
}
pub fn packet_offset(&self) -> usize {
self.packet_offset
}
pub fn padding_len(&self) -> usize {
self.padding_len
}
pub fn frames<'a>(&self, data: &'a [u8]) -> Frames<'a, '_> {
Frames {
data,
sizes: &self.sizes[..self.count],
offset: self.payload_offset,
}
}
pub fn frame<'a>(&self, data: &'a [u8], index: usize) -> &'a [u8] {
self.frames(data).nth(index).unwrap_or(&[])
}
pub fn padding<'a>(&self, data: &'a [u8]) -> &'a [u8] {
data.get(self.packet_offset - self.padding_len..self.packet_offset).unwrap_or(&[])
}
}
#[derive(Debug, Clone)]
pub struct Frames<'a, 'p> {
data: &'a [u8],
sizes: &'p [u16],
offset: usize,
}
impl<'a> Iterator for Frames<'a, '_> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
let (&size, rest) = self.sizes.split_first()?;
self.sizes = rest;
let start = self.offset;
self.offset += size as usize;
Some(self.data.get(start..self.offset).unwrap_or(&[]))
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.sizes.len(), Some(self.sizes.len()))
}
}
impl ExactSizeIterator for Frames<'_, '_> {}
pub fn samples_per_frame(toc: u8, sampling_rate: SampleRate) -> usize {
let fs = sampling_rate.hz() as i64;
let audiosize = if toc & 0x80 != 0 {
(fs << ((toc >> 3) & 0x3)) / 400
} else if toc & 0x60 == 0x60 {
if toc & 0x08 != 0 { fs / 50 } else { fs / 100 }
} else {
let shift = (toc >> 3) & 0x3;
if shift == 3 { fs * 60 / 1000 } else { (fs << shift) / 100 }
};
audiosize as usize
}
pub fn bandwidth(toc: u8) -> Bandwidth {
if toc & 0x80 != 0 {
match (toc >> 5) & 0x3 {
0 => Bandwidth::Narrowband,
1 => Bandwidth::Wideband,
2 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
}
} else if toc & 0x60 == 0x60 {
if toc & 0x10 != 0 { Bandwidth::Fullband } else { Bandwidth::Superwideband }
} else {
match (toc >> 5) & 0x3 {
0 => Bandwidth::Narrowband,
1 => Bandwidth::Mediumband,
_ => Bandwidth::Wideband,
}
}
}
pub fn nb_channels(toc: u8) -> Channels {
if toc & 0x4 != 0 { Channels::Stereo } else { Channels::Mono }
}
pub fn nb_frames(data: &[u8]) -> Result<usize, Error> {
let Some(&toc) = data.first() else {
return Err(Error::BAD_ARG);
};
match toc & 0x3 {
0 => Ok(1),
1 | 2 => Ok(2),
_ => match data.get(1) {
Some(&count) => Ok((count & 0x3F) as usize),
None => Err(Error::INVALID_PACKET),
},
}
}
pub fn nb_samples(data: &[u8], sampling_rate: SampleRate) -> Result<usize, Error> {
let samples = nb_frames(data)? * samples_per_frame(data[0], sampling_rate);
if samples as i64 * 25 > sampling_rate.hz() as i64 * 3 {
return Err(Error::INVALID_PACKET);
}
Ok(samples)
}
pub fn has_lbrr(data: &[u8]) -> Result<bool, Error> {
let Some(&toc) = data.first() else {
return Err(Error::INVALID_PACKET);
};
if toc & 0x80 != 0 {
return Ok(false);
}
let frame_size = samples_per_frame(toc, SampleRate::Hz48000);
let nb_frames = if frame_size > 960 { frame_size / 960 } else { 1 };
let stereo = toc & 0x4 != 0;
let parsed = parse(data, Framing::Normal)?;
let Some(&first) = parsed.frame(data, 0).first() else {
return Ok(false);
};
let mut lbrr = (first >> (7 - nb_frames)) & 1 != 0;
if stereo {
lbrr = lbrr || (first >> (6 - 2 * nb_frames)) & 1 != 0;
}
Ok(lbrr)
}
#[inline]
pub(crate) fn size_field_len(size: u16) -> usize {
1 + (size >= 252) as usize
}
#[inline]
fn encode_size(size: u16, out: &mut [u8]) -> usize {
if size < 252 {
out[0] = size as u8;
1
} else {
let first = 252 + (size & 0x3);
out[0] = first as u8;
out[1] = ((size - first) >> 2) as u8;
2
}
}
#[inline]
fn parse_size(data: &[u8], pos: usize, len: i64) -> Option<(i64, i64)> {
if len < 1 {
return None;
}
let first = *data.get(pos)? as i64;
if first < 252 {
Some((1, first))
} else if len < 2 {
None
} else {
Some((2, 4 * (*data.get(pos + 1)? as i64) + first))
}
}
pub fn parse(data: &[u8], framing: Framing) -> Result<ParsedPacket, Error> {
let self_delimited = framing.is_self_delimited();
let Some(&toc) = data.first() else {
return Err(Error::INVALID_PACKET);
};
let framesize = samples_per_frame(toc, SampleRate::Hz48000);
let mut pos = 1usize;
let mut len = data.len() as i64 - 1;
let mut last_size = len;
let mut cbr = false;
let mut pad = 0i64;
let mut sizes = [0u16; MAX_FRAMES];
let count: usize;
match toc & 0x3 {
0 => count = 1,
1 => {
count = 2;
cbr = true;
if !self_delimited {
if len & 0x1 != 0 {
return Err(Error::INVALID_PACKET);
}
last_size = len / 2;
}
}
2 => {
count = 2;
let Some((bytes, size)) = parse_size(data, pos, len) else {
return Err(Error::INVALID_PACKET);
};
len -= bytes;
if size > len {
return Err(Error::INVALID_PACKET);
}
pos += bytes as usize;
sizes[0] = size as u16;
last_size = len - size;
}
_ => {
if len < 1 {
return Err(Error::INVALID_PACKET);
}
let ch = data[pos];
pos += 1;
count = (ch & 0x3F) as usize;
if count == 0 || framesize * count > MAX_PACKET_SAMPLES {
return Err(Error::INVALID_PACKET);
}
len -= 1;
if ch & 0x40 != 0 {
loop {
if len <= 0 {
return Err(Error::INVALID_PACKET);
}
let p = data[pos];
pos += 1;
len -= 1;
let tmp = if p == 255 { 254 } else { p as i64 };
len -= tmp;
pad += tmp;
if p != 255 {
break;
}
}
}
if len < 0 {
return Err(Error::INVALID_PACKET);
}
cbr = ch & 0x80 == 0;
if !cbr {
last_size = len;
for size_out in sizes.iter_mut().take(count - 1) {
let Some((bytes, size)) = parse_size(data, pos, len) else {
return Err(Error::INVALID_PACKET);
};
len -= bytes;
if size > len {
return Err(Error::INVALID_PACKET);
}
pos += bytes as usize;
*size_out = size as u16;
last_size -= bytes + size;
}
if last_size < 0 {
return Err(Error::INVALID_PACKET);
}
} else if !self_delimited {
last_size = len / count as i64;
if last_size * count as i64 != len {
return Err(Error::INVALID_PACKET);
}
}
}
}
if self_delimited {
let Some((bytes, size)) = parse_size(data, pos, len) else {
return Err(Error::INVALID_PACKET);
};
len -= bytes;
if size > len {
return Err(Error::INVALID_PACKET);
}
pos += bytes as usize;
if cbr {
if size * count as i64 > len {
return Err(Error::INVALID_PACKET);
}
sizes[..count].fill(size as u16);
} else {
if bytes + size > last_size {
return Err(Error::INVALID_PACKET);
}
sizes[count - 1] = size as u16;
}
} else {
if last_size > MAX_FRAME_BYTES as i64 {
return Err(Error::INVALID_PACKET);
}
if cbr {
sizes[..count].fill(last_size as u16);
} else {
sizes[count - 1] = last_size as u16;
}
}
let frame_bytes: usize = sizes[..count].iter().map(|&s| s as usize).sum();
Ok(ParsedPacket {
toc,
count,
sizes,
payload_offset: pos,
packet_offset: pad as usize + pos + frame_bytes,
padding_len: pad as usize,
})
}
pub fn multistream_validate(
data: &[u8],
nb_streams: usize,
sampling_rate: SampleRate,
) -> Result<usize, Error> {
if nb_streams == 0 {
return Err(Error::BAD_ARG);
}
let mut rest = data;
let mut samples = 0usize;
for s in 0..nb_streams {
if rest.is_empty() {
return Err(Error::INVALID_PACKET);
}
let packet = parse(rest, Framing::from_flag(s != nb_streams - 1))?;
let tmp_samples = packet.count * samples_per_frame(packet.toc, sampling_rate);
if tmp_samples as i64 * 25 > sampling_rate.hz() as i64 * 3 {
return Err(Error::INVALID_PACKET);
}
if s != 0 && samples != tmp_samples {
return Err(Error::INVALID_PACKET);
}
samples = tmp_samples;
rest = rest.get(packet.packet_offset..).ok_or(Error::INVALID_PACKET)?;
}
Ok(samples)
}
struct Layout {
code: u8,
vbr: bool,
pad_amount: usize,
header_len: usize,
ext_len: usize,
total: usize,
}
fn layout(
sizes: &[u16],
maxlen: usize,
self_delimited: bool,
pad: bool,
extensions: &[Extension],
) -> Result<Layout, Error> {
let count = sizes.len();
let payload: usize = sizes.iter().map(|&s| s as usize).sum();
let last_size_len = if self_delimited { size_field_len(sizes[count - 1]) } else { 0 };
let mut code = 3u8;
let mut header_len = 0usize;
if count == 1 {
code = 0;
header_len = 1;
} else if count == 2 {
if sizes[1] == sizes[0] {
code = 1;
header_len = 1;
} else {
code = 2;
header_len = 1 + size_field_len(sizes[0]);
}
}
header_len += last_size_len;
if count <= 2 && header_len + payload > maxlen {
return Err(Error::BUFFER_TOO_SMALL);
}
let mut vbr = false;
let mut pad_amount = 0usize;
let mut ext_len = 0usize;
if count > 2 || (pad && header_len + payload < maxlen) || !extensions.is_empty() {
code = 3;
vbr = sizes[1..].iter().any(|&s| s != sizes[0]);
header_len = 2 + last_size_len;
if vbr {
header_len += sizes[..count - 1].iter().map(|&s| size_field_len(s)).sum::<usize>();
}
let tot_size = header_len + payload;
if tot_size > maxlen {
return Err(Error::BUFFER_TOO_SMALL);
}
if pad {
pad_amount = maxlen - tot_size;
}
if !extensions.is_empty() {
ext_len = extensions::generated_len(maxlen - tot_size, extensions, count)?;
if !pad {
pad_amount = ext_len + if ext_len > 0 { ext_len.div_ceil(254) } else { 1 };
}
}
if pad_amount != 0 {
let nb_255s = (pad_amount - 1) / 255;
if tot_size + ext_len + nb_255s + 1 > maxlen {
return Err(Error::BUFFER_TOO_SMALL);
}
header_len += nb_255s + 1;
}
return Ok(Layout { code, vbr, pad_amount, header_len, ext_len, total: tot_size + pad_amount });
}
Ok(Layout { code, vbr, pad_amount, header_len, ext_len, total: header_len + payload })
}
fn write_header(toc: u8, sizes: &[u16], plan: &Layout, self_delimited: bool, out: &mut [u8]) {
let count = sizes.len();
out[0] = (toc & 0xFC) | plan.code;
let mut pos = 1usize;
if plan.code == 2 {
pos += encode_size(sizes[0], &mut out[pos..]);
} else if plan.code == 3 {
out[1] = count as u8 | if plan.vbr { 0x80 } else { 0 };
pos = 2;
if plan.pad_amount != 0 {
out[1] |= 0x40;
let nb_255s = (plan.pad_amount - 1) / 255;
out[pos..pos + nb_255s].fill(255);
pos += nb_255s;
out[pos] = (plan.pad_amount - 255 * nb_255s - 1) as u8;
pos += 1;
}
if plan.vbr {
for &size in &sizes[..count - 1] {
pos += encode_size(size, &mut out[pos..]);
}
}
}
if self_delimited {
pos += encode_size(sizes[count - 1], &mut out[pos..]);
}
debug_assert_eq!(pos, plan.header_len);
}
fn emit(
toc: u8,
sizes: &[u16],
out: &mut [u8],
self_delimited: bool,
pad: bool,
extensions: &[Extension],
place_frames: impl FnOnce(&mut [u8], usize),
) -> Result<usize, Error> {
let plan = layout(sizes, out.len(), self_delimited, pad, extensions)?;
place_frames(out, plan.header_len);
write_header(toc, sizes, &plan, self_delimited, out);
let pos = plan.header_len + sizes.iter().map(|&s| s as usize).sum::<usize>();
if !extensions.is_empty() {
let ext_begin = plan.total - plan.ext_len;
out[pos..ext_begin].fill(0x01);
let written = extensions::generate(&mut out[ext_begin..plan.total], extensions, sizes.len(), Padding::Shortest);
debug_assert_eq!(written, Ok(plan.ext_len));
} else if pad {
out[pos..].fill(0);
}
Ok(plan.total)
}
fn frame_sizes(toc: u8, frames: &[&[u8]]) -> Result<[u16; MAX_FRAMES], Error> {
let count = frames.len();
if count == 0
|| count > MAX_FRAMES
|| count * samples_per_frame(toc, SampleRate::Hz48000) > MAX_PACKET_SAMPLES
{
return Err(Error::BAD_ARG);
}
let mut sizes = [0u16; MAX_FRAMES];
for (size, frame) in sizes.iter_mut().zip(frames) {
if frame.len() > MAX_FRAME_BYTES {
return Err(Error::BAD_ARG);
}
*size = frame.len() as u16;
}
Ok(sizes)
}
fn copy_frames(frames: &[&[u8]], out: &mut [u8], at: usize) {
let mut pos = at;
for frame in frames {
out[pos..pos + frame.len()].copy_from_slice(frame);
pos += frame.len();
}
}
pub fn write(
toc: u8,
frames: &[&[u8]],
out: &mut [u8],
framing: Framing,
padding: Padding,
) -> Result<usize, Error> {
let sizes = frame_sizes(toc, frames)?;
let sizes = &sizes[..frames.len()];
emit(toc, sizes, out, framing.is_self_delimited(), padding.fills_buffer(), &[], |out, at| {
copy_frames(frames, out, at)
})
}
#[derive(Clone)]
pub struct Repacketizer<'a> {
toc: u8,
nb_frames: usize,
frames: [&'a [u8]; MAX_FRAMES],
paddings: [&'a [u8]; MAX_FRAMES],
padding_nb_frames: [u8; MAX_FRAMES],
}
impl core::fmt::Debug for Repacketizer<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Repacketizer")
.field("toc", &self.toc)
.field("nb_frames", &self.nb_frames)
.finish_non_exhaustive()
}
}
impl Default for Repacketizer<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Repacketizer<'a> {
pub const fn new() -> Self {
Repacketizer {
toc: 0,
nb_frames: 0,
frames: [&[]; MAX_FRAMES],
paddings: [&[]; MAX_FRAMES],
padding_nb_frames: [0; MAX_FRAMES],
}
}
pub fn reset(&mut self) {
self.nb_frames = 0;
}
pub fn nb_frames(&self) -> usize {
self.nb_frames
}
pub fn cat(&mut self, data: &'a [u8]) -> Result<(), Error> {
let Some(&toc) = data.first() else {
return Err(Error::INVALID_PACKET);
};
if self.nb_frames == 0 {
self.toc = toc;
} else if (self.toc & 0xFC) != (toc & 0xFC) {
return Err(Error::INVALID_PACKET);
}
let curr_nb_frames = nb_frames(data)?;
if curr_nb_frames < 1 {
return Err(Error::INVALID_PACKET);
}
if (curr_nb_frames + self.nb_frames) * samples_per_frame(self.toc, SampleRate::Hz8000) > 960 {
return Err(Error::INVALID_PACKET);
}
let packet = parse(data, Framing::Normal)?;
let n = self.nb_frames;
for (k, frame) in packet.frames(data).enumerate() {
self.frames[n + k] = frame;
self.paddings[n + k] = &[];
self.padding_nb_frames[n + k] = 0;
}
self.paddings[n] = packet.padding(data);
self.padding_nb_frames[n] = packet.count as u8;
self.nb_frames += packet.count;
Ok(())
}
pub fn out_range(&self, begin: usize, end: usize, out: &mut [u8]) -> Result<usize, Error> {
self.out_range_ext(begin, end, out, Framing::Normal, Padding::Shortest, &[])
}
pub fn out(&self, out: &mut [u8]) -> Result<usize, Error> {
self.out_range_ext(0, self.nb_frames, out, Framing::Normal, Padding::Shortest, &[])
}
pub fn out_range_ext(
&self,
begin: usize,
end: usize,
out: &mut [u8],
framing: Framing,
padding: Padding,
extensions: &[Extension],
) -> Result<usize, Error> {
let (self_delimited, pad) = (framing.is_self_delimited(), padding.fills_buffer());
if begin >= end || end > self.nb_frames {
return Err(Error::BAD_ARG);
}
let frames = &self.frames[begin..end];
let mut sizes = [0u16; MAX_FRAMES];
for (size, frame) in sizes.iter_mut().zip(frames) {
*size = frame.len() as u16;
}
let sizes = &sizes[..frames.len()];
let mut found = 0;
for i in begin..end {
found += extensions::count_checked(self.paddings[i], self.padding_nb_frames[i] as usize)
.map_err(|_| Error::INTERNAL_ERROR)?;
}
let place = |out: &mut [u8], at: usize| copy_frames(frames, out, at);
if found == 0 {
return emit(self.toc, sizes, out, self_delimited, pad, extensions, place);
}
let mut all = ExtensionList::with_capacity(extensions.len() + found)?;
for &ext in extensions {
all.push(ext);
}
for i in begin..end {
let iter = ExtensionIterator::new(self.paddings[i], self.padding_nb_frames[i] as usize);
for mut ext in iter.map_while(Result::ok) {
ext.frame += i - begin;
all.push(ext);
}
}
emit(self.toc, sizes, out, self_delimited, pad, all.as_slice(), place)
}
}
pub fn pad(data: &mut [u8], len: usize, new_len: usize) -> Result<(), Error> {
pad_with_extensions(data, len, new_len, Padding::FillBuffer, &[]).map(|_| ())
}
pub fn pad_with_extensions(
data: &mut [u8],
len: usize,
new_len: usize,
padding: Padding,
extensions: &[Extension],
) -> Result<usize, Error> {
let pad = padding.fills_buffer();
if len < 1 || len > data.len() {
return Err(Error::BAD_ARG);
}
if len == new_len && extensions.is_empty() {
return Ok(len);
}
if len > new_len || new_len > data.len() {
return Err(Error::BAD_ARG);
}
let packet = parse(&data[..len], Framing::Normal)?;
let found = extensions::count_checked(packet.padding(&data[..len]), packet.count).map_err(|_| Error::INTERNAL_ERROR)?;
if found == 0 {
return pad_in_place(data, new_len, &packet, pad, extensions);
}
pad_keeping_extensions(data, new_len, &packet, pad, extensions, found)
}
fn pad_in_place(
data: &mut [u8],
new_len: usize,
packet: &ParsedPacket,
pad: bool,
extensions: &[Extension],
) -> Result<usize, Error> {
let sizes = &packet.sizes[..packet.count];
let start = packet.payload_offset;
let payload: usize = sizes.iter().map(|&s| s as usize).sum();
emit(packet.toc, sizes, &mut data[..new_len], false, pad, extensions, |out, at| {
out.copy_within(start..start + payload, at)
})
}
const PADDING_COPY: usize = MAX_FRAME_BYTES;
#[inline(never)]
fn pad_keeping_extensions(
data: &mut [u8],
new_len: usize,
packet: &ParsedPacket,
pad: bool,
extensions: &[Extension],
found: usize,
) -> Result<usize, Error> {
let old = packet.padding(data);
let mut buf: FixedVec<u8, PADDING_COPY> = FixedVec::new();
#[cfg(feature = "alloc")]
let owned;
let copy: &[u8] = if old.len() <= PADDING_COPY {
buf.extend_from_slice(old);
&buf
} else {
#[cfg(feature = "alloc")]
{
owned = old.to_vec();
&owned
}
#[cfg(not(feature = "alloc"))]
return Err(Error::new(
crate::ErrorKind::AllocFail,
"more than 1275 bytes of padding need the alloc feature",
));
};
let mut all = ExtensionList::with_capacity(extensions.len() + found)?;
for &ext in extensions {
all.push(ext);
}
for ext in ExtensionIterator::new(copy, packet.count).map_while(Result::ok) {
all.push(ext);
}
pad_in_place(data, new_len, packet, pad, all.as_slice())
}
pub fn unpad(data: &mut [u8], len: usize) -> Result<usize, Error> {
if len < 1 || len > data.len() {
return Err(Error::BAD_ARG);
}
let packet = parse(&data[..len], Framing::Normal)?;
pad_in_place(data, len, &packet, false, &[])
}
pub fn multistream_pad(
data: &mut [u8],
len: usize,
new_len: usize,
nb_streams: usize,
) -> Result<(), Error> {
if len < 1 || len > data.len() {
return Err(Error::BAD_ARG);
}
if len == new_len {
return Ok(());
}
if len > new_len || new_len > data.len() {
return Err(Error::BAD_ARG);
}
let amount = new_len - len;
let mut start = 0usize;
for _ in 1..nb_streams {
if start >= len {
return Err(Error::INVALID_PACKET);
}
start += parse(&data[start..len], Framing::SelfDelimited)?.packet_offset;
}
let last_len = len.checked_sub(start).ok_or(Error::INVALID_PACKET)?;
pad(&mut data[start..], last_len, last_len + amount)
}
pub fn multistream_unpad(data: &mut [u8], len: usize, nb_streams: usize) -> Result<usize, Error> {
if len < 1 || len > data.len() {
return Err(Error::BAD_ARG);
}
let mut src = 0usize;
let mut dst = 0usize;
for s in 0..nb_streams {
let self_delimited = s != nb_streams - 1;
if src >= len {
return Err(Error::INVALID_PACKET);
}
let packet = parse(&data[src..len], Framing::from_flag(self_delimited))?;
let sizes = &packet.sizes[..packet.count];
let start = src - dst + packet.payload_offset;
let payload: usize = sizes.iter().map(|&s| s as usize).sum();
let written = emit(packet.toc, sizes, &mut data[dst..len], self_delimited, false, &[], |out, at| {
out.copy_within(start..start + payload, at)
})?;
dst += written;
src += packet.packet_offset;
}
Ok(dst)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
const TOC_CELT_20: u8 = 0xF8;
const TOC_CELT_2_5: u8 = 0xE0;
#[test]
fn samples_per_frame_covers_every_configuration() {
assert_eq!(samples_per_frame(0x00, SampleRate::Hz48000), 480);
assert_eq!(samples_per_frame(0x08, SampleRate::Hz48000), 960);
assert_eq!(samples_per_frame(0x10, SampleRate::Hz48000), 1920);
assert_eq!(samples_per_frame(0x18, SampleRate::Hz48000), 2880);
assert_eq!(samples_per_frame(0x60, SampleRate::Hz16000), 160);
assert_eq!(samples_per_frame(0x68, SampleRate::Hz16000), 320);
assert_eq!(samples_per_frame(0x80, SampleRate::Hz8000), 20);
assert_eq!(samples_per_frame(0x88, SampleRate::Hz8000), 40);
assert_eq!(samples_per_frame(0x90, SampleRate::Hz8000), 80);
assert_eq!(samples_per_frame(0x98, SampleRate::Hz8000), 160);
}
#[test]
fn code_0_is_one_frame_to_the_end() {
let p = parse(&[TOC_CELT_20, 1, 2, 3], Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.payload_offset, p.packet_offset), (1, 3, 1, 4));
let p = parse(&[TOC_CELT_20], Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.packet_offset), (1, 0, 1));
assert!(parse(&[], Framing::Normal).is_err());
}
#[test]
fn code_1_needs_an_even_payload() {
let p = parse(&[TOC_CELT_20 | 1, 1, 2, 3, 4], Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.sizes[1]), (2, 2, 2));
assert_eq!(p.frame(&[TOC_CELT_20 | 1, 1, 2, 3, 4], 1), &[3, 4]);
assert!(parse(&[TOC_CELT_20 | 1, 1, 2, 3], Framing::Normal).is_err());
}
#[test]
fn code_2_reads_the_first_size() {
let data = [TOC_CELT_20 | 2, 1, 9, 8, 7];
let p = parse(&data, Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.sizes[1], p.payload_offset), (2, 1, 2, 2));
let frames: Vec<&[u8]> = p.frames(&data).collect();
assert_eq!(frames, [&[9u8][..], &[8, 7][..]]);
assert!(parse(&[TOC_CELT_20 | 2, 4, 9, 8, 7], Framing::Normal).is_err());
assert!(parse(&[TOC_CELT_20 | 2], Framing::Normal).is_err());
}
#[test]
fn code_3_checks_count_duration_and_padding() {
assert!(parse(&[TOC_CELT_20 | 3, 0], Framing::Normal).is_err());
assert!(parse(&[TOC_CELT_20 | 3, 7], Framing::Normal).is_err());
assert_eq!(parse(&[TOC_CELT_2_5 | 3, 48], Framing::Normal).unwrap().count, 48);
assert!(parse(&[TOC_CELT_2_5 | 3, 49], Framing::Normal).is_err());
assert!(parse(&[TOC_CELT_20 | 3, 2, 1, 2, 3], Framing::Normal).is_err());
let p = parse(&[TOC_CELT_20 | 3, 0x41, 2, 7, 0, 0], Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.padding_len, p.packet_offset), (1, 1, 2, 6));
assert!(parse(&[TOC_CELT_20 | 3, 0x41, 9, 7], Framing::Normal).is_err());
assert!(parse(&[TOC_CELT_20 | 3, 0x41], Framing::Normal).is_err());
}
#[test]
fn self_delimited_packet_ends_before_the_next_stream() {
let data = [TOC_CELT_20, 2, 5, 6, 0xAA, 0xBB, 0xCC];
let p = parse(&data, Framing::SelfDelimited).unwrap();
assert_eq!((p.count, p.sizes[0], p.payload_offset, p.packet_offset), (1, 2, 2, 4));
let data = [TOC_CELT_20 | 1, 2, 5, 6, 7, 8, 0xAA];
let p = parse(&data, Framing::SelfDelimited).unwrap();
assert_eq!((p.count, p.sizes[0], p.sizes[1], p.packet_offset), (2, 2, 2, 6));
assert!(parse(&[TOC_CELT_20, 3, 5, 6], Framing::SelfDelimited).is_err());
assert!(parse(&[TOC_CELT_20 | 1, 2, 5, 6, 7], Framing::SelfDelimited).is_err());
}
#[test]
fn a_frame_may_not_exceed_1275_bytes() {
let mut data = vec![0u8; 1 + 1276];
data[0] = TOC_CELT_20;
assert!(parse(&data, Framing::Normal).is_err());
assert_eq!(parse(&data[..1276], Framing::Normal).unwrap().sizes[0], 1275);
}
#[test]
fn write_picks_the_frame_count_code() {
let mut out = [0u8; 64];
let a = [1u8, 2, 3];
let b = [4u8, 5, 6];
let c = [7u8];
let n = write(TOC_CELT_20 | 3, &[&a], &mut out, Framing::Normal, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20, 1, 2, 3]);
let n = write(TOC_CELT_20, &[&a, &b], &mut out, Framing::Normal, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20 | 1, 1, 2, 3, 4, 5, 6]);
let n = write(TOC_CELT_20, &[&a, &c], &mut out, Framing::Normal, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20 | 2, 3, 1, 2, 3, 7]);
let n = write(TOC_CELT_20, &[&a, &b, &a], &mut out, Framing::Normal, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20 | 3, 3, 1, 2, 3, 4, 5, 6, 1, 2, 3]);
let n = write(TOC_CELT_20, &[&a, &c, &b], &mut out, Framing::Normal, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20 | 3, 0x83, 3, 1, 1, 2, 3, 7, 4, 5, 6]);
}
#[test]
fn write_self_delimited_adds_the_last_size() {
let mut out = [0u8; 16];
let n = write(TOC_CELT_20, &[&[9, 9]], &mut out, Framing::SelfDelimited, Padding::Shortest).unwrap();
assert_eq!(&out[..n], &[TOC_CELT_20, 2, 9, 9]);
let p = parse(&out[..n], Framing::SelfDelimited).unwrap();
assert_eq!((p.count, p.sizes[0], p.packet_offset), (1, 2, n));
}
#[test]
fn write_pads_to_the_exact_length() {
let frame = [5u8; 4];
let mut out = [0xEEu8; 6];
assert_eq!(write(TOC_CELT_20, &[&frame], &mut out, Framing::Normal, Padding::FillBuffer), Ok(6));
assert_eq!(out, [TOC_CELT_20 | 3, 1, 5, 5, 5, 5]);
let mut out = [0xEEu8; 5];
assert_eq!(write(TOC_CELT_20, &[&frame], &mut out, Framing::Normal, Padding::FillBuffer), Ok(5));
assert_eq!(out, [TOC_CELT_20, 5, 5, 5, 5]);
let mut out = [0xEEu8; 10];
assert_eq!(write(TOC_CELT_20, &[&frame], &mut out, Framing::Normal, Padding::FillBuffer), Ok(10));
assert_eq!(out, [TOC_CELT_20 | 3, 0x41, 3, 5, 5, 5, 5, 0, 0, 0]);
let p = parse(&out, Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.padding_len), (1, 4, 3));
}
#[test]
fn write_rejects_bad_input_without_panicking() {
let mut out = [0u8; 8];
assert!(write(TOC_CELT_20, &[], &mut out, Framing::Normal, Padding::Shortest).is_err());
assert!(write(TOC_CELT_20, &[&[0; 9]], &mut out, Framing::Normal, Padding::Shortest).is_err());
assert!(write(TOC_CELT_20, &[&[0; 1276]], &mut [0u8; 2000], Framing::Normal, Padding::Shortest).is_err());
let f: &[u8] = &[1];
assert!(write(TOC_CELT_20, &[f; 7], &mut [0u8; 64], Framing::Normal, Padding::Shortest).is_err());
assert!(write(TOC_CELT_20, &[f], &mut [], Framing::Normal, Padding::Shortest).is_err());
}
#[test]
fn pad_moves_the_frames_in_place() {
let mut data = [TOC_CELT_20 | 2, 1, 9, 8, 7, 0, 0, 0, 0, 0];
pad(&mut data, 5, 10).unwrap();
assert_eq!(data, [TOC_CELT_20 | 3, 0xC2, 3, 1, 9, 8, 7, 0, 0, 0]);
let p = parse(&data, Framing::Normal).unwrap();
assert_eq!((p.count, p.sizes[0], p.sizes[1], p.padding_len), (2, 1, 2, 3));
assert_eq!(pad(&mut data, 10, 10), Ok(()));
assert!(pad(&mut data, 10, 9).is_err());
assert!(pad(&mut data, 0, 5).is_err());
assert!(pad(&mut data, 5, 11).is_err());
assert_eq!(pad(&mut [0xFF], 1, 1), Ok(()));
}
#[test]
fn pad_with_extensions_writes_the_extensions_or_fails() {
let ext = [Extension::new(5, 0, &[1])];
let plain = [TOC_CELT_20, 1, 2, 3];
let mut data = plain;
for padding in [Padding::Shortest, Padding::FillBuffer] {
assert_eq!(pad_with_extensions(&mut data, 4, 4, padding, &[]), Ok(4));
assert_eq!(data, plain);
}
for new_len in [4, 5, 7] {
let mut data = [0u8; 8];
data[..4].copy_from_slice(&plain);
let err = pad_with_extensions(&mut data, 4, new_len, Padding::Shortest, &ext).unwrap_err();
assert_eq!(err.kind(), crate::ErrorKind::BufferTooSmall, "new_len {new_len}");
}
let mut data = [0u8; 8];
data[..4].copy_from_slice(&plain);
assert_eq!(pad_with_extensions(&mut data, 4, 8, Padding::Shortest, &ext), Ok(8));
let p = parse(&data, Framing::Normal).unwrap();
let mut found = [Extension::default(); 2];
assert_eq!(extensions::parse(p.padding(&data), &mut found, p.nb_frames()), Ok(1));
assert_eq!(found[0], ext[0]);
for (padding, want) in [(Padding::Shortest, 8), (Padding::FillBuffer, 12)] {
let mut data = [0u8; 12];
data[..4].copy_from_slice(&plain);
pad(&mut data, 4, 12).unwrap();
assert_eq!(pad_with_extensions(&mut data, 12, 12, padding, &ext), Ok(want));
let p = parse(&data[..want], Framing::Normal).unwrap();
assert!(p.frames(&data[..want]).eq([&plain[1..]]));
let mut found = [Extension::default(); 2];
assert_eq!(extensions::parse(p.padding(&data[..want]), &mut found, 1), Ok(1));
assert_eq!(found[0], ext[0]);
}
}
#[test]
fn multistream_validate_wants_equal_durations() {
let ok = [TOC_CELT_20, 1, 7, TOC_CELT_20, 8, 9];
assert_eq!(multistream_validate(&ok, 2, SampleRate::Hz48000), Ok(960));
let bad = [TOC_CELT_20, 1, 7, TOC_CELT_2_5, 8, 9];
assert!(multistream_validate(&bad, 2, SampleRate::Hz48000).is_err());
assert!(multistream_validate(&[TOC_CELT_20, 1, 7], 2, SampleRate::Hz48000).is_err());
assert!(multistream_validate(&ok, 0, SampleRate::Hz48000).is_err());
}
#[test]
fn multistream_pad_pads_the_last_stream() {
let mut data = [TOC_CELT_20, 1, 7, TOC_CELT_20, 8, 9, 0, 0, 0];
multistream_pad(&mut data, 6, 9, 2).unwrap();
assert_eq!(data, [TOC_CELT_20, 1, 7, TOC_CELT_20 | 3, 0x41, 1, 8, 9, 0]);
assert_eq!(multistream_validate(&data, 2, SampleRate::Hz48000), Ok(960));
}
#[test]
fn bandwidth_and_channels_of_every_toc_byte() {
let expected = |config: u8| match config {
0..=3 | 16..=19 => Bandwidth::Narrowband,
4..=7 => Bandwidth::Mediumband,
8..=11 | 20..=23 => Bandwidth::Wideband,
12..=13 | 24..=27 => Bandwidth::Superwideband,
_ => Bandwidth::Fullband,
};
for toc in 0..=255u8 {
assert_eq!(bandwidth(toc), expected(toc >> 3), "TOC {toc:#04x}");
let channels = if toc & 0x04 != 0 { Channels::Stereo } else { Channels::Mono };
assert_eq!(nb_channels(toc), channels, "TOC {toc:#04x}");
}
}
#[test]
fn parsed_packet_methods_give_what_parse_found() {
let data = [TOC_CELT_20 | 3, 0xC3, 2, 1, 2, 9, 8, 7, 6, 0, 0];
let p = parse(&data, Framing::Normal).unwrap();
assert_eq!(p.toc(), TOC_CELT_20 | 3);
assert_eq!(p.nb_frames(), 3);
assert_eq!(p.frame_sizes(), &[1, 2, 1]);
assert_eq!((p.payload_offset(), p.packet_offset(), p.padding_len()), (5, 11, 2));
let frames: Vec<&[u8]> = p.frames(&data).collect();
assert_eq!(frames, [&[9u8][..], &[8, 7][..], &[6][..]]);
assert_eq!(p.padding(&data), &[0, 0]);
let data = [TOC_CELT_20 | 4, 2, 5, 6, 0xAA];
let p = parse(&data, Framing::SelfDelimited).unwrap();
assert_eq!((p.toc(), p.nb_frames(), p.frame_sizes()), (TOC_CELT_20 | 4, 1, &[2u16][..]));
assert_eq!((p.payload_offset(), p.packet_offset(), p.padding_len()), (2, 4, 0));
assert_eq!(nb_channels(p.toc()), Channels::Stereo);
let p = parse(&data, Framing::Normal).unwrap();
assert_eq!((p.frame_sizes(), p.payload_offset(), p.packet_offset()), (&[4u16][..], 1, 5));
}
#[test]
fn parsed_packet_with_other_data_gives_empty_slices() {
let data = [TOC_CELT_20 | 3, 0xC3, 2, 1, 2, 9, 8, 7, 6, 0, 0];
let p = parse(&data, Framing::Normal).unwrap();
for short in [&data[..0], &data[..3], &data[..5]] {
assert_eq!(p.frames(short).count(), 3);
assert!(p.frames(short).all(|f| f.is_empty()));
assert_eq!(p.frame(short, 2), &[] as &[u8]);
assert_eq!(p.padding(short), &[] as &[u8]);
}
let cut = &data[..10];
assert_eq!(p.frame(cut, 2), &[6]);
assert_eq!(p.padding(cut), &[] as &[u8]);
assert_eq!(p.frame(&data, 3), &[] as &[u8]);
assert_eq!(p.frame(&data, usize::MAX), &[] as &[u8]);
}
#[test]
fn framing_and_padding_follow_the_libopus_flags() {
assert_eq!(Framing::from_flag(false), Framing::Normal);
assert_eq!(Framing::from_flag(true), Framing::SelfDelimited);
assert!(Framing::SelfDelimited.is_self_delimited() && !Framing::Normal.is_self_delimited());
assert_eq!(Padding::from_flag(false), Padding::Shortest);
assert_eq!(Padding::from_flag(true), Padding::FillBuffer);
assert!(Padding::FillBuffer.fills_buffer() && !Padding::Shortest.fills_buffer());
}
}