use crate::celt::opus_select_arch;
use crate::celt::select_celt_float2int16_impl;
#[cfg(not(feature = "fixed_point"))]
use crate::celt::{CELT_SIG_SCALE, float2int};
use crate::celt::{EcDec, OpusRes, ec_laplace_decode_p0, ec_tell};
use crate::dred_constants::{
DRED_LATENT_DIM, DRED_MAX_DATA_SIZE, DRED_NUM_FEATURES, DRED_NUM_REDUNDANCY_FRAMES,
DRED_STATE_DIM,
};
#[cfg(feature = "dred")]
use crate::dred_rdovae_dec::{
DEC_OUTPUT_OUT_SIZE, RdovaeDec, RdovaeDecState, rdovae_dec_init_states, rdovae_decode_all,
rdovae_decode_qframe,
};
use crate::dred_stats_data::{
DRED_LATENT_P0_Q8, DRED_LATENT_QUANT_SCALES_Q8, DRED_LATENT_R_Q8, DRED_STATE_P0_Q8,
DRED_STATE_QUANT_SCALES_Q8, DRED_STATE_R_Q8,
};
use crate::extensions::{ExtensionError, OpusExtensionIterator};
use crate::opus_decoder::{OpusDecodeError, OpusDecoder, opus_decode_native};
use crate::packet::{PacketError, opus_packet_get_samples_per_frame, opus_packet_parse_impl};
use alloc::vec;
use alloc::vec::Vec;
const DRED_EXTENSION_ID: u8 = 126;
const DRED_EXPERIMENTAL_VERSION: u8 = 10;
const DRED_EXPERIMENTAL_BYTES: usize = 2;
const DRED_FRAME_OFFSET_DIVISOR: i32 = 120;
const DRED_FEC_FEATURES_LEN: usize = 2 * DRED_NUM_REDUNDANCY_FRAMES * DRED_NUM_FEATURES;
const DRED_LATENTS_LEN: usize = (DRED_NUM_REDUNDANCY_FRAMES / 2) * DRED_LATENT_DIM;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpusDredError {
BadArgument,
BufferTooSmall,
InvalidPacket,
InternalError,
Unimplemented,
}
impl OpusDredError {
#[inline]
pub const fn code(self) -> i32 {
match self {
Self::BadArgument => -1,
Self::BufferTooSmall => -2,
Self::InternalError => -3,
Self::InvalidPacket => -4,
Self::Unimplemented => -5,
}
}
}
impl From<PacketError> for OpusDredError {
#[inline]
fn from(value: PacketError) -> Self {
match value {
PacketError::BadArgument => Self::BadArgument,
PacketError::InvalidPacket => Self::InvalidPacket,
}
}
}
impl From<ExtensionError> for OpusDredError {
#[inline]
fn from(value: ExtensionError) -> Self {
match value {
ExtensionError::BadArgument => Self::BadArgument,
ExtensionError::BufferTooSmall => Self::BufferTooSmall,
ExtensionError::InvalidPacket => Self::InvalidPacket,
}
}
}
impl From<OpusDecodeError> for OpusDredError {
#[inline]
fn from(value: OpusDecodeError) -> Self {
match value {
OpusDecodeError::BadArgument => Self::BadArgument,
OpusDecodeError::BufferTooSmall => Self::BufferTooSmall,
OpusDecodeError::InvalidPacket => Self::InvalidPacket,
OpusDecodeError::InternalError => Self::InternalError,
OpusDecodeError::Unimplemented => Self::Unimplemented,
}
}
}
#[derive(Debug, Default)]
pub struct OpusDredDecoder {
#[cfg(feature = "dred")]
model: RdovaeDec,
loaded: bool,
arch: i32,
}
#[derive(Debug, Clone)]
pub struct OpusDred {
fec_features: [f32; DRED_FEC_FEATURES_LEN],
state: [f32; DRED_STATE_DIM],
latents: [f32; DRED_LATENTS_LEN],
nb_latents: i32,
process_stage: i32,
dred_offset: i32,
}
impl Default for OpusDred {
fn default() -> Self {
Self {
fec_features: [0.0; DRED_FEC_FEATURES_LEN],
state: [0.0; DRED_STATE_DIM],
latents: [0.0; DRED_LATENTS_LEN],
nb_latents: 0,
process_stage: 0,
dred_offset: 0,
}
}
}
#[cfg(feature = "dred")]
#[derive(Debug, Clone)]
pub struct DredVectorDecoder {
model: RdovaeDec,
arch: i32,
}
#[cfg(feature = "dred")]
impl DredVectorDecoder {
#[must_use]
pub fn new() -> Self {
Self {
model: RdovaeDec::new(),
arch: opus_select_arch(),
}
}
pub fn decode_packet(
&self,
q0: u32,
nb_chunks: usize,
payload: &[u8],
features: &mut [f32],
) -> Result<usize, OpusDredError> {
if nb_chunks == 0 {
return Ok(0);
}
if nb_chunks % 2 != 0 {
return Err(OpusDredError::BadArgument);
}
let frames = nb_chunks.checked_mul(2).ok_or(OpusDredError::BadArgument)?;
let required = frames
.checked_mul(DRED_NUM_FEATURES)
.ok_or(OpusDredError::BadArgument)?;
if features.len() < required {
return Err(OpusDredError::BufferTooSmall);
}
let q0 = usize::try_from(q0).map_err(|_| OpusDredError::BadArgument)?;
let state_offset = q0
.checked_mul(DRED_STATE_DIM)
.ok_or(OpusDredError::BadArgument)?;
if state_offset + DRED_STATE_DIM > DRED_STATE_QUANT_SCALES_Q8.len() {
return Err(OpusDredError::BadArgument);
}
let latent_offset = q0
.checked_mul(DRED_LATENT_DIM)
.ok_or(OpusDredError::BadArgument)?;
if latent_offset + DRED_LATENT_DIM > DRED_LATENT_QUANT_SCALES_Q8.len() {
return Err(OpusDredError::BadArgument);
}
let mut buffer = payload.to_vec();
let mut ec = EcDec::new(&mut buffer);
let mut initial_state = [0.0f32; DRED_STATE_DIM];
dred_decode_latents(
&mut ec,
&mut initial_state,
&DRED_STATE_QUANT_SCALES_Q8[state_offset..state_offset + DRED_STATE_DIM],
&DRED_STATE_R_Q8[state_offset..state_offset + DRED_STATE_DIM],
&DRED_STATE_P0_Q8[state_offset..state_offset + DRED_STATE_DIM],
);
let mut dec_state = RdovaeDecState::default();
rdovae_dec_init_states(&mut dec_state, &self.model, &initial_state, self.arch);
let mut latent = [0.0f32; DRED_LATENT_DIM];
let mut dec_tmp = [0.0f32; DEC_OUTPUT_OUT_SIZE];
let mut i = nb_chunks as i32 - 1;
while i >= 0 {
dred_decode_latents(
&mut ec,
&mut latent,
&DRED_LATENT_QUANT_SCALES_Q8[latent_offset..latent_offset + DRED_LATENT_DIM],
&DRED_LATENT_R_Q8[latent_offset..latent_offset + DRED_LATENT_DIM],
&DRED_LATENT_P0_Q8[latent_offset..latent_offset + DRED_LATENT_DIM],
);
rdovae_decode_qframe(
&mut dec_state,
&self.model,
&mut dec_tmp,
&latent,
self.arch,
);
let base = 2 * i - 2;
if base < 0 {
return Err(OpusDredError::BadArgument);
}
let base = base as usize;
for k in 0..4 {
let dst = (base + k) * DRED_NUM_FEATURES;
let src = (3 - k) * DRED_NUM_FEATURES;
features[dst..dst + DRED_NUM_FEATURES]
.copy_from_slice(&dec_tmp[src..src + DRED_NUM_FEATURES]);
}
i -= 2;
}
Ok(frames)
}
}
#[derive(Debug, Clone, Copy)]
struct DredPayload<'a> {
payload: &'a [u8],
dred_frame_offset: i32,
}
fn dred_decode_latents(dec: &mut EcDec<'_>, output: &mut [f32], scale: &[u8], r: &[u8], p0: &[u8]) {
debug_assert_eq!(output.len(), scale.len());
debug_assert_eq!(output.len(), r.len());
debug_assert_eq!(output.len(), p0.len());
for (idx, out) in output.iter_mut().enumerate() {
let q = if r[idx] == 0 || p0[idx] == 255 {
0
} else {
ec_laplace_decode_p0(dec, u16::from(p0[idx]) << 7, u16::from(r[idx]) << 7)
};
let denom = if scale[idx] == 0 { 1 } else { scale[idx] };
*out = (q as f32) * 256.0 / f32::from(denom);
}
}
fn compute_quantizer(q0: i32, d_q: i32, qmax: i32, index: i32) -> i32 {
const D_Q_TABLE: [i32; 8] = [0, 2, 3, 4, 6, 8, 12, 16];
debug_assert!(
(0..D_Q_TABLE.len() as i32).contains(&d_q),
"dQ index out of range"
);
let quant = q0 + (D_Q_TABLE[d_q as usize] * index + 8) / 16;
quant.min(qmax)
}
fn dred_ec_decode(
dec: &mut OpusDred,
bytes: &[u8],
min_feature_frames: i32,
dred_frame_offset: i32,
) -> Result<i32, OpusDredError> {
debug_assert!(
DRED_NUM_REDUNDANCY_FRAMES % 2 == 0,
"redundancy frame count must be even"
);
let mut buffer = [0u8; DRED_MAX_DATA_SIZE];
if bytes.len() > buffer.len() {
return Err(OpusDredError::BufferTooSmall);
}
buffer[..bytes.len()].copy_from_slice(bytes);
let mut ec = EcDec::new(&mut buffer[..bytes.len()]);
let q0 = ec.dec_uint(16) as i32;
let d_q = ec.dec_uint(8) as i32;
let extra_offset = if ec.dec_uint(2) != 0 {
32 * ec.dec_uint(256) as i32
} else {
0
};
dec.dred_offset = 16 - ec.dec_uint(32) as i32 - extra_offset + dred_frame_offset;
let mut qmax = 15;
if q0 < 14 && d_q > 0 {
let nvals = 15 - (q0 + 1);
let ft = 2 * nvals;
let s = ec.decode(ft as u32) as i32;
if s >= nvals {
qmax = q0 + (s - nvals) + 1;
ec.update(s as u32, (s + 1) as u32, ft as u32);
} else {
ec.update(0, nvals as u32, ft as u32);
}
}
let state_qoffset = (q0 * DRED_STATE_DIM as i32) as usize;
dred_decode_latents(
&mut ec,
&mut dec.state,
&DRED_STATE_QUANT_SCALES_Q8[state_qoffset..state_qoffset + DRED_STATE_DIM],
&DRED_STATE_R_Q8[state_qoffset..state_qoffset + DRED_STATE_DIM],
&DRED_STATE_P0_Q8[state_qoffset..state_qoffset + DRED_STATE_DIM],
);
let max_frames =
((min_feature_frames + 1) / 2).clamp(0, DRED_NUM_REDUNDANCY_FRAMES as i32) as usize;
let mut i = 0usize;
while i < max_frames {
if 8 * bytes.len() as i32 - ec_tell(ec.ctx()) <= 7 {
break;
}
let q_level = compute_quantizer(q0, d_q, qmax, (i / 2) as i32);
let offset = (q_level * DRED_LATENT_DIM as i32) as usize;
let latent_start = (i / 2) * DRED_LATENT_DIM;
dred_decode_latents(
&mut ec,
&mut dec.latents[latent_start..latent_start + DRED_LATENT_DIM],
&DRED_LATENT_QUANT_SCALES_Q8[offset..offset + DRED_LATENT_DIM],
&DRED_LATENT_R_Q8[offset..offset + DRED_LATENT_DIM],
&DRED_LATENT_P0_Q8[offset..offset + DRED_LATENT_DIM],
);
i += 2;
}
dec.process_stage = 1;
dec.nb_latents = (i / 2) as i32;
Ok(dec.nb_latents)
}
fn dred_find_payload<'a>(data: &'a [u8]) -> Result<Option<DredPayload<'a>>, OpusDredError> {
let parsed = opus_packet_parse_impl(data, data.len(), false)?;
let frame_size = opus_packet_get_samples_per_frame(data, 48_000)?;
let frame_size = i32::try_from(frame_size).map_err(|_| OpusDredError::InvalidPacket)?;
let mut iter = OpusExtensionIterator::new(parsed.padding, parsed.frame_count);
loop {
let Some(ext) = iter.find(DRED_EXTENSION_ID)? else {
return Ok(None);
};
let ext_len = usize::try_from(ext.len).map_err(|_| OpusDredError::InvalidPacket)?;
if ext_len > ext.data.len() {
return Err(OpusDredError::InvalidPacket);
}
let dred_frame_offset = ext
.frame
.checked_mul(frame_size)
.and_then(|value| value.checked_div(DRED_FRAME_OFFSET_DIVISOR))
.ok_or(OpusDredError::InvalidPacket)?;
if ext_len > DRED_EXPERIMENTAL_BYTES
&& ext.data.len() >= DRED_EXPERIMENTAL_BYTES
&& ext.data[0] == b'D'
&& ext.data[1] == DRED_EXPERIMENTAL_VERSION
{
let payload = &ext.data[DRED_EXPERIMENTAL_BYTES..ext_len];
return Ok(Some(DredPayload {
payload,
dred_frame_offset,
}));
}
}
}
#[inline]
pub fn opus_dred_decoder_get_size() -> usize {
core::mem::size_of::<OpusDredDecoder>()
}
pub fn opus_dred_decoder_init(decoder: &mut OpusDredDecoder) -> Result<(), OpusDredError> {
decoder.loaded = false;
decoder.arch = opus_select_arch();
#[cfg(feature = "dred")]
{
decoder.model = RdovaeDec::new();
decoder.loaded = true;
}
Ok(())
}
pub fn opus_dred_decoder_create() -> Result<OpusDredDecoder, OpusDredError> {
let mut decoder = OpusDredDecoder::default();
opus_dred_decoder_init(&mut decoder)?;
Ok(decoder)
}
#[inline]
pub fn opus_dred_decoder_destroy(_decoder: OpusDredDecoder) {}
pub enum OpusDredDecoderCtlRequest<'req> {
SetDnnBlob(&'req [u8]),
}
pub fn opus_dred_decoder_ctl(
decoder: &mut OpusDredDecoder,
request: OpusDredDecoderCtlRequest<'_>,
) -> Result<(), OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
match request {
OpusDredDecoderCtlRequest::SetDnnBlob(data) => {
if data.is_empty() {
return Err(OpusDredError::BadArgument);
}
#[cfg(feature = "dred")]
{
let model =
RdovaeDec::from_weights(data).map_err(|_| OpusDredError::BadArgument)?;
decoder.model = model;
decoder.loaded = true;
}
Ok(())
}
}
}
pub fn opus_dred_get_size() -> usize {
if cfg!(feature = "dred") {
core::mem::size_of::<OpusDred>()
} else {
0
}
}
pub fn opus_dred_alloc() -> Result<OpusDred, OpusDredError> {
if cfg!(feature = "dred") {
Ok(OpusDred::default())
} else {
Err(OpusDredError::Unimplemented)
}
}
#[inline]
pub fn opus_dred_free(_dred: OpusDred) {}
#[allow(clippy::too_many_arguments)]
pub fn opus_dred_parse(
decoder: &OpusDredDecoder,
dred: &mut OpusDred,
data: &[u8],
max_dred_samples: i32,
sampling_rate: i32,
dred_end: Option<&mut i32>,
defer_processing: bool,
) -> Result<i32, OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
if !decoder.loaded {
return Err(OpusDredError::Unimplemented);
}
dred.process_stage = -1;
if let Some(payload) = dred_find_payload(data)? {
let DredPayload {
payload: dred_payload,
dred_frame_offset,
} = payload;
let offset = 100 * max_dred_samples / sampling_rate;
let min_feature_frames = (2 + offset).min((2 * DRED_NUM_REDUNDANCY_FRAMES) as i32);
dred_ec_decode(dred, dred_payload, min_feature_frames, dred_frame_offset)?;
if !defer_processing {
let src = dred.clone();
opus_dred_process(decoder, &src, dred)?;
}
if let Some(out) = dred_end {
*out = (-(dred.dred_offset) * sampling_rate / 400).max(0);
}
return Ok(
(dred.nb_latents * sampling_rate / 25 - dred.dred_offset * sampling_rate / 400).max(0),
);
}
if let Some(out) = dred_end {
*out = 0;
}
Ok(0)
}
pub fn opus_dred_process(
decoder: &OpusDredDecoder,
src: &OpusDred,
dst: &mut OpusDred,
) -> Result<(), OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
if src.process_stage != 1 && src.process_stage != 2 {
return Err(OpusDredError::BadArgument);
}
if !decoder.loaded {
return Err(OpusDredError::Unimplemented);
}
if !core::ptr::eq(src, dst) {
*dst = src.clone();
}
if dst.process_stage == 2 {
return Ok(());
}
#[cfg(feature = "dred")]
rdovae_decode_all(
&decoder.model,
&mut dst.fec_features,
&dst.state,
&dst.latents,
dst.nb_latents,
decoder.arch,
);
dst.process_stage = 2;
Ok(())
}
#[cfg(feature = "deep_plc")]
fn inject_dred_fec_features(
decoder: &mut OpusDecoder<'_>,
dred: &OpusDred,
dred_offset: i32,
frame_size: usize,
) {
if dred.process_stage != 2 {
return;
}
let f10 = decoder.sample_rate() / 100;
if f10 <= 0 {
return;
}
let frame_size = i32::try_from(frame_size).unwrap_or(i32::MAX);
let lpcnet = decoder.lpcnet_mut();
lpcnet.fec_clear();
let init_frames = if lpcnet.blend == 0 { 2 } else { 0 };
let features_per_frame = (frame_size / f10).max(1);
let needed_feature_frames = init_frames + features_per_frame;
lpcnet.fec_clear();
let offset = (dred_offset as f32 + (dred.dred_offset * f10 / 4) as f32) / f10 as f32;
let base_offset = libm::floorf(offset) as i32;
let max_feature_offset = dred.nb_latents.saturating_mul(4).saturating_sub(1);
for i in 0..needed_feature_frames {
let feature_offset = init_frames - i - 2 + base_offset;
if feature_offset < 0 {
continue;
}
if feature_offset <= max_feature_offset {
let start = feature_offset as usize * DRED_NUM_FEATURES;
let end = start + DRED_NUM_FEATURES;
debug_assert!(end <= dred.fec_features.len());
if end <= dred.fec_features.len() {
lpcnet.fec_add(Some(&dred.fec_features[start..end]));
}
} else {
lpcnet.fec_add(None);
}
}
}
fn res_to_int24(sample: OpusRes) -> i32 {
#[cfg(feature = "fixed_point")]
{
crate::celt::res2int24(crate::celt::float2res(sample))
}
#[cfg(not(feature = "fixed_point"))]
{
let scale = CELT_SIG_SCALE * 256.0;
let scaled = (sample * scale).clamp(-8_388_608.0, 8_388_607.0);
float2int(scaled)
}
}
pub fn opus_decoder_dred_decode(
decoder: &mut OpusDecoder<'_>,
dred: &OpusDred,
dred_offset: i32,
pcm: &mut [i16],
frame_size: usize,
) -> Result<usize, OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
if frame_size == 0 {
return Err(OpusDredError::BadArgument);
}
let channels = usize::try_from(decoder.channels).map_err(|_| OpusDredError::BadArgument)?;
if channels == 0 || channels > 2 {
return Err(OpusDredError::BadArgument);
}
let total_samples = frame_size
.checked_mul(channels)
.ok_or(OpusDredError::BadArgument)?;
if pcm.len() < total_samples {
return Err(OpusDredError::BufferTooSmall);
}
#[cfg(feature = "deep_plc")]
inject_dred_fec_features(decoder, dred, dred_offset, frame_size);
#[cfg(not(feature = "deep_plc"))]
let _ = (dred, dred_offset);
let mut out: Vec<OpusRes> = vec![OpusRes::default(); total_samples];
let decoded = opus_decode_native(
decoder, None, 0, &mut out, frame_size, false, false, None, true,
)?;
let decoded_samples = decoded
.checked_mul(channels)
.ok_or(OpusDredError::BadArgument)?;
if pcm.len() < decoded_samples {
return Err(OpusDredError::BufferTooSmall);
}
select_celt_float2int16_impl(decoder.arch())(
&out[..decoded_samples],
&mut pcm[..decoded_samples],
);
Ok(decoded)
}
pub fn opus_decoder_dred_decode24(
decoder: &mut OpusDecoder<'_>,
dred: &OpusDred,
dred_offset: i32,
pcm: &mut [i32],
frame_size: usize,
) -> Result<usize, OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
if frame_size == 0 {
return Err(OpusDredError::BadArgument);
}
let channels = usize::try_from(decoder.channels).map_err(|_| OpusDredError::BadArgument)?;
if channels == 0 || channels > 2 {
return Err(OpusDredError::BadArgument);
}
let total_samples = frame_size
.checked_mul(channels)
.ok_or(OpusDredError::BadArgument)?;
if pcm.len() < total_samples {
return Err(OpusDredError::BufferTooSmall);
}
#[cfg(feature = "deep_plc")]
inject_dred_fec_features(decoder, dred, dred_offset, frame_size);
#[cfg(not(feature = "deep_plc"))]
let _ = (dred, dred_offset);
let mut out: Vec<OpusRes> = vec![OpusRes::default(); total_samples];
let decoded = opus_decode_native(
decoder, None, 0, &mut out, frame_size, false, false, None, true,
)?;
let decoded_samples = decoded
.checked_mul(channels)
.ok_or(OpusDredError::BadArgument)?;
if pcm.len() < decoded_samples {
return Err(OpusDredError::BufferTooSmall);
}
for (dst, &src) in pcm.iter_mut().take(decoded_samples).zip(out.iter()) {
*dst = res_to_int24(src);
}
Ok(decoded)
}
pub fn opus_decoder_dred_decode_float(
decoder: &mut OpusDecoder<'_>,
dred: &OpusDred,
dred_offset: i32,
pcm: &mut [f32],
frame_size: usize,
) -> Result<usize, OpusDredError> {
if !cfg!(feature = "dred") {
return Err(OpusDredError::Unimplemented);
}
if frame_size == 0 {
return Err(OpusDredError::BadArgument);
}
#[cfg(feature = "deep_plc")]
inject_dred_fec_features(decoder, dred, dred_offset, frame_size);
#[cfg(not(feature = "deep_plc"))]
let _ = (dred, dred_offset);
opus_decode_native(decoder, None, 0, pcm, frame_size, false, false, None, false)
.map_err(OpusDredError::from)
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use crate::extensions::{OpusExtensionData, opus_packet_extensions_generate};
use crate::opus_decoder::opus_decoder_create;
use alloc::vec;
use alloc::vec::Vec;
use std::env;
fn build_packet_with_padding(frame_count: usize, frame_len: usize, padding: &[u8]) -> Vec<u8> {
assert!(frame_count > 0 && frame_count < 64);
assert!(padding.len() > 0 && padding.len() < 255);
let mut packet = Vec::with_capacity(3 + frame_count * frame_len + padding.len());
packet.push(0x03);
packet.push(0x40 | frame_count as u8);
packet.push(padding.len() as u8);
packet.resize(packet.len() + frame_count * frame_len, 0);
packet.extend_from_slice(padding);
packet
}
fn build_dred_padding(frame_count: usize, frame: i32, payload: &[u8]) -> Vec<u8> {
let mut ext_bytes = Vec::with_capacity(DRED_EXPERIMENTAL_BYTES + payload.len());
ext_bytes.push(b'D');
ext_bytes.push(DRED_EXPERIMENTAL_VERSION);
ext_bytes.extend_from_slice(payload);
let ext = OpusExtensionData {
id: DRED_EXTENSION_ID,
frame,
data: &ext_bytes,
len: i32::try_from(ext_bytes.len()).expect("ext len fits i32"),
};
let max_len = 255usize;
let required = opus_packet_extensions_generate(None, max_len, &[ext], frame_count, false)
.expect("generate ext len");
let mut padding = Vec::with_capacity(required);
padding.resize(required, 0);
let written = opus_packet_extensions_generate(
Some(&mut padding),
required,
&[ext],
frame_count,
false,
)
.expect("generate ext bytes");
assert_eq!(written, required);
padding
}
fn seed_from_env() -> u32 {
env::var("SEED")
.ok()
.and_then(|value| value.parse::<u32>().ok())
.unwrap_or(0xDEAD_BEEF)
}
fn iterations_from_env() -> usize {
env::var("DRED_RANDOM_ITERS")
.ok()
.and_then(|value| value.parse::<usize>().ok())
.unwrap_or(1_000)
}
#[test]
fn dred_size_and_alloc_match_feature_state() {
let size = opus_dred_get_size();
if cfg!(feature = "dred") {
assert!(size > 0);
let dred = opus_dred_alloc().expect("dred alloc");
opus_dred_free(dred);
} else {
assert_eq!(size, 0);
assert_eq!(opus_dred_alloc().unwrap_err(), OpusDredError::Unimplemented);
}
}
#[test]
fn dred_decoder_ctl_rejects_empty_blob() {
if !cfg!(feature = "dred") {
return;
}
let mut decoder = opus_dred_decoder_create().expect("decoder");
assert_eq!(
opus_dred_decoder_ctl(&mut decoder, OpusDredDecoderCtlRequest::SetDnnBlob(&[]))
.unwrap_err(),
OpusDredError::BadArgument
);
}
#[test]
fn dred_parse_succeeds_without_payload() {
if !cfg!(feature = "dred") {
return;
}
let decoder = opus_dred_decoder_create().expect("decoder");
let mut dred = opus_dred_alloc().expect("dred alloc");
let data = [0u8; 4];
let mut dred_end = -1;
let decoded = opus_dred_parse(
&decoder,
&mut dred,
&data,
48000,
48000,
Some(&mut dred_end),
false,
)
.expect("parse");
assert_eq!(decoded, 0);
assert_eq!(dred_end, 0);
opus_dred_free(dred);
opus_dred_decoder_destroy(decoder);
}
#[test]
fn dred_find_payload_returns_none_without_extension() {
let packet = [0x00u8];
let payload = dred_find_payload(&packet).expect("parse");
assert!(payload.is_none());
}
#[test]
fn dred_find_payload_extracts_payload_and_offset() {
let payload_bytes = [0xAA, 0xBB];
let padding = build_dred_padding(2, 1, &payload_bytes);
let packet = build_packet_with_padding(2, 1, &padding);
let payload = dred_find_payload(&packet).expect("parse").expect("payload");
assert_eq!(payload.payload, payload_bytes);
let frame_size =
opus_packet_get_samples_per_frame(&packet, 48_000).expect("frame size") as i32;
let expected_offset = frame_size / DRED_FRAME_OFFSET_DIVISOR;
assert_eq!(payload.dred_frame_offset, expected_offset);
}
#[test]
fn compute_quantizer_matches_reference() {
assert_eq!(super::compute_quantizer(6, 0, 15, 0), 6);
assert_eq!(super::compute_quantizer(6, 1, 15, 8), 7);
assert_eq!(super::compute_quantizer(14, 7, 15, 10), 15);
}
#[test]
fn dred_random_payloads_do_not_break_processing() {
if !cfg!(feature = "dred") {
return;
}
const MAX_EXTENSION_SIZE: usize = 200;
#[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)
}
}
let decoder = opus_dred_decoder_create().expect("decoder");
let mut dred = opus_dred_alloc().expect("dred alloc");
let seed = seed_from_env();
let iterations = iterations_from_env();
let mut rng = FastRand::new(seed);
let mut payload = [0u8; MAX_EXTENSION_SIZE];
for _ in 0..iterations {
let len = (rng.next() as usize) % (MAX_EXTENSION_SIZE + 1);
for byte in payload.iter_mut().take(len) {
*byte = (rng.next() & 0xFF) as u8;
}
let mut dred_end = 0;
let defer_processing = (rng.next() & 0x1) != 0;
let res = opus_dred_parse(
&decoder,
&mut dred,
&payload[..len],
48_000,
48_000,
Some(&mut dred_end),
defer_processing,
);
if let Ok(samples) = res {
if samples > 0 {
let src = dred.clone();
let process = opus_dred_process(&decoder, &src, &mut dred);
assert_eq!(process, Ok(()));
assert!(samples >= dred_end);
}
}
}
opus_dred_free(dred);
opus_dred_decoder_destroy(decoder);
}
#[test]
fn dred_decode_int16_runs_on_plc() {
if !cfg!(feature = "dred") {
return;
}
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder");
let dred = OpusDred::default();
let frame_size = 480;
let mut pcm = vec![1i16; frame_size];
let decoded = opus_decoder_dred_decode(&mut decoder, &dred, 0, &mut pcm, frame_size)
.expect("dred decode");
assert_eq!(decoded, frame_size);
assert!(pcm.iter().all(|&value| value == 0));
}
#[test]
fn dred_decode_int24_runs_on_plc() {
if !cfg!(feature = "dred") {
return;
}
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder");
let dred = OpusDred::default();
let frame_size = 480;
let mut pcm = vec![1i32; frame_size];
let decoded = opus_decoder_dred_decode24(&mut decoder, &dred, 0, &mut pcm, frame_size)
.expect("dred decode");
assert_eq!(decoded, frame_size);
assert!(pcm.iter().all(|&value| value == 0));
}
#[test]
fn dred_decode_float_runs_on_plc() {
if !cfg!(feature = "dred") {
return;
}
let mut decoder = opus_decoder_create(48_000, 1).expect("decoder");
let dred = OpusDred::default();
let frame_size = 480;
let mut pcm = vec![1.0f32; frame_size];
let decoded = opus_decoder_dred_decode_float(&mut decoder, &dred, 0, &mut pcm, frame_size)
.expect("dred decode");
assert_eq!(decoded, frame_size);
assert!(pcm.iter().all(|&value| value == 0.0));
}
}