Bug 1317609 - update rust mp4 parser to support avc extra data. r=kinetik
authorAlfredo.Yang <ayang@mozilla.com>
Fri, 25 Nov 2016 09:08:37 +0800
changeset 324229 836a645d53513265a5bfc171047dfffddfb57406
parent 324228 b1ebe4a9aec027472a0ec09031795656677f7644
child 324230 a79e0f500e85dd0da9aeca1801ccee6ed258b884
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewerskinetik
bugs1317609
milestone53.0a1
Bug 1317609 - update rust mp4 parser to support avc extra data. r=kinetik MozReview-Commit-ID: 7sWkDrXD8bs
media/libstagefright/binding/include/mp4parse.h
media/libstagefright/binding/mp4parse/src/lib.rs
media/libstagefright/binding/mp4parse/src/tests.rs
media/libstagefright/binding/mp4parse/tests/public.rs
media/libstagefright/binding/mp4parse_capi/src/lib.rs
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -64,16 +64,17 @@ typedef struct mp4parse_track_audio_info
 	mp4parse_byte_data codec_specific_config;
 } mp4parse_track_audio_info;
 
 typedef struct mp4parse_track_video_info {
 	uint32_t display_width;
 	uint32_t display_height;
 	uint16_t image_width;
 	uint16_t image_height;
+	mp4parse_byte_data extra_data;
 } mp4parse_track_video_info;
 
 typedef struct mp4parse_fragment_info {
 	uint64_t fragment_duration;
 } mp4parse_fragment_info;
 
 typedef struct mp4parse_parser mp4parse_parser;
 
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -200,22 +200,22 @@ struct SampleDescriptionBox {
 #[derive(Debug, Clone)]
 pub enum SampleEntry {
     Audio(AudioSampleEntry),
     Video(VideoSampleEntry),
     Unknown,
 }
 
 #[allow(non_camel_case_types)]
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Default)]
 pub struct ES_Descriptor {
     pub audio_codec: CodecType,
     pub audio_sample_rate: Option<u32>,
     pub audio_channel_count: Option<u16>,
-    pub codec_specific_config: Vec<u8>,
+    pub codec_esds: Vec<u8>,
 }
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone)]
 pub enum AudioCodecSpecific {
     ES_Descriptor(ES_Descriptor),
     FLACSpecificBox(FLACSpecificBox),
     OpusSpecificBox(OpusSpecificBox),
@@ -454,23 +454,23 @@ impl<'a, T: Read> BMFFBox<'a, T> {
 
 /// Read and parse a box header.
 ///
 /// Call this first to determine the type of a particular mp4 box
 /// and its length. Used internally for dispatching to specific
 /// parsers for the internal content, or to get the length to
 /// skip unknown or uninteresting boxes.
 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 size32 = be_u32(src)?;
+    let name = BoxType::from(be_u32(src)?);
     let size = match size32 {
         // valid only for top-level box and indicates it's the last box in the file.  usually mdat.
         0 => return Err(Error::Unsupported("unknown sized box")),
         1 => {
-            let size64 = try!(be_u64(src));
+            let size64 = 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,
     };
@@ -483,33 +483,33 @@ fn read_box_header<T: ReadBytesExt>(src:
         name: name,
         size: size,
         offset: offset,
     })
 }
 
 /// Parse the extra header fields for a full box.
 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());
+    let version = src.read_u8()?;
+    let flags_a = src.read_u8()?;
+    let flags_b = src.read_u8()?;
+    let flags_c = src.read_u8()?;
     Ok((version,
         (flags_a as u32) << 16 | (flags_b as u32) << 8 | (flags_c as u32)))
 }
 
 /// Skip over the entire contents of a box.
 fn skip_box_content<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
     // Skip the contents of unknown chunks.
     let to_skip = {
         let header = src.get_header();
         log!("{:?} (skipped)", header);
         (header.size - header.offset) as usize
     };
-    assert!(to_skip == src.bytes_left());
+    assert_eq!(to_skip, src.bytes_left());
     skip(src, to_skip)
 }
 
 /// Skip over the remain data of a box.
 fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
     let remain = {
         let header = src.get_header();
         let len = src.bytes_left();
@@ -533,17 +533,17 @@ macro_rules! check_parser_state {
 /// Metadata is accumulated in the passed-through `MediaContext` struct,
 /// which can be examined later.
 pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
     let mut found_ftyp = false;
     let mut found_moov = false;
     // TODO(kinetik): Top-level parsing should handle zero-sized boxes
     // rather than throwing an error.
     let mut iter = BoxIter::new(f);
-    while let Some(mut b) = try!(iter.next_box()) {
+    while let Some(mut b) = iter.next_box()? {
         // box ordering: ftyp before any variable length box (inc. moov),
         // but may not be first box in file if file signatures etc. present
         // fragmented mp4 order: ftyp, moov, pairs of moof/mdat (1-multiple), mfra
 
         // "special": uuid, wide (= 8 bytes)
         // isom: moov, mdat, free, skip, udta, ftyp, moof, mfra
         // iso2: pdin, meta
         // iso3: meco
@@ -551,25 +551,25 @@ pub fn read_mp4<T: Read>(f: &mut T, cont
         // unknown, maybe: id32
 
         // qt: pnot
 
         // possibly allow anything where all printable and/or all lowercase printable
         // "four printable characters from the ISO 8859-1 character set"
         match b.head.name {
             BoxType::FileTypeBox => {
-                let ftyp = try!(read_ftyp(&mut b));
+                let ftyp = read_ftyp(&mut b)?;
                 found_ftyp = true;
                 log!("{:?}", ftyp);
             }
             BoxType::MovieBox => {
-                try!(read_moov(&mut b, context));
+                read_moov(&mut b, context)?;
                 found_moov = true;
             }
-            _ => try!(skip_box_content(&mut b)),
+            _ => 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"
             });
@@ -582,146 +582,146 @@ pub fn read_mp4<T: Read>(f: &mut T, cont
     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));
+    let mvhd = 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::MovieHeaderBox => {
-                let (mvhd, timescale) = try!(parse_mvhd(&mut b));
+                let (mvhd, timescale) = 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));
+                read_trak(&mut b, &mut track)?;
                 context.tracks.push(track);
             }
             BoxType::MovieExtendsBox => {
-                let mvex = try!(read_mvex(&mut b));
+                let mvex = read_mvex(&mut b)?;
                 log!("{:?}", mvex);
                 context.mvex = Some(mvex);
             }
             BoxType::ProtectionSystemSpecificHeaderBox => {
-                let pssh = try!(read_pssh(&mut b));
+                let pssh = read_pssh(&mut b)?;
                 log!("{:?}", pssh);
                 context.psshs.push(pssh);
             }
-            _ => try!(skip_box_content(&mut b)),
+            _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
 
 fn read_pssh<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSystemSpecificHeaderBox> {
     let mut box_content = Vec::with_capacity(src.head.size as usize);
-    try!(src.read_to_end(&mut box_content));
+    src.read_to_end(&mut box_content)?;
 
     let (system_id, kid, data) = {
         let pssh = &mut Cursor::new(box_content.as_slice());
 
-        let (version, _) = try!(read_fullbox_extra(pssh));
+        let (version, _) = read_fullbox_extra(pssh)?;
 
-        let system_id = try!(read_buf(pssh, 16));
+        let system_id = read_buf(pssh, 16)?;
 
         let mut kid: Vec<ByteData> = Vec::new();
         if version > 0 {
-            let count = try!(be_i32(pssh));
+            let count = be_i32(pssh)?;
             for _ in 0..count {
-                let item = try!(read_buf(pssh, 16));
+                let item = read_buf(pssh, 16)?;
                 kid.push(item);
             }
         }
 
-        let data_size = try!(be_i32(pssh)) as usize;
-        let data = try!(read_buf(pssh, data_size));
+        let data_size = be_i32(pssh)? as usize;
+        let data = read_buf(pssh, data_size)?;
 
         (system_id, kid, data)
     };
 
     let mut pssh_box = Vec::new();
-    try!(write_be_u32(&mut pssh_box, src.head.size as u32));
+    write_be_u32(&mut pssh_box, src.head.size as u32)?;
     pssh_box.append(&mut b"pssh".to_vec());
     pssh_box.append(&mut box_content);
 
     Ok(ProtectionSystemSpecificHeaderBox {
         system_id: system_id,
         kid: kid,
         data: data,
         box_content: pssh_box,
     })
 }
 
 fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> {
     let mut iter = src.box_iter();
     let mut fragment_duration = None;
-    while let Some(mut b) = try!(iter.next_box()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::MovieExtendsHeaderBox => {
-                let duration = try!(read_mehd(&mut b));
+                let duration = read_mehd(&mut b)?;
                 fragment_duration = Some(duration);
             },
-            _ => try!(skip_box_content(&mut b)),
+            _ => skip_box_content(&mut b)?,
         }
     }
     Ok(MovieExtendsBox {
         fragment_duration: fragment_duration,
     })
 }
 
 fn read_mehd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaScaledTime> {
-    let (version, _) = try!(read_fullbox_extra(src));
+    let (version, _) = read_fullbox_extra(src)?;
     let fragment_duration = match version {
-        1 => try!(be_u64(src)),
-        0 => try!(be_u32(src)) as u64,
+        1 => be_u64(src)?,
+        0 => be_u32(src)? as u64,
         _ => return Err(Error::InvalidData("unhandled mehd version")),
     };
     Ok(MediaScaledTime(fragment_duration))
 }
 
 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::TrackHeaderBox => {
-                let tkhd = try!(read_tkhd(&mut b));
+                let tkhd = 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)),
+            BoxType::EditBox => read_edts(&mut b, track)?,
+            BoxType::MediaBox => read_mdia(&mut b, track)?,
+            _ => 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::EditListBox => {
-                let elst = try!(read_elst(&mut b));
+                let elst = 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 {
@@ -732,589 +732,601 @@ fn read_edts<T: Read>(f: &mut BMFFBox<T>
                 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)),
+            _ => 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 mdhd = 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::MediaHeaderBox => {
-                let (mdhd, duration, timescale) = try!(parse_mdhd(&mut b, track));
+                let (mdhd, duration, timescale) = parse_mdhd(&mut b, track)?;
                 track.duration = duration;
                 track.timescale = timescale;
                 log!("{:?}", mdhd);
             }
             BoxType::HandlerBox => {
-                let hdlr = try!(read_hdlr(&mut b));
+                let hdlr = read_hdlr(&mut b)?;
                 match hdlr.handler_type {
                     0x76696465 /* 'vide' */ => track.track_type = TrackType::Video,
                     0x736f756e /* 'soun' */ => track.track_type = TrackType::Audio,
                     _ => (),
                 }
                 log!("{:?}", hdlr);
             }
-            BoxType::MediaInformationBox => try!(read_minf(&mut b, track)),
-            _ => try!(skip_box_content(&mut b)),
+            BoxType::MediaInformationBox => read_minf(&mut b, track)?,
+            _ => 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
-            BoxType::SampleTableBox => try!(read_stbl(&mut b, track)),
-            _ => try!(skip_box_content(&mut b)),
+            BoxType::SampleTableBox => read_stbl(&mut b, track)?,
+            _ => 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()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::SampleDescriptionBox => {
-                let stsd = try!(read_stsd(&mut b, track));
+                let stsd = read_stsd(&mut b, track)?;
                 log!("{:?}", stsd);
             }
             BoxType::TimeToSampleBox => {
-                let stts = try!(read_stts(&mut b));
+                let stts = read_stts(&mut b)?;
                 track.empty_sample_boxes.empty_stts = stts.samples.is_empty();
                 log!("{:?}", stts);
             }
             BoxType::SampleToChunkBox => {
-                let stsc = try!(read_stsc(&mut b));
+                let stsc = read_stsc(&mut b)?;
                 track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty();
                 log!("{:?}", stsc);
             }
             BoxType::SampleSizeBox => {
-                let stsz = try!(read_stsz(&mut b));
+                let stsz = read_stsz(&mut b)?;
                 log!("{:?}", stsz);
             }
             BoxType::ChunkOffsetBox => {
-                let stco = try!(read_stco(&mut b));
+                let stco = read_stco(&mut b)?;
                 track.empty_sample_boxes.empty_stco = stco.offsets.is_empty();
                 log!("{:?}", stco);
             }
             BoxType::ChunkLargeOffsetBox => {
-                let co64 = try!(read_co64(&mut b));
+                let co64 = read_co64(&mut b)?;
                 log!("{:?}", co64);
             }
             BoxType::SyncSampleBox => {
-                let stss = try!(read_stss(&mut b));
+                let stss = read_stss(&mut b)?;
                 log!("{:?}", stss);
             }
-            _ => try!(skip_box_content(&mut b)),
+            _ => skip_box_content(&mut b)?,
         };
         check_parser_state!(b.content);
     }
     Ok(())
 }
 
 /// Parse an ftyp box.
 fn read_ftyp<T: Read>(src: &mut BMFFBox<T>) -> Result<FileTypeBox> {
-    let major = try!(be_u32(src));
-    let minor = try!(be_u32(src));
+    let major = be_u32(src)?;
+    let minor = be_u32(src)?;
     let bytes_left = src.bytes_left();
     if bytes_left % 4 != 0 {
         return Err(Error::InvalidData("invalid ftyp size"));
     }
     // Is a brand_count of zero valid?
     let brand_count = bytes_left / 4;
     let mut brands = Vec::new();
     for _ in 0..brand_count {
-        brands.push(try!(be_u32(src)));
+        brands.push(be_u32(src)?);
     }
     Ok(FileTypeBox {
         major_brand: major,
         minor_version: minor,
         compatible_brands: brands,
     })
 }
 
 /// Parse an mvhd box.
 fn read_mvhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieHeaderBox> {
-    let (version, _) = try!(read_fullbox_extra(src));
+    let (version, _) = read_fullbox_extra(src)?;
     match version {
         // 64 bit creation and modification times.
         1 => {
-            try!(skip(src, 16));
+            skip(src, 16)?;
         }
         // 32 bit creation and modification times.
         0 => {
-            try!(skip(src, 8));
+            skip(src, 8)?;
         }
         _ => return Err(Error::InvalidData("unhandled mvhd version")),
     }
-    let timescale = try!(be_u32(src));
+    let timescale = be_u32(src)?;
     let duration = match version {
-        1 => try!(be_u64(src)),
+        1 => be_u64(src)?,
         0 => {
-            let d = try!(be_u32(src));
+            let d = be_u32(src)?;
             if d == std::u32::MAX {
                 std::u64::MAX
             } else {
                 d as u64
             }
         }
         _ => return Err(Error::InvalidData("unhandled mvhd version")),
     };
     // Skip remaining fields.
-    try!(skip(src, 80));
+    skip(src, 80)?;
     Ok(MovieHeaderBox {
         timescale: timescale,
         duration: duration,
     })
 }
 
 /// Parse a tkhd box.
 fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
-    let (version, flags) = try!(read_fullbox_extra(src));
+    let (version, flags) = read_fullbox_extra(src)?;
     let disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0;
     match version {
         // 64 bit creation and modification times.
         1 => {
-            try!(skip(src, 16));
+            skip(src, 16)?;
         }
         // 32 bit creation and modification times.
         0 => {
-            try!(skip(src, 8));
+            skip(src, 8)?;
         }
         _ => return Err(Error::InvalidData("unhandled tkhd version")),
     }
-    let track_id = try!(be_u32(src));
-    try!(skip(src, 4));
+    let track_id = be_u32(src)?;
+    skip(src, 4)?;
     let duration = match version {
-        1 => try!(be_u64(src)),
-        0 => try!(be_u32(src)) as u64,
+        1 => be_u64(src)?,
+        0 => be_u32(src)? as u64,
         _ => return Err(Error::InvalidData("unhandled tkhd version")),
     };
     // Skip uninteresting fields.
-    try!(skip(src, 52));
-    let width = try!(be_u32(src));
-    let height = try!(be_u32(src));
+    skip(src, 52)?;
+    let width = be_u32(src)?;
+    let height = be_u32(src)?;
     Ok(TrackHeaderBox {
         track_id: track_id,
         disabled: disabled,
         duration: duration,
         width: width,
         height: height,
     })
 }
 
 /// Parse a elst box.
 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));
+    let (version, _) = read_fullbox_extra(src)?;
+    let edit_count = 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 => {
                 // 64 bit segment duration and media times.
-                (try!(be_u64(src)), try!(be_i64(src)))
+                (be_u64(src)?, be_i64(src)?)
             }
             0 => {
                 // 32 bit segment duration and media times.
-                (try!(be_u32(src)) as u64, try!(be_i32(src)) as i64)
+                (be_u32(src)? as u64, 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));
+        let media_rate_integer = be_i16(src)?;
+        let media_rate_fraction = 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,
     })
 }
 
 /// Parse a mdhd box.
 fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> {
-    let (version, _) = try!(read_fullbox_extra(src));
+    let (version, _) = read_fullbox_extra(src)?;
     let (timescale, duration) = match version {
         1 => {
             // Skip 64-bit creation and modification times.
-            try!(skip(src, 16));
+            skip(src, 16)?;
 
             // 64 bit duration.
-            (try!(be_u32(src)), try!(be_u64(src)))
+            (be_u32(src)?, be_u64(src)?)
         }
         0 => {
             // Skip 32-bit creation and modification times.
-            try!(skip(src, 8));
+            skip(src, 8)?;
 
             // 32 bit duration.
-            let timescale = try!(be_u32(src));
+            let timescale = be_u32(src)?;
             let duration = {
                 // Since we convert the 32-bit duration to 64-bit by
                 // upcasting, we need to preserve the special all-1s
                 // ("unknown") case by hand.
-                let d = try!(be_u32(src));
+                let d = be_u32(src)?;
                 if d == std::u32::MAX {
                     std::u64::MAX
                 } else {
                     d as u64
                 }
             };
             (timescale, duration)
         }
         _ => return Err(Error::InvalidData("unhandled mdhd version")),
     };
 
     // Skip uninteresting fields.
-    try!(skip(src, 4));
+    skip(src, 4)?;
 
     Ok(MediaHeaderBox {
         timescale: timescale,
         duration: duration,
     })
 }
 
 /// Parse a stco box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let offset_count = be_u32(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
-        offsets.push(try!(be_u32(src)) as u64);
+        offsets.push(be_u32(src)? as u64);
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(ChunkOffsetBox {
         offsets: offsets,
     })
 }
 
 /// Parse a co64 box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let offset_count = be_u32(src)?;
     let mut offsets = Vec::new();
     for _ in 0..offset_count {
-        offsets.push(try!(be_u64(src)));
+        offsets.push(be_u64(src)?);
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(ChunkOffsetBox {
         offsets: offsets,
     })
 }
 
 /// Parse a stss box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let sample_count = be_u32(src)?;
     let mut samples = Vec::new();
     for _ in 0..sample_count {
-        samples.push(try!(be_u32(src)));
+        samples.push(be_u32(src)?);
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(SyncSampleBox {
         samples: samples,
     })
 }
 
 /// Parse a stsc box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let sample_count = 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));
+        let first_chunk = be_u32(src)?;
+        let samples_per_chunk = be_u32(src)?;
+        let sample_description_index = be_u32(src)?;
         samples.push(SampleToChunk {
             first_chunk: first_chunk,
             samples_per_chunk: samples_per_chunk,
             sample_description_index: sample_description_index,
         });
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(SampleToChunkBox {
         samples: samples,
     })
 }
 
 /// Parse a stsz box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let sample_size = be_u32(src)?;
+    let sample_count = 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)));
+            sample_sizes.push(be_u32(src)?);
         }
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(SampleSizeBox {
         sample_size: sample_size,
         sample_sizes: sample_sizes,
     })
 }
 
 /// Parse a stts box.
 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 (_, _) = read_fullbox_extra(src)?;
+    let sample_count = 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));
+        let sample_count = be_u32(src)?;
+        let sample_delta = be_u32(src)?;
         samples.push(Sample {
             sample_count: sample_count,
             sample_delta: sample_delta,
         });
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(TimeToSampleBox {
         samples: samples,
     })
 }
 
 /// Parse a VPx Config Box.
 fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
-    let (version, _) = try!(read_fullbox_extra(src));
+    let (version, _) = 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 profile = src.read_u8()?;
+    let level = src.read_u8()?;
     let (bit_depth, color_space) = {
-        let byte = try!(src.read_u8());
+        let byte = src.read_u8()?;
         ((byte >> 4) & 0x0f, byte & 0x0f)
     };
     let (chroma_subsampling, transfer_function, video_full_range) = {
-        let byte = try!(src.read_u8());
+        let byte = 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));
+    let codec_init_size = be_u16(src)?;
+    let codec_init = read_buf(src, codec_init_size as usize)?;
 
     // TODO(rillian): validate field value ranges.
     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_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
-    let temp = try!(src.read_u8());
+    let temp = src.read_u8()?;
     let block_type = temp & 0x7f;
-    let length = try!(be_u24(src));
+    let length = be_u24(src)?;
     if length as usize > src.bytes_left() {
         return Err(Error::InvalidData(
                 "FLACMetadataBlock larger than parent box"));
     }
-    let data = try!(read_buf(src, length as usize));
+    let data = read_buf(src, length as usize)?;
     Ok(FLACMetadataBlock {
         block_type: block_type,
         data: data,
     })
 }
 
-fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+fn find_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
     // Tags for elementary stream description
     const ESDESCR_TAG: u8          = 0x03;
     const DECODER_CONFIG_TAG: u8   = 0x04;
     const DECODER_SPECIFIC_TAG: u8 = 0x05;
 
+    let mut remains = data;
+
+    while !remains.is_empty() {
+        let des = &mut Cursor::new(remains);
+        let tag = des.read_u8()?;
+
+        let extend_or_len = des.read_u8()?;
+        // extension tag start from 0x80.
+        let end = if extend_or_len >= 0x80 {
+            // Extension found, skip remaining extension.
+            skip(des, 2)?;
+            des.read_u8()? as u64 + des.position()
+        } else {
+            extend_or_len as u64 + des.position()
+        };
+
+        if end as usize > remains.len() {
+            return Err(Error::InvalidData("Invalid descriptor."));
+        }
+
+        let descriptor = &remains[des.position() as usize .. end as usize];
+
+        match tag {
+            ESDESCR_TAG => {
+                read_es_descriptor(descriptor, esds)?;
+            },
+            DECODER_CONFIG_TAG => {
+                read_dc_descriptor(descriptor, esds)?;
+            },
+            DECODER_SPECIFIC_TAG => {
+                read_ds_descriptor(descriptor, esds)?;
+            },
+            _ => {
+                log!("Unsupported descriptor, tag {}", tag);
+            },
+        }
+
+        remains = &remains[end as usize .. remains.len()];
+    }
+
+    Ok(())
+}
+
+fn read_ds_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
     let frequency_table =
         vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000),
              (0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050),
              (0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000),
              (0xc, 7350)];
 
-    let (_, _) = try!(read_fullbox_extra(src));
+    let des = &mut Cursor::new(data);
+
+    let audio_specific_config = be_u16(des)?;
+
+    let sample_index = (audio_specific_config & 0x07FF) >> 7;
+
+    let channel_counts = (audio_specific_config & 0x007F) >> 3;
+
+    let sample_frequency =
+        frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
+
+    esds.audio_sample_rate = sample_frequency;
+    esds.audio_channel_count = Some(channel_counts);
+
+    Ok(())
+}
+
+fn read_dc_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
+    let des = &mut Cursor::new(data);
+    let object_profile = des.read_u8()?;
+
+    // Skip uninteresting fields.
+    skip(des, 12)?;
+
+    if data.len() > des.position() as usize {
+        find_descriptor(&data[des.position() as usize .. data.len()], esds)?;
+    }
+
+    esds.audio_codec = match object_profile {
+        0x40 | 0x41 => CodecType::AAC,
+        0x6B => CodecType::MP3,
+        _ => CodecType::Unknown,
+    };
+
+    Ok(())
+}
+
+fn read_es_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
+    let des = &mut Cursor::new(data);
+
+    skip(des, 2)?;
+
+    let esds_flags = des.read_u8()?;
+
+    // Stream dependency flag, first bit from left most.
+    if esds_flags & 0x80 > 0 {
+        // Skip uninteresting fields.
+        skip(des, 2)?;
+    }
+
+    // Url flag, second bit from left most.
+    if esds_flags & 0x40 > 0 {
+        // Skip uninteresting fields.
+        let skip_es_len: usize = des.read_u8()? as usize + 2;
+        skip(des, skip_es_len)?;
+    }
+
+    if data.len() > des.position() as usize {
+        find_descriptor(&data[des.position() as usize .. data.len()], esds)?;
+    }
+
+    Ok(())
+}
+
+fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+    let (_, _) = read_fullbox_extra(src)?;
 
     let esds_size = src.head.size - src.head.offset - 4;
     if esds_size > BUF_SIZE_LIMIT {
         return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
     }
-    let esds_array = try!(read_buf(src, esds_size as usize));
-
-    // Parsing DecoderConfig descriptor to get the object_profile_indicator
-    // for correct codec type, audio sample rate and channel counts.
-    let (object_profile_indicator, sample_frequency, channels) = {
-        let mut object_profile: u8 = 0;
-        let mut sample_frequency = None;
-        let mut channels = None;
-
-        // clone a esds cursor for parsing.
-        let esds = &mut Cursor::new(&esds_array);
-        let next_tag = try!(esds.read_u8());
-
-        if next_tag != ESDESCR_TAG {
-            return Err(Error::Unsupported("fail to parse ES descriptor"));
-        }
-
-        let esds_extend = try!(esds.read_u8());
-        // extension tag start from 0x80.
-        let esds_end = if esds_extend >= 0x80 {
-            // skip remaining extension.
-            try!(skip(esds, 2));
-            esds.position() + try!(esds.read_u8()) as u64
-        } else {
-            esds.position() + esds_extend as u64
-        };
-        try!(skip(esds, 2));
-
-        let esds_flags = try!(esds.read_u8());
-
-        // Stream dependency flag, first bit from left most.
-        if esds_flags & 0x80 > 0 {
-            // Skip uninteresting fields.
-            try!(skip(esds, 2));
-        }
-
-        // Url flag, second bit from left most.
-        if esds_flags & 0x40 > 0 {
-            // Skip uninteresting fields.
-            let skip_es_len: usize = try!(esds.read_u8()) as usize + 2;
-            try!(skip(esds, skip_es_len));
-        }
+    let esds_array = read_buf(src, esds_size as usize)?;
 
-        // find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG)
-        if esds_end > esds.position() {
-            let next_tag = try!(esds.read_u8());
-            if next_tag == DECODER_CONFIG_TAG {
-                let dcds_extend = try!(esds.read_u8());
-                // extension tag start from 0x80.
-                if dcds_extend >= 0x80 {
-                    // skip remains extension and length.
-                    try!(skip(esds, 3));
-                }
-
-                object_profile = try!(esds.read_u8());
-
-                // Skip uninteresting fields.
-                try!(skip(esds, 12));
-            }
-        }
-
-
-        // find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG)
-        if esds_end > esds.position() {
-            let next_tag = try!(esds.read_u8());
-            if next_tag == DECODER_SPECIFIC_TAG {
-                let dsds_extend = try!(esds.read_u8());
-                // extension tag start from 0x80.
-                if dsds_extend >= 0x80 {
-                    // skip remains extension and length.
-                    try!(skip(esds, 3));
-                }
+    let mut es_data = ES_Descriptor::default();
+    find_descriptor(&esds_array, &mut es_data)?;
 
-                let audio_specific_config = try!(be_u16(esds));
-
-                let sample_index = (audio_specific_config & 0x07FF) >> 7;
-
-                let channel_counts = (audio_specific_config & 0x007F) >> 3;
-
-                sample_frequency =
-                    frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
-
-                channels = Some(channel_counts);
-            }
-        }
-
-        (object_profile, sample_frequency, channels)
-    };
+    es_data.codec_esds = esds_array;
 
-    let codec = match object_profile_indicator {
-        0x40 | 0x41 => CodecType::AAC,
-        0x6B => CodecType::MP3,
-        _ => CodecType::Unknown,
-    };
-
-    if codec == CodecType::Unknown {
-        return Err(Error::Unsupported("unknown audio codec"));
-    }
-
-    Ok(ES_Descriptor {
-        audio_codec: codec,
-        audio_sample_rate: sample_frequency,
-        audio_channel_count: channels,
-        codec_specific_config: esds_array,
-    })
+    Ok(es_data)
 }
 
 /// Parse `FLACSpecificBox`.
 fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> {
-    let (version, flags) = try!(read_fullbox_extra(src));
+    let (version, flags) = read_fullbox_extra(src)?;
     if version != 0 {
         return Err(Error::Unsupported("unknown dfLa (FLAC) version"));
     }
     if flags != 0 {
         return Err(Error::InvalidData("no-zero dfLa (FLAC) flags"));
     }
     let mut blocks = Vec::new();
     while src.bytes_left() > 0 {
-        let block = try!(read_flac_metadata(src));
+        let block = read_flac_metadata(src)?;
         blocks.push(block);
     }
     // The box must have at least one meta block, and the first block
     // must be the METADATA_BLOCK_STREAMINFO
     if blocks.is_empty() {
         return Err(Error::InvalidData("FLACSpecificBox missing metadata"));
     } else if blocks[0].block_type != 0 {
         println!("flac metadata block:\n  {:?}", blocks[0]);
@@ -1327,33 +1339,33 @@ fn read_dfla<T: Read>(src: &mut BMFFBox<
     Ok(FLACSpecificBox {
         version: version,
         blocks: blocks,
     })
 }
 
 /// Parse `OpusSpecificBox`.
 fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
-    let version = try!(src.read_u8());
+    let version = src.read_u8()?;
     if version != 0 {
         return Err(Error::Unsupported("unknown dOps (Opus) 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 output_channel_count = src.read_u8()?;
+    let pre_skip = be_u16(src)?;
+    let input_sample_rate = be_u32(src)?;
+    let output_gain = be_i16(src)?;
+    let channel_mapping_family = 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));
+        let stream_count = src.read_u8()?;
+        let coupled_count = src.read_u8()?;
+        let channel_mapping = read_buf(src, output_channel_count as usize)?;
 
         Some(ChannelMappingTable {
             stream_count: stream_count,
             coupled_count: coupled_count,
             channel_mapping: channel_mapping,
         })
     };
 
@@ -1383,54 +1395,54 @@ pub fn serialize_opus_header<W: byteorde
                 return Err(Error::InvalidData("Couldn't write OpusHead tag."));
             }
         }
     }
     // In mp4 encapsulation, the version field is 0, but in ogg
     // it is 1. While decoders generally accept zero as well, write
     // out the version of the header we're supporting rather than
     // whatever we parsed out of mp4.
-    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));
+    dst.write_u8(1)?;
+    dst.write_u8(opus.output_channel_count)?;
+    dst.write_u16::<byteorder::LittleEndian>(opus.pre_skip)?;
+    dst.write_u32::<byteorder::LittleEndian>(opus.input_sample_rate)?;
+    dst.write_i16::<byteorder::LittleEndian>(opus.output_gain)?;
+    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));
+            dst.write_u8(table.stream_count)?;
+            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(())
 }
 
 /// Parse a hdlr box.
 fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> {
-    let (_, _) = try!(read_fullbox_extra(src));
+    let (_, _) = read_fullbox_extra(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 4));
+    skip(src, 4)?;
 
-    let handler_type = try!(be_u32(src));
+    let handler_type = be_u32(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 12));
+    skip(src, 12)?;
 
     let bytes_left = src.bytes_left();
-    let _name = try!(read_null_terminated_string(src, bytes_left));
+    let _name = read_null_terminated_string(src, bytes_left)?;
 
     Ok(HandlerBox {
         handler_type: handler_type,
     })
 }
 
 /// Parse an video description inside an stsd box.
 fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
@@ -1439,64 +1451,64 @@ fn read_video_sample_entry<T: Read>(src:
         BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264,
         BoxType::VP8SampleEntry => CodecType::VP8,
         BoxType::VP9SampleEntry => CodecType::VP9,
         BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
         _ => CodecType::Unknown,
     };
 
     // Skip uninteresting fields.
-    try!(skip(src, 6));
+    skip(src, 6)?;
 
-    let data_reference_index = try!(be_u16(src));
+    let data_reference_index = be_u16(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 16));
+    skip(src, 16)?;
 
-    let width = try!(be_u16(src));
-    let height = try!(be_u16(src));
+    let width = be_u16(src)?;
+    let height = be_u16(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 14));
+    skip(src, 14)?;
 
-    let _compressorname = try!(read_fixed_length_pascal_string(src, 32));
+    let _compressorname = read_fixed_length_pascal_string(src, 32)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 4));
+    skip(src, 4)?;
 
     // Skip clap/pasp/etc. for now.
     let mut codec_specific = None;
     let mut iter = src.box_iter();
-    while let Some(mut b) = try!(iter.next_box()) {
+    while let Some(mut b) = 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));
+                let avcc = read_buf(&mut b.content, avcc_size as usize)?;
                 // TODO(kinetik): Parse avcC box?  For now we just stash the data.
                 codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc));
             }
             BoxType::VPCodecConfigurationBox => { // vpcC
                 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));
+                let vpcc = read_vpcc(&mut b)?;
                 codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc));
             }
-            _ => try!(skip_box_content(&mut b)),
+            _ => 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,
@@ -1504,23 +1516,23 @@ fn read_video_sample_entry<T: Read>(src:
             codec_specific: codec_specific,
         }))
         .ok_or_else(|| Error::InvalidData("malformed video sample entry"))
 }
 
 fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
     let mut codec_specific = None;
     let mut iter = src.box_iter();
-    while let Some(mut b) = try!(iter.next_box()) {
+    while let Some(mut b) = iter.next_box()? {
         match b.head.name {
             BoxType::ESDBox => {
-                let esds = try!(read_esds(&mut b));
+                let esds = read_esds(&mut b)?;
                 codec_specific = Some(esds);
             },
-            _ => try!(skip_box_content(&mut b)),
+            _ => skip_box_content(&mut b)?,
         }
     }
 
     codec_specific.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
 }
 
 /// Parse an audio description inside an stsd box.
 fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
@@ -1530,87 +1542,87 @@ fn read_audio_sample_entry<T: Read>(src:
         BoxType::MP4AudioSampleEntry => CodecType::AAC,
         BoxType::FLACSampleEntry => CodecType::FLAC,
         BoxType::OpusSampleEntry => CodecType::Opus,
         BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio,
         _ => CodecType::Unknown,
     };
 
     // Skip uninteresting fields.
-    try!(skip(src, 6));
+    skip(src, 6)?;
 
-    let data_reference_index = try!(be_u16(src));
+    let data_reference_index = be_u16(src)?;
 
     // XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant
     // uses it, need to work out if we have to support it.  Without checking
     // here and reading extra fields after samplerate (or bailing with an
     // error), the parser loses sync completely.
-    let version = try!(be_u16(src));
+    let version = be_u16(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 6));
+    skip(src, 6)?;
 
-    let channelcount = try!(be_u16(src));
-    let samplesize = try!(be_u16(src));
+    let channelcount = be_u16(src)?;
+    let samplesize = be_u16(src)?;
 
     // Skip uninteresting fields.
-    try!(skip(src, 4));
+    skip(src, 4)?;
 
-    let samplerate = try!(be_u32(src));
+    let samplerate = be_u32(src)?;
 
     match version {
         0 => (),
         1 => {
             // Quicktime sound sample description version 1.
             // Skip uninteresting fields.
-            try!(skip(src, 16));
+            skip(src, 16)?;
         },
         _ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
     }
 
     // Skip chan/etc. for now.
     let mut codec_specific = None;
     let mut iter = src.box_iter();
-    while let Some(mut b) = try!(iter.next_box()) {
+    while let Some(mut b) = 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 esds = try!(read_esds(&mut b));
+                let esds = read_esds(&mut b)?;
                 track.codec_type = esds.audio_codec;
                 codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds));
             }
             BoxType::FLACSpecificBox => {
                 if name != BoxType::FLACSampleEntry ||
                     codec_specific.is_some() {
                     return Err(Error::InvalidData("malformed audio sample entry"));
                 }
-                let dfla = try!(read_dfla(&mut b));
+                let dfla = read_dfla(&mut b)?;
                 track.codec_type = CodecType::FLAC;
                 codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla));
             }
             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));
+                let dops = read_dops(&mut b)?;
                 track.codec_type = CodecType::Opus;
                 codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
             }
             BoxType::QTWaveAtom => {
-                let qt_esds = try!(read_qt_wave_atom(&mut b));
+                let qt_esds = read_qt_wave_atom(&mut b)?;
                 track.codec_type = qt_esds.audio_codec;
                 codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
             }
-            _ => try!(skip_box_content(&mut b)),
+            _ => 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,
@@ -1618,38 +1630,38 @@ fn read_audio_sample_entry<T: Read>(src:
             samplerate: samplerate,
             codec_specific: codec_specific,
         }))
         .ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
 }
 
 /// Parse a stsd box.
 fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
-    let (_, _) = try!(read_fullbox_extra(src));
+    let (_, _) = read_fullbox_extra(src)?;
 
-    let description_count = try!(be_u32(src));
+    let description_count = be_u32(src)?;
     let mut descriptions = Vec::new();
 
     {
         // TODO(kinetik): check if/when more than one desc per track? do we need to support?
         let mut iter = src.box_iter();
-        while let Some(mut b) = try!(iter.next_box()) {
+        while let Some(mut b) = iter.next_box()? {
             let description = match track.track_type {
                 TrackType::Video => read_video_sample_entry(&mut b, track),
                 TrackType::Audio => read_audio_sample_entry(&mut b, track),
                 TrackType::Unknown => Err(Error::Unsupported("unknown track type")),
             };
             let description = match description {
                 Ok(desc) => desc,
                 Err(Error::Unsupported(_)) => {
                     // read_{audio,video}_desc may have returned Unsupported
                     // after partially reading the box content, so we can't
                     // simply use skip_box_content here.
                     let to_skip = b.bytes_left();
-                    try!(skip(&mut b, to_skip));
+                    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 **");
@@ -1658,80 +1670,80 @@ fn read_stsd<T: Read>(src: &mut BMFFBox<
             check_parser_state!(b.content);
             if descriptions.len() == description_count as usize {
                 break;
             }
         }
     }
 
     // Padding could be added in some contents.
-    try!(skip_box_remain(src));
+    skip_box_remain(src)?;
 
     Ok(SampleDescriptionBox {
         descriptions: descriptions,
     })
 }
 
 /// Skip a number of bytes that we don't care to parse.
 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));
+        let len = src.take(buf_size as u64).read(&mut buf)?;
         if len == 0 {
             return Err(Error::UnexpectedEOF);
         }
         bytes -= len;
     }
     Ok(())
 }
 
 /// Read size bytes into a Vector or return error.
 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));
+    let r = src.read(&mut buf)?;
     if r != size {
         return Err(Error::InvalidData("failed buffer read"));
     }
     Ok(buf)
 }
 
 // TODO(kinetik): Find a copy of ISO/IEC 14496-1 to confirm various string encodings.
 // XXX(kinetik): definition of "null-terminated" string is fuzzy, we have:
 // - zero or more byte strings, with a single null terminating the string.
 // - zero byte strings with no null terminator (i.e. zero space in the box for the string)
 // - length-prefixed strings with no null terminator (e.g. bear_rotate_0.mp4)
 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());
+        let c = 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));
+    let len = src.read_u8()?;
+    let buf = read_buf(src, len as usize)?;
     String::from_utf8(buf).map_err(From::from)
 }
 
 // Weird string encoding with a length prefix and a fixed sized buffer which
 // contains padding if the string doesn't fill the buffer.
 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()));
+    let len = cmp::min(src.read_u8()? as usize, size - 1);
+    let buf = read_buf(src, len)?;
+    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> {
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -143,18 +143,18 @@ fn read_truncated_ftyp() {
     let mut stream = make_box(BoxSize::UncheckedShort(24), b"ftyp", |s| {
         s.append_bytes(b"mp42")
             .B32(0) // minor version
             .append_bytes(b"isom")
     });
     let mut context = MediaContext::new();
     match read_mp4(&mut stream, &mut context) {
         Err(Error::UnexpectedEOF) => (),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 #[test]
 fn read_ftyp_case() {
     // Brands in BMFF are represented as a u32, so it would seem clear that
     // 0x6d703432 ("mp42") is not equal to 0x4d503432 ("MP42"), but some
     // demuxers treat these as case-insensitive strings, e.g. street.mp4's
@@ -588,18 +588,18 @@ fn serialize_opus_header() {
         pre_skip: 342,
         input_sample_rate: 24000,
         output_gain: 0,
         channel_mapping_family: 0,
         channel_mapping_table: None,
     };
     let mut v = Vec::<u8>::new();
     super::serialize_opus_header(&opus, &mut v).unwrap();
-    assert!(v.len() == 19);
-    assert!(v == vec![
+    assert_eq!(v.len(), 19);
+    assert_eq!(v, vec![
             0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
             0x01, 0x01, 0x56, 0x01,
             0xc0, 0x5d, 0x00, 0x00,
             0x00, 0x00, 0x00,
     ]);
     let opus = super::OpusSpecificBox {
         version: 0,
         output_channel_count: 6,
@@ -610,18 +610,18 @@ fn serialize_opus_header() {
         channel_mapping_table: Some(super::ChannelMappingTable {
             stream_count: 4,
             coupled_count: 2,
             channel_mapping: vec![0, 4, 1, 2, 3, 5],
         }),
     };
     let mut v = Vec::<u8>::new();
     super::serialize_opus_header(&opus, &mut v).unwrap();
-    assert!(v.len() == 27);
-    assert!(v == vec![
+    assert_eq!(v.len(), 27);
+    assert_eq!(v, vec![
             0x4f, 0x70, 0x75, 0x73, 0x48,0x65, 0x61, 0x64,
             0x01, 0x06, 0x98, 0x00,
             0x80, 0xbb, 0x00, 0x00,
             0x00, 0x00, 0x01, 0x04, 0x02,
             0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
     ]);
 }
 
@@ -640,18 +640,18 @@ fn avcc_limit() {
          .append_bytes(b"avcC")
          .append_repeated(0, 100)
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     let mut track = super::Track::new(0);
     match super::read_video_sample_entry(&mut stream, &mut track) {
         Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 #[test]
 fn esds_limit() {
     let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
         s.append_repeated(0, 6)
          .B16(1)
@@ -666,18 +666,18 @@ fn esds_limit() {
          .append_bytes(b"esds")
          .append_repeated(0, 100)
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     let mut track = super::Track::new(0);
     match super::read_audio_sample_entry(&mut stream, &mut track) {
         Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 #[test]
 fn esds_limit_2() {
     let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
         s.append_repeated(0, 6)
          .B16(1)
@@ -692,34 +692,34 @@ fn esds_limit_2() {
          .append_bytes(b"esds")
          .append_repeated(0, 4)
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     let mut track = super::Track::new(0);
     match super::read_audio_sample_entry(&mut stream, &mut track) {
         Err(Error::UnexpectedEOF) => (),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 #[test]
 fn read_elst_zero_entries() {
     let mut stream = make_fullbox(BoxSize::Auto, b"elst", 0, |s| {
         s.B32(0)
          .B16(12)
          .B16(34)
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     match super::read_elst(&mut stream) {
         Err(Error::InvalidData(s)) => assert_eq!(s, "invalid edit count"),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 fn make_elst() -> Cursor<Vec<u8>> {
     make_fullbox(BoxSize::Auto, b"elst", 1, |s| {
         s.B32(1)
         // first entry
          .B64(1234) // duration
@@ -736,18 +736,18 @@ fn read_edts_bogus() {
     let mut stream = make_box(BoxSize::Auto, b"edts", |s| {
         s.append_bytes(&make_elst().into_inner())
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
     let mut track = super::Track::new(0);
     match super::read_edts(&mut stream, &mut track) {
         Err(Error::InvalidData(s)) => assert_eq!(s, "expected additional edit"),
-        Ok(_) => assert!(false, "expected an error result"),
-        _ => assert!(false, "expected a different error result"),
+        Ok(_) => panic!("expected an error result"),
+        _ => panic!("expected a different error result"),
     }
 }
 
 #[test]
 fn invalid_pascal_string() {
     // String claims to be 32 bytes long (we provide 33 bytes to account for
     // the 1 byte length prefix).
     let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
@@ -822,17 +822,17 @@ fn skip_padding_in_stsd() {
     super::read_stsd(&mut stream, &mut super::Track::new(0))
           .expect("fail to skip padding: stsd");
 }
 
 #[test]
 fn read_qt_wave_atom() {
     let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| {
         s.B8(0x03)  // elementary stream descriptor tag
-         .B8(0x0b)  // esds length
+         .B8(0x12)  // esds length
          .append_repeated(0, 2)
          .B8(0x00)  // flags
          .B8(0x04)  // decoder config descriptor tag
          .B8(0x0d)  // dcds length
          .B8(0x6b)  // mp3
          .append_repeated(0, 12)
     }).into_inner();
     let wave = make_box(BoxSize::Auto, b"wave", |s| {
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -73,18 +73,18 @@ fn public_api() {
                     mp4::AudioCodecSpecific::ES_Descriptor(esds) => {
                         assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
                         assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
                         "ES"
                     }
                     mp4::AudioCodecSpecific::FLACSpecificBox(flac) => {
                         // STREAMINFO block must be present and first.
                         assert!(flac.blocks.len() > 0);
-                        assert!(flac.blocks[0].block_type == 0);
-                        assert!(flac.blocks[0].data.len() == 34);
+                        assert_eq!(flac.blocks[0].block_type, 0);
+                        assert_eq!(flac.blocks[0].data.len(), 34);
                         "FLAC"
                     }
                     mp4::AudioCodecSpecific::OpusSpecificBox(opus) => {
                         // We don't enter in here, we just check if fields are public.
                         assert!(opus.version > 0);
                         "Opus"
                     }
                 }, "ES");
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -136,28 +136,26 @@ pub struct mp4parse_pssh_info {
 #[repr(C)]
 pub struct mp4parse_track_audio_info {
     pub channels: u16,
     pub bit_depth: u16,
     pub sample_rate: u32,
     // TODO(kinetik):
     // int32_t profile;
     // int32_t extended_profile; // check types
-    codec_specific_config: mp4parse_byte_data,
+    pub codec_specific_config: mp4parse_byte_data,
 }
 
 #[repr(C)]
 pub struct mp4parse_track_video_info {
     pub display_width: u32,
     pub display_height: u32,
     pub image_width: u16,
     pub image_height: u16,
-    // TODO(kinetik):
-    // extra_data
-    // codec_specific_config
+    pub extra_data: mp4parse_byte_data,
 }
 
 #[repr(C)]
 pub struct mp4parse_fragment_info {
     pub fragment_duration: u64,
     // TODO:
     // info in trex box.
 }
@@ -331,17 +329,17 @@ fn rational_scale(numerator: u64, denomi
 }
 
 fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> {
     let microseconds_per_second = 1000000;
     rational_scale(time.0, scale.0, microseconds_per_second)
 }
 
 fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> {
-    assert!(time.1 == scale.1);
+    assert_eq!(time.1, scale.1);
     let microseconds_per_second = 1000000;
     rational_scale(time.0, scale.0, microseconds_per_second)
 }
 
 /// Fill the supplied `mp4parse_track_info` with metadata for `track`.
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
     if parser.is_null() || info.is_null() || (*parser).poisoned() {
@@ -455,21 +453,21 @@ pub unsafe extern fn mp4parse_get_track_
     };
 
     (*info).channels = audio.channelcount;
     (*info).bit_depth = audio.samplesize;
     (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
 
     match audio.codec_specific {
         AudioCodecSpecific::ES_Descriptor(ref v) => {
-            if v.codec_specific_config.len() > std::u32::MAX as usize {
+            if v.codec_esds.len() > std::u32::MAX as usize {
                 return MP4PARSE_ERROR_INVALID;
             }
-            (*info).codec_specific_config.length = v.codec_specific_config.len() as u32;
-            (*info).codec_specific_config.data = v.codec_specific_config.as_ptr();
+            (*info).codec_specific_config.length = v.codec_esds.len() as u32;
+            (*info).codec_specific_config.data = v.codec_esds.as_ptr();
             if let Some(rate) = v.audio_sample_rate {
                 (*info).sample_rate = rate;
             }
             if let Some(channels) = v.audio_channel_count {
                 (*info).channels = channels;
             }
         }
         AudioCodecSpecific::FLACSpecificBox(ref flac) => {
@@ -542,16 +540,23 @@ pub unsafe extern fn mp4parse_get_track_
         (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
         (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
     } else {
         return MP4PARSE_ERROR_INVALID;
     }
     (*info).image_width = video.width;
     (*info).image_height = video.height;
 
+    match video.codec_specific {
+        VideoCodecSpecific::AVCConfig(ref avc) => {
+            (*info).extra_data.set_data(avc);
+        },
+        _ => {},
+    }
+
     MP4PARSE_OK
 }
 
 /// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error {
     if parser.is_null() || info.is_null() || (*parser).poisoned() {
         return MP4PARSE_ERROR_BADARG;
@@ -681,19 +686,19 @@ fn free_null_parser() {
     }
 }
 
 #[test]
 fn get_track_count_null_parser() {
     unsafe {
         let mut count: u32 = 0;
         let rv = mp4parse_get_track_count(std::ptr::null(), std::ptr::null_mut());
-        assert!(rv == MP4PARSE_ERROR_BADARG);
+        assert_eq!(rv, MP4PARSE_ERROR_BADARG);
         let rv = mp4parse_get_track_count(std::ptr::null(), &mut count);
-        assert!(rv == MP4PARSE_ERROR_BADARG);
+        assert_eq!(rv, MP4PARSE_ERROR_BADARG);
     }
 }
 
 #[test]
 fn arg_validation() {
     unsafe {
         // Passing a null mp4parse_io is an error.
         let parser = mp4parse_new(std::ptr::null());
@@ -732,16 +737,17 @@ fn arg_validation() {
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
 
         let mut dummy_video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            extra_data: mp4parse_byte_data::default(),
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
 
         let mut dummy_audio = Default::default();
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
     }
 }
 
@@ -776,16 +782,17 @@ fn arg_validation_with_parser() {
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info));
 
         let mut dummy_video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            extra_data: mp4parse_byte_data::default(),
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
 
         let mut dummy_audio = Default::default();
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
 
         mp4parse_free(parser);
     }
@@ -802,17 +809,17 @@ fn get_track_count_poisoned_parser() {
         let parser = mp4parse_new(&io);
         assert!(!parser.is_null());
 
         // Our mp4parse_io read should simply fail with an error.
         assert_eq!(MP4PARSE_ERROR_IO, mp4parse_read(parser));
 
         let mut count: u32 = 0;
         let rv = mp4parse_get_track_count(parser, &mut count);
-        assert!(rv == MP4PARSE_ERROR_BADARG);
+        assert_eq!(rv, MP4PARSE_ERROR_BADARG);
     }
 }
 
 #[test]
 fn arg_validation_with_data() {
     unsafe {
         let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
         let io = mp4parse_io { read: valid_read,
@@ -847,16 +854,17 @@ fn arg_validation_with_data() {
         assert_eq!(info.duration, 61333);
         assert_eq!(info.media_time, 21333);
 
         let mut video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            extra_data: mp4parse_byte_data::default(),
         };
         assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video));
         assert_eq!(video.display_width, 320);
         assert_eq!(video.display_height, 240);
         assert_eq!(video.image_width, 320);
         assert_eq!(video.image_height, 240);
 
         let mut audio = Default::default();
@@ -878,17 +886,18 @@ fn arg_validation_with_data() {
         assert_eq!(info.codec, mp4parse_codec::MP4PARSE_CODEC_UNKNOWN);
         assert_eq!(info.track_id, 0);
         assert_eq!(info.duration, 0);
         assert_eq!(info.media_time, 0);
 
         let mut video = mp4parse_track_video_info { display_width: 0,
                                                     display_height: 0,
                                                     image_width: 0,
-                                                    image_height: 0 };
+                                                    image_height: 0,
+                                                    extra_data: mp4parse_byte_data::default(),};
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video));
         assert_eq!(video.display_width, 0);
         assert_eq!(video.display_height, 0);
         assert_eq!(video.image_width, 0);
         assert_eq!(video.image_height, 0);
 
         let mut audio = Default::default();
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio));