Bug 1349133 - Update mp4 rust parser. r?kinetik draft
authorAlfredo.Yang <ayang@mozilla.com>
Wed, 22 Mar 2017 09:53:58 +0800
changeset 502587 e6950162971c8a9f0f1c6aa801d64360b106cafb
parent 502586 0a5406f4ffae9f31eecdb8f077b2c0c78a5c19bf
child 550199 94017ca0f9190d7fb92d5c4a9534d091d427ec72
push id50327
push userbmo:ayang@mozilla.com
push dateWed, 22 Mar 2017 02:12:31 +0000
reviewerskinetik
bugs1349133
milestone55.0a1
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
@@ -439,28 +439,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
@@ -437,24 +437,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]]