mod bitcursor;
mod dec;
#[cfg(feature = "caf")]
mod caf;
#[cfg(feature = "mp4")]
mod mp4;
#[cfg(any(feature = "caf", feature = "mp4"))]
mod reader;
pub use dec::{Decoder, Sample};
#[cfg(any(feature = "caf", feature = "mp4"))]
pub use reader::{Format, Packets, ReadError, Reader, Samples};
use std::error;
use std::fmt;
#[derive(Debug)]
pub struct InvalidData {
message: &'static str,
}
impl error::Error for InvalidData {
fn description(&self) -> &str {
self.message
}
}
impl fmt::Display for InvalidData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.message)
}
}
impl From<bitcursor::NotEnoughData> for InvalidData {
fn from(_: bitcursor::NotEnoughData) -> InvalidData {
invalid_data("packet is not long enough")
}
}
fn invalid_data(message: &'static str) -> InvalidData {
InvalidData { message }
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct StreamInfo {
frame_length: u32,
compatible_version: u8,
bit_depth: u8,
pb: u8, mb: u8, kb: u8, num_channels: u8,
max_run: u16,
max_frame_bytes: u32,
avg_bit_rate: u32,
sample_rate: u32,
}
impl StreamInfo {
pub fn from_cookie(mut cookie: &[u8]) -> Result<StreamInfo, InvalidData> {
if cookie.len() < 24 {
return Err(invalid_data("magic cookie is not the correct length"));
};
if &cookie[4..8] == b"frma" {
cookie = &cookie[12..];
}
if &cookie[4..8] == b"alac" {
cookie = &cookie[12..];
}
if cookie.len() < 24 {
return Err(invalid_data("magic cookie is not the correct length"));
}
Ok(StreamInfo {
frame_length: read_be_u32(&cookie[0..4]),
compatible_version: cookie[4],
bit_depth: cookie[5],
pb: cookie[6],
mb: cookie[7],
kb: cookie[8],
num_channels: cookie[9],
max_run: read_be_u16(&cookie[10..12]),
max_frame_bytes: read_be_u32(&cookie[12..16]),
avg_bit_rate: read_be_u32(&cookie[16..20]),
sample_rate: read_be_u32(&cookie[20..24]),
})
}
pub fn from_sdp_format_parameters(params: &str) -> Result<StreamInfo, InvalidData> {
use std::str::FromStr;
fn parse<T: FromStr>(val: Option<&str>) -> Result<T, InvalidData> {
let val = val.ok_or(invalid_data("too few sdp format parameters"))?;
val.parse()
.map_err(|_| invalid_data("invalid sdp format parameter"))
}
let mut params = params.split_whitespace();
let config = StreamInfo {
frame_length: parse(params.next())?,
compatible_version: parse(params.next())?,
bit_depth: parse(params.next())?,
pb: parse(params.next())?,
mb: parse(params.next())?,
kb: parse(params.next())?,
num_channels: parse(params.next())?,
max_run: parse(params.next())?,
max_frame_bytes: parse(params.next())?,
avg_bit_rate: parse(params.next())?,
sample_rate: parse(params.next())?,
};
if params.next().is_some() {
return Err(invalid_data("too many sdp format parameters"));
}
Ok(config)
}
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
pub fn bit_depth(&self) -> u8 {
self.bit_depth
}
pub fn channels(&self) -> u8 {
self.num_channels
}
pub fn max_frames_per_packet(&self) -> u32 {
self.frame_length
}
pub fn max_samples_per_packet(&self) -> u32 {
self.frame_length * self.num_channels as u32
}
}
fn read_be_u16(buf: &[u8]) -> u16 {
assert_eq!(buf.len(), 2);
((buf[0] as u16) << 8) | (buf[1] as u16)
}
fn read_be_u32(buf: &[u8]) -> u32 {
assert_eq!(buf.len(), 4);
((buf[0] as u32) << 24) | ((buf[1] as u32) << 16) | ((buf[2] as u32) << 8) | (buf[3] as u32)
}
#[cfg(test)]
mod tests {
use super::StreamInfo;
#[test]
fn test_from_cookie() {
let cookie_bytes = include_bytes!("../tests/data/magic_cookie.bin");
let cookie = StreamInfo::from_cookie(cookie_bytes).unwrap();
let comparison = StreamInfo {
frame_length: 4096,
compatible_version: 0,
bit_depth: 16,
pb: 40,
mb: 10,
kb: 14,
num_channels: 2,
max_run: 255,
max_frame_bytes: 0,
avg_bit_rate: 0,
sample_rate: 44100,
};
assert_eq!(cookie, comparison);
}
#[test]
fn test_from_sdp_format_parameters() {
let params = "4096 0 16 40 10 14 2 255 0 0 44100";
let cookie = StreamInfo::from_sdp_format_parameters(params).unwrap();
let comparison = StreamInfo {
frame_length: 4096,
compatible_version: 0,
bit_depth: 16,
pb: 40,
mb: 10,
kb: 14,
num_channels: 2,
max_run: 255,
max_frame_bytes: 0,
avg_bit_rate: 0,
sample_rate: 44100,
};
assert_eq!(cookie, comparison);
}
}