From d07b0b4ad2e39789242043df078c182197ec4812 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Wed, 24 Feb 2016 02:06:01 -0600 Subject: [PATCH] Add Decoder, packets, and Error handling --- src/lib.rs | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 274 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 47d2d87..be102c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,39 +1,66 @@ extern crate opus_sys as ffi; extern crate libc; +use std::ffi::CStr; + use libc::{c_int}; +// TODO: Documentation + +// ============================================================================ +// Constants + // Generic CTLs const OPUS_RESET_STATE: c_int = 4028; // void const OPUS_GET_FINAL_RANGE: c_int = 4031; // *uint const OPUS_GET_BANDWIDTH: c_int = 4009; // *int const OPUS_GET_SAMPLE_RATE: c_int = 4029; // *int +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum CodingMode { Voip = 2048, Audio = 2049, LowDelay = 2051, } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum Channels { - Mono = 1, - Stereo = 2, + Stereo = 1, + Mono = 2, } -pub struct Error(&'static str, c_int); - -fn check(what: &'static str, code: c_int) -> Result<()> { - if code < 0 { - Err(Error(what, code)) - } else { - Ok(()) - } +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub enum Bandwidth { + Narrowband = 1101, + Mediumband = 1102, + Wideband = 1103, + Superwideband = 1104, + Fullband = 1105, } -pub type Result = std::result::Result; +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ErrorCode { + BadArg = -1, + BufferTooSmall = -2, + InternalError = -3, + InvalidPacket = -4, + Unimplemented = -5, + InvalidState = -6, + AllocFail = -7, + Unknown = -8, +} + +pub fn version() -> &'static str { + // verison string should always be ASCII + unsafe { CStr::from_ptr(ffi::opus_get_version_string()) }.to_str().unwrap() +} + +// ============================================================================ +// Encoder pub struct Encoder { ptr: *mut ffi::OpusEncoder, + channels: Channels, } impl Encoder { @@ -45,15 +72,15 @@ impl Encoder { mode as c_int, &mut error) }; if error != ffi::OPUS_OK || ptr.is_null() { - Err(Error("opus_encoder_create", error)) + Err(Error::from_code("opus_encoder_create", error)) } else { - Ok(Encoder { ptr: ptr }) + Ok(Encoder { ptr: ptr, channels: channels }) } } pub fn encode(&mut self, input: &[i16], output: &mut [u8]) -> Result { let len = unsafe { ffi::opus_encode(self.ptr, - input.as_ptr(), input.len() as c_int, + input.as_ptr(), input.len() as c_int / self.channels as c_int, output.as_mut_ptr(), output.len() as c_int) }; try!(check("opus_encode", len)); Ok(len as usize) @@ -61,7 +88,7 @@ impl Encoder { pub fn encode_float(&mut self, input: &[f32], output: &mut [u8]) -> Result { let len = unsafe { ffi::opus_encode_float(self.ptr, - input.as_ptr(), input.len() as c_int, + input.as_ptr(), input.len() as c_int / self.channels as c_int, output.as_mut_ptr(), output.len() as c_int) }; try!(check("opus_encode_float", len)); Ok(len as usize) @@ -92,7 +119,7 @@ impl Encoder { Ok(value) } - // TODO: Many more CTLs + // TODO: Encoder-specific CTLs } impl Drop for Encoder { @@ -100,3 +127,234 @@ impl Drop for Encoder { unsafe { ffi::opus_encoder_destroy(self.ptr) } } } + +// ============================================================================ +// Decoder + +pub struct Decoder { + ptr: *mut ffi::OpusDecoder, + channels: Channels, +} + +impl Decoder { + pub fn new(sample_rate: u32, channels: Channels) -> Result { + let mut error = 0; + let ptr = unsafe { ffi::opus_decoder_create( + sample_rate as i32, + channels as c_int, + &mut error) }; + if error != ffi::OPUS_OK || ptr.is_null() { + Err(Error::from_code("opus_decoder_create", error)) + } else { + Ok(Decoder { ptr: ptr, channels: channels }) + } + } + + // TODO: support null inputs ("packet loss") + pub fn decode(&mut self, input: &[u8], output: &mut [i16], fec: bool) -> Result { + let len = unsafe { ffi::opus_decode(self.ptr, + input.as_ptr(), input.len() as c_int, + output.as_mut_ptr(), output.len() as c_int / self.channels as c_int, + fec as c_int) }; + try!(check("opus_decode", len)); + Ok(len as usize) + } + + pub fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> Result { + let len = unsafe { ffi::opus_decode_float(self.ptr, + input.as_ptr(), input.len() as c_int, + output.as_mut_ptr(), output.len() as c_int / self.channels as c_int, + fec as c_int) }; + try!(check("opus_decode_float", len)); + Ok(len as usize) + } + + pub fn get_nb_samples(&self, packet: &[u8]) -> Result { + let len = unsafe { ffi::opus_decoder_get_nb_samples(self.ptr, + packet.as_ptr(), packet.len() as i32) }; + try!(check("opus_decoder_get_nb_samples", len)); + Ok(len as usize) + } + + // TODO: Generic CTLs + // TODO: Decoder-specific CTLs +} + +impl Drop for Decoder { + fn drop(&mut self) { + unsafe { ffi::opus_decoder_destroy(self.ptr) } + } +} + +// ============================================================================ +// Packet Analysis + +pub mod packet { + use super::*; + use super::{ffi, check}; + use std::{ptr, slice}; + use libc::c_int; + + pub fn get_bandwidth(packet: &[u8]) -> Result { + if packet.len() < 1 { + return Err(Error::from_code("opus_packet_get_bandwidth", ffi::OPUS_BAD_ARG)) + } + let bandwidth = unsafe { ffi::opus_packet_get_bandwidth(packet.as_ptr()) }; + try!(check("opus_packet_get_bandwidth", bandwidth)); + match bandwidth { + 1101 => Ok(Bandwidth::Narrowband), + 1102 => Ok(Bandwidth::Mediumband), + 1103 => Ok(Bandwidth::Wideband), + 1104 => Ok(Bandwidth::Superwideband), + 1105 => Ok(Bandwidth::Fullband), + _ => Err(Error::from_code("opus_packet_get_bandwidth", ffi::OPUS_BAD_ARG)), + } + } + + pub fn get_nb_channels(packet: &[u8]) -> Result { + if packet.len() < 1 { + return Err(Error::from_code("opus_packet_get_nb_channels", ffi::OPUS_BAD_ARG)) + } + let channels = unsafe { ffi::opus_packet_get_nb_channels(packet.as_ptr()) }; + try!(check("opus_packet_get_nb_channels", channels)); + match channels { + 1 => Ok(Channels::Mono), + 2 => Ok(Channels::Stereo), + _ => Err(Error::from_code("opus_packet_get_nb_channels", ffi::OPUS_BAD_ARG)), + } + } + + pub fn get_nb_frames(packet: &[u8]) -> Result { + let frames = unsafe { ffi::opus_packet_get_nb_frames(packet.as_ptr(), packet.len() as c_int) }; + try!(check("opus_packet_get_nb_frames", frames)); + Ok(frames as usize) + } + + pub fn get_nb_samples(packet: &[u8], sample_rate: u32) -> Result { + let frames = unsafe { ffi::opus_packet_get_nb_samples( + packet.as_ptr(), packet.len() as c_int, + sample_rate as c_int) }; + try!(check("opus_packet_get_nb_samples", frames)); + Ok(frames as usize) + } + + pub fn get_samples_per_frame(packet: &[u8], sample_rate: u32) -> Result { + if packet.len() < 1 { + return Err(Error::from_code("opus_packet_get_samples_per_frame", ffi::OPUS_BAD_ARG)) + } + let samples = unsafe { ffi::opus_packet_get_samples_per_frame(packet.as_ptr(), sample_rate as c_int) }; + try!(check("opus_packet_get_samples_per_frame", samples)); + Ok(samples as usize) + } + + pub fn parse(packet: &[u8]) -> Result { + let mut toc: u8 = 0; + let mut frames = [ptr::null(); 48]; + let mut sizes = [0i16; 48]; + let mut payload_offset: i32 = 0; + let num_frames = unsafe { ffi::opus_packet_parse( + packet.as_ptr(), packet.len() as c_int, + &mut toc, frames.as_mut_ptr(), + sizes.as_mut_ptr(), &mut payload_offset) }; + try!(check("opus_packet_parse", num_frames)); + + let mut frames_vec = Vec::with_capacity(num_frames as usize); + for i in 0..num_frames as usize { + frames_vec.push(unsafe { slice::from_raw_parts(frames[i], sizes[i] as usize) }); + } + + Ok(Packet { + toc: toc, + frames: frames_vec, + payload_offset: payload_offset as usize, + }) + } + + pub struct Packet<'a> { + pub toc: u8, + pub frames: Vec<&'a [u8]>, + pub payload_offset: usize, + } +} + +// ============================================================================ +// Float Soft Clipping + +pub struct SoftClip { + channels: Channels, + memory: [f32; 2], +} + +impl SoftClip { + pub fn new(channels: Channels) -> SoftClip { + SoftClip { channels: channels, memory: [0.0; 2] } + } + + pub fn apply(&mut self, signal: &mut [f32]) { + unsafe { ffi::opus_pcm_soft_clip( + signal.as_mut_ptr(), + signal.len() as c_int / self.channels as c_int, + self.channels as c_int, + self.memory.as_mut_ptr()) }; + } +} + +// ============================================================================ +// TODO: Repacketizer + +// ============================================================================ +// TODO: Multistream API + +// ============================================================================ +// Error Handling + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub struct Error { + function: &'static str, + description: &'static str, + code: ErrorCode, +} + +impl Error { + fn from_code(what: &'static str, code: c_int) -> Error { + // description should always be ASCII + let description = unsafe { CStr::from_ptr(ffi::opus_strerror(code)) }.to_str().unwrap(); + let code = match code { + -1 => ErrorCode::BadArg, + -2 => ErrorCode::BufferTooSmall, + -3 => ErrorCode::InternalError, + -4 => ErrorCode::InvalidPacket, + -5 => ErrorCode::Unimplemented, + -6 => ErrorCode::InvalidState, + -7 => ErrorCode::AllocFail, + _ => ErrorCode::Unknown, + }; + Error { function: what, description: description, code: code } + } + + pub fn function(&self) -> &'static str { self.function } + pub fn description(&self) -> &'static str { self.description } + pub fn code(&self) -> ErrorCode { self.code } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}: {}", self.function, self.description) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + self.description + } +} + +fn check(what: &'static str, code: c_int) -> Result<()> { + if code < 0 { + Err(Error::from_code(what, code)) + } else { + Ok(()) + } +}