use opus::{Channels, Decoder};
use crate::codec::constants::{I16_SCALE, OPUS_SAMPLE_RATE_HZ};
use crate::codec::encoder::{OpusChannels, OpusFrameDuration};
pub struct OpusDecoder {
inner: Decoder,
channels: usize,
maximum_frame_samples_per_channel: usize,
}
#[derive(Debug, thiserror::Error)]
pub enum OpusDecodeError {
#[error(
"requested {requested_samples_per_channel} Opus samples per channel exceeds configured maximum {maximum_samples_per_channel}"
)]
FrameDurationExceedsConfiguredMaximum {
requested_samples_per_channel: usize,
maximum_samples_per_channel: usize,
},
#[error("Opus decode failed: {0}")]
Opus(#[from] opus::Error),
}
impl OpusDecoder {
pub fn new() -> Result<Self, opus::Error> {
Self::with_channels(OpusChannels::Mono)
}
pub fn with_channels(channels: OpusChannels) -> Result<Self, opus::Error> {
Self::with_max_frame_duration(channels, OpusFrameDuration::Ms20)
}
pub fn with_max_frame_duration(
channels: OpusChannels,
maximum_frame_duration: OpusFrameDuration,
) -> Result<Self, opus::Error> {
let (ch, n) = match channels {
OpusChannels::Mono => (Channels::Mono, 1),
OpusChannels::Stereo => (Channels::Stereo, 2),
};
Ok(Self {
inner: Decoder::new(OPUS_SAMPLE_RATE_HZ, ch)?,
channels: n,
maximum_frame_samples_per_channel: maximum_frame_duration.samples_at_48k(),
})
}
pub fn decode_into(
&mut self,
payload: &[u8],
out: &mut Vec<f32>,
fec: bool,
) -> Result<usize, OpusDecodeError> {
let maximum_interleaved_samples = self.maximum_frame_samples_per_channel * self.channels;
if maximum_interleaved_samples <= 1_920 {
let mut scratch = [0_i16; 1_920];
decode_packet(
&mut self.inner,
self.channels,
payload,
out,
fec,
&mut scratch[..maximum_interleaved_samples],
)
} else {
let mut scratch = [0_i16; 5_760];
decode_packet(
&mut self.inner,
self.channels,
payload,
out,
fec,
&mut scratch[..maximum_interleaved_samples],
)
}
}
pub fn decode_plc_into(
&mut self,
frame_duration: OpusFrameDuration,
out: &mut Vec<f32>,
) -> Result<usize, OpusDecodeError> {
let frame_samples_per_channel = frame_duration.samples_at_48k();
let total_samples = frame_samples_per_channel * self.channels;
if frame_samples_per_channel > self.maximum_frame_samples_per_channel {
return Err(OpusDecodeError::FrameDurationExceedsConfiguredMaximum {
requested_samples_per_channel: frame_samples_per_channel,
maximum_samples_per_channel: self.maximum_frame_samples_per_channel,
});
}
if total_samples <= 1_920 {
let mut scratch = [0_i16; 1_920];
decode_packet(
&mut self.inner,
self.channels,
&[],
out,
false,
&mut scratch[..total_samples],
)
} else {
let mut scratch = [0_i16; 5_760];
decode_packet(
&mut self.inner,
self.channels,
&[],
out,
false,
&mut scratch[..total_samples],
)
}
}
}
fn decode_packet(
decoder: &mut Decoder,
channels: usize,
payload: &[u8],
output: &mut Vec<f32>,
fec: bool,
scratch: &mut [i16],
) -> Result<usize, OpusDecodeError> {
let decoded_samples_per_channel = decoder.decode(payload, scratch, fec)?;
let decoded_samples = decoded_samples_per_channel * channels;
let before_samples = output.len();
output.resize(before_samples + decoded_samples, 0.0);
for (destination, &source) in output[before_samples..]
.iter_mut()
.zip(&scratch[..decoded_samples])
{
*destination = source as f32 / I16_SCALE;
}
Ok(decoded_samples)
}
impl Default for OpusDecoder {
fn default() -> Self {
Self::new().expect("OpusDecoder::new failed with fixed parameters — libopus not linked?")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::constants::OPUS_FRAME_SAMPLES;
use crate::codec::encoder::OpusEncoder;
#[test]
fn given_encoded_opus_packet_when_decoded_then_contains_960_samples() {
let mut enc = OpusEncoder::new().unwrap();
let mut dec = OpusDecoder::new().unwrap();
let pcm_in = vec![0.0f32; OPUS_FRAME_SAMPLES];
let mut packet = Vec::new();
enc.encode_into(&pcm_in, &mut packet).unwrap();
let mut pcm_out = Vec::new();
let n = dec.decode_into(&packet, &mut pcm_out, false).unwrap();
assert_eq!(n, OPUS_FRAME_SAMPLES);
assert_eq!(pcm_out.len(), OPUS_FRAME_SAMPLES);
}
#[test]
fn given_mono_decoder_when_concealing_10ms_then_480_samples_are_appended() {
let mut decoder = OpusDecoder::new().unwrap();
let mut pcm_out = Vec::with_capacity(480);
let decoded_samples = decoder
.decode_plc_into(OpusFrameDuration::Ms10, &mut pcm_out)
.unwrap();
assert_eq!(decoded_samples, 480);
assert_eq!(pcm_out.len(), 480);
}
#[test]
fn given_stereo_decoder_when_concealing_20ms_then_1920_samples_are_appended() {
let mut decoder = OpusDecoder::with_channels(OpusChannels::Stereo).unwrap();
let mut pcm_out = Vec::with_capacity(1_920);
let decoded_samples = decoder
.decode_plc_into(OpusFrameDuration::Ms20, &mut pcm_out)
.unwrap();
assert_eq!(decoded_samples, 1_920);
assert_eq!(pcm_out.len(), 1_920);
}
#[test]
fn given_presized_output_when_concealing_repeatedly_then_capacity_stays_fixed() {
let mut decoder = OpusDecoder::new().unwrap();
let mut pcm_out = Vec::with_capacity(OPUS_FRAME_SAMPLES);
let initial_capacity_samples = pcm_out.capacity();
decoder
.decode_plc_into(OpusFrameDuration::Ms20, &mut pcm_out)
.unwrap();
pcm_out.clear();
decoder
.decode_plc_into(OpusFrameDuration::Ms20, &mut pcm_out)
.unwrap();
assert_eq!(pcm_out.capacity(), initial_capacity_samples);
}
#[test]
fn given_20ms_decoder_when_60ms_concealment_is_requested_then_typed_bound_error_is_returned() {
let mut decoder = OpusDecoder::new().unwrap();
let mut pcm_out = Vec::new();
let error = decoder
.decode_plc_into(OpusFrameDuration::Ms60, &mut pcm_out)
.unwrap_err();
assert!(matches!(
error,
OpusDecodeError::FrameDurationExceedsConfiguredMaximum {
requested_samples_per_channel: 2_880,
maximum_samples_per_channel: 960,
}
));
assert!(pcm_out.is_empty());
}
}