Bug 1175322 - Import mp4parse v0.0.8 source. r=k17e
authorRalph Giles <giles@mozilla.com>
Tue, 16 Jun 2015 15:50:00 -0700
changeset 249204 8d802bb49f338f87e72117baa2f35d62dad736fe
parent 249203 0a03c1ef13d098287902a197db167b23577dcb14
child 249205 758e4197540257606a0d817855a1d294d53316a0
push id61172
push userrgiles@mozilla.com
push dateTue, 16 Jun 2015 23:24:38 +0000
treeherdermozilla-inbound@5988e0ef6cef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersk17e
bugs1175322
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1175322 - Import mp4parse v0.0.8 source. r=k17e Source from https://notabug.org/rillian/mp4parse-rust
media/libstagefright/binding/MP4Metadata.rs
media/libstagefright/moz.build
new file mode 100644
--- /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);
+}
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -76,16 +76,21 @@ SOURCES += [
     'frameworks/av/media/libstagefright/foundation/hexdump.cpp',
     'frameworks/av/media/libstagefright/MetaData.cpp',
     'system/core/libutils/RefBase.cpp',
     'system/core/libutils/String16.cpp',
     'system/core/libutils/String8.cpp',
     'system/core/libutils/VectorImpl.cpp',
 ]
 
+if CONFIG['MOZ_RUST']:
+    SOURCES += [
+        'binding/MP4Metadata.rs',
+    ]
+
 UNIFIED_SOURCES += [
     'binding/Adts.cpp',
     'binding/AnnexB.cpp',
     'binding/Box.cpp',
     'binding/BufferStream.cpp',
     'binding/DecoderData.cpp',
     'binding/H264.cpp',
     'binding/Index.cpp',