#![forbid(unsafe_code)]
extern crate byteorder;
pub mod chunks;
mod enums;
mod error;
pub use enums::ChunkType;
pub use enums::FormatType;
pub use error::CafError;
use chunks::CafChunk;
use chunks::CafChunkHeader;
use std::io::{Read, Seek, SeekFrom};
use byteorder::{BigEndian as Be, ReadBytesExt};
const CAF_HEADER_MAGIC :[u8; 8] = [0x63, 0x61, 0x66, 0x66, 0x00, 0x01, 0x00, 0x00];
pub struct CafChunkReader<T> where T :Read {
rdr :T,
}
impl<T> CafChunkReader<T> where T :Read {
pub fn new(mut rdr :T) -> Result<Self, CafError> {
let mut hdr_buf = [0;8];
try!(rdr.read_exact(&mut hdr_buf));
if hdr_buf != CAF_HEADER_MAGIC {
return Err(CafError::NotCaf);
}
Ok(CafChunkReader { rdr : rdr })
}
pub fn into_inner(self) -> T {
self.rdr
}
pub fn read_chunk(&mut self) -> Result<CafChunk, CafError> {
let hdr = try!(self.read_chunk_header());
self.read_chunk_body(&hdr)
}
pub fn read_chunk_body(&mut self, hdr :&CafChunkHeader)
-> Result<CafChunk, CafError> {
if hdr.ch_size == -1 {
panic!("unspecified chunk size is not yet implemented");
}
let mut chunk_content = vec![0; hdr.ch_size as usize];
try!(self.rdr.read_exact(&mut chunk_content));
chunks::decode_chunk(hdr.ch_type, chunk_content)
}
pub fn read_chunk_header(&mut self) -> Result<CafChunkHeader, CafError> {
let chunk_type_u32 = try!(self.rdr.read_u32::<Be>());
let chunk_type = ChunkType::from(chunk_type_u32);
let chunk_size = try!(self.rdr.read_i64::<Be>());
Ok(CafChunkHeader {
ch_type : chunk_type,
ch_size : chunk_size,
})
}
}
impl<T> CafChunkReader<T> where T :Read + Seek {
pub fn to_next_chunk(&mut self, hdr :&CafChunkHeader) -> Result<(), CafError> {
if hdr.ch_size == -1 {
panic!("can't seek to end of chunk with unspecified chunk size.");
}
try!(self.rdr.seek(SeekFrom::Current(hdr.ch_size)));
Ok(())
}
pub fn to_previous_chunk(&mut self, hdr :&CafChunkHeader) -> Result<(), CafError> {
if hdr.ch_size == -1 {
panic!("can't seek to end of chunk with unspecified chunk size.");
}
try!(self.rdr.seek(SeekFrom::Current(-hdr.ch_size)));
Ok(())
}
pub fn read_chunks_to_mem(&mut self,
mut required :Vec<ChunkType>, content_read :&[ChunkType])
-> Result<(Vec<CafChunk>, Vec<CafChunkHeader>), CafError> {
let mut res = Vec::with_capacity(content_read.len());
let mut read_headers = Vec::new();
loop {
let hdr = try!(self.read_chunk_header());
let mut required_idx = None;
let mut content_read_found = false;
for (i, &searched_type) in required.iter().enumerate() {
if searched_type == hdr.ch_type {
required_idx = Some(i);
break;
}
}
for &searched_type in content_read.iter() {
if searched_type == hdr.ch_type {
content_read_found = true;
break;
}
}
if hdr.ch_size == -1 {
}
match required_idx { None => (), Some(i) => { required.remove(i); } }
if content_read_found {
res.push(try!(self.read_chunk_body(&hdr)));
} else {
try!(self.to_next_chunk(&hdr));
}
read_headers.push(hdr.clone());
if required.len() == 0 {
break;
}
}
Ok((res, read_headers))
}
}
pub struct CafPacketReader<T> where T :Read + Seek {
ch_rdr :CafChunkReader<T>,
pub audio_desc :chunks::AudioDescription,
pub packet_table :Option<chunks::PacketTable>,
pub chunks :Vec<CafChunk>,
pub edit_count :u32,
audio_chunk_len :i64,
audio_chunk_offs :i64,
packet_idx :usize,
}
impl<T> CafPacketReader<T> where T :Read + Seek {
pub fn new(rdr :T, filter_by :Vec<ChunkType>) -> Result<Self, CafError> {
let ch_rdr = try!(CafChunkReader::new(rdr));
return CafPacketReader::from_chunk_reader(ch_rdr, filter_by);
}
pub fn from_chunk_reader(mut ch_rdr :CafChunkReader<T>,
mut filter_by :Vec<ChunkType>) -> Result<Self, CafError> {
filter_by.push(ChunkType::AudioDescription);
let mut content_read = filter_by.clone();
content_read.push(ChunkType::PacketTable);
let (mut chunks_in_mem, mut read_headers) =
try!(ch_rdr.read_chunks_to_mem(filter_by, &content_read));
let mut audio_desc_idx = None;
let mut packet_table_idx = None;
for (idx, ch) in chunks_in_mem.iter().enumerate() {
use ChunkType::*;
match ch.get_type() {
AudioDescription => (audio_desc_idx = Some(idx)),
PacketTable => (packet_table_idx = Some(idx)),
_ => (),
}
}
macro_rules! remove_and_unwrap {
($idx:expr, $id:ident) => {
match chunks_in_mem.remove($idx) {
CafChunk::$id(v) => v,
_ => panic!(),
}
}
}
let audio_desc = remove_and_unwrap!(audio_desc_idx.unwrap(), Desc);
let p_table_required = audio_desc.bytes_per_packet == 0 ||
audio_desc.frames_per_packet == 0;
let packet_table = match packet_table_idx {
Some(i) => Some(remove_and_unwrap!(i, PacketTable)),
None if p_table_required => {
let (chunks, hdrs) = try!(ch_rdr.read_chunks_to_mem(
vec![ChunkType::PacketTable],
&content_read));
chunks_in_mem.extend_from_slice(&chunks);
read_headers.extend_from_slice(&hdrs);
for (idx, ch) in chunks_in_mem.iter().enumerate() {
use ChunkType::*;
match ch.get_type() {
PacketTable => (packet_table_idx = Some(idx)),
_ => (),
}
}
Some(remove_and_unwrap!(packet_table_idx.unwrap(), PacketTable))
},
None => None,
};
let mut audio_chunk_len = 0;
let mut seek_backwards = 0;
const HEADER_LEN :i64 = 12;
for hdr in read_headers.iter() {
if seek_backwards > 0 || hdr.ch_type == ChunkType::AudioData {
seek_backwards += HEADER_LEN;
seek_backwards += hdr.ch_size;
}
if hdr.ch_type == ChunkType::AudioData {
audio_chunk_len = hdr.ch_size;
}
}
if seek_backwards != 0 {
seek_backwards -= HEADER_LEN;
try!(ch_rdr.rdr.seek(SeekFrom::Current(-(seek_backwards as i64))));
} else {
loop {
let ch_hdr = try!(ch_rdr.read_chunk_header());
if ch_hdr.ch_type == ChunkType::AudioData {
audio_chunk_len = ch_hdr.ch_size;
break;
} else {
try!(ch_rdr.to_next_chunk(&ch_hdr));
}
}
}
let edit_count = {
use byteorder::{ReadBytesExt, BigEndian};
try!(ch_rdr.rdr.read_u32::<BigEndian>())
};
Ok(CafPacketReader {
ch_rdr : ch_rdr,
audio_desc : audio_desc,
packet_table : packet_table,
chunks : chunks_in_mem,
edit_count : edit_count,
audio_chunk_len : audio_chunk_len,
audio_chunk_offs : 4, packet_idx : 0,
})
}
pub fn into_inner(self) -> CafChunkReader<T> {
self.ch_rdr
}
pub fn packet_size_is_constant(&self) -> bool {
return self.audio_desc.bytes_per_packet != 0;
}
pub fn next_packet_size(&self) -> Option<usize> {
let res = match self.audio_desc.bytes_per_packet {
0 => match self.packet_table.as_ref()
.unwrap().lengths.get(self.packet_idx) {
Some(v) => *v as usize,
None => return None,
},
v => v as usize,
};
if self.audio_chunk_len != -1 &&
self.audio_chunk_offs + res as i64 > self.audio_chunk_len {
None
} else {
Some(res)
}
}
pub fn next_packet(&mut self) -> Result<Option<Vec<u8>>, CafError> {
let next_packet_size = match self.next_packet_size() {
Some(v) => v,
None => return Ok(None),
};
let mut arr = vec![0; next_packet_size];
try!(self.ch_rdr.rdr.read_exact(&mut arr));
self.packet_idx += 1;
self.audio_chunk_offs += next_packet_size as i64;
return Ok(Some(arr));
}
pub fn read_packet_into(&mut self, data :&mut [u8]) -> Result<(), CafError> {
try!(self.ch_rdr.rdr.read_exact(data));
self.packet_idx += 1;
self.audio_chunk_offs += data.len() as i64;
return Ok(());
}
pub fn get_packet_count(&self) -> Option<usize> {
match &self.packet_table {
&Some(ref t) => Some(t.lengths.len()),
&None => match self.audio_desc.bytes_per_packet {
0 => panic!("No packet table was stored by the constructor"),
_ if self.audio_chunk_len == -1 => None,
v => Some((self.audio_chunk_len as usize - 4) / v as usize),
},
}
}
pub fn get_packet_idx(&self) -> usize {
self.packet_idx
}
pub fn seek_to_packet(&mut self, packet_idx :usize) -> Result<(), CafError> {
let min_idx = ::std::cmp::min(self.packet_idx, packet_idx);
let max_idx = ::std::cmp::min(self.packet_idx, packet_idx);
let offs :i64 = match self.audio_desc.bytes_per_packet {
0 => self.packet_table.as_ref()
.unwrap().lengths[min_idx..max_idx].iter().map(|v| *v as i64).sum(),
v => (max_idx - min_idx) as i64 * v as i64,
};
if self.packet_idx < packet_idx {
try!(self.ch_rdr.rdr.seek(SeekFrom::Current(offs)));
} else if self.packet_idx > packet_idx {
try!(self.ch_rdr.rdr.seek(SeekFrom::Current(-offs)));
} else {
}
Ok(())
}
}