use mousiki::c_style_api::opus::opus_pcm_soft_clip;
use mousiki::c_style_api::opus_decoder::{
OpusDecodeError, OpusDecoder, OpusDecoderCtlRequest, opus_decode, opus_decoder_create,
opus_decoder_ctl, opus_decoder_get_nb_samples,
};
use mousiki::c_style_api::packet::opus_packet_get_nb_channels;
const MAX_PACKET: usize = 1500;
const MAX_FRAME_SAMP: usize = 5760;
#[derive(Clone)]
struct FastRand {
rz: u32,
rw: u32,
}
impl FastRand {
fn new(seed: u32) -> Self {
Self { rz: seed, rw: seed }
}
fn next(&mut self) -> u32 {
self.rz = 36969u32
.wrapping_mul(self.rz & 0xFFFF)
.wrapping_add(self.rz >> 16);
self.rw = 18000u32
.wrapping_mul(self.rw & 0xFFFF)
.wrapping_add(self.rw >> 16);
(self.rz << 16).wrapping_add(self.rw)
}
}
fn deb2_impl(t: &mut [u8], out: &mut [u8], p: &mut usize, k: usize, x: usize, y: usize) {
if x > 2 {
if y < 3 {
for i in 0..y {
*p = p.saturating_sub(1);
out[*p] = t[i + 1];
}
}
return;
}
t[x] = t[x - y];
deb2_impl(t, out, p, k, x + 1, y);
for i in (t[x - y] + 1)..(k as u8) {
t[x] = i;
deb2_impl(t, out, p, k, x + 1, x);
}
}
fn debruijn2(k: usize) -> Vec<u8> {
let mut out = vec![0u8; k * k];
let mut t = vec![0u8; k * 2];
let mut p = k * k;
deb2_impl(&mut t, &mut out, &mut p, k, 1, 1);
out
}
fn seed_from_env() -> u32 {
std::env::var("SEED")
.ok()
.and_then(|value| value.parse::<u32>().ok())
.unwrap_or(0xC0DEC0DE)
}
fn no_fuzz() -> bool {
if std::env::var_os("TEST_OPUS_FUZZ").is_some() {
return false;
}
std::env::var_os("TEST_OPUS_NOFUZZ").is_some() || cfg!(debug_assertions)
}
fn strict_final_range() -> bool {
std::env::var_os("TEST_OPUS_STRICT_FINAL_RANGE").is_some()
}
fn set_random_complexity(decoder: &mut OpusDecoder<'_>, rng: &mut FastRand) -> Result<(), String> {
let complexity = (rng.next() % 11) as i32;
opus_decoder_ctl(decoder, OpusDecoderCtlRequest::SetComplexity(complexity))
.map_err(|err| format!("set complexity failed: {err:?}"))
}
fn decode_i16(
decoder: &mut OpusDecoder<'_>,
data: Option<&[u8]>,
len: usize,
pcm: &mut [i16],
frame_size: usize,
decode_fec: bool,
context: &str,
) -> Result<usize, String> {
opus_decode(decoder, data, len, pcm, frame_size, decode_fec)
.map_err(|err| format!("{context} failed: {err:?}"))
}
fn last_packet_duration(decoder: &mut OpusDecoder<'_>) -> Result<i32, String> {
let mut dur = 0i32;
opus_decoder_ctl(
decoder,
OpusDecoderCtlRequest::GetLastPacketDuration(&mut dur),
)
.map_err(|err| format!("get last packet duration failed: {err:?}"))?;
Ok(dur)
}
fn final_range(decoder: &mut OpusDecoder<'_>) -> Result<u32, String> {
let mut range = 0u32;
opus_decoder_ctl(decoder, OpusDecoderCtlRequest::GetFinalRange(&mut range))
.map_err(|err| format!("get final range failed: {err:?}"))?;
Ok(range)
}
fn test_decoder_code0(no_fuzz: bool, rng: &mut FastRand) -> Result<(), String> {
let fsv = [48_000, 24_000, 16_000, 12_000, 8000];
let mut decs = Vec::with_capacity(10);
let strict_range = strict_final_range();
for t in 0..(fsv.len() * 2) {
let fs = fsv[t / 2];
let channels = (t & 1) + 1;
let dec = opus_decoder_create(fs, channels as i32)
.map_err(|err| format!("decoder create failed: {err:?}"))?;
decs.push(dec);
}
let mut packet = vec![0u8; MAX_PACKET];
let guard_offset = 16usize;
let outbuf_len = MAX_FRAME_SAMP * 2;
let mut outbuf_int = vec![32749i16; (MAX_FRAME_SAMP + 16) * 2];
{
let outbuf = &mut outbuf_int[guard_offset..guard_offset + outbuf_len];
for t in 0..decs.len() {
let factor = 48_000 / fsv[t / 2];
let plc_size = (120 / factor) as usize;
for &fec in &[false, true] {
set_random_complexity(&mut decs[t], rng)?;
let out_samples = decode_i16(&mut decs[t], None, 0, outbuf, plc_size, fec, "plc")?;
if out_samples != plc_size {
return Err("plc returned unexpected sample count".to_string());
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != plc_size as i32 {
return Err("plc last packet duration mismatch".to_string());
}
let err = opus_decode(&mut decs[t], None, 0, outbuf, plc_size + 2, fec);
if !matches!(err, Err(OpusDecodeError::BadArgument)) {
return Err("non-2.5ms PLC frame size should be rejected".to_string());
}
let len_cases = [0usize, 1, 10, rng.next() as usize];
for &len in &len_cases {
let out_samples =
decode_i16(&mut decs[t], None, len, outbuf, plc_size, fec, "plc")?;
if out_samples != plc_size {
return Err("plc returned unexpected sample count".to_string());
}
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != plc_size as i32 {
return Err("plc last packet duration mismatch".to_string());
}
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..0]),
0,
outbuf,
plc_size,
fec,
"zero-length packet",
)?;
if out_samples != plc_size {
return Err("zero-length packet PLC mismatch".to_string());
}
outbuf[0] = 32749;
let err = opus_decode(&mut decs[t], Some(&packet[..0]), 0, outbuf, 0, fec);
if !matches!(err, Err(OpusDecodeError::BadArgument)) {
return Err("zero frame size should be rejected".to_string());
}
if outbuf[0] != 32749 {
return Err("zero-length decode modified output buffer".to_string());
}
let err = opus_decode(
&mut decs[t],
Some(&packet),
packet.len() + 1,
outbuf,
MAX_FRAME_SAMP,
fec,
);
if !matches!(err, Err(OpusDecodeError::BadArgument)) {
return Err("oversized packet length should be rejected".to_string());
}
let err = opus_decode(
&mut decs[t],
Some(&packet),
usize::MAX,
outbuf,
MAX_FRAME_SAMP,
fec,
);
if !matches!(err, Err(OpusDecodeError::BadArgument)) {
return Err("oversized packet length should be rejected".to_string());
}
opus_decoder_ctl(&mut decs[t], OpusDecoderCtlRequest::ResetState)
.map_err(|err| format!("reset decoder failed: {err:?}"))?;
}
}
for i in 0u8..64 {
let mut expected = [0usize; 10];
packet[0] = i << 2;
packet[1] = 255;
packet[2] = 255;
let channels = opus_packet_get_nb_channels(&packet[..1])
.map_err(|err| format!("get nb channels failed: {err:?}"))?;
if channels != (i as usize & 1) + 1 {
return Err("channel count mismatch".to_string());
}
for (t, dec) in decs.iter().enumerate() {
expected[t] = opus_decoder_get_nb_samples(dec, &packet, 1)
.map_err(|err| format!("get nb samples failed: {err:?}"))?;
if expected[t] > 2880 {
return Err("unexpected nb samples for code 0".to_string());
}
}
for j in 0u16..256 {
packet[1] = j as u8;
let mut dec_final_range2 = 0u32;
for t in 0..decs.len() {
set_random_complexity(&mut decs[t], rng)?;
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..3]),
3,
outbuf,
MAX_FRAME_SAMP,
false,
"code 0 decode",
)?;
if out_samples != expected[t] {
return Err("code 0 decoded sample count mismatch".to_string());
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != out_samples as i32 {
return Err("code 0 last packet duration mismatch".to_string());
}
let range = final_range(&mut decs[t])?;
if t == 0 {
dec_final_range2 = range;
} else if range != dec_final_range2 {
return Err("final range mismatch across decoders".to_string());
}
}
}
for t in 0..decs.len() {
let factor = 48_000 / fsv[t / 2];
for _ in 0..6 {
set_random_complexity(&mut decs[t], rng)?;
let out_samples =
decode_i16(&mut decs[t], None, 0, outbuf, expected[t], false, "plc")?;
if out_samples != expected[t] {
return Err("plc returned unexpected sample count".to_string());
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != out_samples as i32 {
return Err("plc last packet duration mismatch".to_string());
}
}
if expected[t] != (120 / factor) as usize {
let plc_size = (120 / factor) as usize;
let out_samples =
decode_i16(&mut decs[t], None, 0, outbuf, plc_size, false, "plc")?;
if out_samples != plc_size {
return Err("short PLC returned unexpected sample count".to_string());
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != out_samples as i32 {
return Err("short PLC last packet duration mismatch".to_string());
}
}
let err = opus_decode(
&mut decs[t],
Some(&packet[..2]),
2,
outbuf,
expected[t].saturating_sub(1),
false,
);
if err.is_ok() {
return Err("short frame size should be rejected".to_string());
}
}
}
if !no_fuzz {
let cmodes = [16u8, 20, 24, 28];
let cres = [116290185u32, 2172123586, 2172123586, 2172123586];
let lres = [3285687739u32, 1481572662, 694350475];
let lmodes = [0u8, 4, 8];
let mode = (rng.next() % 4) as usize;
packet[0] = cmodes[mode] << 3;
let mut dec_final_acc = 0u32;
let t = (rng.next() % 10) as usize;
for i in 0..65536u32 {
let factor = 48_000 / fsv[t / 2];
packet[1] = (i >> 8) as u8;
packet[2] = (i & 255) as u8;
packet[3] = 255;
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..4]),
4,
outbuf,
MAX_FRAME_SAMP,
false,
"3-byte prefix",
)?;
if out_samples != (120 / factor) as usize {
return Err("3-byte prefix sample count mismatch".to_string());
}
let range = final_range(&mut decs[t])?;
dec_final_acc = dec_final_acc.wrapping_add(range);
}
if dec_final_acc != cres[mode] {
return Err("3-byte prefix final range mismatch".to_string());
}
let mode = (rng.next() % 3) as usize;
packet[0] = lmodes[mode] << 3;
dec_final_acc = 0;
let t = (rng.next() % 10) as usize;
for i in 0..65536u32 {
let factor = 48_000 / fsv[t / 2];
packet[1] = (i >> 8) as u8;
packet[2] = (i & 255) as u8;
packet[3] = 255;
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..4]),
4,
outbuf,
MAX_FRAME_SAMP,
false,
"3-byte prefix long",
)?;
if out_samples != (480 / factor) as usize {
return Err("3-byte prefix long sample count mismatch".to_string());
}
let range = final_range(&mut decs[t])?;
dec_final_acc = dec_final_acc.wrapping_add(range);
}
if strict_range && dec_final_acc != lres[mode] {
return Err("3-byte prefix long final range mismatch".to_string());
}
let skip = (rng.next() % 7) as usize;
for i in 0u8..64 {
packet[0] = i << 2;
let mut expected = [0usize; 10];
for (t, dec) in decs.iter().enumerate() {
expected[t] = opus_decoder_get_nb_samples(dec, &packet, 1)
.map_err(|err| format!("get nb samples failed: {err:?}"))?;
}
for j in (2 + skip..1275).step_by(4) {
for jj in 0..j {
packet[jj + 1] = (rng.next() & 255) as u8;
}
let mut dec_final_range2 = 0u32;
for t in 0..decs.len() {
set_random_complexity(&mut decs[t], rng)?;
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..j + 1]),
j + 1,
outbuf,
MAX_FRAME_SAMP,
false,
"random packets",
)?;
if out_samples != expected[t] {
return Err("random packet sample count mismatch".to_string());
}
let range = final_range(&mut decs[t])?;
if t == 0 {
dec_final_range2 = range;
} else if range != dec_final_range2 {
return Err("final range mismatch across decoders".to_string());
}
}
}
}
let modes = debruijn2(64);
let plen = ((rng.next() % 18 + 3) * 8) as usize + skip + 3;
let mut decbak = opus_decoder_create(48_000, 1)
.map_err(|err| format!("decoder create failed: {err:?}"))?;
for i in 0..4096usize {
packet[0] = modes[i] << 2;
let mut expected = [0usize; 10];
for (t, dec) in decs.iter().enumerate() {
expected[t] = opus_decoder_get_nb_samples(dec, &packet, plen)
.map_err(|err| format!("get nb samples failed: {err:?}"))?;
}
for j in 0..plen {
packet[j + 1] = ((rng.next() | rng.next()) & 255) as u8;
}
opus_decoder_ctl(&mut decbak, OpusDecoderCtlRequest::ResetState)
.map_err(|err| format!("reset decoder failed: {err:?}"))?;
let out_samples = decode_i16(
&mut decbak,
Some(&packet[..plen + 1]),
plen + 1,
outbuf,
expected[0],
true,
"fec decode",
)?;
if out_samples != expected[0] {
return Err("fec decode sample count mismatch".to_string());
}
opus_decoder_ctl(&mut decbak, OpusDecoderCtlRequest::ResetState)
.map_err(|err| format!("reset decoder failed: {err:?}"))?;
let out_samples = decode_i16(
&mut decbak,
None,
0,
outbuf,
MAX_FRAME_SAMP,
true,
"fec plc",
)?;
if out_samples < 20 {
return Err("fec plc returned too few samples".to_string());
}
opus_decoder_ctl(&mut decbak, OpusDecoderCtlRequest::ResetState)
.map_err(|err| format!("reset decoder failed: {err:?}"))?;
let out_samples =
decode_i16(&mut decbak, None, 0, outbuf, MAX_FRAME_SAMP, false, "plc")?;
if out_samples < 20 {
return Err("plc returned too few samples".to_string());
}
for t in 0..decs.len() {
set_random_complexity(&mut decs[t], rng)?;
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..plen + 1]),
plen + 1,
outbuf,
MAX_FRAME_SAMP,
false,
"random packets",
)?;
if out_samples != expected[t] {
return Err("random packet sample count mismatch".to_string());
}
let dur = last_packet_duration(&mut decs[t])?;
if dur != out_samples as i32 {
return Err("random packet last packet duration mismatch".to_string());
}
}
}
let plen = ((rng.next() % 18 + 3) * 8) as usize + skip + 3;
let t = (rng.next() & 3) as usize;
for i in 0..4096usize {
packet[0] = modes[i] << 2;
let expected = opus_decoder_get_nb_samples(&decs[t], &packet, plen)
.map_err(|err| format!("get nb samples failed: {err:?}"))?;
for _ in 0..10 {
set_random_complexity(&mut decs[t], rng)?;
for j in 0..plen {
packet[j + 1] = ((rng.next() | rng.next()) & 255) as u8;
}
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..plen + 1]),
plen + 1,
outbuf,
MAX_FRAME_SAMP,
false,
"random packets",
)?;
if out_samples != expected {
return Err("random packet sample count mismatch".to_string());
}
}
}
let tmodes = [25u8 << 2];
let tseeds = [140441u32];
let tlen = [157usize];
let tret = [480usize];
let t = (rng.next() & 1) as usize;
for i in 0..1usize {
packet[0] = tmodes[i];
let mut local_rng = FastRand::new(tseeds[i]);
for j in 1..tlen[i] {
packet[j] = (local_rng.next() & 255) as u8;
}
let out_samples = decode_i16(
&mut decs[t],
Some(&packet[..tlen[i]]),
tlen[i],
outbuf,
MAX_FRAME_SAMP,
false,
"preselected packets",
)?;
if out_samples != tret[i] {
return Err("preselected packet sample count mismatch".to_string());
}
}
}
}
let prefix_ok = outbuf_int[..guard_offset]
.iter()
.all(|&sample| sample == 32749);
let suffix_ok = outbuf_int[guard_offset + outbuf_len..]
.iter()
.all(|&sample| sample == 32749);
if !prefix_ok || !suffix_ok {
return Err("guard samples were modified".to_string());
}
Ok(())
}
#[test]
fn opus_decode_code0() {
let seed = seed_from_env();
let mut rng = FastRand::new(seed);
test_decoder_code0(no_fuzz(), &mut rng).unwrap_or_else(|err| panic!("{err} (seed={seed})"));
}
#[test]
fn opus_decode_soft_clip() {
let mut x = vec![0f32; 1024];
let mut s = [0f32; 8];
for i in 0..1024 {
for (j, sample) in x.iter_mut().enumerate() {
*sample = ((j & 255) as f32) * (1.0 / 32.0) - 4.0;
}
opus_pcm_soft_clip(&mut x[i..], 1024 - i, 1, &mut s);
for &sample in &x[i..] {
if !(sample <= 1.0 && sample >= -1.0) {
panic!("soft clip exceeded bounds");
}
}
}
for i in 1..9usize {
for (j, sample) in x.iter_mut().enumerate() {
*sample = ((j & 255) as f32) * (1.0 / 32.0) - 4.0;
}
let frame_size = 1024 / i;
opus_pcm_soft_clip(&mut x, frame_size, i, &mut s);
for &sample in &x[..frame_size * i] {
if !(sample <= 1.0 && sample >= -1.0) {
panic!("soft clip exceeded bounds");
}
}
}
opus_pcm_soft_clip(&mut x, 0, 1, &mut s);
opus_pcm_soft_clip(&mut x, 1, 0, &mut s);
opus_pcm_soft_clip(&mut x, 1, 1, &mut []);
opus_pcm_soft_clip(&mut x, 1, 9, &mut s);
opus_pcm_soft_clip(&mut [], 1, 1, &mut s);
}