#![warn(missing_docs)]
use std::ffi::{CStr, c_int};
mod sys;
pub const BUILD_REPOSITORY: &str = sys::BUILD_METADATA_REPOSITORY;
pub const BUILD_VERSION: &str = sys::BUILD_METADATA_VERSION;
pub fn version_string() -> String {
unsafe {
let ptr = sys::opus_get_version_string();
if ptr.is_null() {
return String::from("unknown");
}
CStr::from_ptr(ptr).to_string_lossy().into_owned()
}
}
pub fn packet_get_bandwidth(packet: &[u8]) -> Result<Bandwidth, Error> {
if packet.is_empty() {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "packet_get_bandwidth",
});
}
let result = unsafe { sys::opus_packet_get_bandwidth(packet.as_ptr()) };
Error::check(result, "opus_packet_get_bandwidth")?;
Bandwidth::from_opus(result).ok_or(Error {
code: sys::OPUS_INVALID_PACKET,
function: "packet_get_bandwidth",
})
}
pub fn packet_get_nb_channels(packet: &[u8]) -> Result<u8, Error> {
if packet.is_empty() {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "packet_get_nb_channels",
});
}
let result = unsafe { sys::opus_packet_get_nb_channels(packet.as_ptr()) };
Error::check(result, "opus_packet_get_nb_channels")?;
Ok(result as u8)
}
pub fn packet_get_nb_frames(packet: &[u8]) -> Result<usize, Error> {
let len = Error::len_as_c_int(packet.len(), "opus_packet_get_nb_frames")?;
let result = unsafe { sys::opus_packet_get_nb_frames(packet.as_ptr(), len) };
Error::check(result, "opus_packet_get_nb_frames")?;
Ok(result as usize)
}
pub fn packet_get_samples_per_frame(packet: &[u8], sample_rate: u32) -> Result<usize, Error> {
if packet.is_empty() {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "packet_get_samples_per_frame",
});
}
let result =
unsafe { sys::opus_packet_get_samples_per_frame(packet.as_ptr(), sample_rate as i32) };
Ok(result as usize)
}
pub fn packet_get_nb_samples(packet: &[u8], sample_rate: u32) -> Result<usize, Error> {
let len = Error::len_as_c_int(packet.len(), "opus_packet_get_nb_samples")?;
let result =
unsafe { sys::opus_packet_get_nb_samples(packet.as_ptr(), len, sample_rate as i32) };
Error::check(result, "opus_packet_get_nb_samples")?;
Ok(result as usize)
}
#[derive(Debug)]
pub struct Error {
code: c_int,
function: &'static str,
}
impl Error {
fn check(code: c_int, function: &'static str) -> Result<(), Self> {
if code >= 0 {
Ok(())
} else {
Err(Self { code, function })
}
}
fn len_as_c_int(len: usize, function: &'static str) -> Result<c_int, Self> {
c_int::try_from(len).map_err(|_| Self {
code: sys::OPUS_BAD_ARG,
function,
})
}
pub fn code(&self) -> c_int {
self.code
}
pub fn function(&self) -> &'static str {
self.function
}
fn reason(&self) -> Option<&CStr> {
let reason = unsafe { sys::opus_strerror(self.code) };
if reason.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(reason) })
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(reason) = self.reason() {
write!(
f,
"[{}] {}() failed: code={}, reason={}",
env!("CARGO_PKG_NAME"),
self.function,
self.code,
reason.to_string_lossy()
)
} else {
write!(
f,
"[{}] {}() failed: code={}",
env!("CARGO_PKG_NAME"),
self.function,
self.code
)
}
}
}
impl std::error::Error for Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Application {
Voip,
Audio,
LowDelay,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FrameDuration {
Ms2_5,
Ms5,
Ms10,
Ms20,
Ms40,
Ms60,
}
impl FrameDuration {
fn samples_per_frame(self, sample_rate: u32) -> usize {
match self {
Self::Ms2_5 => sample_rate as usize / 400,
Self::Ms5 => sample_rate as usize / 200,
Self::Ms10 => sample_rate as usize / 100,
Self::Ms20 => sample_rate as usize / 50,
Self::Ms40 => sample_rate as usize / 25,
Self::Ms60 => sample_rate as usize * 3 / 50,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Bandwidth {
Narrowband,
Mediumband,
Wideband,
Superwideband,
Fullband,
}
impl Bandwidth {
fn to_opus(self) -> i32 {
match self {
Self::Narrowband => sys::OPUS_BANDWIDTH_NARROWBAND as i32,
Self::Mediumband => sys::OPUS_BANDWIDTH_MEDIUMBAND as i32,
Self::Wideband => sys::OPUS_BANDWIDTH_WIDEBAND as i32,
Self::Superwideband => sys::OPUS_BANDWIDTH_SUPERWIDEBAND as i32,
Self::Fullband => sys::OPUS_BANDWIDTH_FULLBAND as i32,
}
}
pub fn from_opus(value: i32) -> Option<Self> {
match value as u32 {
sys::OPUS_BANDWIDTH_NARROWBAND => Some(Self::Narrowband),
sys::OPUS_BANDWIDTH_MEDIUMBAND => Some(Self::Mediumband),
sys::OPUS_BANDWIDTH_WIDEBAND => Some(Self::Wideband),
sys::OPUS_BANDWIDTH_SUPERWIDEBAND => Some(Self::Superwideband),
sys::OPUS_BANDWIDTH_FULLBAND => Some(Self::Fullband),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Signal {
Voice,
Music,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ForceChannels {
Mono,
Stereo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InbandFec {
Disabled,
Enabled,
EnabledKeepMusic,
}
#[derive(Debug, Clone)]
pub struct EncoderConfig {
pub sample_rate: u32,
pub channels: u8,
pub bitrate: Option<u32>,
pub application: Option<Application>,
pub frame_duration: Option<FrameDuration>,
pub complexity: Option<u8>,
pub vbr: Option<bool>,
pub vbr_constraint: Option<bool>,
pub max_bandwidth: Option<Bandwidth>,
pub bandwidth: Option<Bandwidth>,
pub signal: Option<Signal>,
pub force_channels: Option<ForceChannels>,
pub inband_fec: Option<InbandFec>,
pub packet_loss_perc: Option<u8>,
pub dtx: Option<bool>,
pub lsb_depth: Option<u8>,
pub prediction_disabled: Option<bool>,
pub phase_inversion_disabled: Option<bool>,
#[cfg(feature = "dred")]
pub dred_duration: Option<u32>,
}
impl EncoderConfig {
pub fn new(sample_rate: u32, channels: u8) -> Self {
Self {
sample_rate,
channels,
bitrate: None,
application: None,
frame_duration: None,
complexity: None,
vbr: None,
vbr_constraint: None,
max_bandwidth: None,
bandwidth: None,
signal: None,
force_channels: None,
inband_fec: None,
packet_loss_perc: None,
dtx: None,
lsb_depth: None,
prediction_disabled: None,
phase_inversion_disabled: None,
#[cfg(feature = "dred")]
dred_duration: None,
}
}
}
#[derive(Debug)]
pub struct Encoder {
inner: *mut sys::OpusEncoder,
channels: u8,
frame_samples: usize,
encode_buf: Vec<u8>,
}
unsafe fn set_encoder_ctl(
inner: *mut sys::OpusEncoder,
request: u32,
value: c_int,
name: &'static str,
) -> Result<(), Error> {
let code = unsafe { sys::opus_encoder_ctl(inner, request as i32, value) };
Error::check(code, name)
}
struct EncoderGuard {
inner: *mut sys::OpusEncoder,
defused: bool,
}
impl EncoderGuard {
fn new(inner: *mut sys::OpusEncoder) -> Self {
Self {
inner,
defused: false,
}
}
fn defuse(mut self) -> *mut sys::OpusEncoder {
self.defused = true;
self.inner
}
}
impl Drop for EncoderGuard {
fn drop(&mut self) {
if !self.defused {
unsafe {
sys::opus_encoder_destroy(self.inner);
}
}
}
}
impl Encoder {
pub fn new(config: EncoderConfig) -> Result<Self, Error> {
if config.sample_rate == 0 {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "Encoder::new(sample_rate)",
});
}
if config.channels == 0 {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "Encoder::new(channels)",
});
}
let application = config.application.unwrap_or(Application::Audio);
let frame_duration = config.frame_duration.unwrap_or(FrameDuration::Ms20);
let frame_samples = frame_duration.samples_per_frame(config.sample_rate);
let app_value = match application {
Application::Voip => sys::OPUS_APPLICATION_VOIP,
Application::Audio => sys::OPUS_APPLICATION_AUDIO,
Application::LowDelay => sys::OPUS_APPLICATION_RESTRICTED_LOWDELAY,
};
let mut error = 0;
let inner = unsafe {
sys::opus_encoder_create(
config.sample_rate as i32,
config.channels as c_int,
app_value as i32,
&mut error,
)
};
Error::check(error, "opus_encoder_create")?;
if inner.is_null() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encoder_create returned null",
});
}
let guard = EncoderGuard::new(inner);
if let Some(bitrate) = config.bitrate {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_BITRATE_REQUEST,
bitrate as c_int,
"opus_encoder_ctl(OPUS_SET_BITRATE)",
)?;
}
}
if let Some(complexity) = config.complexity {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_COMPLEXITY_REQUEST,
complexity as c_int,
"opus_encoder_ctl(OPUS_SET_COMPLEXITY)",
)?;
}
}
if let Some(vbr) = config.vbr {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_VBR_REQUEST,
vbr as c_int,
"opus_encoder_ctl(OPUS_SET_VBR)",
)?;
}
}
if let Some(vbr_constraint) = config.vbr_constraint {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_VBR_CONSTRAINT_REQUEST,
vbr_constraint as c_int,
"opus_encoder_ctl(OPUS_SET_VBR_CONSTRAINT)",
)?;
}
}
if let Some(max_bandwidth) = config.max_bandwidth {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_MAX_BANDWIDTH_REQUEST,
max_bandwidth.to_opus() as c_int,
"opus_encoder_ctl(OPUS_SET_MAX_BANDWIDTH)",
)?;
}
}
if let Some(bandwidth) = config.bandwidth {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_BANDWIDTH_REQUEST,
bandwidth.to_opus() as c_int,
"opus_encoder_ctl(OPUS_SET_BANDWIDTH)",
)?;
}
}
if let Some(signal) = config.signal {
let value = match signal {
Signal::Voice => sys::OPUS_SIGNAL_VOICE as c_int,
Signal::Music => sys::OPUS_SIGNAL_MUSIC as c_int,
};
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_SIGNAL_REQUEST,
value,
"opus_encoder_ctl(OPUS_SET_SIGNAL)",
)?;
}
}
if let Some(force_channels) = config.force_channels {
let value = match force_channels {
ForceChannels::Mono => 1,
ForceChannels::Stereo => 2,
};
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_FORCE_CHANNELS_REQUEST,
value,
"opus_encoder_ctl(OPUS_SET_FORCE_CHANNELS)",
)?;
}
}
if let Some(inband_fec) = config.inband_fec {
let value = match inband_fec {
InbandFec::Disabled => 0,
InbandFec::Enabled => 1,
InbandFec::EnabledKeepMusic => 2,
};
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_INBAND_FEC_REQUEST,
value,
"opus_encoder_ctl(OPUS_SET_INBAND_FEC)",
)?;
}
}
if let Some(packet_loss_perc) = config.packet_loss_perc {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_PACKET_LOSS_PERC_REQUEST,
packet_loss_perc as c_int,
"opus_encoder_ctl(OPUS_SET_PACKET_LOSS_PERC)",
)?;
}
}
if let Some(dtx) = config.dtx {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_DTX_REQUEST,
dtx as c_int,
"opus_encoder_ctl(OPUS_SET_DTX)",
)?;
}
}
if let Some(lsb_depth) = config.lsb_depth {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_LSB_DEPTH_REQUEST,
lsb_depth as c_int,
"opus_encoder_ctl(OPUS_SET_LSB_DEPTH)",
)?;
}
}
if let Some(prediction_disabled) = config.prediction_disabled {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_PREDICTION_DISABLED_REQUEST,
prediction_disabled as c_int,
"opus_encoder_ctl(OPUS_SET_PREDICTION_DISABLED)",
)?;
}
}
if let Some(phase_inversion_disabled) = config.phase_inversion_disabled {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST,
phase_inversion_disabled as c_int,
"opus_encoder_ctl(OPUS_SET_PHASE_INVERSION_DISABLED)",
)?;
}
}
#[cfg(feature = "dred")]
if let Some(dred_duration) = config.dred_duration {
unsafe {
set_encoder_ctl(
inner,
sys::OPUS_SET_DRED_DURATION_REQUEST,
dred_duration as c_int,
"opus_encoder_ctl(OPUS_SET_DRED_DURATION)",
)?;
}
}
let pcm_size = frame_samples * config.channels as usize * size_of::<i16>();
let encode_buf_size = 4000.max(pcm_size);
let inner = guard.defuse();
Ok(Self {
inner,
channels: config.channels,
frame_samples,
encode_buf: vec![0u8; encode_buf_size],
})
}
pub fn get_lookahead(&self) -> Result<u16, Error> {
let mut value = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_LOOKAHEAD_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_LOOKAHEAD)")?;
}
Ok(value as u16)
}
pub fn frame_samples(&self) -> usize {
self.frame_samples
}
pub fn reset(&mut self) -> Result<(), Error> {
unsafe {
let code = sys::opus_encoder_ctl(self.inner, sys::OPUS_RESET_STATE as i32);
Error::check(code, "opus_encoder_ctl(OPUS_RESET_STATE)")
}
}
pub fn get_bitrate(&self) -> Result<u32, Error> {
let mut value: c_int = 0;
unsafe {
let code =
sys::opus_encoder_ctl(self.inner, sys::OPUS_GET_BITRATE_REQUEST as i32, &mut value);
Error::check(code, "opus_encoder_ctl(OPUS_GET_BITRATE)")?;
}
Ok(value as u32)
}
pub fn get_bandwidth(&self) -> Result<Bandwidth, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_BANDWIDTH_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_BANDWIDTH)")?;
}
Bandwidth::from_opus(value as i32).ok_or(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encoder_ctl(OPUS_GET_BANDWIDTH)",
})
}
pub fn get_complexity(&self) -> Result<u8, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_COMPLEXITY_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_COMPLEXITY)")?;
}
Ok(value as u8)
}
pub fn get_vbr(&self) -> Result<bool, Error> {
let mut value: c_int = 0;
unsafe {
let code =
sys::opus_encoder_ctl(self.inner, sys::OPUS_GET_VBR_REQUEST as i32, &mut value);
Error::check(code, "opus_encoder_ctl(OPUS_GET_VBR)")?;
}
Ok(value != 0)
}
pub fn get_inband_fec(&self) -> Result<InbandFec, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_INBAND_FEC_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_INBAND_FEC)")?;
}
match value {
0 => Ok(InbandFec::Disabled),
1 => Ok(InbandFec::Enabled),
2 => Ok(InbandFec::EnabledKeepMusic),
_ => Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encoder_ctl(OPUS_GET_INBAND_FEC)",
}),
}
}
pub fn get_dtx(&self) -> Result<bool, Error> {
let mut value: c_int = 0;
unsafe {
let code =
sys::opus_encoder_ctl(self.inner, sys::OPUS_GET_DTX_REQUEST as i32, &mut value);
Error::check(code, "opus_encoder_ctl(OPUS_GET_DTX)")?;
}
Ok(value != 0)
}
#[cfg(feature = "dred")]
pub fn get_dred_duration(&self) -> Result<u32, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_DRED_DURATION_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_DRED_DURATION)")?;
}
Ok(value as u32)
}
pub fn get_sample_rate(&self) -> Result<u32, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_encoder_ctl(
self.inner,
sys::OPUS_GET_SAMPLE_RATE_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_encoder_ctl(OPUS_GET_SAMPLE_RATE)")?;
}
Ok(value as u32)
}
pub fn encode(&mut self, pcm: &[i16]) -> Result<Vec<u8>, Error> {
self.check_pcm_length(pcm.len(), "Encoder::encode")?;
let size = unsafe {
sys::opus_encode(
self.inner,
pcm.as_ptr(),
self.frame_samples as c_int,
self.encode_buf.as_mut_ptr(),
self.encode_buf.len() as c_int,
)
};
Error::check(size, "opus_encode")?;
if size as usize > self.encode_buf.len() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encode returned size exceeding buffer",
});
}
Ok(self.encode_buf[..size as usize].to_vec())
}
pub fn encode_f32(&mut self, pcm: &[f32]) -> Result<Vec<u8>, Error> {
self.check_pcm_length(pcm.len(), "Encoder::encode_f32")?;
let size = unsafe {
sys::opus_encode_float(
self.inner,
pcm.as_ptr(),
self.frame_samples as c_int,
self.encode_buf.as_mut_ptr(),
self.encode_buf.len() as c_int,
)
};
Error::check(size, "opus_encode_float")?;
if size as usize > self.encode_buf.len() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encode_float returned size exceeding buffer",
});
}
Ok(self.encode_buf[..size as usize].to_vec())
}
pub fn encode_i24(&mut self, pcm: &[i32]) -> Result<Vec<u8>, Error> {
self.check_pcm_length(pcm.len(), "Encoder::encode_i24")?;
let size = unsafe {
sys::opus_encode24(
self.inner,
pcm.as_ptr(),
self.frame_samples as c_int,
self.encode_buf.as_mut_ptr(),
self.encode_buf.len() as c_int,
)
};
Error::check(size, "opus_encode24")?;
if size as usize > self.encode_buf.len() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_encode24 returned size exceeding buffer",
});
}
Ok(self.encode_buf[..size as usize].to_vec())
}
fn check_pcm_length(&self, len: usize, function: &'static str) -> Result<(), Error> {
let expected = self.frame_samples * self.channels as usize;
if len != expected {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function,
});
}
Ok(())
}
}
impl Drop for Encoder {
fn drop(&mut self) {
unsafe {
sys::opus_encoder_destroy(self.inner);
}
}
}
unsafe impl Send for Encoder {}
#[derive(Debug, Clone)]
pub struct DecoderConfig {
pub sample_rate: u32,
pub channels: u8,
pub frame_duration: Option<FrameDuration>,
pub gain: Option<i32>,
}
impl DecoderConfig {
pub fn new(sample_rate: u32, channels: u8) -> Self {
Self {
sample_rate,
channels,
frame_duration: None,
gain: None,
}
}
}
struct DecoderGuard {
inner: *mut sys::OpusDecoder,
defused: bool,
}
impl DecoderGuard {
fn new(inner: *mut sys::OpusDecoder) -> Self {
Self {
inner,
defused: false,
}
}
fn defuse(mut self) -> *mut sys::OpusDecoder {
self.defused = true;
self.inner
}
}
impl Drop for DecoderGuard {
fn drop(&mut self) {
if !self.defused {
unsafe {
sys::opus_decoder_destroy(self.inner);
}
}
}
}
#[derive(Debug)]
pub struct Decoder {
inner: *mut sys::OpusDecoder,
channels: u8,
frame_samples: usize,
decode_buf: Vec<i16>,
}
impl Decoder {
pub fn new(config: DecoderConfig) -> Result<Self, Error> {
if config.sample_rate == 0 {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "Decoder::new(sample_rate)",
});
}
if config.channels == 0 {
return Err(Error {
code: sys::OPUS_BAD_ARG,
function: "Decoder::new(channels)",
});
}
let frame_duration = config.frame_duration.unwrap_or(FrameDuration::Ms20);
let frame_samples = frame_duration.samples_per_frame(config.sample_rate);
let mut error: c_int = 0;
let inner = unsafe {
sys::opus_decoder_create(
config.sample_rate as i32,
config.channels as c_int,
&mut error,
)
};
Error::check(error, "opus_decoder_create")?;
if inner.is_null() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decoder_create returned null",
});
}
let guard = DecoderGuard::new(inner);
if let Some(gain) = config.gain {
unsafe {
let code =
sys::opus_decoder_ctl(inner, sys::OPUS_SET_GAIN_REQUEST as i32, gain as c_int);
Error::check(code, "opus_decoder_ctl(OPUS_SET_GAIN)")?;
}
}
let inner = guard.defuse();
Ok(Self {
inner,
channels: config.channels,
frame_samples,
decode_buf: Vec::new(),
})
}
pub fn frame_samples(&self) -> usize {
self.frame_samples
}
pub fn reset(&mut self) -> Result<(), Error> {
unsafe {
let code = sys::opus_decoder_ctl(self.inner, sys::OPUS_RESET_STATE as i32);
Error::check(code, "opus_decoder_ctl(OPUS_RESET_STATE)")
}
}
pub fn get_bandwidth(&self) -> Result<Bandwidth, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_decoder_ctl(
self.inner,
sys::OPUS_GET_BANDWIDTH_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_decoder_ctl(OPUS_GET_BANDWIDTH)")?;
}
Bandwidth::from_opus(value as i32).ok_or(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decoder_ctl(OPUS_GET_BANDWIDTH)",
})
}
pub fn get_gain(&self) -> Result<i32, Error> {
let mut value: c_int = 0;
unsafe {
let code =
sys::opus_decoder_ctl(self.inner, sys::OPUS_GET_GAIN_REQUEST as i32, &mut value);
Error::check(code, "opus_decoder_ctl(OPUS_GET_GAIN)")?;
}
Ok(value as i32)
}
pub fn get_last_packet_duration(&self) -> Result<usize, Error> {
let mut value: c_int = 0;
unsafe {
let code = sys::opus_decoder_ctl(
self.inner,
sys::OPUS_GET_LAST_PACKET_DURATION_REQUEST as i32,
&mut value,
);
Error::check(code, "opus_decoder_ctl(OPUS_GET_LAST_PACKET_DURATION)")?;
}
Ok(value as usize)
}
pub fn get_pitch(&self) -> Result<i32, Error> {
let mut value: c_int = 0;
unsafe {
let code =
sys::opus_decoder_ctl(self.inner, sys::OPUS_GET_PITCH_REQUEST as i32, &mut value);
Error::check(code, "opus_decoder_ctl(OPUS_GET_PITCH)")?;
}
Ok(value as i32)
}
pub fn decode(&mut self, encoded: &[u8]) -> Result<Vec<i16>, Error> {
self.decode_i16_internal(encoded, 0)
}
pub fn decode_fec(&mut self, encoded: &[u8]) -> Result<Vec<i16>, Error> {
self.decode_i16_internal(encoded, 1)
}
pub fn decode_plc(&mut self) -> Result<Vec<i16>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
self.decode_buf.resize(buf_size, 0);
let decoded_samples = unsafe {
sys::opus_decode(
self.inner,
std::ptr::null(),
0,
self.decode_buf.as_mut_ptr(),
self.frame_samples as c_int,
0,
)
};
Error::check(decoded_samples, "opus_decode(PLC)")?;
let actual_size = decoded_samples as usize * self.channels as usize;
Ok(self.decode_buf[..actual_size].to_vec())
}
pub fn decode_f32(&mut self, encoded: &[u8]) -> Result<Vec<f32>, Error> {
self.decode_f32_internal(encoded, 0)
}
pub fn decode_fec_f32(&mut self, encoded: &[u8]) -> Result<Vec<f32>, Error> {
self.decode_f32_internal(encoded, 1)
}
pub fn decode_plc_f32(&mut self) -> Result<Vec<f32>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
let mut buf = vec![0.0f32; buf_size];
let decoded_samples = unsafe {
sys::opus_decode_float(
self.inner,
std::ptr::null(),
0,
buf.as_mut_ptr(),
self.frame_samples as c_int,
0,
)
};
Error::check(decoded_samples, "opus_decode_float(PLC)")?;
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
pub fn decode_i24(&mut self, encoded: &[u8]) -> Result<Vec<i32>, Error> {
self.decode_i24_internal(encoded, 0)
}
pub fn decode_fec_i24(&mut self, encoded: &[u8]) -> Result<Vec<i32>, Error> {
self.decode_i24_internal(encoded, 1)
}
pub fn decode_plc_i24(&mut self) -> Result<Vec<i32>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
let mut buf = vec![0i32; buf_size];
let decoded_samples = unsafe {
sys::opus_decode24(
self.inner,
std::ptr::null(),
0,
buf.as_mut_ptr(),
self.frame_samples as c_int,
0,
)
};
Error::check(decoded_samples, "opus_decode24(PLC)")?;
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
#[cfg(feature = "dred")]
pub fn dred_decode(&mut self, dred: &Dred, dred_offset: i32) -> Result<Vec<i16>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
self.decode_buf.resize(buf_size, 0);
let decoded_samples = unsafe {
sys::opus_decoder_dred_decode(
self.inner,
dred.inner,
dred_offset,
self.decode_buf.as_mut_ptr(),
self.frame_samples as c_int,
)
};
Error::check(decoded_samples, "opus_decoder_dred_decode")?;
if decoded_samples as usize > self.frame_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decoder_dred_decode returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
Ok(self.decode_buf[..actual_size].to_vec())
}
#[cfg(feature = "dred")]
pub fn dred_decode_f32(&mut self, dred: &Dred, dred_offset: i32) -> Result<Vec<f32>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
let mut buf = vec![0.0f32; buf_size];
let decoded_samples = unsafe {
sys::opus_decoder_dred_decode_float(
self.inner,
dred.inner,
dred_offset,
buf.as_mut_ptr(),
self.frame_samples as c_int,
)
};
Error::check(decoded_samples, "opus_decoder_dred_decode_float")?;
if decoded_samples as usize > self.frame_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decoder_dred_decode_float returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
#[cfg(feature = "dred")]
pub fn dred_decode_i24(&mut self, dred: &Dred, dred_offset: i32) -> Result<Vec<i32>, Error> {
let buf_size = self.frame_samples * self.channels as usize;
let mut buf = vec![0i32; buf_size];
let decoded_samples = unsafe {
sys::opus_decoder_dred_decode24(
self.inner,
dred.inner,
dred_offset,
buf.as_mut_ptr(),
self.frame_samples as c_int,
)
};
Error::check(decoded_samples, "opus_decoder_dred_decode24")?;
if decoded_samples as usize > self.frame_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decoder_dred_decode24 returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
fn decode_i16_internal(&mut self, encoded: &[u8], fec: c_int) -> Result<Vec<i16>, Error> {
let encoded_len = Error::len_as_c_int(encoded.len(), "opus_decode")?;
let nb_samples = self.get_nb_samples(encoded)?;
let buf_size = nb_samples * self.channels as usize;
self.decode_buf.resize(buf_size, 0);
let decoded_samples = unsafe {
sys::opus_decode(
self.inner,
encoded.as_ptr(),
encoded_len,
self.decode_buf.as_mut_ptr(),
nb_samples as c_int,
fec,
)
};
Error::check(decoded_samples, "opus_decode")?;
if decoded_samples as usize > nb_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decode returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
Ok(self.decode_buf[..actual_size].to_vec())
}
fn decode_f32_internal(&mut self, encoded: &[u8], fec: c_int) -> Result<Vec<f32>, Error> {
let encoded_len = Error::len_as_c_int(encoded.len(), "opus_decode_float")?;
let nb_samples = self.get_nb_samples(encoded)?;
let buf_size = nb_samples * self.channels as usize;
let mut buf = vec![0.0f32; buf_size];
let decoded_samples = unsafe {
sys::opus_decode_float(
self.inner,
encoded.as_ptr(),
encoded_len,
buf.as_mut_ptr(),
nb_samples as c_int,
fec,
)
};
Error::check(decoded_samples, "opus_decode_float")?;
if decoded_samples as usize > nb_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decode_float returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
fn decode_i24_internal(&mut self, encoded: &[u8], fec: c_int) -> Result<Vec<i32>, Error> {
let encoded_len = Error::len_as_c_int(encoded.len(), "opus_decode24")?;
let nb_samples = self.get_nb_samples(encoded)?;
let buf_size = nb_samples * self.channels as usize;
let mut buf = vec![0i32; buf_size];
let decoded_samples = unsafe {
sys::opus_decode24(
self.inner,
encoded.as_ptr(),
encoded_len,
buf.as_mut_ptr(),
nb_samples as c_int,
fec,
)
};
Error::check(decoded_samples, "opus_decode24")?;
if decoded_samples as usize > nb_samples {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_decode24 returned samples exceeding buffer",
});
}
let actual_size = decoded_samples as usize * self.channels as usize;
buf.truncate(actual_size);
Ok(buf)
}
fn get_nb_samples(&self, packet: &[u8]) -> Result<usize, Error> {
let len = Error::len_as_c_int(packet.len(), "opus_decoder_get_nb_samples")?;
unsafe {
let samples = sys::opus_decoder_get_nb_samples(self.inner, packet.as_ptr(), len);
Error::check(samples, "opus_decoder_get_nb_samples")?;
Ok(samples as usize)
}
}
}
impl Drop for Decoder {
fn drop(&mut self) {
unsafe {
sys::opus_decoder_destroy(self.inner);
}
}
}
unsafe impl Send for Decoder {}
#[cfg(feature = "dred")]
#[derive(Debug)]
pub struct DredDecoder {
inner: *mut sys::OpusDREDDecoder,
}
#[cfg(feature = "dred")]
impl DredDecoder {
pub fn new() -> Result<Self, Error> {
let mut error: c_int = 0;
let inner = unsafe { sys::opus_dred_decoder_create(&mut error) };
Error::check(error, "opus_dred_decoder_create")?;
if inner.is_null() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_dred_decoder_create returned null",
});
}
Ok(Self { inner })
}
pub fn parse(
&mut self,
dred: &mut Dred,
data: &[u8],
max_dred_samples: i32,
sample_rate: i32,
) -> Result<i32, Error> {
let data_len = Error::len_as_c_int(data.len(), "opus_dred_parse")?;
let mut dred_end: c_int = 0;
let result = unsafe {
sys::opus_dred_parse(
self.inner,
dred.inner,
data.as_ptr(),
data_len,
max_dred_samples,
sample_rate,
&mut dred_end,
0,
)
};
Error::check(result, "opus_dred_parse")?;
Ok(result)
}
pub fn process(&mut self, src: &Dred, dst: &mut Dred) -> Result<(), Error> {
let code = unsafe { sys::opus_dred_process(self.inner, src.inner, dst.inner) };
Error::check(code, "opus_dred_process")
}
}
#[cfg(feature = "dred")]
impl Drop for DredDecoder {
fn drop(&mut self) {
unsafe {
sys::opus_dred_decoder_destroy(self.inner);
}
}
}
#[cfg(feature = "dred")]
unsafe impl Send for DredDecoder {}
#[cfg(feature = "dred")]
#[derive(Debug)]
pub struct Dred {
inner: *mut sys::OpusDRED,
}
#[cfg(feature = "dred")]
impl Dred {
pub fn new() -> Result<Self, Error> {
let mut error: c_int = 0;
let inner = unsafe { sys::opus_dred_alloc(&mut error) };
Error::check(error, "opus_dred_alloc")?;
if inner.is_null() {
return Err(Error {
code: sys::OPUS_INTERNAL_ERROR,
function: "opus_dred_alloc returned null",
});
}
Ok(Self { inner })
}
}
#[cfg(feature = "dred")]
impl Drop for Dred {
fn drop(&mut self) {
unsafe {
sys::opus_dred_free(self.inner);
}
}
}
#[cfg(feature = "dred")]
unsafe impl Send for Dred {}
#[cfg(test)]
mod tests {
use super::*;
const TEST_SAMPLE_RATE: u32 = 48000;
const TEST_CHANNELS: u8 = 2;
const FRAME_SAMPLES: usize = 960;
const FRAME_SIZE: usize = FRAME_SAMPLES * TEST_CHANNELS as usize;
fn encoder_config(bitrate: Option<u32>) -> EncoderConfig {
EncoderConfig {
bitrate,
..EncoderConfig::new(TEST_SAMPLE_RATE, TEST_CHANNELS)
}
}
fn decoder_config() -> DecoderConfig {
DecoderConfig::new(TEST_SAMPLE_RATE, TEST_CHANNELS)
}
fn sine_wave_i16() -> Vec<i16> {
let mut pcm = vec![0i16; FRAME_SIZE];
for i in 0..FRAME_SAMPLES {
let t = i as f64 / TEST_SAMPLE_RATE as f64;
let sample = (2.0 * std::f64::consts::PI * 440.0 * t).sin();
let value = (sample * i16::MAX as f64) as i16;
pcm[i * 2] = value;
pcm[i * 2 + 1] = value;
}
pcm
}
fn sine_wave_f32() -> Vec<f32> {
let mut pcm = vec![0.0f32; FRAME_SIZE];
for i in 0..FRAME_SAMPLES {
let t = i as f64 / TEST_SAMPLE_RATE as f64;
let sample = (2.0 * std::f64::consts::PI * 440.0 * t).sin() as f32;
pcm[i * 2] = sample;
pcm[i * 2 + 1] = sample;
}
pcm
}
fn sine_wave_i24() -> Vec<i32> {
let max_24bit = 0x7F_FFFF_i32;
let mut pcm = vec![0i32; FRAME_SIZE];
for i in 0..FRAME_SAMPLES {
let t = i as f64 / TEST_SAMPLE_RATE as f64;
let sample = (2.0 * std::f64::consts::PI * 440.0 * t).sin();
let value = (sample * max_24bit as f64) as i32;
pcm[i * 2] = value;
pcm[i * 2 + 1] = value;
}
pcm
}
fn rms_i16(pcm: &[i16]) -> f64 {
let sum: f64 = pcm.iter().map(|&s| (s as f64) * (s as f64)).sum();
(sum / pcm.len() as f64).sqrt()
}
fn rms_f32(pcm: &[f32]) -> f64 {
let sum: f64 = pcm.iter().map(|&s| (s as f64) * (s as f64)).sum();
(sum / pcm.len() as f64).sqrt()
}
fn rms_i32(pcm: &[i32]) -> f64 {
let sum: f64 = pcm.iter().map(|&s| (s as f64) * (s as f64)).sum();
(sum / pcm.len() as f64).sqrt()
}
#[test]
fn init_encoder() {
assert!(Encoder::new(encoder_config(Some(64_000))).is_ok());
assert!(Encoder::new(encoder_config(None)).is_ok());
assert!(Encoder::new(encoder_config(Some(0))).is_err());
assert!(
Encoder::new(EncoderConfig {
sample_rate: 0,
..encoder_config(None)
})
.is_err()
);
assert!(
Encoder::new(EncoderConfig {
channels: 0,
..encoder_config(None)
})
.is_err()
);
}
#[test]
fn init_encoder_with_options() {
let config = EncoderConfig {
sample_rate: TEST_SAMPLE_RATE,
channels: TEST_CHANNELS,
bitrate: Some(128_000),
application: Some(Application::Voip),
frame_duration: Some(FrameDuration::Ms20),
complexity: Some(5),
vbr: Some(true),
vbr_constraint: Some(true),
max_bandwidth: Some(Bandwidth::Fullband),
bandwidth: None,
signal: Some(Signal::Voice),
force_channels: None,
inband_fec: Some(InbandFec::Enabled),
packet_loss_perc: Some(10),
dtx: Some(true),
lsb_depth: Some(16),
prediction_disabled: Some(false),
phase_inversion_disabled: Some(false),
#[cfg(feature = "dred")]
dred_duration: Some(5),
};
assert!(Encoder::new(config).is_ok());
}
#[test]
fn init_decoder() {
assert!(Decoder::new(DecoderConfig::new(TEST_SAMPLE_RATE, 2)).is_ok());
assert!(Decoder::new(DecoderConfig::new(TEST_SAMPLE_RATE, 1)).is_ok());
assert!(Decoder::new(DecoderConfig::new(TEST_SAMPLE_RATE, 20)).is_err());
assert!(Decoder::new(DecoderConfig::new(TEST_SAMPLE_RATE, 0)).is_err());
assert!(Decoder::new(DecoderConfig::new(0, 2)).is_err());
}
#[test]
fn init_decoder_with_gain() {
assert!(
Decoder::new(DecoderConfig {
gain: Some(256),
..decoder_config()
})
.is_ok()
);
assert!(
Decoder::new(DecoderConfig {
gain: Some(40000),
..decoder_config()
})
.is_err()
);
}
#[test]
fn encode_pcm_length_mismatch() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
assert!(encoder.encode(&[0i16; 100]).is_err());
}
#[test]
fn encode_decode_roundtrip() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
assert_eq!(decoded.len(), FRAME_SIZE);
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"decoded RMS ({output_rms:.1}) is too low compared to input RMS ({input_rms:.1})"
);
}
#[test]
fn decode_fec() {
let config = EncoderConfig {
application: Some(Application::Voip),
inband_fec: Some(InbandFec::Enabled),
packet_loss_perc: Some(50),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let packet1 = encoder.encode(&input).unwrap();
let packet2 = encoder.encode(&input).unwrap();
decoder.decode(&packet1).unwrap();
let fec_decoded = decoder.decode_fec(&packet2).unwrap();
assert_eq!(fec_decoded.len(), FRAME_SIZE);
let fec_rms = rms_i16(&fec_decoded);
assert!(
fec_rms > 0.0,
"FEC decoded frame should not be silent, got RMS={fec_rms}"
);
let decoded2 = decoder.decode(&packet2).unwrap();
let output_rms = rms_i16(&decoded2);
assert!(
output_rms > 0.0,
"decoded frame after FEC should not be silent"
);
}
#[test]
fn decode_plc() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
let plc = decoder.decode_plc().unwrap();
assert_eq!(plc.len(), FRAME_SIZE);
let plc_rms = rms_i16(&plc);
assert!(
plc_rms > 0.0,
"PLC frame after sine wave should not be silent, got RMS={plc_rms}"
);
}
#[test]
fn encode_f32_decode_f32_roundtrip() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_f32();
let input_rms = rms_f32(&input);
for _ in 0..5 {
let encoded = encoder.encode_f32(&input).unwrap();
decoder.decode_f32(&encoded).unwrap();
}
let encoded = encoder.encode_f32(&input).unwrap();
let decoded = decoder.decode_f32(&encoded).unwrap();
assert_eq!(decoded.len(), FRAME_SIZE);
let output_rms = rms_f32(&decoded);
assert!(
output_rms > input_rms * 0.5,
"decoded RMS ({output_rms:.4}) is too low compared to input RMS ({input_rms:.4})"
);
}
#[test]
fn decode_fec_f32() {
let config = EncoderConfig {
application: Some(Application::Voip),
inband_fec: Some(InbandFec::Enabled),
packet_loss_perc: Some(50),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_f32();
for _ in 0..5 {
let encoded = encoder.encode_f32(&input).unwrap();
decoder.decode_f32(&encoded).unwrap();
}
let packet1 = encoder.encode_f32(&input).unwrap();
let packet2 = encoder.encode_f32(&input).unwrap();
decoder.decode_f32(&packet1).unwrap();
let fec_decoded = decoder.decode_fec_f32(&packet2).unwrap();
assert_eq!(fec_decoded.len(), FRAME_SIZE);
let fec_rms = rms_f32(&fec_decoded);
assert!(
fec_rms > 0.0,
"FEC decoded f32 frame should not be silent, got RMS={fec_rms}"
);
let decoded2 = decoder.decode_f32(&packet2).unwrap();
let output_rms = rms_f32(&decoded2);
assert!(
output_rms > 0.0,
"decoded f32 frame after FEC should not be silent"
);
}
#[test]
fn decode_plc_f32() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_f32();
for _ in 0..5 {
let encoded = encoder.encode_f32(&input).unwrap();
decoder.decode_f32(&encoded).unwrap();
}
let encoded = encoder.encode_f32(&input).unwrap();
decoder.decode_f32(&encoded).unwrap();
let plc = decoder.decode_plc_f32().unwrap();
assert_eq!(plc.len(), FRAME_SIZE);
let plc_rms = rms_f32(&plc);
assert!(
plc_rms > 0.0,
"PLC f32 frame after sine wave should not be silent, got RMS={plc_rms}"
);
}
#[test]
fn encode_i24_decode_i24_roundtrip() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i24();
let input_rms = rms_i32(&input);
for _ in 0..5 {
let encoded = encoder.encode_i24(&input).unwrap();
decoder.decode_i24(&encoded).unwrap();
}
let encoded = encoder.encode_i24(&input).unwrap();
let decoded = decoder.decode_i24(&encoded).unwrap();
assert_eq!(decoded.len(), FRAME_SIZE);
let output_rms = rms_i32(&decoded);
assert!(
output_rms > input_rms * 0.5,
"decoded i24 RMS ({output_rms:.1}) is too low compared to input RMS ({input_rms:.1})"
);
}
#[test]
fn decode_fec_i24() {
let config = EncoderConfig {
application: Some(Application::Voip),
inband_fec: Some(InbandFec::Enabled),
packet_loss_perc: Some(50),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i24();
for _ in 0..5 {
let encoded = encoder.encode_i24(&input).unwrap();
decoder.decode_i24(&encoded).unwrap();
}
let packet1 = encoder.encode_i24(&input).unwrap();
let packet2 = encoder.encode_i24(&input).unwrap();
decoder.decode_i24(&packet1).unwrap();
let fec_decoded = decoder.decode_fec_i24(&packet2).unwrap();
assert_eq!(fec_decoded.len(), FRAME_SIZE);
let fec_rms = rms_i32(&fec_decoded);
assert!(
fec_rms > 0.0,
"FEC decoded i24 frame should not be silent, got RMS={fec_rms}"
);
let decoded2 = decoder.decode_i24(&packet2).unwrap();
let output_rms = rms_i32(&decoded2);
assert!(
output_rms > 0.0,
"decoded i24 frame after FEC should not be silent"
);
}
#[test]
fn decode_plc_i24() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i24();
for _ in 0..5 {
let encoded = encoder.encode_i24(&input).unwrap();
decoder.decode_i24(&encoded).unwrap();
}
let encoded = encoder.encode_i24(&input).unwrap();
decoder.decode_i24(&encoded).unwrap();
let plc = decoder.decode_plc_i24().unwrap();
assert_eq!(plc.len(), FRAME_SIZE);
let plc_rms = rms_i32(&plc);
assert!(
plc_rms > 0.0,
"PLC i24 frame after sine wave should not be silent, got RMS={plc_rms}"
);
}
#[test]
fn decoder_reset() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
decoder.reset().unwrap();
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"decoded RMS after reset ({output_rms:.1}) is too low"
);
}
fn sine_wave_i16_params(sample_rate: u32, channels: u8, frame_samples: usize) -> Vec<i16> {
let total = frame_samples * channels as usize;
let mut pcm = vec![0i16; total];
for i in 0..frame_samples {
let t = i as f64 / sample_rate as f64;
let sample = (2.0 * std::f64::consts::PI * 440.0 * t).sin();
let value = (sample * i16::MAX as f64) as i16;
for ch in 0..channels as usize {
pcm[i * channels as usize + ch] = value;
}
}
pcm
}
fn roundtrip_params(sample_rate: u32, channels: u8, frame_duration: FrameDuration) {
let frame_samples = frame_duration.samples_per_frame(sample_rate);
let enc_config = EncoderConfig {
sample_rate,
channels,
bitrate: Some(64_000),
frame_duration: Some(frame_duration),
..EncoderConfig::new(sample_rate, channels)
};
let dec_config = DecoderConfig {
sample_rate,
channels,
frame_duration: Some(frame_duration),
gain: None,
};
let mut encoder = Encoder::new(enc_config).unwrap();
let mut decoder = Decoder::new(dec_config).unwrap();
let input = sine_wave_i16_params(sample_rate, channels, frame_samples);
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
assert_eq!(
decoded.len(),
frame_samples * channels as usize,
"sample_rate={sample_rate}, channels={channels}, frame_duration={frame_duration:?}"
);
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"sample_rate={sample_rate}, channels={channels}, frame_duration={frame_duration:?}: decoded RMS ({output_rms:.1}) is too low compared to input RMS ({input_rms:.1})"
);
}
#[test]
fn roundtrip_8000hz() {
roundtrip_params(8000, 2, FrameDuration::Ms20);
}
#[test]
fn roundtrip_12000hz() {
roundtrip_params(12000, 2, FrameDuration::Ms20);
}
#[test]
fn roundtrip_16000hz() {
roundtrip_params(16000, 2, FrameDuration::Ms20);
}
#[test]
fn roundtrip_24000hz() {
roundtrip_params(24000, 2, FrameDuration::Ms20);
}
#[test]
fn roundtrip_mono() {
roundtrip_params(48000, 1, FrameDuration::Ms20);
}
#[test]
fn roundtrip_frame_2_5ms() {
roundtrip_params(48000, 2, FrameDuration::Ms2_5);
}
#[test]
fn roundtrip_frame_5ms() {
roundtrip_params(48000, 2, FrameDuration::Ms5);
}
#[test]
fn roundtrip_frame_10ms() {
roundtrip_params(48000, 2, FrameDuration::Ms10);
}
#[test]
fn roundtrip_frame_40ms() {
roundtrip_params(48000, 2, FrameDuration::Ms40);
}
#[test]
fn roundtrip_frame_60ms() {
roundtrip_params(48000, 2, FrameDuration::Ms60);
}
#[test]
fn roundtrip_voip() {
let config = EncoderConfig {
application: Some(Application::Voip),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"Voip mode: decoded RMS ({output_rms:.1}) is too low"
);
}
#[test]
fn roundtrip_low_delay() {
let config = EncoderConfig {
application: Some(Application::LowDelay),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"LowDelay mode: decoded RMS ({output_rms:.1}) is too low"
);
}
#[test]
fn continuous_frames_100() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for i in 0..100 {
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
assert_eq!(decoded.len(), FRAME_SIZE, "frame {i}: wrong decoded length");
if i >= 5 {
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"frame {i}: decoded RMS ({output_rms:.1}) is too low"
);
}
}
}
#[test]
fn encode_decode_min_bitrate() {
let enc_config = EncoderConfig {
bitrate: Some(500),
..EncoderConfig::new(48000, 2)
};
let mut encoder = Encoder::new(enc_config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
for _ in 0..6 {
let encoded = encoder.encode(&input).unwrap();
assert!(!encoded.is_empty());
decoder.decode(&encoded).unwrap();
}
}
#[test]
fn encode_decode_max_bitrate() {
let enc_config = EncoderConfig {
bitrate: Some(512_000),
..EncoderConfig::new(48000, 2)
};
let mut encoder = Encoder::new(enc_config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"max bitrate: decoded RMS ({output_rms:.1}) is too low"
);
}
#[test]
fn encode_decode_complexity_0() {
let config = EncoderConfig {
complexity: Some(0),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(config).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"complexity 0: decoded RMS ({output_rms:.1}) is too low"
);
}
#[test]
fn encoder_multiple_reset() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let input = sine_wave_i16();
for _ in 0..5 {
encoder.encode(&input).unwrap();
encoder.reset().unwrap();
}
let encoded = encoder.encode(&input).unwrap();
assert!(!encoded.is_empty());
}
#[test]
fn decoder_multiple_reset() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let input_rms = rms_i16(&input);
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
decoder.reset().unwrap();
}
for _ in 0..5 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let decoded = decoder.decode(&encoded).unwrap();
let output_rms = rms_i16(&decoded);
assert!(
output_rms > input_rms * 0.5,
"decoded RMS after multiple resets ({output_rms:.1}) is too low"
);
}
#[test]
fn decode_corrupted_packet() {
let mut decoder = Decoder::new(decoder_config()).unwrap();
let corrupted = vec![0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xFF, 0x42, 0x13];
let _ = decoder.decode(&corrupted);
}
#[test]
fn decode_truncated_packet() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut decoder = Decoder::new(decoder_config()).unwrap();
let input = sine_wave_i16();
let encoded = encoder.encode(&input).unwrap();
let truncated = &encoded[..encoded.len() / 2];
let _ = decoder.decode(truncated);
}
#[test]
fn decode_single_byte_packet() {
let mut decoder = Decoder::new(decoder_config()).unwrap();
let _ = decoder.decode(&[0x00]);
}
#[test]
fn encoder_is_send() {
fn assert_send<T: Send>() {}
assert_send::<Encoder>();
}
#[test]
fn decoder_is_send() {
fn assert_send<T: Send>() {}
assert_send::<Decoder>();
}
#[test]
fn error_reason() {
let e = Error::check(sys::OPUS_BAD_ARG, "test").expect_err("not an error");
assert!(e.reason().is_some());
}
#[test]
fn opus_version_string() {
let version = version_string();
assert!(version.starts_with("libopus"));
}
#[test]
fn encoder_get_lookahead() {
let encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let lookahead = encoder.get_lookahead().unwrap();
assert!(lookahead > 0);
}
#[test]
fn packet_info_from_encoded() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let input = sine_wave_i16();
let encoded = encoder.encode(&input).unwrap();
let bw = packet_get_bandwidth(&encoded).unwrap();
assert!(
bw == Bandwidth::Fullband || bw == Bandwidth::Superwideband,
"unexpected bandwidth: {bw:?}"
);
let channels = packet_get_nb_channels(&encoded).unwrap();
assert!(
channels == 1 || channels == 2,
"unexpected channels: {channels}"
);
let nb_frames = packet_get_nb_frames(&encoded).unwrap();
assert!(nb_frames >= 1, "expected at least 1 frame, got {nb_frames}");
let samples_per_frame = packet_get_samples_per_frame(&encoded, TEST_SAMPLE_RATE).unwrap();
assert!(
samples_per_frame > 0,
"expected positive samples_per_frame, got {samples_per_frame}"
);
let nb_samples = packet_get_nb_samples(&encoded, TEST_SAMPLE_RATE).unwrap();
assert_eq!(nb_samples, samples_per_frame * nb_frames);
}
#[test]
fn packet_samples_per_frame_all_rates() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let input = sine_wave_i16();
let encoded = encoder.encode(&input).unwrap();
for &rate in &[8000u32, 12000, 16000, 24000, 48000] {
let spf = packet_get_samples_per_frame(&encoded, rate).unwrap();
assert!(spf > 0, "rate={rate}: expected positive samples_per_frame");
}
}
#[test]
fn packet_info_empty_packet() {
assert!(packet_get_bandwidth(&[]).is_err());
assert!(packet_get_nb_channels(&[]).is_err());
assert!(packet_get_samples_per_frame(&[], 48000).is_err());
}
#[test]
fn bandwidth_from_opus_roundtrip() {
for bw in [
Bandwidth::Narrowband,
Bandwidth::Mediumband,
Bandwidth::Wideband,
Bandwidth::Superwideband,
Bandwidth::Fullband,
] {
let opus_value = bw.to_opus();
let converted = Bandwidth::from_opus(opus_value).unwrap();
assert_eq!(bw, converted);
}
assert!(Bandwidth::from_opus(9999).is_none());
}
#[test]
fn encoder_get_ctls() {
let config = EncoderConfig {
bitrate: Some(96_000),
complexity: Some(5),
vbr: Some(false),
inband_fec: Some(InbandFec::Enabled),
dtx: Some(true),
..EncoderConfig::new(48000, 2)
};
let encoder = Encoder::new(config).unwrap();
assert_eq!(encoder.get_bitrate().unwrap(), 96_000);
assert_eq!(encoder.get_complexity().unwrap(), 5);
assert!(!encoder.get_vbr().unwrap());
assert_eq!(encoder.get_inband_fec().unwrap(), InbandFec::Enabled);
assert!(encoder.get_dtx().unwrap());
assert_eq!(encoder.get_sample_rate().unwrap(), 48000);
}
#[test]
fn encoder_get_bandwidth_after_encode() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let input = sine_wave_i16();
encoder.encode(&input).unwrap();
let bw = encoder.get_bandwidth().unwrap();
assert!(
bw == Bandwidth::Fullband
|| bw == Bandwidth::Superwideband
|| bw == Bandwidth::Wideband,
"unexpected bandwidth: {bw:?}"
);
}
#[test]
fn decoder_get_ctls() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let config = DecoderConfig {
gain: Some(256),
..decoder_config()
};
let mut decoder = Decoder::new(config).unwrap();
assert_eq!(decoder.get_gain().unwrap(), 256);
assert_eq!(decoder.get_last_packet_duration().unwrap(), 0);
let input = sine_wave_i16();
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
let duration = decoder.get_last_packet_duration().unwrap();
assert_eq!(duration, FRAME_SAMPLES);
let bw = decoder.get_bandwidth().unwrap();
assert!(
bw == Bandwidth::Fullband
|| bw == Bandwidth::Superwideband
|| bw == Bandwidth::Wideband,
"unexpected bandwidth: {bw:?}"
);
let pitch = decoder.get_pitch().unwrap();
assert!(pitch >= 0, "unexpected pitch: {pitch}");
}
#[cfg(feature = "dred")]
#[test]
fn dred_roundtrip() {
let enc_config = EncoderConfig {
dred_duration: Some(10),
..encoder_config(Some(64_000))
};
let mut encoder = Encoder::new(enc_config).unwrap();
assert_eq!(encoder.get_dred_duration().unwrap(), 10);
let mut decoder = Decoder::new(decoder_config()).unwrap();
let mut dred_decoder = DredDecoder::new().unwrap();
let mut dred = Dred::new().unwrap();
let input = sine_wave_i16();
for _ in 0..10 {
let encoded = encoder.encode(&input).unwrap();
decoder.decode(&encoded).unwrap();
}
let encoded = encoder.encode(&input).unwrap();
let offset = dred_decoder
.parse(&mut dred, &encoded, 48000, 48000)
.unwrap();
if offset > 0 {
let decoded = decoder.dred_decode(&dred, offset).unwrap();
assert!(!decoded.is_empty());
}
}
#[cfg(feature = "dred")]
#[test]
fn dred_disabled_parse() {
let mut encoder = Encoder::new(encoder_config(Some(64_000))).unwrap();
let mut dred_decoder = DredDecoder::new().unwrap();
let mut dred = Dred::new().unwrap();
let input = sine_wave_i16();
let encoded = encoder.encode(&input).unwrap();
let offset = dred_decoder
.parse(&mut dred, &encoded, 48000, 48000)
.unwrap();
assert_eq!(offset, 0, "DRED should not be present in non-DRED packet");
}
}