Add Decoder, packets, and Error handling

This commit is contained in:
Tad Hardesty committed 2016-02-24 02:06:01 -06:00
1 parent 2d7ef8336d
commit d07b0b4ad2
1 file changed
+274 -16
+274 -16
View File
@@ -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<T> = std::result::Result<T, Error>;
#[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<usize> {
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<usize> {
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<Decoder> {
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<usize> {
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<usize> {
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<usize> {
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<Bandwidth> {
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<Channels> {
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<usize> {
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<usize> {
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<usize> {
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<Packet> {
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<T> = std::result::Result<T, Error>;
#[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(())
}
}