#![cfg_attr(feature = "fuzz", feature(plugin))]
#![cfg_attr(feature = "fuzz", plugin(afl_plugin))]
#[cfg(feature = "fuzz")]
extern crate afl;
extern crate byteorder;
use byteorder::ReadBytesExt;
use std::io::{Read, Take};
use std::cmp;
pub mod capi;
pub use capi::*;
mod boxes;
use boxes::BoxType;
#[cfg(test)]
mod tests;
const BUF_SIZE_LIMIT: u64 = 1024 * 1024;
static DEBUG_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::ATOMIC_BOOL_INIT;
pub fn set_debug_mode(mode: bool) {
DEBUG_MODE.store(mode, std::sync::atomic::Ordering::SeqCst);
}
#[inline(always)]
fn get_debug_mode() -> bool {
DEBUG_MODE.load(std::sync::atomic::Ordering::Relaxed)
}
macro_rules! log {
($($args:tt)*) => (
if get_debug_mode() {
println!( $( $args )* );
}
)
}
#[derive(Debug)]
pub enum Error {
InvalidData(&'static str),
Unsupported(&'static str),
UnexpectedEOF,
Io(std::io::Error),
NoMoov,
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
match err.kind() {
std::io::ErrorKind::UnexpectedEof => Error::UnexpectedEOF,
_ => Error::Io(err),
}
}
}
impl From<std::string::FromUtf8Error> for Error {
fn from(_: std::string::FromUtf8Error) -> Error {
Error::InvalidData("invalid utf8")
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy)]
struct BoxHeader {
name: BoxType,
size: u64,
offset: u64,
}
#[derive(Debug)]
struct FileTypeBox {
major_brand: u32,
minor_version: u32,
compatible_brands: Vec<u32>,
}
#[derive(Debug)]
struct MovieHeaderBox {
timescale: u32,
duration: u64,
}
#[derive(Debug, Clone)]
pub struct TrackHeaderBox {
track_id: u32,
pub disabled: bool,
pub duration: u64,
pub width: u32,
pub height: u32,
}
#[derive(Debug)]
struct EditListBox {
edits: Vec<Edit>,
}
#[derive(Debug)]
struct Edit {
segment_duration: u64,
media_time: i64,
media_rate_integer: i16,
media_rate_fraction: i16,
}
#[derive(Debug)]
struct MediaHeaderBox {
timescale: u32,
duration: u64,
}
#[derive(Debug)]
struct ChunkOffsetBox {
offsets: Vec<u64>,
}
#[derive(Debug)]
struct SyncSampleBox {
samples: Vec<u32>,
}
#[derive(Debug)]
struct SampleToChunkBox {
samples: Vec<SampleToChunk>,
}
#[derive(Debug)]
struct SampleToChunk {
first_chunk: u32,
samples_per_chunk: u32,
sample_description_index: u32,
}
#[derive(Debug)]
struct SampleSizeBox {
sample_size: u32,
sample_sizes: Vec<u32>,
}
#[derive(Debug)]
struct TimeToSampleBox {
samples: Vec<Sample>,
}
#[derive(Debug)]
struct Sample {
sample_count: u32,
sample_delta: u32,
}
#[derive(Debug)]
struct HandlerBox {
handler_type: u32,
}
#[derive(Debug)]
struct SampleDescriptionBox {
descriptions: Vec<SampleEntry>,
}
#[derive(Debug, Clone)]
pub enum SampleEntry {
Audio(AudioSampleEntry),
Video(VideoSampleEntry),
Unknown,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum AudioCodecSpecific {
ES_Descriptor(Vec<u8>),
OpusSpecificBox(OpusSpecificBox),
}
#[derive(Debug, Clone)]
pub struct AudioSampleEntry {
data_reference_index: u16,
channelcount: u16,
pub samplesize: u16,
pub samplerate: u32,
pub codec_specific: AudioCodecSpecific,
}
#[derive(Debug, Clone)]
pub enum VideoCodecSpecific {
AVCConfig(Vec<u8>),
VPxConfig(VPxConfigBox),
}
#[derive(Debug, Clone)]
pub struct VideoSampleEntry {
data_reference_index: u16,
pub width: u16,
pub height: u16,
pub codec_specific: VideoCodecSpecific,
}
#[derive(Debug, Clone)]
pub struct VPxConfigBox {
profile: u8,
level: u8,
pub bit_depth: u8,
pub color_space: u8, pub chroma_subsampling: u8,
transfer_function: u8,
video_full_range: bool,
pub codec_init: Vec<u8>, }
#[derive(Debug, Clone)]
struct ChannelMappingTable {
stream_count: u8,
coupled_count: u8,
channel_mapping: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct OpusSpecificBox {
pub version: u8,
output_channel_count: u8,
pre_skip: u16,
input_sample_rate: u32,
output_gain: i16,
channel_mapping_family: u8,
channel_mapping_table: Option<ChannelMappingTable>,
}
#[derive(Debug, Default)]
pub struct MediaContext {
pub timescale: Option<MediaTimeScale>,
pub tracks: Vec<Track>,
}
impl MediaContext {
pub fn new() -> MediaContext {
Default::default()
}
}
#[derive(Debug)]
pub enum TrackType {
Audio,
Video,
Unknown,
}
impl Default for TrackType {
fn default() -> Self { TrackType::Unknown }
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct MediaTimeScale(pub u64);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct MediaScaledTime(pub u64);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TrackTimeScale(pub u64, pub usize);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TrackScaledTime(pub u64, pub usize);
#[derive(Debug, Default)]
pub struct Track {
id: usize,
pub track_type: TrackType,
pub empty_duration: Option<MediaScaledTime>,
pub media_time: Option<TrackScaledTime>,
pub timescale: Option<TrackTimeScale>,
pub duration: Option<TrackScaledTime>,
track_id: Option<u32>,
pub mime_type: String,
pub data: Option<SampleEntry>,
pub tkhd: Option<TrackHeaderBox>, }
impl Track {
fn new(id: usize) -> Track {
Track { id: id, ..Default::default() }
}
}
struct BMFFBox<'a, T: 'a + Read> {
head: BoxHeader,
content: Take<&'a mut T>,
}
struct BoxIter<'a, T: 'a + Read> {
src: &'a mut T,
}
impl<'a, T: Read> BoxIter<'a, T> {
fn new(src: &mut T) -> BoxIter<T> {
BoxIter { src: src }
}
fn next_box(&mut self) -> Result<Option<BMFFBox<T>>> {
let r = read_box_header(self.src);
match r {
Ok(h) => Ok(Some(BMFFBox {
head: h,
content: self.src.take(h.size - h.offset),
})),
Err(Error::UnexpectedEOF) => Ok(None),
Err(e) => Err(e),
}
}
}
impl<'a, T: Read> Read for BMFFBox<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.content.read(buf)
}
}
impl<'a, T: Read> BMFFBox<'a, T> {
fn bytes_left(&self) -> usize {
self.content.limit() as usize
}
fn get_header(&self) -> &BoxHeader {
&self.head
}
fn box_iter<'b>(&'b mut self) -> BoxIter<BMFFBox<'a, T>> {
BoxIter::new(self)
}
}
fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
let size32 = try!(be_u32(src));
let name = BoxType::from(try!(be_u32(src)));
let size = match size32 {
0 => return Err(Error::Unsupported("unknown sized box")),
1 => {
let size64 = try!(be_u64(src));
if size64 < 16 {
return Err(Error::InvalidData("malformed wide size"));
}
size64
}
2...7 => return Err(Error::InvalidData("malformed size")),
_ => size32 as u64,
};
let offset = match size32 {
1 => 4 + 4 + 8,
_ => 4 + 4,
};
assert!(offset <= size);
Ok(BoxHeader {
name: name,
size: size,
offset: offset,
})
}
fn read_fullbox_extra<T: ReadBytesExt>(src: &mut T) -> Result<(u8, u32)> {
let version = try!(src.read_u8());
let flags_a = try!(src.read_u8());
let flags_b = try!(src.read_u8());
let flags_c = try!(src.read_u8());
Ok((version,
(flags_a as u32) << 16 | (flags_b as u32) << 8 | (flags_c as u32)))
}
fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
let to_skip = {
let header = src.get_header();
log!("{:?} (skipped)", header);
(header.size - header.offset) as usize
};
assert!(to_skip == src.bytes_left());
skip(src, to_skip)
}
macro_rules! check_parser_state {
( $src:expr ) => {
if $src.limit() > 0 {
log!("bad parser state: {} content bytes left", $src.limit());
return Err(Error::InvalidData("unread box content or bad parser sync"));
}
}
}
pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
let mut found_ftyp = false;
let mut found_moov = false;
let mut iter = BoxIter::new(f);
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::FileTypeBox => {
let ftyp = try!(read_ftyp(&mut b));
found_ftyp = true;
log!("{:?}", ftyp);
}
BoxType::MovieBox => {
try!(read_moov(&mut b, context));
found_moov = true;
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
if found_moov {
log!("found moov {}, could stop pure 'moov' parser now", if found_ftyp {
"and ftyp"
} else {
"but no ftyp"
});
}
}
if found_moov {
Ok(())
} else {
Err(Error::NoMoov)
}
}
fn parse_mvhd<T: Read>(f: &mut BMFFBox<T>) -> Result<(MovieHeaderBox, Option<MediaTimeScale>)> {
let mvhd = try!(read_mvhd(f));
if mvhd.timescale == 0 {
return Err(Error::InvalidData("zero timescale in mdhd"));
}
let timescale = Some(MediaTimeScale(mvhd.timescale as u64));
Ok((mvhd, timescale))
}
fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::MovieHeaderBox => {
let (mvhd, timescale) = try!(parse_mvhd(&mut b));
context.timescale = timescale;
log!("{:?}", mvhd);
}
BoxType::TrackBox => {
let mut track = Track::new(context.tracks.len());
try!(read_trak(&mut b, &mut track));
context.tracks.push(track);
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::TrackHeaderBox => {
let tkhd = try!(read_tkhd(&mut b));
track.track_id = Some(tkhd.track_id);
track.tkhd = Some(tkhd.clone());
log!("{:?}", tkhd);
}
BoxType::EditBox => try!(read_edts(&mut b, track)),
BoxType::MediaBox => try!(read_mdia(&mut b, track)),
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::EditListBox => {
let elst = try!(read_elst(&mut b));
let mut empty_duration = 0;
let mut idx = 0;
if elst.edits.len() > 2 {
return Err(Error::Unsupported("more than two edits"));
}
if elst.edits[idx].media_time == -1 {
empty_duration = elst.edits[idx].segment_duration;
if elst.edits.len() < 2 {
return Err(Error::InvalidData("expected additional edit"));
}
idx += 1;
}
track.empty_duration = Some(MediaScaledTime(empty_duration));
if elst.edits[idx].media_time < 0 {
return Err(Error::InvalidData("unexpected negative media time in edit"));
}
track.media_time = Some(TrackScaledTime(elst.edits[idx].media_time as u64,
track.id));
log!("{:?}", elst);
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn parse_mdhd<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<(MediaHeaderBox, Option<TrackScaledTime>, Option<TrackTimeScale>)> {
let mdhd = try!(read_mdhd(f));
let duration = match mdhd.duration {
std::u64::MAX => None,
duration => Some(TrackScaledTime(duration, track.id)),
};
if mdhd.timescale == 0 {
return Err(Error::InvalidData("zero timescale in mdhd"));
}
let timescale = Some(TrackTimeScale(mdhd.timescale as u64, track.id));
Ok((mdhd, duration, timescale))
}
fn read_mdia<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::MediaHeaderBox => {
let (mdhd, duration, timescale) = try!(parse_mdhd(&mut b, track));
track.duration = duration;
track.timescale = timescale;
log!("{:?}", mdhd);
}
BoxType::HandlerBox => {
let hdlr = try!(read_hdlr(&mut b));
match hdlr.handler_type {
0x76696465 => track.track_type = TrackType::Video,
0x736f756e => track.track_type = TrackType::Audio,
_ => (),
}
log!("{:?}", hdlr);
}
BoxType::MediaInformationBox => try!(read_minf(&mut b, track)),
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn read_minf<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::SampleTableBox => try!(read_stbl(&mut b, track)),
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::SampleDescriptionBox => {
let stsd = try!(read_stsd(&mut b, track));
log!("{:?}", stsd);
}
BoxType::TimeToSampleBox => {
let stts = try!(read_stts(&mut b));
log!("{:?}", stts);
}
BoxType::SampleToChunkBox => {
let stsc = try!(read_stsc(&mut b));
log!("{:?}", stsc);
}
BoxType::SampleSizeBox => {
let stsz = try!(read_stsz(&mut b));
log!("{:?}", stsz);
}
BoxType::ChunkOffsetBox => {
let stco = try!(read_stco(&mut b));
log!("{:?}", stco);
}
BoxType::ChunkLargeOffsetBox => {
let co64 = try!(read_co64(&mut b));
log!("{:?}", co64);
}
BoxType::SyncSampleBox => {
let stss = try!(read_stss(&mut b));
log!("{:?}", stss);
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
let major = try!(be_u32(src));
let minor = try!(be_u32(src));
let bytes_left = src.bytes_left();
if bytes_left % 4 != 0 {
return Err(Error::InvalidData("invalid ftyp size"));
}
let brand_count = bytes_left / 4;
let mut brands = Vec::new();
for _ in 0..brand_count {
brands.push(try!(be_u32(src)));
}
Ok(FileTypeBox {
major_brand: major,
minor_version: minor,
compatible_brands: brands,
})
}
fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
let (version, _) = try!(read_fullbox_extra(src));
match version {
1 => {
try!(skip(src, 16));
}
0 => {
try!(skip(src, 8));
}
_ => return Err(Error::InvalidData("unhandled mvhd version")),
}
let timescale = try!(be_u32(src));
let duration = match version {
1 => try!(be_u64(src)),
0 => {
let d = try!(be_u32(src));
if d == std::u32::MAX {
std::u64::MAX
} else {
d as u64
}
}
_ => return Err(Error::InvalidData("unhandled mvhd version")),
};
try!(skip(src, 80));
Ok(MovieHeaderBox {
timescale: timescale,
duration: duration,
})
}
fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
let (version, flags) = try!(read_fullbox_extra(src));
let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0;
match version {
1 => {
try!(skip(src, 16));
}
0 => {
try!(skip(src, 8));
}
_ => return Err(Error::InvalidData("unhandled tkhd version")),
}
let track_id = try!(be_u32(src));
try!(skip(src, 4));
let duration = match version {
1 => try!(be_u64(src)),
0 => try!(be_u32(src)) as u64,
_ => return Err(Error::InvalidData("unhandled tkhd version")),
};
try!(skip(src, 52));
let width = try!(be_u32(src));
let height = try!(be_u32(src));
Ok(TrackHeaderBox {
track_id: track_id,
disabled: disabled,
duration: duration,
width: width,
height: height,
})
}
fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> {
let (version, _) = try!(read_fullbox_extra(src));
let edit_count = try!(be_u32(src));
if edit_count == 0 {
return Err(Error::InvalidData("invalid edit count"));
}
let mut edits = Vec::new();
for _ in 0..edit_count {
let (segment_duration, media_time) = match version {
1 => {
(try!(be_u64(src)), try!(be_i64(src)))
}
0 => {
(try!(be_u32(src)) as u64, try!(be_i32(src)) as i64)
}
_ => return Err(Error::InvalidData("unhandled elst version")),
};
let media_rate_integer = try!(be_i16(src));
let media_rate_fraction = try!(be_i16(src));
edits.push(Edit {
segment_duration: segment_duration,
media_time: media_time,
media_rate_integer: media_rate_integer,
media_rate_fraction: media_rate_fraction,
})
}
Ok(EditListBox {
edits: edits,
})
}
fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> {
let (version, _) = try!(read_fullbox_extra(src));
let (timescale, duration) = match version {
1 => {
try!(skip(src, 16));
(try!(be_u32(src)), try!(be_u64(src)))
}
0 => {
try!(skip(src, 8));
let timescale = try!(be_u32(src));
let duration = {
let d = try!(be_u32(src));
if d == std::u32::MAX {
std::u64::MAX
} else {
d as u64
}
};
(timescale, duration)
}
_ => return Err(Error::InvalidData("unhandled mdhd version")),
};
try!(skip(src, 4));
Ok(MediaHeaderBox {
timescale: timescale,
duration: duration,
})
}
fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
let (_, _) = try!(read_fullbox_extra(src));
let offset_count = try!(be_u32(src));
let mut offsets = Vec::new();
for _ in 0..offset_count {
offsets.push(try!(be_u32(src)) as u64);
}
Ok(ChunkOffsetBox {
offsets: offsets,
})
}
fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
let (_, _) = try!(read_fullbox_extra(src));
let offset_count = try!(be_u32(src));
let mut offsets = Vec::new();
for _ in 0..offset_count {
offsets.push(try!(be_u64(src)));
}
Ok(ChunkOffsetBox {
offsets: offsets,
})
}
fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
let (_, _) = try!(read_fullbox_extra(src));
let sample_count = try!(be_u32(src));
let mut samples = Vec::new();
for _ in 0..sample_count {
samples.push(try!(be_u32(src)));
}
Ok(SyncSampleBox {
samples: samples,
})
}
fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
let (_, _) = try!(read_fullbox_extra(src));
let sample_count = try!(be_u32(src));
let mut samples = Vec::new();
for _ in 0..sample_count {
let first_chunk = try!(be_u32(src));
let samples_per_chunk = try!(be_u32(src));
let sample_description_index = try!(be_u32(src));
samples.push(SampleToChunk {
first_chunk: first_chunk,
samples_per_chunk: samples_per_chunk,
sample_description_index: sample_description_index,
});
}
Ok(SampleToChunkBox {
samples: samples,
})
}
fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
let (_, _) = try!(read_fullbox_extra(src));
let sample_size = try!(be_u32(src));
let sample_count = try!(be_u32(src));
let mut sample_sizes = Vec::new();
if sample_size == 0 {
for _ in 0..sample_count {
sample_sizes.push(try!(be_u32(src)));
}
}
Ok(SampleSizeBox {
sample_size: sample_size,
sample_sizes: sample_sizes,
})
}
fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> {
let (_, _) = try!(read_fullbox_extra(src));
let sample_count = try!(be_u32(src));
let mut samples = Vec::new();
for _ in 0..sample_count {
let sample_count = try!(be_u32(src));
let sample_delta = try!(be_u32(src));
samples.push(Sample {
sample_count: sample_count,
sample_delta: sample_delta,
});
}
Ok(TimeToSampleBox {
samples: samples,
})
}
fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
let (version, _) = try!(read_fullbox_extra(src));
if version != 0 {
return Err(Error::Unsupported("unknown vpcC version"));
}
let profile = try!(src.read_u8());
let level = try!(src.read_u8());
let (bit_depth, color_space) = {
let byte = try!(src.read_u8());
((byte >> 4) & 0x0f, byte & 0x0f)
};
let (chroma_subsampling, transfer_function, video_full_range) = {
let byte = try!(src.read_u8());
((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1)
};
let codec_init_size = try!(be_u16(src));
let codec_init = try!(read_buf(src, codec_init_size as usize));
Ok(VPxConfigBox {
profile: profile,
level: level,
bit_depth: bit_depth,
color_space: color_space,
chroma_subsampling: chroma_subsampling,
transfer_function: transfer_function,
video_full_range: video_full_range,
codec_init: codec_init,
})
}
fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
let version = try!(src.read_u8());
if version != 0 {
return Err(Error::Unsupported("unknown dOps version"));
}
let output_channel_count = try!(src.read_u8());
let pre_skip = try!(be_u16(src));
let input_sample_rate = try!(be_u32(src));
let output_gain = try!(be_i16(src));
let channel_mapping_family = try!(src.read_u8());
let channel_mapping_table = if channel_mapping_family == 0 {
None
} else {
let stream_count = try!(src.read_u8());
let coupled_count = try!(src.read_u8());
let channel_mapping = try!(read_buf(src, output_channel_count as usize));
Some(ChannelMappingTable {
stream_count: stream_count,
coupled_count: coupled_count,
channel_mapping: channel_mapping,
})
};
Ok(OpusSpecificBox {
version: version,
output_channel_count: output_channel_count,
pre_skip: pre_skip,
input_sample_rate: input_sample_rate,
output_gain: output_gain,
channel_mapping_family: channel_mapping_family,
channel_mapping_table: channel_mapping_table,
})
}
pub fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> {
match dst.write(b"OpusHead") {
Err(e) => return Err(Error::from(e)),
Ok(bytes) => {
if bytes != 8 {
return Err(Error::InvalidData("Couldn't write OpusHead tag."));
}
}
}
try!(dst.write_u8(1));
try!(dst.write_u8(opus.output_channel_count));
try!(dst.write_u16::<byteorder::LittleEndian>(opus.pre_skip));
try!(dst.write_u32::<byteorder::LittleEndian>(opus.input_sample_rate));
try!(dst.write_i16::<byteorder::LittleEndian>(opus.output_gain));
try!(dst.write_u8(opus.channel_mapping_family));
match opus.channel_mapping_table {
None => {}
Some(ref table) => {
try!(dst.write_u8(table.stream_count));
try!(dst.write_u8(table.coupled_count));
match dst.write(&table.channel_mapping) {
Err(e) => return Err(Error::from(e)),
Ok(bytes) => {
if bytes != table.channel_mapping.len() {
return Err(Error::InvalidData("Couldn't write channel mapping table data."));
}
}
}
}
};
Ok(())
}
fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> {
let (_, _) = try!(read_fullbox_extra(src));
try!(skip(src, 4));
let handler_type = try!(be_u32(src));
try!(skip(src, 12));
let bytes_left = src.bytes_left();
let _name = try!(read_null_terminated_string(src, bytes_left));
Ok(HandlerBox {
handler_type: handler_type,
})
}
fn read_video_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
track.mime_type = match name {
BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => String::from("video/avc"),
BoxType::VP8SampleEntry => String::from("video/vp8"),
BoxType::VP9SampleEntry => String::from("video/vp9"),
BoxType::ProtectedVisualSampleEntry => String::from("video/crypto"),
_ => return Err(Error::Unsupported("unhandled video sample entry type")),
};
try!(skip(src, 6));
let data_reference_index = try!(be_u16(src));
try!(skip(src, 16));
let width = try!(be_u16(src));
let height = try!(be_u16(src));
try!(skip(src, 14));
let _compressorname = try!(read_fixed_length_pascal_string(src, 32));
try!(skip(src, 4));
let mut codec_specific = None;
let mut iter = src.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::AVCConfigurationBox => {
if (name != BoxType::AVCSampleEntry &&
name != BoxType::AVC3SampleEntry &&
name != BoxType::ProtectedVisualSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed video sample entry"));
}
let avcc_size = b.head.size - b.head.offset;
if avcc_size > BUF_SIZE_LIMIT {
return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT"));
}
let avcc = try!(read_buf(&mut b.content, avcc_size as usize));
codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc));
}
BoxType::VPCodecConfigurationBox => { if (name != BoxType::VP8SampleEntry &&
name != BoxType::VP9SampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed video sample entry"));
}
let vpcc = try!(read_vpcc(&mut b));
codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc));
}
_ => try!(skip_box_content(&mut b)),
}
check_parser_state!(b.content);
}
codec_specific
.map(|codec_specific| SampleEntry::Video(VideoSampleEntry {
data_reference_index: data_reference_index,
width: width,
height: height,
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
}
fn read_audio_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
track.mime_type = match name {
BoxType::MP4AudioSampleEntry => String::from("audio/mp4a-latm"),
BoxType::OpusSampleEntry => String::from("audio/opus"),
BoxType::ProtectedAudioSampleEntry => String::from("audio/crypto"),
_ => return Err(Error::Unsupported("unhandled audio sample entry type")),
};
try!(skip(src, 6));
let data_reference_index = try!(be_u16(src));
let version = try!(be_u16(src));
try!(skip(src, 6));
let channelcount = try!(be_u16(src));
let samplesize = try!(be_u16(src));
try!(skip(src, 4));
let samplerate = try!(be_u32(src));
match version {
0 => (),
_ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
}
let mut codec_specific = None;
let mut iter = src.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::ESDBox => {
if (name != BoxType::MP4AudioSampleEntry &&
name != BoxType::ProtectedAudioSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed audio sample entry"));
}
let (_, _) = try!(read_fullbox_extra(&mut b.content));
let esds_size = b.head.size - b.head.offset - 4;
if esds_size > BUF_SIZE_LIMIT {
return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
}
let esds = try!(read_buf(&mut b.content, esds_size as usize));
codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds));
}
BoxType::OpusSpecificBox => {
if name != BoxType::OpusSampleEntry ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed audio sample entry"));
}
let dops = try!(read_dops(&mut b));
codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
}
_ => try!(skip_box_content(&mut b)),
}
check_parser_state!(b.content);
}
codec_specific
.map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
data_reference_index: data_reference_index,
channelcount: channelcount,
samplesize: samplesize,
samplerate: samplerate,
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
}
fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
let (_, _) = try!(read_fullbox_extra(src));
let description_count = try!(be_u32(src));
let mut descriptions = Vec::new();
let mut iter = src.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
let description = match track.track_type {
TrackType::Video => read_video_desc(&mut b, track),
TrackType::Audio => read_audio_desc(&mut b, track),
TrackType::Unknown => Err(Error::Unsupported("unknown track type")),
};
let description = match description {
Ok(desc) => desc,
Err(Error::Unsupported(_)) => {
let to_skip = b.bytes_left();
try!(skip(&mut b, to_skip));
SampleEntry::Unknown
}
Err(e) => return Err(e),
};
if track.data.is_none() {
track.data = Some(description.clone());
} else {
log!("** don't know how to handle multiple descriptions **");
}
descriptions.push(description);
check_parser_state!(b.content);
if descriptions.len() == description_count as usize {
break;
}
}
Ok(SampleDescriptionBox {
descriptions: descriptions,
})
}
fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
const BUF_SIZE: usize = 64 * 1024;
let mut buf = vec![0; BUF_SIZE];
while bytes > 0 {
let buf_size = cmp::min(bytes, BUF_SIZE);
let len = try!(src.take(buf_size as u64).read(&mut buf));
if len == 0 {
return Err(Error::UnexpectedEOF);
}
bytes -= len;
}
Ok(())
}
fn read_buf<T: ReadBytesExt>(src: &mut T, size: usize) -> Result<Vec<u8>> {
let mut buf = vec![0; size];
let r = try!(src.read(&mut buf));
if r != size {
return Err(Error::InvalidData("failed buffer read"));
}
Ok(buf)
}
fn read_null_terminated_string<T: ReadBytesExt>(src: &mut T, mut size: usize) -> Result<String> {
let mut buf = Vec::new();
while size > 0 {
let c = try!(src.read_u8());
if c == 0 {
break;
}
buf.push(c);
size -= 1;
}
String::from_utf8(buf).map_err(From::from)
}
#[allow(dead_code)]
fn read_pascal_string<T: ReadBytesExt>(src: &mut T) -> Result<String> {
let len = try!(src.read_u8());
let buf = try!(read_buf(src, len as usize));
String::from_utf8(buf).map_err(From::from)
}
fn read_fixed_length_pascal_string<T: Read>(src: &mut T, size: usize) -> Result<String> {
assert!(size > 0);
let len = cmp::min(try!(src.read_u8()) as usize, size - 1);
let buf = try!(read_buf(src, len));
try!(skip(src, size - 1 - buf.len()));
String::from_utf8(buf).map_err(From::from)
}
fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> {
src.read_i16::<byteorder::BigEndian>().map_err(From::from)
}
fn be_i32<T: ReadBytesExt>(src: &mut T) -> Result<i32> {
src.read_i32::<byteorder::BigEndian>().map_err(From::from)
}
fn be_i64<T: ReadBytesExt>(src: &mut T) -> Result<i64> {
src.read_i64::<byteorder::BigEndian>().map_err(From::from)
}
fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
src.read_u16::<byteorder::BigEndian>().map_err(From::from)
}
fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
src.read_u32::<byteorder::BigEndian>().map_err(From::from)
}
fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
src.read_u64::<byteorder::BigEndian>().map_err(From::from)
}