Bug 1349133 - Update mp4 rust parser. r=kinetik
authorAlfredo.Yang <ayang@mozilla.com>
Wed, 22 Mar 2017 09:53:58 +0800
changeset 348972 0be0c4bd3fdbc52d0b75fb018f61551e8e2660d3
parent 348971 bf6ab3d2ec1bdc5e3b9d8e017c7a4837c303d99b
child 348973 5c233ca5e06304f3bdd493c4e8abfe408c46534d
push id39357
push userayang@mozilla.com
push dateThu, 23 Mar 2017 01:27:24 +0000
treeherderautoland@0be0c4bd3fdb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1349133
milestone55.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 1349133 - Update mp4 rust parser. r=kinetik MozReview-Commit-ID: JDKFUsJBpS9
media/libstagefright/binding/include/mp4parse.h
media/libstagefright/binding/mp4parse-cargo.patch
media/libstagefright/binding/mp4parse/Cargo.toml
media/libstagefright/binding/mp4parse/src/lib.rs
media/libstagefright/binding/mp4parse_capi/Cargo.toml
media/libstagefright/binding/mp4parse_capi/src/lib.rs
media/libstagefright/binding/update-rust.sh
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -83,16 +83,17 @@ typedef struct mp4parse_track_audio_info
 	mp4parse_sinf_info protected_data;
 } mp4parse_track_audio_info;
 
 typedef struct mp4parse_track_video_info {
 	uint32_t display_width;
 	uint32_t display_height;
 	uint16_t image_width;
 	uint16_t image_height;
+	uint16_t rotation;
 	mp4parse_byte_data extra_data;
 	mp4parse_sinf_info protected_data;
 } mp4parse_track_video_info;
 
 typedef struct mp4parse_fragment_info {
 	uint64_t fragment_duration;
 } mp4parse_fragment_info;
 
--- a/media/libstagefright/binding/mp4parse-cargo.patch
+++ b/media/libstagefright/binding/mp4parse-cargo.patch
@@ -1,50 +1,53 @@
 diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefright/binding/mp4parse/Cargo.toml
 index ff9422c..814c4c6 100644
 --- a/media/libstagefright/binding/mp4parse/Cargo.toml
 +++ b/media/libstagefright/binding/mp4parse/Cargo.toml
-@@ -18,18 +18,12 @@ exclude = [
+@@ -18,19 +18,13 @@ exclude = [
  ]
  
  [dependencies]
 -byteorder = "1.0.0"
 -afl = { version = "0.1.1", optional = true }
 -afl-plugin = { version = "0.1.1", optional = true }
 -abort_on_panic = { version = "1.0.0", optional = true }
 -bitreader = { version = "0.3.0" }
+-num-traits = "0.1.37"
 +byteorder = "1.0.0"
 +bitreader = { version = "0.3.0" }
++num-traits = "0.1.37"
 
  [dev-dependencies]
  test-assembler = "0.1.2"
  
 -[features]
 -fuzz = ["afl", "afl-plugin", "abort_on_panic"]
 -
  # 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_capi/Cargo.toml b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
 index aeeebc65..5c0836a 100644
 --- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
 +++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
-@@ -18,21 +18,12 @@ exclude = [
+@@ -18,22 +18,13 @@ exclude = [
    "*.mp4",
  ]
  
 -build = "build.rs"
 -
 -[badges]
 -travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
 +build = false
  
  [dependencies]
  byteorder = "1.0.0"
  mp4parse = {version = "0.7.1", path = "../mp4parse"}
+ num-traits = "0.1.37"
  
 -[build-dependencies]
 -rusty-cheddar = "0.3.2"
 -
 -[features]
 -fuzz = ["mp4parse/fuzz"]
 -
  # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
--- a/media/libstagefright/binding/mp4parse/Cargo.toml
+++ b/media/libstagefright/binding/mp4parse/Cargo.toml
@@ -20,15 +20,16 @@ exclude = [
 ]
 
 [badges]
 travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
 
 [dependencies]
 byteorder = "1.0.0"
 bitreader = { version = "0.3.0" }
+num-traits = "0.1.37"
 
 [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
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -5,21 +5,23 @@
 // 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;
 extern crate bitreader;
+extern crate num_traits;
 use byteorder::{ReadBytesExt, WriteBytesExt};
 use bitreader::{BitReader, ReadInto};
 use std::io::{Read, Take};
 use std::io::Cursor;
 use std::cmp;
+use num_traits::Num;
 
 mod boxes;
 use boxes::{BoxType, FourCC};
 
 // Unit tests.
 #[cfg(test)]
 mod tests;
 
@@ -113,24 +115,38 @@ struct FileTypeBox {
 
 /// Movie header box 'mvhd'.
 #[derive(Debug)]
 struct MovieHeaderBox {
     pub timescale: u32,
     duration: u64,
 }
 
+#[derive(Debug, Clone, Copy)]
+pub struct Matrix {
+    pub a: i32, // 16.16 fix point
+    pub b: i32, // 16.16 fix point
+    pub u: i32, // 2.30 fix point
+    pub c: i32, // 16.16 fix point
+    pub d: i32, // 16.16 fix point
+    pub v: i32, // 2.30 fix point
+    pub x: i32, // 16.16 fix point
+    pub y: i32, // 16.16 fix point
+    pub w: i32, // 2.30 fix point
+}
+
 /// 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,
+    pub matrix: Matrix,
 }
 
 /// Edit list box 'elst'
 #[derive(Debug)]
 struct EditListBox {
     edits: Vec<Edit>,
 }
 
@@ -401,22 +417,30 @@ 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);
+pub struct TrackTimeScale<T: Num>(pub T, 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);
+pub struct TrackScaledTime<T: Num>(pub T, pub usize);
+
+impl <T> std::ops::Add for TrackScaledTime<T> where T: Num {
+    type Output = TrackScaledTime<T>;
+
+    fn add(self, other: TrackScaledTime<T>) -> TrackScaledTime<T> {
+        TrackScaledTime::<T>(self.0 + other.0, self.1)
+    }
+}
 
 /// A fragmented file contains no sample data in stts, stsc, and stco.
 #[derive(Debug, Default)]
 pub struct EmptySampleTableBoxes {
     // TODO: Track has stts, stsc and stco, this structure can be discarded.
     pub empty_stts: bool,
     pub empty_stsc: bool,
     pub empty_stco: bool,
@@ -426,22 +450,22 @@ pub struct EmptySampleTableBoxes {
 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 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 media_time: Option<TrackScaledTime<u64>>,
+    pub timescale: Option<TrackTimeScale<u64>>,
+    pub duration: Option<TrackScaledTime<u64>>,
     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.
     pub stts: Option<TimeToSampleBox>,
     pub stsc: Option<SampleToChunkBox>,
     pub stsz: Option<SampleSizeBox>,
@@ -788,37 +812,37 @@ fn read_edts<T: Read>(f: &mut BMFFBox<T>
                         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.media_time = Some(TrackScaledTime::<u64>(elst.edits[idx].media_time as u64,
                                                         track.id));
                 log!("{:?}", elst);
             }
             _ => 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>)> {
+fn parse_mdhd<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<(MediaHeaderBox, Option<TrackScaledTime<u64>>, Option<TrackTimeScale<u64>>)> {
     let mdhd = read_mdhd(f)?;
     let duration = match mdhd.duration {
         std::u64::MAX => None,
-        duration => Some(TrackScaledTime(duration, track.id)),
+        duration => Some(TrackScaledTime::<u64>(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));
+    let timescale = Some(TrackTimeScale::<u64>(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) = iter.next_box()? {
         match b.head.name {
             BoxType::MediaHeaderBox => {
@@ -984,25 +1008,33 @@ fn read_tkhd<T: Read>(src: &mut BMFFBox<
     let track_id = be_u32(src)?;
     skip(src, 4)?;
     let duration = match version {
         1 => be_u64(src)?,
         0 => be_u32(src)? as u64,
         _ => return Err(Error::InvalidData("unhandled tkhd version")),
     };
     // Skip uninteresting fields.
-    skip(src, 52)?;
+    skip(src, 16)?;
+
+    let matrix = Matrix{
+        a: be_i32(src)?, b: be_i32(src)?, u: be_i32(src)?,
+        c: be_i32(src)?, d: be_i32(src)?, v: be_i32(src)?,
+        x: be_i32(src)?, y: be_i32(src)?, w: be_i32(src)?,
+    };
+
     let width = be_u32(src)?;
     let height = be_u32(src)?;
     Ok(TrackHeaderBox {
         track_id: track_id,
         disabled: disabled,
         duration: duration,
         width: width,
         height: height,
+        matrix: matrix,
     })
 }
 
 /// Parse a elst box.
 fn read_elst<T: Read>(src: &mut BMFFBox<T>) -> Result<EditListBox> {
     let (version, _) = read_fullbox_extra(src)?;
     let edit_count = be_u32(src)?;
     if edit_count == 0 {
@@ -1160,22 +1192,20 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<
 
     if src.bytes_left() < (counts as usize * 8) {
         return Err(Error::InvalidData("insufficient data in 'ctts' box"));
     }
 
     let mut offsets = Vec::new();
     for _ in 0..counts {
         let (sample_count, time_offset) = match version {
-            0 => {
-                let count = be_u32(src)?;
-                let offset = TimeOffsetVersion::Version0(be_u32(src)?);
-                (count, offset)
-            },
-            1 => {
+            // According to spec, Version0 shoule be used when version == 0;
+            // however, some buggy contents have negative value when version == 0.
+            // So we always use Version1 here.
+            0...1 => {
                 let count = be_u32(src)?;
                 let offset = TimeOffsetVersion::Version1(be_i32(src)?);
                 (count, offset)
             },
             _ => {
                 return Err(Error::InvalidData("unsupported version in 'ctts' box"));
             }
         };
--- a/media/libstagefright/binding/mp4parse_capi/Cargo.toml
+++ b/media/libstagefright/binding/mp4parse_capi/Cargo.toml
@@ -18,12 +18,13 @@ exclude = [
   "*.mp4",
 ]
 
 build = false
 
 [dependencies]
 byteorder = "1.0.0"
 mp4parse = {version = "0.7.1", path = "../mp4parse"}
+num-traits = "0.1.37"
 
 # Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
 [profile.release]
 debug-assertions = true
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -31,20 +31,22 @@
 //! ```
 
 // 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;
 extern crate byteorder;
+extern crate num_traits;
 
 use std::io::Read;
 use std::collections::HashMap;
 use byteorder::WriteBytesExt;
+use num_traits::{PrimInt, Zero};
 
 // Symbols we need from our rust api.
 use mp4parse::MediaContext;
 use mp4parse::TrackType;
 use mp4parse::read_mp4;
 use mp4parse::Error;
 use mp4parse::SampleEntry;
 use mp4parse::AudioCodecSpecific;
@@ -189,16 +191,17 @@ pub struct mp4parse_track_audio_info {
 
 #[repr(C)]
 #[derive(Default)]
 pub struct mp4parse_track_video_info {
     pub display_width: u32,
     pub display_height: u32,
     pub image_width: u16,
     pub image_height: u16,
+    pub rotation: u16,
     pub extra_data: mp4parse_byte_data,
     pub protected_data: mp4parse_sinf_info,
 }
 
 #[repr(C)]
 #[derive(Default)]
 pub struct mp4parse_fragment_info {
     pub fragment_duration: u64,
@@ -369,38 +372,43 @@ pub unsafe extern fn mp4parse_get_track_
 /// Calculate numerator * scale / denominator, if possible.
 ///
 /// Applying the associativity of integer arithmetic, we divide first
 /// and add the remainder after multiplying each term separately
 /// to preserve precision while leaving more headroom. That is,
 /// (n * s) / d is split into floor(n / d) * s + (n % d) * s / d.
 ///
 /// Return None on overflow or if the denominator is zero.
-fn rational_scale(numerator: u64, denominator: u64, scale: u64) -> Option<u64> {
-    if denominator == 0 {
+fn rational_scale<T, S>(numerator: T, denominator: T, scale2: S) -> Option<T>
+    where T: PrimInt + Zero, S: PrimInt {
+    if denominator.is_zero() {
         return None;
     }
+
     let integer = numerator / denominator;
     let remainder = numerator % denominator;
-    match integer.checked_mul(scale) {
-        Some(integer) => remainder.checked_mul(scale)
-            .and_then(|remainder| (remainder/denominator).checked_add(integer)),
-        None => None,
-    }
+    num_traits::cast(scale2).and_then(|s| {
+        match integer.checked_mul(&s) {
+            Some(integer) => remainder.checked_mul(&s)
+                .and_then(|remainder| (remainder/denominator).checked_add(&integer)),
+            None => None,
+        }
+    })
 }
 
 fn media_time_to_us(time: MediaScaledTime, scale: MediaTimeScale) -> Option<u64> {
     let microseconds_per_second = 1000000;
-    rational_scale(time.0, scale.0, microseconds_per_second)
+    rational_scale::<u64, u64>(time.0, scale.0, microseconds_per_second)
 }
 
-fn track_time_to_us(time: TrackScaledTime, scale: TrackTimeScale) -> Option<u64> {
+fn track_time_to_us<T>(time: TrackScaledTime<T>, scale: TrackTimeScale<T>) -> Option<T>
+    where T: PrimInt + Zero {
     assert_eq!(time.1, scale.1);
     let microseconds_per_second = 1000000;
-    rational_scale(time.0, scale.0, microseconds_per_second)
+    rational_scale::<T, u64>(time.0, scale.0, microseconds_per_second)
 }
 
 /// Fill the supplied `mp4parse_track_info` with metadata for `track`.
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
     if parser.is_null() || info.is_null() || (*parser).poisoned() {
         return MP4PARSE_ERROR_BADARG;
     }
@@ -621,16 +629,24 @@ pub unsafe extern fn mp4parse_get_track_
     let video = match *video {
         SampleEntry::Video(ref x) => x,
         _ => return MP4PARSE_ERROR_INVALID,
     };
 
     if let Some(ref tkhd) = track.tkhd {
         (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
         (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
+        let matrix = (tkhd.matrix.a >> 16, tkhd.matrix.b >> 16,
+                      tkhd.matrix.c >> 16, tkhd.matrix.d >> 16);
+        (*info).rotation = match matrix {
+            ( 0,  1, -1,  0) => 90, // rotate 90 degrees
+            (-1,  0,  0, -1) => 180, // rotate 180 degrees
+            ( 0, -1,  1,  0) => 270, // rotate 270 degrees
+            _ => 0,
+        };
     } else {
         return MP4PARSE_ERROR_INVALID;
     }
     (*info).image_width = video.width;
     (*info).image_height = video.height;
 
     match video.codec_specific {
         VideoCodecSpecific::AVCConfig(ref avc) => {
@@ -718,16 +734,17 @@ pub unsafe extern fn mp4parse_get_indice
 // (sample_with_the_same_offset_count, offset) => (offset), (offset), (offset) ...
 //
 // For example:
 // (2, 10), (4, 9) into (10, 10, 9, 9, 9, 9) by calling next_offset_time().
 struct TimeOffsetIterator<'a> {
     cur_sample_range: std::ops::Range<u32>,
     cur_offset: i64,
     ctts_iter: Option<std::slice::Iter<'a, mp4parse::TimeOffset>>,
+    track_id: usize,
 }
 
 impl<'a> Iterator for TimeOffsetIterator<'a> {
     type Item = i64;
 
     fn next(&mut self) -> Option<i64> {
         let has_sample = self.cur_sample_range.next()
             .or_else(|| {
@@ -756,37 +773,38 @@ impl<'a> Iterator for TimeOffsetIterator
                 self.cur_sample_range.next()
             });
 
         has_sample.and(Some(self.cur_offset))
     }
 }
 
 impl<'a> TimeOffsetIterator<'a> {
-    fn next_offset_time(&mut self) -> i64 {
+    fn next_offset_time(&mut self) -> TrackScaledTime<i64> {
         match self.next() {
-            Some(v) => v as i64,
-            _ => 0,
+            Some(v) => TrackScaledTime::<i64>(v as i64, self.track_id),
+            _ => TrackScaledTime::<i64>(0, self.track_id),
         }
     }
 }
 
 // Convert 'stts' compact table to full table by iterator,
 // (sample_count_with_the_same_time, time) => (time, time, time) ... repeats
 // sample_count_with_the_same_time.
 //
 // For example:
 // (2, 3000), (1, 2999) to (3000, 3000, 2999).
-struct TimeToSampleIteraor<'a> {
+struct TimeToSampleIterator<'a> {
     cur_sample_count: std::ops::Range<u32>,
     cur_sample_delta: u32,
     stts_iter: std::slice::Iter<'a, mp4parse::Sample>,
+    track_id: usize,
 }
 
-impl<'a> Iterator for TimeToSampleIteraor<'a> {
+impl<'a> Iterator for TimeToSampleIterator<'a> {
     type Item = u32;
 
     fn next(&mut self) -> Option<u32> {
         let has_sample = self.cur_sample_count.next()
             .or_else(|| {
                 self.cur_sample_count = match self.stts_iter.next() {
                     Some(v) => {
                         self.cur_sample_delta = v.sample_delta;
@@ -797,21 +815,21 @@ impl<'a> Iterator for TimeToSampleIterao
 
                 self.cur_sample_count.next()
             });
 
         has_sample.and(Some(self.cur_sample_delta))
     }
 }
 
-impl<'a> TimeToSampleIteraor<'a> {
-    fn next_delta(&mut self) -> u32 {
+impl<'a> TimeToSampleIterator<'a> {
+    fn next_delta(&mut self) -> TrackScaledTime<i64> {
         match self.next() {
-            Some(v) => v,
-            _ => 0,
+            Some(v) => TrackScaledTime::<i64>(v as i64, self.track_id),
+            _ => TrackScaledTime::<i64>(0, self.track_id),
         }
     }
 }
 
 // Convert 'stco' compact table to full table by iterator.
 // (start_chunk_num, sample_number) => (start_chunk_num, sample_number),
 //                                     (start_chunk_num + 1, sample_number),
 //                                     (start_chunk_num + 2, sample_number),
@@ -851,43 +869,20 @@ impl<'a> Iterator for SampleToChunkItera
                 self.remain_chunk_count -= self.chunks.len() as u32;
                 self.chunks.next()
             });
 
         has_chunk.map_or(None, |id| { Some((id, self.sample_count)) })
     }
 }
 
-// A helper struct to convert track time to us.
-struct PresentationTime {
-    time: i64,
-    scale: TrackTimeScale
-}
-
-impl PresentationTime {
-    fn new(time: i64, scale: TrackTimeScale) -> PresentationTime {
-        PresentationTime {
-            time: time,
-            scale: scale,
-        }
-    }
-
-    fn to_us(&self) -> i64 {
-        let track_time = TrackScaledTime(self.time as u64, self.scale.1);
-        match track_time_to_us(track_time, self.scale) {
-            Some(v) => v as i64,
-            _ => 0,
-        }
-    }
-}
-
 fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4parse_indice>> {
     let timescale = match track.timescale {
-        Some(t) => t,
-        _ => return None,
+        Some(ref t) => TrackTimeScale::<i64>(t.0 as i64, t.1),
+        _ => TrackTimeScale::<i64>(0, 0),
     };
 
     let (stsc, stco, stsz, stts) =
         match (&track.stsc, &track.stco, &track.stsz, &track.stts) {
             (&Some(ref a), &Some(ref b), &Some(ref c), &Some(ref d)) => (a, b, c, d),
             _ => return None,
         };
 
@@ -952,44 +947,64 @@ fn create_sample_table(track: &Track, tr
         Some(ref v) => Some(v.samples.as_slice().iter()),
         _ => None,
     };
 
     let mut ctts_offset_iter = TimeOffsetIterator {
         cur_sample_range: (0 .. 0),
         cur_offset: 0,
         ctts_iter: ctts_iter,
+        track_id: track.id,
     };
 
-    let mut stts_iter = TimeToSampleIteraor {
+    let mut stts_iter = TimeToSampleIterator {
         cur_sample_count: (0 .. 0),
         cur_sample_delta: 0,
         stts_iter: stts.samples.as_slice().iter(),
+        track_id: track.id,
     };
 
     // sum_delta is the sum of stts_iter delta.
     // According to sepc:
     //      decode time => DT(n) = DT(n-1) + STTS(n)
     //      composition time => CT(n) = DT(n) + CTTS(n)
     // Note:
     //      composition time needs to add the track offset time from 'elst' table.
-    let mut sum_delta = PresentationTime::new(0, timescale);
+    let mut sum_delta = TrackScaledTime::<i64>(0, track.id);
     for sample in sample_table.as_mut_slice() {
-        let decode_time = sum_delta.to_us();
-        sum_delta.time += stts_iter.next_delta() as i64;
+        let decode_time = sum_delta;
+        sum_delta = sum_delta + stts_iter.next_delta();
 
         // ctts_offset is the current sample offset time.
-        let ctts_offset = PresentationTime::new(ctts_offset_iter.next_offset_time(), timescale);
+        let ctts_offset = ctts_offset_iter.next_offset_time();
+
+        // ctts_offset could be negative but (decode_time + ctts_offset) should always be positive
+        // value.
+        let start_composition = track_time_to_us(decode_time + ctts_offset, timescale).and_then(|t| {
+            if t < 0 { return None; }
+            Some(t)
+        });
 
-        let start_composition = decode_time + ctts_offset.to_us() + track_offset_time;
-        let end_composition = sum_delta.to_us() + ctts_offset.to_us() + track_offset_time;
+        // ctts_offset could be negative but (sum_delta + ctts_offset) should always be positive
+        // value.
+        let end_composition = track_time_to_us(sum_delta + ctts_offset, timescale).and_then(|t| {
+            if t < 0 { return None; }
+            Some(t)
+        });
+
+        let start_decode = track_time_to_us(decode_time, timescale);
 
-        sample.start_decode = decode_time;
-        sample.start_composition = start_composition;
-        sample.end_composition = end_composition;
+        match (start_composition, end_composition, start_decode) {
+            (Some(s_c), Some(e_c), Some(s_d)) => {
+                sample.start_composition = s_c + track_offset_time;
+                sample.end_composition = e_c + track_offset_time;
+                sample.start_decode = s_d;
+            },
+            _ => return None,
+        }
     }
 
     // Correct composition end time due to 'ctts' causes composition time re-ordering.
     //
     // Composition end time is not in specification. However, gecko needs it, so we need to
     // calculate to correct the composition end time.
     if track.ctts.is_some() {
         // Create an index table refers to sample_table and sorted by start_composisiton time.
@@ -1210,16 +1225,17 @@ fn arg_validation() {
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
 
         let mut dummy_video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            rotation: 0,
             extra_data: mp4parse_byte_data::default(),
             protected_data: Default::default(),
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
 
         let mut dummy_audio = Default::default();
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
     }
@@ -1256,16 +1272,17 @@ fn arg_validation_with_parser() {
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info));
 
         let mut dummy_video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            rotation: 0,
             extra_data: mp4parse_byte_data::default(),
             protected_data: Default::default(),
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
 
         let mut dummy_audio = Default::default();
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
 
@@ -1329,16 +1346,17 @@ fn arg_validation_with_data() {
         assert_eq!(info.duration, 61333);
         assert_eq!(info.media_time, 21333);
 
         let mut video = mp4parse_track_video_info {
             display_width: 0,
             display_height: 0,
             image_width: 0,
             image_height: 0,
+            rotation: 0,
             extra_data: mp4parse_byte_data::default(),
             protected_data: Default::default(),
         };
         assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video));
         assert_eq!(video.display_width, 320);
         assert_eq!(video.display_height, 240);
         assert_eq!(video.image_width, 320);
         assert_eq!(video.image_height, 240);
@@ -1363,16 +1381,17 @@ fn arg_validation_with_data() {
         assert_eq!(info.track_id, 0);
         assert_eq!(info.duration, 0);
         assert_eq!(info.media_time, 0);
 
         let mut video = mp4parse_track_video_info { display_width: 0,
                                                     display_height: 0,
                                                     image_width: 0,
                                                     image_height: 0,
+                                                    rotation: 0,
                                                     extra_data: mp4parse_byte_data::default(),
                                                     protected_data: Default::default(),
         };
         assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video));
         assert_eq!(video.display_width, 0);
         assert_eq!(video.display_height, 0);
         assert_eq!(video.image_width, 0);
         assert_eq!(video.image_height, 0);
@@ -1384,31 +1403,31 @@ fn arg_validation_with_data() {
         assert_eq!(audio.sample_rate, 0);
 
         mp4parse_free(parser);
     }
 }
 
 #[test]
 fn rational_scale_overflow() {
-    assert_eq!(rational_scale(17, 3, 1000), Some(5666));
+    assert_eq!(rational_scale::<u64, u64>(17, 3, 1000), Some(5666));
     let large = 0x4000_0000_0000_0000;
-    assert_eq!(rational_scale(large, 2, 2), Some(large));
-    assert_eq!(rational_scale(large, 4, 4), Some(large));
-    assert_eq!(rational_scale(large, 2, 8), None);
-    assert_eq!(rational_scale(large, 8, 4), Some(large/2));
-    assert_eq!(rational_scale(large + 1, 4, 4), Some(large+1));
-    assert_eq!(rational_scale(large, 40, 1000), None);
+    assert_eq!(rational_scale::<u64, u64>(large, 2, 2), Some(large));
+    assert_eq!(rational_scale::<u64, u64>(large, 4, 4), Some(large));
+    assert_eq!(rational_scale::<u64, u64>(large, 2, 8), None);
+    assert_eq!(rational_scale::<u64, u64>(large, 8, 4), Some(large/2));
+    assert_eq!(rational_scale::<u64, u64>(large + 1, 4, 4), Some(large+1));
+    assert_eq!(rational_scale::<u64, u64>(large, 40, 1000), None);
 }
 
 #[test]
 fn media_time_overflow() {
   let scale = MediaTimeScale(90000);
   let duration = MediaScaledTime(9007199254710000);
   assert_eq!(media_time_to_us(duration, scale), Some(100079991719000000));
 }
 
 #[test]
 fn track_time_overflow() {
-  let scale = TrackTimeScale(44100, 0);
-  let duration = TrackScaledTime(4413527634807900, 0);
+  let scale = TrackTimeScale(44100u64, 0);
+  let duration = TrackScaledTime(4413527634807900u64, 0);
   assert_eq!(track_time_to_us(duration, scale), Some(100079991719000000));
 }
--- a/media/libstagefright/binding/update-rust.sh
+++ b/media/libstagefright/binding/update-rust.sh
@@ -1,13 +1,13 @@
 #!/bin/sh -e
 # Script to update mp4parse-rust sources to latest upstream
 
 # Default version.
-VER=6cd06d46565f55a8259d4ad7f083c52d6335750f
+VER=b78dc3e4e80ce4132e7880ae068d0672cbfeaa48
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -438,28 +438,30 @@ dependencies = [
 ]
 
 [[package]]
 name = "mp4parse"
 version = "0.7.1"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse-gtest"
 version = "0.1.0"
 
 [[package]]
 name = "mp4parse_capi"
 version = "0.7.1"
 dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse 0.7.1",
+ "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "nom"
 version = "1.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -436,24 +436,26 @@ dependencies = [
 ]
 
 [[package]]
 name = "mp4parse"
 version = "0.7.1"
 dependencies = [
  "bitreader 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp4parse_capi"
 version = "0.7.1"
 dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "mp4parse 0.7.1",
+ "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "nom"
 version = "1.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]