use crate::frames::{MAX_FRAMES_PER_PACKET, MAX_FRAME_BYTES};
use crate::toc::{FrameCountCode, OpusTocByte};
use crate::Error;
pub fn encode_length(length: usize, out: &mut Vec<u8>) -> Result<(), Error> {
if length > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
if length < 252 {
out.push(length as u8);
} else {
let first = 252 + ((length - 252) % 4);
let second = (length - first) / 4;
out.push(first as u8);
out.push(second as u8);
}
Ok(())
}
fn write_padding_chain(mut padding: usize, out: &mut Vec<u8>) {
while padding >= 255 {
out.push(255);
padding -= 254;
}
out.push(padding as u8);
}
fn validate_shape(toc_byte: u8, frames: &[&[u8]]) -> Result<OpusTocByte, Error> {
let toc = OpusTocByte::from_byte(toc_byte);
for f in frames {
if f.len() > MAX_FRAME_BYTES {
return Err(Error::MalformedPacket);
}
}
match toc.frame_count_code {
FrameCountCode::One => {
if frames.len() != 1 {
return Err(Error::MalformedPacket);
}
}
FrameCountCode::TwoEqual => {
if frames.len() != 2 || frames[0].len() != frames[1].len() {
return Err(Error::MalformedPacket);
}
}
FrameCountCode::TwoUnequal => {
if frames.len() != 2 {
return Err(Error::MalformedPacket);
}
}
FrameCountCode::Arbitrary => {
let m = frames.len();
if m == 0 || m > MAX_FRAMES_PER_PACKET as usize {
return Err(Error::MalformedPacket);
}
if m as u32 * toc.frame_size_tenths_ms as u32 > 1200 {
return Err(Error::MalformedPacket);
}
}
}
Ok(toc)
}
pub fn compose_packet(toc_byte: u8, frames: &[&[u8]]) -> Result<Vec<u8>, Error> {
let toc = validate_shape(toc_byte, frames)?;
match toc.frame_count_code {
FrameCountCode::One => Ok([&[toc_byte], frames[0]].concat()),
FrameCountCode::TwoEqual => Ok([&[toc_byte], frames[0], frames[1]].concat()),
FrameCountCode::TwoUnequal => {
let mut out = Vec::with_capacity(3 + frames[0].len() + frames[1].len());
out.push(toc_byte);
encode_length(frames[0].len(), &mut out)?;
out.extend_from_slice(frames[0]);
out.extend_from_slice(frames[1]);
Ok(out)
}
FrameCountCode::Arbitrary => {
let cbr = frames.iter().all(|f| f.len() == frames[0].len());
compose_packet_code3(toc_byte, frames, !cbr, 0)
}
}
}
pub fn compose_packet_code3(
toc_byte: u8,
frames: &[&[u8]],
vbr: bool,
padding: usize,
) -> Result<Vec<u8>, Error> {
let toc = validate_shape(toc_byte, frames)?;
if toc.frame_count_code != FrameCountCode::Arbitrary {
return Err(Error::MalformedPacket);
}
if !vbr && frames.iter().any(|f| f.len() != frames[0].len()) {
return Err(Error::MalformedPacket);
}
let m = frames.len();
let mut out = Vec::new();
out.push(toc_byte);
out.push(((m as u8) << 2) | (u8::from(padding > 0) << 1) | u8::from(vbr));
if padding > 0 {
write_padding_chain(padding, &mut out);
}
if vbr {
for f in &frames[..m - 1] {
encode_length(f.len(), &mut out)?;
}
}
for f in frames {
out.extend_from_slice(f);
}
out.resize(out.len() + padding, 0);
Ok(out)
}
pub fn compose_self_delimited(
toc_byte: u8,
frames: &[&[u8]],
vbr: bool,
padding: usize,
) -> Result<Vec<u8>, Error> {
let toc = validate_shape(toc_byte, frames)?;
if toc.frame_count_code != FrameCountCode::Arbitrary && (vbr || padding > 0) {
return Err(Error::MalformedPacket);
}
let mut out = Vec::new();
out.push(toc_byte);
match toc.frame_count_code {
FrameCountCode::One => {
encode_length(frames[0].len(), &mut out)?;
out.extend_from_slice(frames[0]);
}
FrameCountCode::TwoEqual => {
encode_length(frames[0].len(), &mut out)?;
out.extend_from_slice(frames[0]);
out.extend_from_slice(frames[1]);
}
FrameCountCode::TwoUnequal => {
encode_length(frames[0].len(), &mut out)?;
encode_length(frames[1].len(), &mut out)?;
out.extend_from_slice(frames[0]);
out.extend_from_slice(frames[1]);
}
FrameCountCode::Arbitrary => {
if !vbr && frames.iter().any(|f| f.len() != frames[0].len()) {
return Err(Error::MalformedPacket);
}
let m = frames.len();
out.push(((m as u8) << 2) | (u8::from(padding > 0) << 1) | u8::from(vbr));
if padding > 0 {
write_padding_chain(padding, &mut out);
}
if vbr {
for f in &frames[..m - 1] {
encode_length(f.len(), &mut out)?;
}
encode_length(frames[m - 1].len(), &mut out)?;
} else {
encode_length(frames[0].len(), &mut out)?;
}
for f in frames {
out.extend_from_slice(f);
}
out.resize(out.len() + padding, 0);
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frames::{decode_length, OpusPacket};
use crate::framing_self_delim::parse_self_delimited;
use crate::toc::{Bandwidth, Mode};
struct Lcg(u64);
impl Lcg {
fn next_u32(&mut self) -> u32 {
self.0 = self
.0
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(self.0 >> 32) as u32
}
fn below(&mut self, n: u32) -> u32 {
self.next_u32() % n
}
}
fn random_frame(rng: &mut Lcg, max_len: u32) -> Vec<u8> {
let len = rng.below(max_len + 1) as usize;
(0..len).map(|_| rng.next_u32() as u8).collect()
}
fn toc(code: FrameCountCode) -> u8 {
OpusTocByte::compose_byte(Mode::CeltOnly, Bandwidth::Fb, 100, false, code).unwrap()
}
#[test]
fn encode_length_roundtrips_all_values() {
for len in 0..=MAX_FRAME_BYTES {
let mut buf = Vec::new();
encode_length(len, &mut buf).unwrap();
let (decoded, consumed) = decode_length(&buf).unwrap();
assert_eq!((decoded, consumed), (len, buf.len()), "length {len}");
}
let mut buf = Vec::new();
assert!(encode_length(MAX_FRAME_BYTES + 1, &mut buf).is_err());
}
#[test]
fn compose_parse_roundtrip_all_codes() {
let mut rng = Lcg(0x0385_C0DE);
for round in 0..200 {
let (toc_byte, frames): (u8, Vec<Vec<u8>>) = match rng.below(4) {
0 => (toc(FrameCountCode::One), vec![random_frame(&mut rng, 1275)]),
1 => {
let f = random_frame(&mut rng, 1275);
(toc(FrameCountCode::TwoEqual), vec![f.clone(), f])
}
2 => (
toc(FrameCountCode::TwoUnequal),
vec![random_frame(&mut rng, 1275), random_frame(&mut rng, 1275)],
),
_ => {
let m = 1 + rng.below(12) as usize;
(
toc(FrameCountCode::Arbitrary),
(0..m).map(|_| random_frame(&mut rng, 300)).collect(),
)
}
};
let slices: Vec<&[u8]> = frames.iter().map(|f| f.as_slice()).collect();
let packet = compose_packet(toc_byte, &slices).expect("compose");
let parsed = OpusPacket::parse(&packet).expect("parse");
assert_eq!(
parsed.toc,
OpusTocByte::from_byte(toc_byte),
"round {round}"
);
assert_eq!(parsed.frames(), &slices[..], "round {round}");
assert_eq!(parsed.padding, 0, "round {round}");
}
}
#[test]
fn compose_code3_vbr_and_padding_roundtrip() {
let mut rng = Lcg(0x0AD5_0385);
let toc_byte = toc(FrameCountCode::Arbitrary);
for &padding in &[0usize, 1, 42, 253, 254, 255, 300, 600] {
let m = 1 + rng.below(6) as usize;
let frames: Vec<Vec<u8>> = (0..m).map(|_| random_frame(&mut rng, 200)).collect();
let slices: Vec<&[u8]> = frames.iter().map(|f| f.as_slice()).collect();
for vbr in [false, true] {
if !vbr && !slices.iter().all(|f| f.len() == slices[0].len()) {
continue;
}
let packet =
compose_packet_code3(toc_byte, &slices, vbr, padding).expect("compose");
let parsed = OpusPacket::parse(&packet).expect("parse");
assert_eq!(parsed.frames(), &slices[..], "vbr={vbr} padding={padding}");
assert_eq!(parsed.padding, padding, "vbr={vbr} padding={padding}");
}
}
}
#[test]
fn compose_self_delimited_roundtrip_and_chain() {
let mut rng = Lcg(0x5E1F_DE11);
let mut chained = Vec::new();
let mut expected: Vec<(u8, Vec<Vec<u8>>)> = Vec::new();
for code_pick in 0..4u32 {
let (toc_byte, frames, vbr, padding): (u8, Vec<Vec<u8>>, bool, usize) = match code_pick
{
0 => (
toc(FrameCountCode::One),
vec![random_frame(&mut rng, 400)],
false,
0,
),
1 => {
let f = random_frame(&mut rng, 400);
(toc(FrameCountCode::TwoEqual), vec![f.clone(), f], false, 0)
}
2 => (
toc(FrameCountCode::TwoUnequal),
vec![random_frame(&mut rng, 400), random_frame(&mut rng, 400)],
false,
0,
),
_ => (
toc(FrameCountCode::Arbitrary),
(0..5).map(|_| random_frame(&mut rng, 300)).collect(),
true,
77,
),
};
let slices: Vec<&[u8]> = frames.iter().map(|f| f.as_slice()).collect();
let packet =
compose_self_delimited(toc_byte, &slices, vbr, padding).expect("compose sd");
let parsed = parse_self_delimited(&packet).expect("parse sd");
assert_eq!(parsed.consumed, packet.len(), "code {code_pick}");
assert_eq!(parsed.packet.frames(), &slices[..], "code {code_pick}");
assert_eq!(parsed.packet.padding, padding, "code {code_pick}");
chained.extend_from_slice(&packet);
expected.push((toc_byte, frames));
}
let mut cursor = 0usize;
for (toc_byte, frames) in &expected {
let parsed = parse_self_delimited(&chained[cursor..]).expect("chained parse");
assert_eq!(parsed.packet.toc, OpusTocByte::from_byte(*toc_byte));
let slices: Vec<&[u8]> = frames.iter().map(|f| f.as_slice()).collect();
assert_eq!(parsed.packet.frames(), &slices[..]);
cursor += parsed.consumed;
}
assert_eq!(cursor, chained.len());
}
#[test]
fn compose_rejects_shape_violations() {
let f10 = vec![0u8; 10];
let f11 = vec![0u8; 11];
let big = vec![0u8; MAX_FRAME_BYTES + 1];
assert!(compose_packet(toc(FrameCountCode::One), &[&f10, &f10]).is_err());
assert!(compose_packet(toc(FrameCountCode::TwoEqual), &[&f10]).is_err());
assert!(compose_packet(toc(FrameCountCode::TwoUnequal), &[&f10]).is_err());
assert!(compose_packet(toc(FrameCountCode::Arbitrary), &[]).is_err());
assert!(compose_packet(toc(FrameCountCode::TwoEqual), &[&f10, &f11]).is_err());
assert!(compose_packet(toc(FrameCountCode::One), &[&big]).is_err());
let toc_25 = OpusTocByte::compose_byte(
Mode::CeltOnly,
Bandwidth::Fb,
25,
false,
FrameCountCode::Arbitrary,
)
.unwrap();
let many: Vec<&[u8]> = (0..49).map(|_| f10.as_slice()).collect();
assert!(compose_packet(toc_25, &many).is_err());
assert!(compose_packet(toc_25, &many[..48]).is_ok());
let toc_60 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
600,
false,
FrameCountCode::Arbitrary,
)
.unwrap();
assert!(compose_packet(toc_60, &[&f10, &f10, &f10]).is_err());
assert!(compose_packet(toc_60, &[&f10, &f10]).is_ok());
assert!(
compose_packet_code3(toc(FrameCountCode::Arbitrary), &[&f10, &f11], false, 0).is_err()
);
assert!(compose_packet_code3(toc(FrameCountCode::One), &[&f10], false, 0).is_err());
assert!(compose_self_delimited(toc(FrameCountCode::One), &[&f10], false, 4).is_err());
assert!(compose_self_delimited(toc(FrameCountCode::One), &[&f10], true, 0).is_err());
assert!(
compose_self_delimited(toc(FrameCountCode::Arbitrary), &[&f10, &f11], false, 0)
.is_err()
);
}
struct SilkScript {
frame_type: u8,
gains: Vec<crate::silk_gains::GainSymbol>,
i2: Vec<i8>,
lsb: Vec<u8>,
e_raw: Vec<i32>,
}
impl SilkScript {
fn new(frame_type: u8, pulses: i32) -> Self {
use crate::silk_excitation::{shell_block_count, SilkFrameSize, SHELL_BLOCK_SAMPLES};
use crate::silk_gains::GainSymbol;
assert!(frame_type < 4, "voiced scripts would need LTP symbols");
let blocks = shell_block_count(Bandwidth::Nb, SilkFrameSize::TwentyMs).unwrap();
let mut e_raw = vec![0i32; blocks * SHELL_BLOCK_SAMPLES];
for b in 0..blocks {
e_raw[b * SHELL_BLOCK_SAMPLES] = pulses;
}
SilkScript {
frame_type,
gains: vec![
GainSymbol::Independent(40),
GainSymbol::Delta(10),
GainSymbol::Delta(15),
GainSymbol::Delta(20),
],
i2: vec![0i8; 10],
lsb: vec![0u8; blocks],
e_raw,
}
}
fn symbols(&self) -> crate::silk_decode::SilkFrameSymbols<'_> {
crate::silk_decode::SilkFrameSymbols {
header: crate::silk_frame::SilkHeaderSymbols {
stereo: None,
mid_only_flag: None,
frame_type: self.frame_type,
},
gains: &self.gains,
lsf_stage1: 5,
lsf_stage2_i2: &self.i2,
lsf_interp_w_q2: Some(4),
ltp: None,
lcg_seed: 1,
excitation: crate::silk_excitation::ExcitationSymbols {
rate_level: 3,
lsb_counts: &self.lsb,
e_raw: &self.e_raw,
},
}
}
}
#[test]
fn composed_multiframe_silk_packets_decode_end_to_end() {
use crate::decoder::{FrameDecodeStatus, OpusDecoder};
use crate::silk_packet_encode::encode_silk_only_packet_mono;
let script1 = SilkScript::new(0, 1);
let script2 = SilkScript::new(2, 7);
let (p1, _) = encode_silk_only_packet_mono(Bandwidth::Nb, 200, &[script1.symbols()])
.expect("encode 1");
let (p2, _) = encode_silk_only_packet_mono(Bandwidth::Nb, 200, &[script2.symbols()])
.expect("encode 2");
let body1 = &p1[1..];
let body2 = &p2[1..];
let toc_code2 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::TwoUnequal,
)
.unwrap();
let toc_code3 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::Arbitrary,
)
.unwrap();
let toc_code1 = OpusTocByte::compose_byte(
Mode::SilkOnly,
Bandwidth::Nb,
200,
false,
FrameCountCode::TwoEqual,
)
.unwrap();
let mut candidates: Vec<Vec<u8>> = vec![
compose_packet(toc_code2, &[body1, body2]).unwrap(),
compose_packet_code3(toc_code3, &[body1, body2], true, 9).unwrap(),
];
candidates.push(compose_packet(toc_code1, &[body1, body1]).unwrap());
for (idx, packet) in candidates.iter().enumerate() {
let mut dec = OpusDecoder::new();
let out = dec.decode_packet(packet).expect("decode");
assert_eq!(out.frame_outcomes.len(), 2, "candidate {idx}");
for fo in &out.frame_outcomes {
assert_eq!(
fo.status,
FrameDecodeStatus::SilkParamsDecoded,
"candidate {idx}"
);
}
assert_eq!(out.samples_per_channel(), 2 * 960, "candidate {idx}");
}
}
}