mod backend;
mod input;
#[cfg(feature = "test-upstream-libopus")]
pub use backend::UpstreamLibopusBackend;
pub use backend::{OpusBackend, UnsafeLibopusBackend};
pub use input::{
Application, Bandwidth, Channels, CommonOptions, Complexity, DecodeArgs, EncodeArgs,
EncoderOptions, FrameSize, SampleRate,
};
use crate::{
opus_strerror, OPUS_AUTO, OPUS_FRAMESIZE_ARG, OPUS_GET_FINAL_RANGE_REQUEST,
OPUS_GET_LOOKAHEAD_REQUEST, OPUS_SET_BANDWIDTH_REQUEST, OPUS_SET_BITRATE_REQUEST,
OPUS_SET_COMPLEXITY_REQUEST, OPUS_SET_DTX_REQUEST, OPUS_SET_EXPERT_FRAME_DURATION_REQUEST,
OPUS_SET_FORCE_CHANNELS_REQUEST, OPUS_SET_LSB_DEPTH_REQUEST, OPUS_SET_VBR_CONSTRAINT_REQUEST,
OPUS_SET_VBR_REQUEST,
};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Cursor, Read, Write};
pub const MAX_PACKET: usize = 1500;
const MAX_FRAME_SIZE: usize = 48000 * 2;
fn handle_opus_error(err: i32, call: &str) -> u32 {
if err < 0 {
panic!("{} failed: {}", call, opus_strerror(err));
}
err as u32
}
macro_rules! checked_opus_encoder_ctl {
($backend:ident, $st:expr, $request:expr, $($arg:expr),*) => {
{
let ret = $backend.opus_encoder_ctl_impl(
$st,
$request,
$crate::varargs!($($arg),*)
);
handle_opus_error(ret, stringify!(opus_encoder_ctl!($st:expr, $request:expr, $($arg:expr),*)));
ret
}
}
}
macro_rules! checked_opus_decoder_ctl {
($backend:ident, $st:expr, $request:expr, $($arg:expr),*) => {
{
let ret = $backend.opus_decoder_ctl_impl(
$st,
$request,
$crate::varargs!($($arg),*)
);
handle_opus_error(ret, stringify!(opus_decoder_ctl!($st:expr, $request:expr, $($arg:expr),*)));
ret
}
}
}
pub fn opus_demo_encode(
backend: &dyn OpusBackend,
data: &[u8],
EncodeArgs {
sampling_rate,
channels,
application,
bitrate,
options,
}: EncodeArgs,
) -> Vec<u8> {
let channels: usize = channels.into();
let mut samples = Vec::new();
for data in data.chunks_exact(2) {
samples.push(i16::from_le_bytes(data.try_into().unwrap()));
}
let enc = {
let mut err: i32 = 0;
let enc = unsafe {
backend.opus_encoder_create(
usize::from(sampling_rate) as i32,
channels as i32,
application.into_opus(),
&mut err,
)
};
handle_opus_error(err, "opus_encoder_create");
enc
};
let mut skip: i32 = 0;
if options.common.inbandfec {
panic!("inbandfec not supported")
}
if options.common.loss != 0 {
panic!("packet loss simulation not supported")
}
unsafe {
checked_opus_encoder_ctl!(backend, enc, OPUS_SET_BITRATE_REQUEST, bitrate as i32);
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_SET_BANDWIDTH_REQUEST,
options.bandwidth.map_or(OPUS_AUTO, |v| v.into_opus())
);
checked_opus_encoder_ctl!(backend, enc, OPUS_SET_VBR_REQUEST, !options.cbr as i32);
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_SET_VBR_CONSTRAINT_REQUEST,
options.cvbr as i32
);
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_SET_COMPLEXITY_REQUEST,
i32::from(options.complexity)
);
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_SET_FORCE_CHANNELS_REQUEST,
if options.forcemono { 1 } else { OPUS_AUTO }
);
checked_opus_encoder_ctl!(backend, enc, OPUS_SET_DTX_REQUEST, options.dtx as i32);
checked_opus_encoder_ctl!(backend, enc, OPUS_GET_LOOKAHEAD_REQUEST, &mut skip);
checked_opus_encoder_ctl!(backend, enc, OPUS_SET_LSB_DEPTH_REQUEST, 16);
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_SET_EXPERT_FRAME_DURATION_REQUEST,
OPUS_FRAMESIZE_ARG
);
}
let frame_size: usize = options.framesize.samples_for_rate(sampling_rate);
let samples_len = samples.len();
let samples_len = samples_len + (frame_size - (samples_len % frame_size));
samples.resize(samples_len, 0);
let mut output = Vec::<u8>::new();
let mut buffer = vec![0u8; options.max_payload];
for frame in samples.chunks_exact(frame_size * channels) {
let res = handle_opus_error(
unsafe {
backend.opus_encode(
enc,
frame.as_ptr(),
frame_size as i32,
buffer.as_mut_ptr(),
buffer.len() as i32,
)
},
"opus_encode",
);
let data = &buffer[..res as usize];
let mut enc_final_range: u32 = 0;
unsafe {
checked_opus_encoder_ctl!(
backend,
enc,
OPUS_GET_FINAL_RANGE_REQUEST,
&mut enc_final_range
);
};
output.write_i32::<BigEndian>(data.len() as i32).unwrap();
output.write_u32::<BigEndian>(enc_final_range).unwrap();
output.write_all(data).unwrap();
}
unsafe {
backend.opus_encoder_destroy(enc);
}
output
}
pub fn opus_demo_decode(
backend: &dyn OpusBackend,
data: &[u8],
DecodeArgs {
sampling_rate,
channels,
options,
}: DecodeArgs,
) -> Vec<u8> {
let mut cursor = Cursor::new(data);
let len = cursor.get_ref().len();
let channels: usize = channels.into();
let dec = {
let mut err: i32 = 0;
let dec = unsafe {
backend.opus_decoder_create(
usize::from(sampling_rate) as i32,
channels as i32,
&mut err,
)
};
handle_opus_error(err, "opus_decoder_create()");
dec
};
if options.inbandfec {
panic!("inbandfec not supported")
}
if options.loss != 0 {
panic!("packet loss simulation not supported")
}
let mut frame_idx = 0;
let mut data = vec![0u8; MAX_PACKET];
let mut samples = vec![0i16; MAX_FRAME_SIZE * channels];
let mut output = Vec::<u8>::new();
while cursor.position() < len as u64 {
let data_bytes = cursor.read_u32::<BigEndian>().unwrap();
let enc_final_range = cursor.read_u32::<BigEndian>().unwrap();
let data = &mut data[..data_bytes as usize];
cursor.read_exact(data).unwrap();
let output_samples = handle_opus_error(
unsafe {
backend.opus_decode(
dec,
data.as_ptr(),
data.len() as i32,
samples.as_mut_ptr(),
MAX_FRAME_SIZE as i32,
0,
)
},
"opus_decode",
);
let samples = &samples[..output_samples as usize * channels];
let dec_final_range = {
let mut dec_final_range: u32 = 0;
unsafe {
checked_opus_decoder_ctl!(
backend,
dec,
OPUS_GET_FINAL_RANGE_REQUEST,
&mut dec_final_range
);
};
dec_final_range
};
assert_eq!(
enc_final_range, dec_final_range,
"Range coder state mismatch between encoder and decoder in frame {}",
frame_idx
);
for sample in samples {
output.extend_from_slice(&sample.to_le_bytes());
}
frame_idx += 1;
}
unsafe { backend.opus_decoder_destroy(dec) };
output
}