caf/
lib.rs

1// CAF container decoder written in Rust
2//
3// Copyright (c) 2017 est31 <MTest31@outlook.com>
4// and contributors. All rights reserved.
5// Licensed under MIT license, or Apache 2 license,
6// at your option. Please see the LICENSE file
7// attached to this source distribution for details.
8
9
10/*!
11An Apple Core Audio Format (CAF) container decoder
12
13For more information on CAF, see its [wiki page](https://en.wikipedia.org/wiki/Core_Audio_Format), and the [official specification](https://developer.apple.com/documentation/MusicAudio/Reference/CAFSpec/).
14*/
15
16#![forbid(unsafe_code)]
17
18extern crate byteorder;
19
20pub mod chunks;
21mod enums;
22mod error;
23
24pub use enums::ChunkType;
25pub use enums::FormatType;
26pub use error::CafError;
27
28use chunks::CafChunk;
29use chunks::CafChunkHeader;
30
31use std::io::{Read, Seek, SeekFrom};
32use byteorder::{BigEndian as Be, ReadBytesExt};
33
34/// The CAF file header
35const CAF_HEADER_MAGIC :[u8; 8] = [0x63, 0x61, 0x66, 0x66, 0x00, 0x01, 0x00, 0x00];
36
37pub struct CafChunkReader<T> where T :Read {
38	rdr :T,
39}
40
41impl<T> CafChunkReader<T> where T :Read {
42	pub fn new(mut rdr :T) -> Result<Self, CafError> {
43		let mut hdr_buf = [0;8];
44		try!(rdr.read_exact(&mut hdr_buf));
45		if hdr_buf != CAF_HEADER_MAGIC {
46			return Err(CafError::NotCaf);
47		}
48		Ok(CafChunkReader { rdr : rdr })
49	}
50	/// Returns the reader that this Reader wraps
51	pub fn into_inner(self) -> T {
52		self.rdr
53	}
54	// TODO find a better API.
55	// First, we don't want to pass the audio chunk via memory always.
56	// Sometimes a file can be very big, so we better leave the choice
57	// with the users of this library.
58	// Second, we don't want to decode everything ad-hoc, maybe copying
59	// around data that the user doesn't really want.
60	// A good way to fix this would be to add three API functionalities: first one to
61	// iterate the Read stream on a chunk granularity. The only info we read and
62	// return here will be the type and size of the chunk.
63	// The second API functionality should involve a function "give me some sort of view over the raw data of the chunk we last encountered in the first function".
64	// this will be useful for the audio stuff.
65	// The third API functionality would involve a function to read+decode the chunk
66	// directly into memory, very similar to the read_chunk function now.
67	// On top of this three layer API we can provide a simplified API that contains
68	// even more logic, e.g. an iterator over audio packets, or similar. Maybe, it
69	// can be integrated though, not sure...
70	// The first priority though should be to get the alac crate working with our code.
71	pub fn read_chunk(&mut self) -> Result<CafChunk, CafError> {
72		let hdr = try!(self.read_chunk_header());
73		self.read_chunk_body(&hdr)
74	}
75	/// Reads a chunk body into memory and decodes it
76	pub fn read_chunk_body(&mut self, hdr :&CafChunkHeader)
77			-> Result<CafChunk, CafError> {
78		if hdr.ch_size == -1 {
79			// Unspecified chunk size: this means the chunk is extends up to the EOF.
80			// TODO handle this case
81			panic!("unspecified chunk size is not yet implemented");
82		}
83		let mut chunk_content = vec![0; hdr.ch_size as usize];
84		try!(self.rdr.read_exact(&mut chunk_content));
85		chunks::decode_chunk(hdr.ch_type, chunk_content)
86	}
87	/// Reads a chunk header
88	pub fn read_chunk_header(&mut self) -> Result<CafChunkHeader, CafError> {
89		let chunk_type_u32 = try!(self.rdr.read_u32::<Be>());
90		let chunk_type = ChunkType::from(chunk_type_u32);
91		// TODO return some kind of error if chunk_size < 0 and != -1
92		let chunk_size = try!(self.rdr.read_i64::<Be>());
93		Ok(CafChunkHeader {
94			ch_type : chunk_type,
95			ch_size : chunk_size,
96		})
97	}
98}
99
100impl<T> CafChunkReader<T> where T :Read + Seek {
101
102	/**
103	Seeks to the next chunk header in the file
104
105	It is meant to be called directly after a chunk header
106	has been read, with the internal reader's position
107	at the start of a chunk's body. It then seeks to the
108	next chunk header.
109
110	With this function you can ignore chunks, not reading them,
111	if they have uninteresting content, or if further knowledge
112	on the file is needed before their content becomes interesting.
113
114	Panics if the header's chunk size is unspecified per spec (==-1).
115	"Skipping" would make no sense here, as it will put you to the end of the file.
116	*/
117	pub fn to_next_chunk(&mut self, hdr :&CafChunkHeader) -> Result<(), CafError> {
118		if hdr.ch_size == -1 {
119			// This would be EOF, makes no sense...
120			panic!("can't seek to end of chunk with unspecified chunk size.");
121		}
122		try!(self.rdr.seek(SeekFrom::Current(hdr.ch_size)));
123		Ok(())
124	}
125	/**
126	Seeks to the previous chunk header in the file
127
128	It is meant to be called with the internal reader's position
129	at the end of a chunk's body. It then seeks to the start of
130	that chunk body.
131
132	Panics if the header's chunk size is unspecified per spec (==-1).
133	"Skipping" would make no sense here, as it will put you to the end of the file.
134	*/
135	pub fn to_previous_chunk(&mut self, hdr :&CafChunkHeader) -> Result<(), CafError> {
136		if hdr.ch_size == -1 {
137			// This would be EOF, makes no sense...
138			panic!("can't seek to end of chunk with unspecified chunk size.");
139		}
140		try!(self.rdr.seek(SeekFrom::Current(-hdr.ch_size)));
141		Ok(())
142	}
143
144	/**
145	Read chunks from a whitelist to memory
146
147	Uses the given `CafChunkReader` to read all chunks to memory
148	whose types are inside the `content_read` slice.
149	Stops as soon as all chunk were encountered with types in the
150	`required` argument list.
151
152	As we don't have support for reading chunks with unspecified length,
153	you shouldn't use this function to read audio data to memory.
154	Generally, reading the audio data chunk to memory is a bad idea
155	as it may possibly be very big. Instead, use the nice high level
156	`CafPacketReader` struct.
157	*/
158	pub fn read_chunks_to_mem(&mut self,
159			mut required :Vec<ChunkType>, content_read :&[ChunkType])
160			-> Result<(Vec<CafChunk>, Vec<CafChunkHeader>), CafError> {
161		let mut res = Vec::with_capacity(content_read.len());
162		let mut read_headers = Vec::new();
163		loop {
164			let hdr = try!(self.read_chunk_header());
165			let mut required_idx = None;
166			let mut content_read_found = false;
167			for (i, &searched_type) in required.iter().enumerate() {
168				if searched_type == hdr.ch_type {
169					required_idx = Some(i);
170					break;
171				}
172			}
173			for &searched_type in content_read.iter() {
174				if searched_type == hdr.ch_type {
175					content_read_found = true;
176					break;
177				}
178			}
179			if hdr.ch_size == -1 {
180				// TODO: return an error.
181				/*
182				We don't support chunks with unspecified (=-1) length.
183				Reading such a chunk to memory would be a bad idea as they
184				can possibly be gigantic, and are only used for the audio chunk,
185				which is a very uninteresting target to be read to memory anyways.
186				Also, such chunks are only found at the end of the file, and if we
187				encounter them it means we didn't find the chunks we searched for.
188				*/
189			}
190
191			match required_idx { None => (), Some(i) => { required.remove(i); } }
192			if content_read_found {
193				res.push(try!(self.read_chunk_body(&hdr)));
194			} else {
195				try!(self.to_next_chunk(&hdr));
196			}
197			read_headers.push(hdr.clone());
198			if required.len() == 0 {
199				break;
200			}
201		}
202		Ok((res, read_headers))
203	}
204}
205
206/**
207High level Packet reading
208
209Provides a very convenient iterator over the packets of the audio chunk.
210*/
211pub struct CafPacketReader<T> where T :Read + Seek {
212	ch_rdr :CafChunkReader<T>,
213	pub audio_desc :chunks::AudioDescription,
214	pub packet_table :Option<chunks::PacketTable>,
215	pub chunks :Vec<CafChunk>,
216	/// The edit count value stored in the audio chunk.
217	pub edit_count :u32,
218	audio_chunk_len :i64,
219	audio_chunk_offs :i64,
220	packet_idx :usize,
221}
222
223impl<T> CafPacketReader<T> where T :Read + Seek {
224	/// Creates a new CAF packet reader struct from a given reader.
225	///
226	/// With the `filter_by` argument you can pass a list of chunk types
227	/// that are important for you. You shouldn't specify three chunk
228	/// types though: `AudioData`, `AudioDescription` and `PacketTable`.
229	/// These are implicitly retrieved, and you can extract the content
230	/// through iterating over the packets (which are all small parts of
231	/// the `AudioData` chunk), and through the `audio_desc` and `packet_table`
232	/// members.
233	///
234	/// Equal to calling `CafChunkReader::new` and passing its result to
235	/// `from_chunk_reader`.
236	pub fn new(rdr :T, filter_by :Vec<ChunkType>) -> Result<Self, CafError> {
237		let ch_rdr = try!(CafChunkReader::new(rdr));
238		return CafPacketReader::from_chunk_reader(ch_rdr, filter_by);
239	}
240
241	/// Creates a new CAF packet reader struct from a given chunk reader.
242	///
243	/// With the `filter_by` argument you can pass a list of chunk types
244	/// that are important for you. You shouldn't specify three chunk
245	/// types though: `AudioData`, `AudioDescription` and `PacketTable`.
246	/// These are implicitly retrieved, and you can extract the content
247	/// through iterating over the packets (which are all small parts of
248	/// the `AudioData` chunk), and through the `audio_desc` and `packet_table`
249	/// members.
250	pub fn from_chunk_reader(mut ch_rdr :CafChunkReader<T>,
251			mut filter_by :Vec<ChunkType>) -> Result<Self, CafError> {
252
253		// 1. Read all the chunks we need to memory
254		filter_by.push(ChunkType::AudioDescription);
255		let mut content_read = filter_by.clone();
256		content_read.push(ChunkType::PacketTable);
257		let (mut chunks_in_mem, mut read_headers) =
258			try!(ch_rdr.read_chunks_to_mem(filter_by, &content_read));
259
260		// 2. Extract the special chunks we will need later on
261		let mut audio_desc_idx = None;
262		let mut packet_table_idx = None;
263		for (idx, ch) in chunks_in_mem.iter().enumerate() {
264			use ChunkType::*;
265			//println!("{:?}", ch.get_type());
266			match ch.get_type() {
267				AudioDescription => (audio_desc_idx = Some(idx)),
268				PacketTable => (packet_table_idx = Some(idx)),
269				_ => (),
270			}
271		}
272		macro_rules! remove_and_unwrap {
273			($idx:expr, $id:ident) => {
274				match chunks_in_mem.remove($idx) {
275					CafChunk::$id(v) => v,
276					_ => panic!(),
277				}
278			}
279		}
280		let audio_desc = remove_and_unwrap!(audio_desc_idx.unwrap(), Desc);
281		let p_table_required = audio_desc.bytes_per_packet == 0 ||
282			audio_desc.frames_per_packet == 0;
283		let packet_table = match packet_table_idx {
284			Some(i) => Some(remove_and_unwrap!(i, PacketTable)),
285			None if p_table_required => {
286				let (chunks, hdrs) =  try!(ch_rdr.read_chunks_to_mem(
287						vec![ChunkType::PacketTable],
288						&content_read));
289				chunks_in_mem.extend_from_slice(&chunks);
290				read_headers.extend_from_slice(&hdrs);
291				for (idx, ch) in chunks_in_mem.iter().enumerate() {
292					use ChunkType::*;
293					match ch.get_type() {
294						PacketTable => (packet_table_idx = Some(idx)),
295						_ => (),
296					}
297				}
298				Some(remove_and_unwrap!(packet_table_idx.unwrap(), PacketTable))
299			},
300			// Only reaches this if p_table_required == false
301			None => None,
302		};
303
304		// 3. Navigate to audio chunk position.
305		// Check whether we already read the audio block.
306		// If yes, calculate the amount to seek back to get to it.
307		let mut audio_chunk_len = 0;
308		let mut seek_backwards = 0;
309		const HEADER_LEN :i64 = 12;
310		for hdr in read_headers.iter() {
311			if seek_backwards > 0 || hdr.ch_type == ChunkType::AudioData {
312				seek_backwards += HEADER_LEN;
313				seek_backwards += hdr.ch_size;
314			}
315			if hdr.ch_type == ChunkType::AudioData {
316				audio_chunk_len = hdr.ch_size;
317			}
318		}
319		if seek_backwards != 0 {
320			// We already skipped the audio chunk once.
321			// Seek back to it, and we are done.
322			seek_backwards -= HEADER_LEN;
323			//println!("seek_backwards: {}", seek_backwards);
324			try!(ch_rdr.rdr.seek(SeekFrom::Current(-(seek_backwards as i64))));
325		} else {
326			// The audio chunk is ahead of us. Seek towards it.
327			loop {
328				let ch_hdr = try!(ch_rdr.read_chunk_header());
329				if ch_hdr.ch_type == ChunkType::AudioData {
330					audio_chunk_len = ch_hdr.ch_size;
331					break;
332				} else {
333					try!(ch_rdr.to_next_chunk(&ch_hdr));
334				}
335			}
336		}
337		// 4. Read the edit count
338		let edit_count = {
339			use byteorder::{ReadBytesExt, BigEndian};
340			try!(ch_rdr.rdr.read_u32::<BigEndian>())
341		};
342		// 5. Return the result
343		Ok(CafPacketReader {
344			ch_rdr : ch_rdr,
345			audio_desc : audio_desc,
346			packet_table : packet_table,
347			chunks : chunks_in_mem,
348			edit_count : edit_count,
349			audio_chunk_len : audio_chunk_len,
350			audio_chunk_offs : 4, // 4 bytes for the edit count.
351			packet_idx : 0,
352		})
353	}
354	pub fn into_inner(self) -> CafChunkReader<T> {
355		self.ch_rdr
356	}
357	/// Returns whether the size of the packets doesn't change
358	///
359	/// Some formats have a constant, not changing packet size
360	/// (mostly the uncompressed ones).
361	pub fn packet_size_is_constant(&self) -> bool {
362		return self.audio_desc.bytes_per_packet != 0;
363	}
364	/// Returns the size of the next packet in bytes.
365	///
366	/// Returns None if all packets were read,
367	/// Some(_) otherwise.
368	///
369	/// Very useful if you want to allocate the packet
370	/// slice yourself.
371	pub fn next_packet_size(&self) -> Option<usize> {
372		let res = match self.audio_desc.bytes_per_packet {
373			0 => match self.packet_table.as_ref()
374					.unwrap().lengths.get(self.packet_idx) {
375				Some(v) => *v as usize,
376				None => return None,
377			},
378			v => v as usize,
379		};
380		if self.audio_chunk_len != -1 &&
381				self.audio_chunk_offs + res as i64 > self.audio_chunk_len {
382			// We would read outside of the chunk.
383			// In theory this is a format error as the packet table is not
384			// supposed to have such a length combination that the sum is larger
385			// than the size of the audio chunk + 4 for edit_count.
386			// But we are too lazy to return Result<...> here...
387			None
388		} else {
389			Some(res)
390		}
391	}
392	/// Read one packet from the audio chunk
393	///
394	/// Returns Ok(Some(v)) if the next packet could be read successfully,
395	/// Ok(None) if its the last chunk.
396	pub fn next_packet(&mut self) -> Result<Option<Vec<u8>>, CafError> {
397		let next_packet_size = match self.next_packet_size() {
398			Some(v) => v,
399			None => return Ok(None),
400		};
401
402		let mut arr = vec![0; next_packet_size];
403		try!(self.ch_rdr.rdr.read_exact(&mut arr));
404		self.packet_idx += 1;
405		self.audio_chunk_offs += next_packet_size as i64;
406		return Ok(Some(arr));
407	}
408	/// Read one packet from the audio chunk into a pre-allocated array
409	///
410	/// The method doesn't check whether the size of the passed slice matches
411	/// the actual next packet length, it uses the length blindly.
412	/// For correct operation, only use sizes returned from the
413	/// `next_packet_size` function, and only if it didn't return `None`.
414	pub fn read_packet_into(&mut self, data :&mut [u8]) -> Result<(), CafError> {
415		try!(self.ch_rdr.rdr.read_exact(data));
416		self.packet_idx += 1;
417		self.audio_chunk_offs += data.len() as i64;
418		return Ok(());
419	}
420
421	/// Gets the number of packets if its known.
422	pub fn get_packet_count(&self) -> Option<usize> {
423		match &self.packet_table {
424			&Some(ref t) => Some(t.lengths.len()),
425			&None => match self.audio_desc.bytes_per_packet {
426				// We are supposed to never reach this as the constructor
427				// should enforce a packet table to be present if the
428				// number of bytes per packet is unspecified.
429				0 => panic!("No packet table was stored by the constructor"),
430				// If the length of the audio chunk is unspecified,
431				// and there is no packet table,
432				// we won't know the count of packets.
433				_ if self.audio_chunk_len == -1 => None,
434				v => Some((self.audio_chunk_len as usize - 4) / v as usize),
435			},
436		}
437	}
438
439	/// Returns the index of the currently read packet
440	pub fn get_packet_idx(&self) -> usize {
441		self.packet_idx
442	}
443
444	/// Seeks to the packet with the given index
445	///
446	/// This function never has been tested.
447	/// If there are bugs please report them.
448	pub fn seek_to_packet(&mut self, packet_idx :usize) -> Result<(), CafError> {
449
450		let min_idx = ::std::cmp::min(self.packet_idx, packet_idx);
451		let max_idx = ::std::cmp::min(self.packet_idx, packet_idx);
452
453		// The amount we need to seek by.
454		let offs :i64 = match self.audio_desc.bytes_per_packet {
455			0 => self.packet_table.as_ref()
456				.unwrap().lengths[min_idx..max_idx].iter().map(|v| *v as i64).sum(),
457			v => (max_idx - min_idx) as i64 * v as i64,
458		};
459		if self.packet_idx < packet_idx {
460			try!(self.ch_rdr.rdr.seek(SeekFrom::Current(offs)));
461		} else if self.packet_idx > packet_idx {
462			try!(self.ch_rdr.rdr.seek(SeekFrom::Current(-offs)));
463		} else {
464			// No seek needed
465		}
466		Ok(())
467	}
468}