use crate::toc::{FrameCountCode, OpusTocByte};
use crate::Error;
pub const MAX_FRAME_BYTES: usize = 1275;
pub const MAX_FRAMES_PER_PACKET: u8 = 48;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpusPacket<'a> {
pub toc: OpusTocByte,
frames: Vec<&'a [u8]>,
pub padding: usize,
}
impl<'a> OpusPacket<'a> {
pub fn parse(packet: &'a [u8]) -> Result<Self, Error> {
if packet.is_empty() {
return Err(Error::EmptyPacket);
}
let toc = OpusTocByte::from_byte(packet[0]);
let body = &packet[1..];
let (frames, padding) = match toc.frame_count_code {
FrameCountCode::One => parse_code0(body)?,
FrameCountCode::TwoEqual => parse_code1(body)?,
FrameCountCode::TwoUnequal => parse_code2(body)?,
FrameCountCode::Arbitrary => parse_code3(body)?,
};
Ok(Self {
toc,
frames,
padding,
})
}
pub fn frames(&self) -> &[&'a [u8]] {
&self.frames
}
pub fn frame_count(&self) -> usize {
self.frames.len()
}
pub(crate) fn new_self_delim(toc: OpusTocByte, frames: Vec<&'a [u8]>, padding: usize) -> Self {
Self {
toc,
frames,
padding,
}
}
}
pub(crate) fn decode_length(bytes: &[u8]) -> Result<(usize, usize), Error> {
let first = *bytes.first().ok_or(Error::MalformedPacket)? as usize;
if first < 252 {
Ok((first, 1))
} else {
let second = *bytes.get(1).ok_or(Error::MalformedPacket)? as usize;
Ok((second * 4 + first, 2))
}
}
fn parse_code0(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
if body.len() > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
Ok((vec![body], 0))
}
fn parse_code1(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
if body.len() % 2 != 0 {
return Err(Error::MalformedPacket);
}
let half = body.len() / 2;
if half > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
Ok((vec![&body[..half], &body[half..]], 0))
}
fn parse_code2(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
let (n1, consumed) = decode_length(body)?;
if n1 > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
let after_len = &body[consumed..];
if n1 > after_len.len() {
return Err(Error::MalformedPacket);
}
let frame1 = &after_len[..n1];
let frame2 = &after_len[n1..];
if frame2.len() > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
Ok((vec![frame1, frame2], 0))
}
fn parse_code3(body: &[u8]) -> Result<(Vec<&[u8]>, usize), Error> {
let fc = *body.first().ok_or(Error::MalformedPacket)?;
let v_bit = fc & 0x01 != 0;
let p_bit = fc & 0x02 != 0;
let m = fc >> 2;
if m == 0 || m > MAX_FRAMES_PER_PACKET {
return Err(Error::MalformedPacket);
}
let mut cursor = 1usize;
let mut padding_bytes: usize = 0;
let mut padding_header_bytes: usize = 0;
if p_bit {
loop {
let byte = *body.get(cursor).ok_or(Error::MalformedPacket)? as usize;
cursor += 1;
padding_header_bytes += 1;
if byte == 255 {
padding_bytes += 254;
} else {
padding_bytes += byte;
break;
}
}
}
let total_padding = padding_bytes + padding_header_bytes;
if total_padding + 1 > body.len() {
return Err(Error::MalformedPacket);
}
let m = m as usize;
let mut frames: Vec<&[u8]> = Vec::with_capacity(m);
if v_bit {
let mut declared_lengths: Vec<usize> = Vec::with_capacity(m.saturating_sub(1));
for _ in 0..m.saturating_sub(1) {
let (n, consumed) = decode_length(&body[cursor..])?;
if n > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
declared_lengths.push(n);
cursor += consumed;
}
let remaining = body.len() - cursor;
let declared_sum: usize = declared_lengths.iter().copied().sum();
if declared_sum + padding_bytes > remaining {
return Err(Error::MalformedPacket);
}
let last_len = remaining - declared_sum - padding_bytes;
if last_len > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
for n in declared_lengths {
frames.push(&body[cursor..cursor + n]);
cursor += n;
}
frames.push(&body[cursor..cursor + last_len]);
} else {
let r = body.len() - cursor - padding_bytes;
if r % m != 0 {
return Err(Error::MalformedPacket);
}
let per = r / m;
if per > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
for _ in 0..m {
frames.push(&body[cursor..cursor + per]);
cursor += per;
}
}
Ok((frames, padding_bytes))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::toc::{ChannelMapping, FrameCountCode};
fn toc(config: u8, stereo: bool, code: u8) -> u8 {
(config << 3) | ((stereo as u8) << 2) | (code & 0x03)
}
#[test]
fn length_single_byte_values_0_through_251() {
for value in [0u8, 1, 100, 200, 251] {
let (len, n) = decode_length(&[value]).unwrap();
assert_eq!(len, value as usize);
assert_eq!(n, 1);
}
}
#[test]
fn length_two_byte_boundary() {
let (len, n) = decode_length(&[252, 0]).unwrap();
assert_eq!(len, 252);
assert_eq!(n, 2);
let (len, n) = decode_length(&[255, 255]).unwrap();
assert_eq!(len, MAX_FRAME_BYTES);
assert_eq!(n, 2);
let (len, n) = decode_length(&[253, 10]).unwrap();
assert_eq!(len, 293);
assert_eq!(n, 2);
}
#[test]
fn length_two_byte_missing_second_rejects() {
assert_eq!(decode_length(&[252]), Err(Error::MalformedPacket));
assert_eq!(decode_length(&[255]), Err(Error::MalformedPacket));
}
#[test]
fn length_empty_rejects() {
assert_eq!(decode_length(&[]), Err(Error::MalformedPacket));
}
#[test]
fn code0_single_frame_round_trip() {
let mut packet = vec![toc(1, false, 0)];
packet.extend_from_slice(&[0xAA, 0xBB, 0xCC, 0xDD]);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 1);
assert_eq!(parsed.frames()[0], &[0xAA, 0xBB, 0xCC, 0xDD]);
assert_eq!(parsed.padding, 0);
assert_eq!(parsed.toc.frame_count_code, FrameCountCode::One);
}
#[test]
fn code0_toc_only_yields_empty_frame() {
let packet = [toc(1, false, 0)];
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 1);
assert!(parsed.frames()[0].is_empty());
}
#[test]
fn code0_rejects_oversize_frame() {
let mut packet = vec![toc(1, false, 0)];
packet.resize(1 + MAX_FRAME_BYTES + 1, 0);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code1_two_equal_split_at_midpoint() {
let mut packet = vec![toc(1, false, 1)];
packet.extend_from_slice(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66]);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert_eq!(parsed.frames()[0], &[0x11, 0x22, 0x33]);
assert_eq!(parsed.frames()[1], &[0x44, 0x55, 0x66]);
assert_eq!(parsed.toc.frame_count_code, FrameCountCode::TwoEqual);
}
#[test]
fn code1_rejects_odd_body_per_r3() {
let mut packet = vec![toc(1, false, 1)];
packet.extend_from_slice(&[0x11, 0x22, 0x33]);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code1_toc_only_two_empty_frames() {
let packet = [toc(1, false, 1)];
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert!(parsed.frames()[0].is_empty());
assert!(parsed.frames()[1].is_empty());
}
#[test]
fn code2_single_byte_length() {
let mut packet = vec![toc(1, false, 2), 3];
packet.extend_from_slice(&[0xA1, 0xA2, 0xA3]); packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]); let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert_eq!(parsed.frames()[0], &[0xA1, 0xA2, 0xA3]);
assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
}
#[test]
fn code2_two_byte_length() {
let n1 = 268usize;
let mut packet = vec![toc(1, false, 2), 252, 4];
packet.extend(std::iter::repeat(0xAA).take(n1));
packet.extend_from_slice(&[0xBB; 5]); let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert_eq!(parsed.frames()[0].len(), n1);
assert!(parsed.frames()[0].iter().all(|&b| b == 0xAA));
assert_eq!(parsed.frames()[1], &[0xBB; 5]);
}
#[test]
fn code2_one_byte_packet_invalid() {
let packet = [toc(1, false, 2)];
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code2_two_byte_packet_only_legal_when_lengths_are_zero() {
let packet = [toc(1, false, 2), 0];
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert!(parsed.frames()[0].is_empty());
assert!(parsed.frames()[1].is_empty());
let packet = [toc(1, false, 2), 5];
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
let packet = [toc(1, false, 2), 253];
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code2_n1_exceeds_remaining_rejected_per_r4() {
let mut packet = vec![toc(1, false, 2), 10];
packet.extend_from_slice(&[0; 5]);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code3_cbr_no_padding() {
let m: u8 = 3;
let fc = m << 2; let mut packet = vec![toc(15, false, 3), fc];
packet.extend_from_slice(&[0xA1, 0xA2, 0xA3, 0xA4]);
packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]);
packet.extend_from_slice(&[0xC1, 0xC2, 0xC3, 0xC4]);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 3);
assert_eq!(parsed.frames()[0], &[0xA1, 0xA2, 0xA3, 0xA4]);
assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
assert_eq!(parsed.frames()[2], &[0xC1, 0xC2, 0xC3, 0xC4]);
assert_eq!(parsed.padding, 0);
}
#[test]
fn code3_cbr_rejects_non_multiple_of_m_per_r6() {
let fc = 3 << 2;
let mut packet = vec![toc(15, false, 3), fc];
packet.extend_from_slice(&[0; 5]);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code3_cbr_with_padding() {
let m: u8 = 2;
let fc = (m << 2) | 0b10; let mut packet = vec![toc(15, false, 3), fc, 5];
packet.extend_from_slice(&[0xD1, 0xD2, 0xD3]);
packet.extend_from_slice(&[0xE1, 0xE2, 0xE3]);
packet.extend_from_slice(&[0x00; 5]);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 2);
assert_eq!(parsed.frames()[0], &[0xD1, 0xD2, 0xD3]);
assert_eq!(parsed.frames()[1], &[0xE1, 0xE2, 0xE3]);
assert_eq!(parsed.padding, 5);
}
#[test]
fn code3_vbr_no_padding() {
let m: u8 = 3;
let fc = (m << 2) | 0b01;
let mut packet = vec![toc(15, false, 3), fc, 2, 4];
packet.extend_from_slice(&[0xA1, 0xA2]);
packet.extend_from_slice(&[0xB1, 0xB2, 0xB3, 0xB4]);
packet.push(0xC1);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 3);
assert_eq!(parsed.frames()[0], &[0xA1, 0xA2]);
assert_eq!(parsed.frames()[1], &[0xB1, 0xB2, 0xB3, 0xB4]);
assert_eq!(parsed.frames()[2], &[0xC1]);
assert_eq!(parsed.padding, 0);
}
#[test]
fn code3_vbr_with_padding() {
let m: u8 = 4;
let fc = (m << 2) | 0b11; let pad_len = 7u8;
let sizes = [99u8, 77, 66, 68];
let mut packet = vec![toc(15, false, 3), fc, pad_len];
packet.extend_from_slice(&sizes[..3]);
for &n in sizes.iter() {
packet.extend(std::iter::repeat(0xAA).take(n as usize));
}
packet.extend_from_slice(&[0u8; 7]);
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 4);
assert_eq!(parsed.frames()[0].len(), 99);
assert_eq!(parsed.frames()[1].len(), 77);
assert_eq!(parsed.frames()[2].len(), 66);
assert_eq!(parsed.frames()[3].len(), 68);
assert_eq!(parsed.padding, 7);
assert_eq!(parsed.toc.config, 15);
assert_eq!(parsed.toc.channels, ChannelMapping::Mono);
assert_eq!(parsed.toc.frame_count_code, FrameCountCode::Arbitrary);
}
#[test]
fn code3_padding_chain_255_extension() {
let m: u8 = 1;
let fc = (m << 2) | 0b10; let mut packet = vec![toc(15, false, 3), fc, 255, 3];
packet.extend_from_slice(&[0xF1, 0xF2, 0xF3, 0xF4]);
packet.extend(std::iter::repeat(0u8).take(257));
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 1);
assert_eq!(parsed.frames()[0], &[0xF1, 0xF2, 0xF3, 0xF4]);
assert_eq!(parsed.padding, 257);
}
#[test]
fn code3_rejects_m_zero_per_r5() {
let fc = 0; let packet = [toc(15, false, 3), fc];
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code3_rejects_missing_frame_count_byte() {
let packet = [toc(15, false, 3)];
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code3_rejects_padding_overrunning_body() {
let fc = (1 << 2) | 0b10;
let mut packet = vec![toc(15, false, 3), fc, 200];
packet.extend_from_slice(&[0; 5]);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn code3_vbr_rejects_lengths_exceeding_remaining() {
let fc = (3 << 2) | 0b01;
let mut packet = vec![toc(15, false, 3), fc, 100, 100];
packet.extend_from_slice(&[0; 50]);
assert_eq!(OpusPacket::parse(&packet), Err(Error::MalformedPacket));
}
#[test]
fn empty_packet_rejected() {
assert_eq!(OpusPacket::parse(&[]), Err(Error::EmptyPacket));
}
#[test]
fn code3_vbr_max_frame_count_48() {
let m: u8 = MAX_FRAMES_PER_PACKET;
let fc = (m << 2) | 0b01;
let mut packet = vec![toc(16, false, 3), fc];
packet.extend(std::iter::repeat(1u8).take(47)); packet.extend(std::iter::repeat(0xCC).take(48));
let parsed = OpusPacket::parse(&packet).unwrap();
assert_eq!(parsed.frame_count(), 48);
for f in parsed.frames() {
assert_eq!(f, &[0xCC]);
}
}
}