diff options
Diffstat (limited to 'media/libstagefright/binding/mp4parse')
-rw-r--r-- | media/libstagefright/binding/mp4parse/Cargo.toml | 29 | ||||
-rw-r--r-- | media/libstagefright/binding/mp4parse/src/boxes.rs | 62 | ||||
-rw-r--r-- | media/libstagefright/binding/mp4parse/src/lib.rs | 1704 | ||||
-rw-r--r-- | media/libstagefright/binding/mp4parse/src/tests.rs | 860 | ||||
-rw-r--r-- | media/libstagefright/binding/mp4parse/tests/afl.rs | 53 | ||||
-rw-r--r-- | media/libstagefright/binding/mp4parse/tests/minimal.mp4 | bin | 2591 -> 0 bytes | |||
-rw-r--r-- | media/libstagefright/binding/mp4parse/tests/public.rs | 97 |
7 files changed, 0 insertions, 2805 deletions
diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml deleted file mode 100644 index affcef72b..000000000 --- a/media/libstagefright/binding/mp4parse/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "mp4parse" -version = "0.6.0" -authors = [ - "Ralph Giles <giles@mozilla.com>", - "Matthew Gregan <kinetik@flim.org>", - "Alfredo Yang <ayang@mozilla.com>", -] - -description = "Parser for ISO base media file format (mp4)" -documentation = "https://mp4parse-docs.surge.sh/mp4parse/" -license = "MPL-2.0" - -repository = "https://github.com/mozilla/mp4parse-rust" - -# Avoid complaints about trying to package test files. -exclude = [ - "*.mp4", -] - -[dependencies] -byteorder = "0.5.0" - -[dev-dependencies] -test-assembler = "0.1.2" - -# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on. -[profile.release] -debug-assertions = true diff --git a/media/libstagefright/binding/mp4parse/src/boxes.rs b/media/libstagefright/binding/mp4parse/src/boxes.rs deleted file mode 100644 index 689439ade..000000000 --- a/media/libstagefright/binding/mp4parse/src/boxes.rs +++ /dev/null @@ -1,62 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -macro_rules! box_database { - ($($boxenum:ident $boxtype:expr),*,) => { - #[derive(Debug, Clone, Copy, PartialEq)] - pub enum BoxType { - $($boxenum),*, - UnknownBox(u32), - } - - impl From<u32> for BoxType { - fn from(t: u32) -> BoxType { - use self::BoxType::*; - match t { - $($boxtype => $boxenum),*, - _ => UnknownBox(t), - } - } - } - } -} - -box_database!( - FileTypeBox 0x66747970, // "ftyp" - MovieBox 0x6d6f6f76, // "moov" - MovieHeaderBox 0x6d766864, // "mvhd" - TrackBox 0x7472616b, // "trak" - TrackHeaderBox 0x746b6864, // "tkhd" - EditBox 0x65647473, // "edts" - MediaBox 0x6d646961, // "mdia" - EditListBox 0x656c7374, // "elst" - MediaHeaderBox 0x6d646864, // "mdhd" - HandlerBox 0x68646c72, // "hdlr" - MediaInformationBox 0x6d696e66, // "minf" - SampleTableBox 0x7374626c, // "stbl" - SampleDescriptionBox 0x73747364, // "stsd" - TimeToSampleBox 0x73747473, // "stts" - SampleToChunkBox 0x73747363, // "stsc" - SampleSizeBox 0x7374737a, // "stsz" - ChunkOffsetBox 0x7374636f, // "stco" - ChunkLargeOffsetBox 0x636f3634, // "co64" - SyncSampleBox 0x73747373, // "stss" - AVCSampleEntry 0x61766331, // "avc1" - AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec. - AVCConfigurationBox 0x61766343, // "avcC" - MP4AudioSampleEntry 0x6d703461, // "mp4a" - ESDBox 0x65736473, // "esds" - VP8SampleEntry 0x76703038, // "vp08" - VP9SampleEntry 0x76703039, // "vp09" - VPCodecConfigurationBox 0x76706343, // "vpcC" - FLACSampleEntry 0x664c6143, // "fLaC" - FLACSpecificBox 0x64664c61, // "dfLa" - OpusSampleEntry 0x4f707573, // "Opus" - OpusSpecificBox 0x644f7073, // "dOps" - ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec. - ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec. - MovieExtendsBox 0x6d766578, // "mvex" - MovieExtendsHeaderBox 0x6d656864, // "mehd" - QTWaveAtom 0x77617665, // "wave" - quicktime atom -); diff --git a/media/libstagefright/binding/mp4parse/src/lib.rs b/media/libstagefright/binding/mp4parse/src/lib.rs deleted file mode 100644 index 37606a5e2..000000000 --- a/media/libstagefright/binding/mp4parse/src/lib.rs +++ /dev/null @@ -1,1704 +0,0 @@ -//! Module for parsing ISO Base Media Format aka video/mp4 streams. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. -#![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::io::Cursor; -use std::cmp; - -mod boxes; -use boxes::BoxType; - -// Unit tests. -#[cfg(test)] -mod tests; - -// Arbitrary buffer size limit used for raw read_bufs on a box. -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 )* ); - } - ) -} - -/// Describes parser failures. -/// -/// This enum wraps the standard `io::Error` type, unified with -/// our own parser error states and those of crates we use. -#[derive(Debug)] -pub enum Error { - /// Parse error caused by corrupt or malformed data. - InvalidData(&'static str), - /// Parse error caused by limited parser support rather than invalid data. - Unsupported(&'static str), - /// Reflect `std::io::ErrorKind::UnexpectedEof` for short data. - UnexpectedEOF, - /// Propagate underlying errors from `std::io`. - Io(std::io::Error), - /// read_mp4 terminated without detecting a moov box. - 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") - } -} - -/// Result shorthand using our Error enum. -pub type Result<T> = std::result::Result<T, Error>; - -/// Basic ISO box structure. -/// -/// mp4 files are a sequence of possibly-nested 'box' structures. Each box -/// begins with a header describing the length of the box's data and a -/// four-byte box type which identifies the type of the box. Together these -/// are enough to interpret the contents of that section of the file. -#[derive(Debug, Clone, Copy)] -struct BoxHeader { - /// Box type. - name: BoxType, - /// Size of the box in bytes. - size: u64, - /// Offset to the start of the contained data (or header size). - offset: u64, -} - -/// File type box 'ftyp'. -#[derive(Debug)] -struct FileTypeBox { - major_brand: u32, - minor_version: u32, - compatible_brands: Vec<u32>, -} - -/// Movie header box 'mvhd'. -#[derive(Debug)] -struct MovieHeaderBox { - pub timescale: u32, - duration: u64, -} - -/// Track header box 'tkhd' -#[derive(Debug, Clone)] -pub struct TrackHeaderBox { - track_id: u32, - pub disabled: bool, - pub duration: u64, - pub width: u32, - pub height: u32, -} - -/// Edit list box 'elst' -#[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, -} - -/// Media header box 'mdhd' -#[derive(Debug)] -struct MediaHeaderBox { - timescale: u32, - duration: u64, -} - -// Chunk offset box 'stco' or 'co64' -#[derive(Debug)] -struct ChunkOffsetBox { - offsets: Vec<u64>, -} - -// Sync sample box 'stss' -#[derive(Debug)] -struct SyncSampleBox { - samples: Vec<u32>, -} - -// Sample to chunk box 'stsc' -#[derive(Debug)] -struct SampleToChunkBox { - samples: Vec<SampleToChunk>, -} - -#[derive(Debug)] -struct SampleToChunk { - first_chunk: u32, - samples_per_chunk: u32, - sample_description_index: u32, -} - -// Sample size box 'stsz' -#[derive(Debug)] -struct SampleSizeBox { - sample_size: u32, - sample_sizes: Vec<u32>, -} - -// Time to sample box 'stts' -#[derive(Debug)] -struct TimeToSampleBox { - samples: Vec<Sample>, -} - -#[derive(Debug)] -struct Sample { - sample_count: u32, - sample_delta: u32, -} - -// Handler reference box 'hdlr' -#[derive(Debug)] -struct HandlerBox { - handler_type: u32, -} - -// Sample description box 'stsd' -#[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 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>, -} - -#[allow(non_camel_case_types)] -#[derive(Debug, Clone)] -pub enum AudioCodecSpecific { - ES_Descriptor(ES_Descriptor), - FLACSpecificBox(FLACSpecificBox), - OpusSpecificBox(OpusSpecificBox), -} - -#[derive(Debug, Clone)] -pub struct AudioSampleEntry { - data_reference_index: u16, - pub 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, -} - -/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9). -#[derive(Debug, Clone)] -pub struct VPxConfigBox { - profile: u8, - level: u8, - pub bit_depth: u8, - pub color_space: u8, // Really an enum - pub chroma_subsampling: u8, - transfer_function: u8, - video_full_range: bool, - pub codec_init: Vec<u8>, // Empty for vp8/vp9. -} - -#[derive(Debug, Clone)] -pub struct FLACMetadataBlock { - pub block_type: u8, - pub data: Vec<u8>, -} - -/// Represet a FLACSpecificBox 'dfLa' -#[derive(Debug, Clone)] -pub struct FLACSpecificBox { - version: u8, - pub blocks: Vec<FLACMetadataBlock>, -} - -#[derive(Debug, Clone)] -struct ChannelMappingTable { - stream_count: u8, - coupled_count: u8, - channel_mapping: Vec<u8>, -} - -/// Represent an OpusSpecificBox 'dOps' -#[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)] -pub struct MovieExtendsBox { - pub fragment_duration: Option<MediaScaledTime>, -} - -/// Internal data structures. -#[derive(Debug, Default)] -pub struct MediaContext { - pub timescale: Option<MediaTimeScale>, - /// Tracks found in the file. - pub tracks: Vec<Track>, - pub mvex: Option<MovieExtendsBox>, -} - -impl MediaContext { - pub fn new() -> MediaContext { - Default::default() - } -} - -#[derive(Debug, PartialEq)] -pub enum TrackType { - Audio, - Video, - Unknown, -} - -impl Default for TrackType { - fn default() -> Self { TrackType::Unknown } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum CodecType { - Unknown, - MP3, - AAC, - FLAC, - Opus, - H264, - VP9, - VP8, - EncryptedVideo, - EncryptedAudio, -} - -impl Default for CodecType { - fn default() -> Self { CodecType::Unknown } -} - -/// The media's global (mvhd) timescale in units per second. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MediaTimeScale(pub u64); - -/// A time to be scaled by the media's global (mvhd) timescale. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct MediaScaledTime(pub u64); - -/// The track's local (mdhd) timescale. -/// Members are timescale units per second and the track id. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct TrackTimeScale(pub u64, pub usize); - -/// A time to be scaled by the track's local (mdhd) timescale. -/// Members are time in scale units and the track id. -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct TrackScaledTime(pub u64, pub usize); - -/// A fragmented file contains no sample data in stts, stsc, and stco. -#[derive(Debug, Default)] -pub struct EmptySampleTableBoxes { - pub empty_stts : bool, - pub empty_stsc : bool, - pub empty_stco : bool, -} - -/// Check boxes contain data. -impl EmptySampleTableBoxes { - pub fn all_empty(&self) -> bool { - self.empty_stts & self.empty_stsc & self.empty_stco - } -} - -#[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>, - pub track_id: Option<u32>, - pub codec_type: CodecType, - pub empty_sample_boxes: EmptySampleTableBoxes, - pub data: Option<SampleEntry>, - pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this. -} - -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) - } -} - -/// 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 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)); - 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, - }) -} - -/// 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()); - 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()); - 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(); - log!("remain {} (skipped) in {:?}", len, header); - len - }; - skip(src, remain) -} - -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")); - } - } -} - -/// Read the contents of a box, including sub boxes. -/// -/// 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()) { - // 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 - // iso5: styp, sidx, ssix, prft - // 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)); - 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" - }); - } - } - - // XXX(kinetik): This isn't perfect, as a "moov" with no contents is - // treated as okay but we haven't found anything useful. Needs more - // thought for clearer behaviour here. - 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); - } - BoxType::MovieExtendsBox => { - let mvex = try!(read_mvex(&mut b)); - log!("{:?}", mvex); - context.mvex = Some(mvex); - } - _ => try!(skip_box_content(&mut b)), - }; - check_parser_state!(b.content); - } - Ok(()) -} - -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()) { - match b.head.name { - BoxType::MovieExtendsHeaderBox => { - let duration = try!(read_mehd(&mut b)); - fragment_duration = Some(duration); - }, - _ => try!(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 fragment_duration = match version { - 1 => try!(be_u64(src)), - 0 => try!(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()) { - 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 /* '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)), - }; - 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)); - track.empty_sample_boxes.empty_stts = stts.samples.is_empty(); - log!("{:?}", stts); - } - BoxType::SampleToChunkBox => { - let stsc = try!(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)); - log!("{:?}", stsz); - } - BoxType::ChunkOffsetBox => { - let stco = try!(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)); - 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(()) -} - -/// 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 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))); - } - 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)); - match version { - // 64 bit creation and modification times. - 1 => { - try!(skip(src, 16)); - } - // 32 bit creation and modification times. - 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")), - }; - // Skip remaining fields. - try!(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 disabled = flags & 0x1u32 == 0 || flags & 0x2u32 == 0; - match version { - // 64 bit creation and modification times. - 1 => { - try!(skip(src, 16)); - } - // 32 bit creation and modification times. - 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")), - }; - // Skip uninteresting fields. - 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, - }) -} - -/// 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)); - 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))) - } - 0 => { - // 32 bit segment duration and media times. - (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, - }) -} - -/// Parse a mdhd box. -fn read_mdhd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaHeaderBox> { - let (version, _) = try!(read_fullbox_extra(src)); - let (timescale, duration) = match version { - 1 => { - // Skip 64-bit creation and modification times. - try!(skip(src, 16)); - - // 64 bit duration. - (try!(be_u32(src)), try!(be_u64(src))) - } - 0 => { - // Skip 32-bit creation and modification times. - try!(skip(src, 8)); - - // 32 bit duration. - let timescale = try!(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)); - 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)); - - 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 mut offsets = Vec::new(); - for _ in 0..offset_count { - offsets.push(try!(be_u32(src)) as u64); - } - - // Padding could be added in some contents. - try!(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 mut offsets = Vec::new(); - for _ in 0..offset_count { - offsets.push(try!(be_u64(src))); - } - - // Padding could be added in some contents. - try!(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 mut samples = Vec::new(); - for _ in 0..sample_count { - samples.push(try!(be_u32(src))); - } - - // Padding could be added in some contents. - try!(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 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, - }); - } - - // Padding could be added in some contents. - try!(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 mut sample_sizes = Vec::new(); - if sample_size == 0 { - for _ in 0..sample_count { - sample_sizes.push(try!(be_u32(src))); - } - } - - // Padding could be added in some contents. - try!(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 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, - }); - } - - // Padding could be added in some contents. - try!(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)); - 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)); - - // 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 block_type = temp & 0x7f; - let length = try!(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)); - Ok(FLACMetadataBlock { - block_type: block_type, - data: data, - }) -} - -fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> { - // Tags for elementary stream description - const ESDESCR_TAG: u8 = 0x03; - const DECODER_CONFIG_TAG: u8 = 0x04; - const DECODER_SPECIFIC_TAG: u8 = 0x05; - - 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 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)); - } - - // 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 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) - }; - - 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, - }) -} - -/// Parse `FLACSpecificBox`. -fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> { - let (version, flags) = try!(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)); - 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]); - return Err(Error::InvalidData( - "FLACSpecificBox must have STREAMINFO metadata first")); - } else if blocks[0].data.len() != 34 { - return Err(Error::InvalidData( - "FLACSpecificBox STREAMINFO block is the wrong size")); - } - 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()); - 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 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, - }) - }; - - // TODO(kinetik): validate field value ranges. - 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, - }) -} - -/// Re-serialize the Opus codec-specific config data as an `OpusHead` packet. -/// -/// Some decoders expect the initialization data in the format used by the -/// Ogg and WebM encapsulations. To support this we prepend the `OpusHead` -/// tag and byte-swap the data from big- to little-endian relative to the -/// dOps box. -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.")); - } - } - } - // 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)); - 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(()) -} - -/// Parse a hdlr box. -fn read_hdlr<T: Read>(src: &mut BMFFBox<T>) -> Result<HandlerBox> { - let (_, _) = try!(read_fullbox_extra(src)); - - // Skip uninteresting fields. - try!(skip(src, 4)); - - let handler_type = try!(be_u32(src)); - - // Skip uninteresting fields. - 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, - }) -} - -/// Parse an video description inside an stsd box. -fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> { - let name = src.get_header().name; - track.codec_type = match name { - 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)); - - let data_reference_index = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 16)); - - let width = try!(be_u16(src)); - let height = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 14)); - - let _compressorname = try!(read_fixed_length_pascal_string(src, 32)); - - // Skip uninteresting fields. - try!(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()) { - 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)); - // 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)); - 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_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()) { - match b.head.name { - BoxType::ESDBox => { - let esds = try!(read_esds(&mut b)); - codec_specific = Some(esds); - }, - _ => try!(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> { - let name = src.get_header().name; - track.codec_type = match name { - // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg). - 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)); - - let data_reference_index = try!(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)); - - // Skip uninteresting fields. - try!(skip(src, 6)); - - let channelcount = try!(be_u16(src)); - let samplesize = try!(be_u16(src)); - - // Skip uninteresting fields. - try!(skip(src, 4)); - - let samplerate = try!(be_u32(src)); - - match version { - 0 => (), - 1 => { - // Quicktime sound sample description version 1. - // Skip uninteresting fields. - try!(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()) { - 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)); - 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)); - 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)); - track.codec_type = CodecType::Opus; - codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops)); - } - BoxType::QTWaveAtom => { - let qt_esds = try!(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)), - } - 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")) -} - -/// 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 description_count = try!(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()) { - 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)); - 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; - } - } - } - - // Padding could be added in some contents. - try!(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)); - 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)); - 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()); - 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) -} - -// 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())); - 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_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> { - src.read_uint::<byteorder::BigEndian>(3) - .map(|v| v as u32) - .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) -} diff --git a/media/libstagefright/binding/mp4parse/src/tests.rs b/media/libstagefright/binding/mp4parse/src/tests.rs deleted file mode 100644 index 7063b3790..000000000 --- a/media/libstagefright/binding/mp4parse/src/tests.rs +++ /dev/null @@ -1,860 +0,0 @@ -//! Module for parsing ISO Base Media Format aka video/mp4 streams. -//! Internal unit tests. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use std::io::Cursor; -use super::read_mp4; -use super::MediaContext; -use super::Error; -extern crate test_assembler; -use self::test_assembler::*; - -use boxes::BoxType; - -enum BoxSize { - Short(u32), - Long(u64), - UncheckedShort(u32), - UncheckedLong(u64), - Auto, -} - -fn make_box<F>(size: BoxSize, name: &[u8; 4], func: F) -> Cursor<Vec<u8>> - where F: Fn(Section) -> Section -{ - let mut section = Section::new(); - let box_size = Label::new(); - section = match size { - BoxSize::Short(size) | BoxSize::UncheckedShort(size) => section.B32(size), - BoxSize::Long(_) | BoxSize::UncheckedLong(_) => section.B32(1), - BoxSize::Auto => section.B32(&box_size), - }; - section = section.append_bytes(name); - section = match size { - // The spec allows the 32-bit size to be 0 to indicate unknown - // length streams. It's not clear if this is valid when using a - // 64-bit size, so prohibit it for now. - BoxSize::Long(size) => { - assert!(size > 0); - section.B64(size) - } - BoxSize::UncheckedLong(size) => section.B64(size), - _ => section, - }; - section = func(section); - match size { - BoxSize::Short(size) => { - if size > 0 { - assert_eq!(size as u64, section.size()) - } - } - BoxSize::Long(size) => assert_eq!(size, section.size()), - BoxSize::Auto => { - assert!(section.size() <= u32::max_value() as u64, - "Tried to use a long box with BoxSize::Auto"); - box_size.set_const(section.size()); - } - // Skip checking BoxSize::Unchecked* cases. - _ => (), - } - Cursor::new(section.get_contents().unwrap()) -} - -fn make_fullbox<F>(size: BoxSize, name: &[u8; 4], version: u8, func: F) -> Cursor<Vec<u8>> - where F: Fn(Section) -> Section -{ - make_box(size, name, |s| { - func(s.B8(version) - .B8(0) - .B8(0) - .B8(0)) - }) -} - -#[test] -fn read_box_header_short() { - let mut stream = make_box(BoxSize::Short(8), b"test", |s| s); - let header = super::read_box_header(&mut stream).unwrap(); - assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" - assert_eq!(header.size, 8); -} - -#[test] -fn read_box_header_long() { - let mut stream = make_box(BoxSize::Long(16), b"test", |s| s); - let header = super::read_box_header(&mut stream).unwrap(); - assert_eq!(header.name, BoxType::UnknownBox(0x74657374)); // "test" - assert_eq!(header.size, 16); -} - -#[test] -fn read_box_header_short_unknown_size() { - let mut stream = make_box(BoxSize::Short(0), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::Unsupported(s)) => assert_eq!(s, "unknown sized box"), - _ => panic!("unexpected result reading box with unknown size"), - }; -} - -#[test] -fn read_box_header_short_invalid_size() { - let mut stream = make_box(BoxSize::UncheckedShort(2), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::InvalidData(s)) => assert_eq!(s, "malformed size"), - _ => panic!("unexpected result reading box with invalid size"), - }; -} - -#[test] -fn read_box_header_long_invalid_size() { - let mut stream = make_box(BoxSize::UncheckedLong(2), b"test", |s| s); - match super::read_box_header(&mut stream) { - Err(Error::InvalidData(s)) => assert_eq!(s, "malformed wide size"), - _ => panic!("unexpected result reading box with invalid size"), - }; -} - -#[test] -fn read_ftyp() { - let mut stream = make_box(BoxSize::Short(24), b"ftyp", |s| { - s.append_bytes(b"mp42") - .B32(0) // minor version - .append_bytes(b"isom") - .append_bytes(b"mp42") - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FileTypeBox); - assert_eq!(stream.head.size, 24); - let parsed = super::read_ftyp(&mut stream).unwrap(); - assert_eq!(parsed.major_brand, 0x6d703432); // mp42 - assert_eq!(parsed.minor_version, 0); - assert_eq!(parsed.compatible_brands.len(), 2); - assert_eq!(parsed.compatible_brands[0], 0x69736f6d); // isom - assert_eq!(parsed.compatible_brands[1], 0x6d703432); // mp42 -} - -#[test] -fn read_truncated_ftyp() { - // We declare a 24 byte box, but only write 20 bytes. - 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"), - } -} - -#[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 - // major brand is "MP42". I haven't seen case-insensitive - // compatible_brands (which we also test here), but it doesn't seem - // unlikely given the major_brand behaviour. - let mut stream = make_box(BoxSize::Auto, b"ftyp", |s| { - s.append_bytes(b"MP42") - .B32(0) // minor version - .append_bytes(b"ISOM") - .append_bytes(b"MP42") - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FileTypeBox); - assert_eq!(stream.head.size, 24); - let parsed = super::read_ftyp(&mut stream).unwrap(); - assert_eq!(parsed.major_brand, 0x4d503432); - assert_eq!(parsed.minor_version, 0); - assert_eq!(parsed.compatible_brands.len(), 2); - assert_eq!(parsed.compatible_brands[0], 0x49534f4d); // ISOM - assert_eq!(parsed.compatible_brands[1], 0x4d503432); // MP42 -} - -#[test] -fn read_elst_v0() { - let mut stream = make_fullbox(BoxSize::Short(28), b"elst", 0, |s| { - s.B32(1) // list count - // first entry - .B32(1234) // duration - .B32(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::EditListBox); - assert_eq!(stream.head.size, 28); - let parsed = super::read_elst(&mut stream).unwrap(); - assert_eq!(parsed.edits.len(), 1); - assert_eq!(parsed.edits[0].segment_duration, 1234); - assert_eq!(parsed.edits[0].media_time, 5678); - assert_eq!(parsed.edits[0].media_rate_integer, 12); - assert_eq!(parsed.edits[0].media_rate_fraction, 34); -} - -#[test] -fn read_elst_v1() { - let mut stream = make_fullbox(BoxSize::Short(56), b"elst", 1, |s| { - s.B32(2) // list count - // first entry - .B64(1234) // duration - .B64(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - // second entry - .B64(1234) // duration - .B64(5678) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::EditListBox); - assert_eq!(stream.head.size, 56); - let parsed = super::read_elst(&mut stream).unwrap(); - assert_eq!(parsed.edits.len(), 2); - assert_eq!(parsed.edits[1].segment_duration, 1234); - assert_eq!(parsed.edits[1].media_time, 5678); - assert_eq!(parsed.edits[1].media_rate_integer, 12); - assert_eq!(parsed.edits[1].media_rate_fraction, 34); -} - -#[test] -fn read_mdhd_v0() { - let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) // timescale - .B32(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mdhd_v1() { - let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(1234) // timescale - .B64(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 44); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mdhd_unknown_duration() { - let mut stream = make_fullbox(BoxSize::Short(32), b"mdhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) // timescale - .B32(::std::u32::MAX) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_mdhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, ::std::u64::MAX); -} - -#[test] -fn read_mdhd_invalid_timescale() { - let mut stream = make_fullbox(BoxSize::Short(44), b"mdhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(0) // timescale - .B64(5678) // duration - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MediaHeaderBox); - assert_eq!(stream.head.size, 44); - let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0)); - assert_eq!(r.is_err(), true); -} - -#[test] -fn read_mvhd_v0() { - let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) - .B32(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 108); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mvhd_v1() { - let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(1234) - .B64(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 120); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, 5678); -} - -#[test] -fn read_mvhd_invalid_timescale() { - let mut stream = make_fullbox(BoxSize::Short(120), b"mvhd", 1, |s| { - s.B64(0) - .B64(0) - .B32(0) - .B64(5678) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 120); - let r = super::parse_mvhd(&mut stream); - assert_eq!(r.is_err(), true); -} - -#[test] -fn read_mvhd_unknown_duration() { - let mut stream = make_fullbox(BoxSize::Short(108), b"mvhd", 0, |s| { - s.B32(0) - .B32(0) - .B32(1234) - .B32(::std::u32::MAX) - .append_repeated(0, 80) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::MovieHeaderBox); - assert_eq!(stream.head.size, 108); - let parsed = super::read_mvhd(&mut stream).unwrap(); - assert_eq!(parsed.timescale, 1234); - assert_eq!(parsed.duration, ::std::u64::MAX); -} - -#[test] -fn read_vpcc() { - let data_length = 12u16; - let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| { - s.B8(2) - .B8(0) - .B8(0x82) - .B8(0) - .B16(data_length) - .append_repeated(42, data_length as usize) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox); - let r = super::read_vpcc(&mut stream); - assert!(r.is_ok()); -} - -#[test] -fn read_hdlr() { - let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - .append_bytes(b"VideoHandler") - .B8(0) // null-terminate string - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 45); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -#[test] -fn read_hdlr_short_name() { - let mut stream = make_fullbox(BoxSize::Short(33), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - .B8(0) // null-terminate string - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 33); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -#[test] -fn read_hdlr_zero_length_name() { - let mut stream = make_fullbox(BoxSize::Short(32), b"hdlr", 0, |s| { - s.B32(0) - .append_bytes(b"vide") - .B32(0) - .B32(0) - .B32(0) - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::HandlerBox); - assert_eq!(stream.head.size, 32); - let parsed = super::read_hdlr(&mut stream).unwrap(); - assert_eq!(parsed.handler_type, 0x76696465); // vide -} - -fn flac_streaminfo() -> Vec<u8> { - vec![ - 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00, - 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0xc9, - 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3, - 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, 0x05, 0x00, - 0x33, 0xad, - ] -} - -#[test] -fn read_flac() { - let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| { - s.append_repeated(0, 6) // reserved - .B16(1) // data reference index - .B32(0) // reserved - .B32(0) // reserved - .B16(2) // channel count - .B16(16) // bits per sample - .B16(0) // pre_defined - .B16(0) // reserved - .B32(44100 << 16) // Sample rate - .append_bytes(&make_dfla(FlacBlockType::StreamInfo, true, - &flac_streaminfo(), FlacBlockLength::Correct) - .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); - let r = super::read_audio_sample_entry(&mut stream, &mut track); - r.unwrap(); -} - -#[derive(Clone, Copy)] -enum FlacBlockType { - StreamInfo = 0, - _Padding = 1, - _Application = 2, - _Seektable = 3, - _Comment = 4, - _Cuesheet = 5, - _Picture = 6, - _Reserved, - _Invalid = 127, -} - -enum FlacBlockLength { - Correct, - Incorrect(usize), -} - -fn make_dfla(block_type: FlacBlockType, last: bool, data: &Vec<u8>, - data_length: FlacBlockLength) -> Cursor<Vec<u8>> { - assert!(data.len() < 1<<24); - make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| { - let flag = match last { - true => 1, - false => 0, - }; - let size = match data_length { - FlacBlockLength::Correct => (data.len() as u32) & 0xffffff, - FlacBlockLength::Incorrect(size) => { - assert!(size < 1<<24); - (size as u32) & 0xffffff - } - }; - let block_type = (block_type as u32) & 0x7f; - s.B32(flag << 31 | block_type << 24 | size) - .append_bytes(data) - }) -} - -#[test] -fn read_dfla() { - let mut stream = make_dfla(FlacBlockType::StreamInfo, true, - &flac_streaminfo(), FlacBlockLength::Correct); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FLACSpecificBox); - let dfla = super::read_dfla(&mut stream).unwrap(); - assert_eq!(dfla.version, 0); -} - -#[test] -fn long_flac_metadata() { - let streaminfo = flac_streaminfo(); - let mut stream = make_dfla(FlacBlockType::StreamInfo, true, - &streaminfo, - FlacBlockLength::Incorrect(streaminfo.len() + 4)); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::FLACSpecificBox); - let r = super::read_dfla(&mut stream); - assert!(r.is_err()); -} - -#[test] -fn read_opus() { - let mut stream = make_box(BoxSize::Auto, b"Opus", |s| { - s.append_repeated(0, 6) - .B16(1) // data reference index - .B32(0) - .B32(0) - .B16(2) // channel count - .B16(16) // bits per sample - .B16(0) - .B16(0) - .B32(48000 << 16) // Sample rate is always 48 kHz for Opus. - .append_bytes(&make_dops().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); - let r = super::read_audio_sample_entry(&mut stream, &mut track); - assert!(r.is_ok()); -} - -fn make_dops() -> Cursor<Vec<u8>> { - make_box(BoxSize::Auto, b"dOps", |s| { - s.B8(0) // version - .B8(2) // channel count - .B16(348) // pre-skip - .B32(44100) // original sample rate - .B16(0) // gain - .B8(0) // channel mapping - }) -} - -#[test] -fn read_dops() { - let mut stream = make_dops(); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - assert_eq!(stream.head.name, BoxType::OpusSpecificBox); - let r = super::read_dops(&mut stream); - assert!(r.is_ok()); -} - -#[test] -fn serialize_opus_header() { - let opus = super::OpusSpecificBox { - version: 0, - output_channel_count: 1, - 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![ - 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, - pre_skip: 152, - input_sample_rate: 48000, - output_gain: 0, - channel_mapping_family: 1, - 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![ - 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, - ]); -} - -#[test] -fn avcc_limit() { - let mut stream = make_box(BoxSize::Auto, b"avc1", |s| { - s.append_repeated(0, 6) - .B16(1) - .append_repeated(0, 16) - .B16(320) - .B16(240) - .append_repeated(0, 14) - .append_repeated(0, 32) - .append_repeated(0, 4) - .B32(0xffffffff) - .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"), - } -} - -#[test] -fn esds_limit() { - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) - .B32(0) - .B32(0) - .B16(2) - .B16(16) - .B16(0) - .B16(0) - .B32(48000 << 16) - .B32(0xffffffff) - .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"), - } -} - -#[test] -fn esds_limit_2() { - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) - .B32(0) - .B32(0) - .B16(2) - .B16(16) - .B16(0) - .B16(0) - .B32(48000 << 16) - .B32(8) - .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"), - } -} - -#[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"), - } -} - -fn make_elst() -> Cursor<Vec<u8>> { - make_fullbox(BoxSize::Auto, b"elst", 1, |s| { - s.B32(1) - // first entry - .B64(1234) // duration - .B64(0xffffffffffffffff) // time - .B16(12) // rate integer - .B16(34) // rate fraction - }) -} - -#[test] -fn read_edts_bogus() { - // First edit list entry has a media_time of -1, so we expect a second - // edit list entry to be present to provide a valid media_time. - 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"), - } -} - -#[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"; - let mut stream = Cursor::new(pstr); - // Reader wants to limit the total read length to 32 bytes, so any - // returned string must be no longer than 31 bytes. - let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap(); - assert_eq!(s.len(), 31); -} - -#[test] -fn skip_padding_in_boxes() { - // Padding data could be added in the end of these boxes. Parser needs to skip - // them instead of returning error. - let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"]; - - for name in box_names { - let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| { - s.append_repeated(0, 100) // add padding data - }); - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - match name { - b"stts" => { - super::read_stts(&mut stream).expect("fail to skip padding: stts"); - }, - b"stsc" => { - super::read_stsc(&mut stream).expect("fail to skip padding: stsc"); - }, - b"stsz" => { - super::read_stsz(&mut stream).expect("fail to skip padding: stsz"); - }, - b"stco" => { - super::read_stco(&mut stream).expect("fail to skip padding: stco"); - }, - b"co64" => { - super::read_co64(&mut stream).expect("fail to skip padding: co64"); - }, - b"stss" => { - super::read_stss(&mut stream).expect("fail to skip padding: stss"); - }, - _ => (), - } - } -} - -#[test] -fn skip_padding_in_stsd() { - // Padding data could be added in the end of stsd boxes. Parser needs to skip - // them instead of returning error. - let avc = make_box(BoxSize::Auto, b"avc1", |s| { - s.append_repeated(0, 6) - .B16(1) - .append_repeated(0, 16) - .B16(320) - .B16(240) - .append_repeated(0, 14) - .append_repeated(0, 32) - .append_repeated(0, 4) - .B32(0xffffffff) - .append_bytes(b"avcC") - .append_repeated(0, 100) - }).into_inner(); - let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| { - s.B32(1) - .append_bytes(avc.as_slice()) - .append_repeated(0, 100) // add padding data - }); - - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - 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 - .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| { - s.append_bytes(esds.as_slice()) - }).into_inner(); - let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| { - s.append_repeated(0, 6) - .B16(1) // data_reference_count - .B16(1) // verion: qt -> 1 - .append_repeated(0, 6) - .B16(2) - .B16(16) - .append_repeated(0, 4) - .B32(48000 << 16) - .append_repeated(0, 16) - .append_bytes(wave.as_slice()) - }); - - let mut iter = super::BoxIter::new(&mut stream); - let mut stream = iter.next_box().unwrap().unwrap(); - let mut track = super::Track::new(0); - super::read_audio_sample_entry(&mut stream, &mut track) - .expect("fail to read qt wave atom"); - assert_eq!(track.codec_type, super::CodecType::MP3); -} diff --git a/media/libstagefright/binding/mp4parse/tests/afl.rs b/media/libstagefright/binding/mp4parse/tests/afl.rs deleted file mode 100644 index d4ffec0df..000000000 --- a/media/libstagefright/binding/mp4parse/tests/afl.rs +++ /dev/null @@ -1,53 +0,0 @@ -/// Regression tests from American Fuzzy Lop test cases. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -/// These all caused panics at some point during development. - -extern crate mp4parse; - -use std::io::Cursor; - -/// https://github.com/mozilla/mp4parse-rust/issues/2 -/// -/// Test a box with 4-byte size, smaller than the smallest header. -#[test] -fn fuzz_2() { - let mut c = Cursor::new(b"\x00\x00\x00\x04\xa6\x00\x04\xa6".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/4 -/// -/// Test a large (64 bit) box header with zero declared size. -#[test] -fn fuzz_4() { - let mut c = Cursor::new(b"\x00\x00\x00\x01\x30\x30\x30\x30\x00\x00\x00\x00\x00\x00\x00\x00".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/5 -/// -/// Declares 202116104 compatible brands but does not supply them, -/// verifying read is properly bounded at the end of the stream. -#[test] -fn fuzz_5() { - let mut c = Cursor::new(b"\x30\x30\x30\x30\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} - -/// https://github.com/mozilla/mp4parse-rust/issues/6 -/// -/// Declares an ftyp box with a single invalid (short - 3 byte) compatible -/// brand and excludes the extra 3 bytes from the stream. -#[test] -fn fuzz_6() { - let mut c = Cursor::new(b"\x00\x00\x00\x13\x66\x74\x79\x70\x30\x30\x30\x30\x30\x30\x30\x30".to_vec()); - let mut context = mp4parse::MediaContext::new(); - let _ = mp4parse::read_mp4(&mut c, &mut context); -} diff --git a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 b/media/libstagefright/binding/mp4parse/tests/minimal.mp4 Binary files differdeleted file mode 100644 index 9fe1e6722..000000000 --- a/media/libstagefright/binding/mp4parse/tests/minimal.mp4 +++ /dev/null diff --git a/media/libstagefright/binding/mp4parse/tests/public.rs b/media/libstagefright/binding/mp4parse/tests/public.rs deleted file mode 100644 index 1d36f5f5a..000000000 --- a/media/libstagefright/binding/mp4parse/tests/public.rs +++ /dev/null @@ -1,97 +0,0 @@ -/// Check if needed fields are still public. - -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -extern crate mp4parse as mp4; - -use std::io::{Cursor, Read}; -use std::fs::File; - -// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53 -#[test] -fn public_api() { - let mut fd = File::open("tests/minimal.mp4").expect("Unknown file"); - let mut buf = Vec::new(); - fd.read_to_end(&mut buf).expect("File error"); - - let mut c = Cursor::new(&buf); - let mut context = mp4::MediaContext::new(); - mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed"); - assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000))); - for track in context.tracks { - match track.data { - Some(mp4::SampleEntry::Video(v)) => { - // track part - assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0))); - assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); - assert_eq!(track.media_time, Some(mp4::TrackScaledTime(0, 0))); - assert_eq!(track.timescale, Some(mp4::TrackTimeScale(12800, 0))); - assert_eq!(v.width, 320); - assert_eq!(v.height, 240); - - // track.tkhd part - let tkhd = track.tkhd.unwrap(); - assert_eq!(tkhd.disabled, false); - assert_eq!(tkhd.duration, 40); - assert_eq!(tkhd.width, 20971520); - assert_eq!(tkhd.height, 15728640); - - // track.data part - assert_eq!(match v.codec_specific { - mp4::VideoCodecSpecific::AVCConfig(v) => { - assert!(v.len() > 0); - "AVC" - } - mp4::VideoCodecSpecific::VPxConfig(vpx) => { - // We don't enter in here, we just check if fields are public. - assert!(vpx.bit_depth > 0); - assert!(vpx.color_space > 0); - assert!(vpx.chroma_subsampling > 0); - assert!(vpx.codec_init.len() > 0); - "VPx" - } - }, "AVC"); - } - Some(mp4::SampleEntry::Audio(a)) => { - // track part - assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1))); - assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0))); - assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1))); - assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1))); - - // track.tkhd part - let tkhd = track.tkhd.unwrap(); - assert_eq!(tkhd.disabled, false); - assert_eq!(tkhd.duration, 62); - assert_eq!(tkhd.width, 0); - assert_eq!(tkhd.height, 0); - - // track.data part - assert_eq!(match a.codec_specific { - 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); - "FLAC" - } - mp4::AudioCodecSpecific::OpusSpecificBox(opus) => { - // We don't enter in here, we just check if fields are public. - assert!(opus.version > 0); - "Opus" - } - }, "ES"); - assert!(a.samplesize > 0); - assert!(a.samplerate > 0); - } - Some(mp4::SampleEntry::Unknown) | None => {} - } - } -} |