From 11d91c27d5d69426877a193cf9dea59df4856ea3 Mon Sep 17 00:00:00 2001
From: Ralph Giles <giles@mozilla.com>
Date: Wed, 10 Jun 2015 12:51:14 -0700
Subject: Bug 1161350 - Import mp4parse v0.0.8 source. r=k17e
Source from https://notabug.org/rillian/mp4parse-rust
diff --git a/media/libstagefright/binding/MP4Metadata.rs b/media/libstagefright/binding/MP4Metadata.rs
new file mode 100644
index 0000000..83b28cb
--- /dev/null
+++ b/media/libstagefright/binding/MP4Metadata.rs
@@ -0,0 +1,389 @@
+// 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/.
+
+/// Basic ISO box structure.
+pub struct BoxHeader {
+ /// Four character box type
+pub name: u32,
+ /// Size of the box in bytes
+pub size: u64,
+ /// Offset to the start of the contained data (or header size).
+pub offset: u64,
+}
+
+/// File type box 'ftyp'.
+pub struct FileTypeBox {
+ name: u32,
+ size: u64,
+ major_brand: u32,
+ minor_version: u32,
+ compatible_brands: Vec<u32>,
+}
+
+/// Movie header box 'mvhd'.
+pub struct MovieHeaderBox {
+ pub name: u32,
+ pub size: u64,
+ pub timescale: u32,
+ pub duration: u64,
+ // Ignore other fields.
+}
+
+pub struct TrackHeaderBox {
+ pub name: u32,
+ pub size: u64,
+ pub track_id: u32,
+ pub duration: u64,
+ pub width: u32,
+ pub height: u32,
+}
+
+extern crate byteorder;
+use byteorder::{BigEndian, ReadBytesExt};
+use std::io::{Read, Result, Seek, SeekFrom, Take};
+use std::io::Cursor;
+
+/// Parse a box out of a data buffer.
+pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> Result<BoxHeader> {
+ let tmp_size = try!(src.read_u32::<BigEndian>());
+ let name = try!(src.read_u32::<BigEndian>());
+ let size = match tmp_size {
+ 1 => try!(src.read_u64::<BigEndian>()),
+ _ => tmp_size as u64,
+ };
+ assert!(size >= 8);
+ if tmp_size == 1 {
+ assert!(size >= 16);
+ }
+ let offset = match tmp_size {
+ 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) -> (u8, u32) {
+ let version = src.read_u8().unwrap();
+ let flags_a = src.read_u8().unwrap();
+ let flags_b = src.read_u8().unwrap();
+ let flags_c = src.read_u8().unwrap();
+ (version, (flags_a as u32) << 16 |
+ (flags_b as u32) << 8 |
+ (flags_c as u32))
+}
+
+/// Skip over the contents of a box.
+pub fn skip_box_content<T: ReadBytesExt + Seek>
+ (src: &mut T, header: &BoxHeader)
+ -> std::io::Result<u64>
+{
+ src.seek(SeekFrom::Current((header.size - header.offset) as i64))
+}
+
+/// Helper to construct a Take over the contents of a box.
+fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
+ f.take(h.size - h.offset)
+}
+
+/// Helper to construct a Cursor over the contents of a box.
+fn recurse<T: Read>(f: &mut T, h: &BoxHeader) {
+ println!("{} -- recursing", h);
+ // FIXME: I couldn't figure out how to do this without copying.
+ // We use Seek on the Read we return in skip_box_content, but
+ // that trait isn't implemented for a Take like our limit()
+ // returns. Slurping the buffer and wrapping it in a Cursor
+ // functions as a work around.
+ let buf: Vec<u8> = limit(f, &h)
+ .bytes()
+ .map(|u| u.unwrap())
+ .collect();
+ let mut content = Cursor::new(buf);
+ loop {
+ match read_box(&mut content) {
+ Ok(_) => {},
+ Err(e) => {
+ println!("Error '{:?}' reading box", e.kind());
+ break;
+ },
+ }
+ }
+ println!("{} -- end", h);
+}
+
+/// Read the contents of a box, including sub boxes.
+/// Right now it just prints the box value rather than
+/// returning anything.
+pub fn read_box<T: Read + Seek>(f: &mut T) -> Result<()> {
+ read_box_header(f).and_then(|h| {
+ match &(fourcc_to_string(h.name))[..] {
+ "ftyp" => {
+ let mut content = limit(f, &h);
+ let ftyp = read_ftyp(&mut content, &h).unwrap();
+ println!("{}", ftyp);
+ },
+ "moov" => recurse(f, &h),
+ "mvhd" => {
+ let mut content = limit(f, &h);
+ let mvhd = read_mvhd(&mut content, &h).unwrap();
+ println!(" {}", mvhd);
+ },
+ "trak" => recurse(f, &h),
+ "tkhd" => {
+ let mut content = limit(f, &h);
+ let tkhd = read_tkhd(&mut content, &h).unwrap();
+ println!(" {}", tkhd);
+ },
+ _ => {
+ // Skip the contents of unknown chunks.
+ println!("{} (skipped)", h);
+ skip_box_content(f, &h).unwrap();
+ },
+ };
+ Ok(()) // and_then needs a Result to return.
+ })
+}
+
+/// Entry point for C language callers.
+/// Take a buffer and call read_box() on it.
+#[no_mangle]
+pub unsafe extern fn read_box_from_buffer(buffer: *const u8, size: usize)
+ -> bool {
+ use std::slice;
+ use std::thread;
+
+ // Validate arguments from C.
+ if buffer.is_null() || size < 8 {
+ return false;
+ }
+
+ // Wrap the buffer we've been give in a slice.
+ let b = slice::from_raw_parts(buffer, size);
+ let mut c = Cursor::new(b);
+
+ // Parse in a subthread.
+ let task = thread::spawn(move || {
+ read_box(&mut c).unwrap();
+ });
+ // Catch any panics.
+ task.join().is_ok()
+}
+
+
+/// Parse an ftype box.
+pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
+ -> Option<FileTypeBox> {
+ let major = src.read_u32::<BigEndian>().unwrap();
+ let minor = src.read_u32::<BigEndian>().unwrap();
+ let brand_count = (head.size - 8 - 8) /4;
+ let mut brands = Vec::new();
+ for _ in 0..brand_count {
+ brands.push(src.read_u32::<BigEndian>().unwrap());
+ }
+ Some(FileTypeBox{
+ name: head.name,
+ size: head.size,
+ major_brand: major,
+ minor_version: minor,
+ compatible_brands: brands,
+ })
+}
+
+/// Parse an mvhd box.
+pub fn read_mvhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
+ -> Option<MovieHeaderBox> {
+ let (version, _) = read_fullbox_extra(src);
+ match version {
+ 1 => {
+ // 64 bit creation and modification times.
+ let mut skip: Vec<u8> = vec![0; 16];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ },
+ 0 => {
+ // 32 bit creation and modification times.
+ // 64 bit creation and modification times.
+ let mut skip: Vec<u8> = vec![0; 8];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ },
+ _ => panic!("invalid mhdr version"),
+ }
+ let timescale = src.read_u32::<BigEndian>().unwrap();
+ let duration = match version {
+ 1 => src.read_u64::<BigEndian>().unwrap(),
+ 0 => src.read_u32::<BigEndian>().unwrap() as u64,
+ _ => panic!("invalid mhdr version"),
+ };
+ // Skip remaining fields.
+ let mut skip: Vec<u8> = vec![0; 80];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ Some(MovieHeaderBox {
+ name: head.name,
+ size: head.size,
+ timescale: timescale,
+ duration: duration,
+ })
+}
+
+/// Parse a tkhd box.
+pub fn read_tkhd<T: ReadBytesExt>(src: &mut T, head: &BoxHeader)
+ -> Option<TrackHeaderBox> {
+ let (version, flags) = read_fullbox_extra(src);
+ if flags & 0x1u32 == 0 || flags & 0x2u32 == 0 {
+ // Track is disabled.
+ return None;
+ }
+ match version {
+ 1 => {
+ // 64 bit creation and modification times.
+ let mut skip: Vec<u8> = vec![0; 16];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ },
+ 0 => {
+ // 32 bit creation and modification times.
+ // 64 bit creation and modification times.
+ let mut skip: Vec<u8> = vec![0; 8];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ },
+ _ => panic!("invalid tkhd version"),
+ }
+ let track_id = src.read_u32::<BigEndian>().unwrap();
+ let _reserved = src.read_u32::<BigEndian>().unwrap();
+ assert!(_reserved == 0);
+ let duration = match version {
+ 1 => {
+ src.read_u64::<BigEndian>().unwrap()
+ },
+ 0 => src.read_u32::<BigEndian>().unwrap() as u64,
+ _ => panic!("invalid tkhd version"),
+ };
+ let _reserved = src.read_u32::<BigEndian>().unwrap();
+ let _reserved = src.read_u32::<BigEndian>().unwrap();
+ // Skip uninterested fields.
+ let mut skip: Vec<u8> = vec![0; 44];
+ let r = src.read(&mut skip).unwrap();
+ assert!(r == skip.len());
+ let width = src.read_u32::<BigEndian>().unwrap();
+ let height = src.read_u32::<BigEndian>().unwrap();
+ Some(TrackHeaderBox {
+ name: head.name,
+ size: head.size,
+ track_id: track_id,
+ duration: duration,
+ width: width,
+ height: height,
+ })
+}
+
+/// Convert the iso box type or other 4-character value to a string.
+fn fourcc_to_string(name: u32) -> String {
+ let u32_to_vec = |u| {
+ vec!((u >> 24 & 0xffu32) as u8,
+ (u >> 16 & 0xffu32) as u8,
+ (u >> 8 & 0xffu32) as u8,
+ (u & 0xffu32) as u8)
+ };
+ let name_bytes = u32_to_vec(name);
+ String::from_utf8_lossy(&name_bytes).into_owned()
+}
+
+use std::fmt;
+impl fmt::Display for BoxHeader {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
+ }
+}
+
+impl fmt::Display for FileTypeBox {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let name = fourcc_to_string(self.name);
+ let brand = fourcc_to_string(self.major_brand);
+ write!(f, "'{}' {} bytes '{}' v{}", name, self.size,
+ brand, self.minor_version)
+ }
+}
+
+impl fmt::Display for MovieHeaderBox {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let name = fourcc_to_string(self.name);
+ write!(f, "'{}' {} bytes duration {}s", name, self.size,
+ (self.duration as f64)/(self.timescale as f64))
+ }
+}
+
+use std::u16;
+impl fmt::Display for TrackHeaderBox {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let name = fourcc_to_string(self.name);
+ // Dimensions are 16.16 fixed-point.
+ let base = u16::MAX as f64 + 1.0;
+ let width = (self.width as f64) / base;
+ let height = (self.height as f64) / base;
+ write!(f, "'{}' {} bytes duration {} id {} {}x{}",
+ name, self.size, self.duration, self.track_id,
+ width, height)
+ }
+}
+
+#[test]
+fn test_read_box_header() {
+ use std::io::Cursor;
+ use std::io::Write;
+ let mut test: Vec<u8> = vec![0, 0, 0, 8]; // minimal box length
+ write!(&mut test, "test").unwrap(); // box type
+ let mut stream = Cursor::new(test);
+ let parsed = read_box_header(&mut stream).unwrap();
+ assert_eq!(parsed.name, 1952805748);
+ assert_eq!(parsed.size, 8);
+ println!("box {}", parsed);
+}
+
+
+#[test]
+fn test_read_box_header_long() {
+ use std::io::Cursor;
+ let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
+ test.extend("long".to_string().into_bytes()); // box type
+ test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
+ // Skip generating box content.
+ let mut stream = Cursor::new(test);
+ let parsed = read_box_header(&mut stream).unwrap();
+ assert_eq!(parsed.name, 1819242087);
+ assert_eq!(parsed.size, 4096);
+ println!("box {}", parsed);
+}
+
+#[test]
+fn test_read_ftyp() {
+ use std::io::Cursor;
+ use std::io::Write;
+ let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
+ write!(&mut test, "ftyp").unwrap(); // type
+ write!(&mut test, "mp42").unwrap(); // major brand
+ test.extend(vec![0, 0, 0, 0]); // minor version
+ write!(&mut test, "isom").unwrap(); // compatible brands...
+ write!(&mut test, "mp42").unwrap();
+ assert_eq!(test.len(), 24);
+
+ let mut stream = Cursor::new(test);
+ let header = read_box_header(&mut stream).unwrap();
+ let parsed = read_ftyp(&mut stream, &header).unwrap();
+ assert_eq!(parsed.name, 1718909296);
+ assert_eq!(parsed.size, 24);
+ assert_eq!(parsed.major_brand, 1836069938);
+ assert_eq!(parsed.minor_version, 0);
+ assert_eq!(parsed.compatible_brands.len(), 2);
+ println!("box {}", parsed);
+}
diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build
index 7cf1c03..b6fa60f 100644
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -81,6 +81,11 @@ SOURCES += [
'system/core/libutils/VectorImpl.cpp',
]
+if CONFIG['MOZ_RUST']:
+ SOURCES += [
+ 'binding/MP4Metadata.rs',
+ ]
+
UNIFIED_SOURCES += [
'binding/Adts.cpp',
'binding/AnnexB.cpp',
--
2.4.3