Bug 1349133 - Update mp4 rust parser. r=kinetik

MozReview-Commit-ID: JDKFUsJBpS9

--HG--
extra : rebase_source : 4c7f569dbafb3b34c485cbf0c35177d27579455c
This commit is contained in:
Alfredo.Yang 2017-03-22 09:53:58 +08:00
Родитель 66d934bf84
Коммит 33f2386cd8
9 изменённых файлов: 142 добавлений и 83 удалений

Просмотреть файл

@ -88,6 +88,7 @@ typedef struct mp4parse_track_video_info {
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;

Просмотреть файл

@ -2,7 +2,7 @@ diff --git a/media/libstagefright/binding/mp4parse/Cargo.toml b/media/libstagefr
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]
@ -11,8 +11,10 @@ index ff9422c..814c4c6 100644
-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"
@ -27,7 +29,7 @@ diff --git a/media/libstagefright/binding/mp4parse_capi/Cargo.toml b/media/libst
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",
]
@ -40,6 +42,7 @@ index aeeebc65..5c0836a 100644
[dependencies]
byteorder = "1.0.0"
mp4parse = {version = "0.7.1", path = "../mp4parse"}
num-traits = "0.1.37"
-[build-dependencies]
-rusty-cheddar = "0.3.2"

Просмотреть файл

@ -25,6 +25,7 @@ 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"

Просмотреть файл

@ -10,11 +10,13 @@ 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};
@ -118,6 +120,19 @@ struct MovieHeaderBox {
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 {
@ -126,6 +141,7 @@ pub struct TrackHeaderBox {
pub duration: u64,
pub width: u32,
pub height: u32,
pub matrix: Matrix,
}
/// Edit list box 'elst'
@ -406,12 +422,20 @@ 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)]
@ -431,12 +455,12 @@ impl EmptySampleTableBoxes {
#[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,
@ -793,7 +817,7 @@ fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
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);
}
@ -804,16 +828,16 @@ fn read_edts<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
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))
}
@ -989,7 +1013,14 @@ fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
_ => 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 {
@ -998,6 +1029,7 @@ fn read_tkhd<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackHeaderBox> {
duration: duration,
width: width,
height: height,
matrix: matrix,
})
}
@ -1165,12 +1197,10 @@ fn read_ctts<T: Read>(src: &mut BMFFBox<T>) -> Result<CompositionOffsetBox> {
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)

Просмотреть файл

@ -23,6 +23,7 @@ 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]

Просмотреть файл

@ -36,10 +36,12 @@
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;
@ -194,6 +196,7 @@ pub struct mp4parse_track_video_info {
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,
}
@ -374,28 +377,33 @@ pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, co
/// (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)),
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`.
@ -626,6 +634,14 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser,
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;
}
@ -723,6 +739,7 @@ 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> {
@ -761,10 +778,10 @@ impl<'a> Iterator for TimeOffsetIterator<'a> {
}
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),
}
}
}
@ -775,13 +792,14 @@ impl<'a> TimeOffsetIterator<'a> {
//
// 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> {
@ -802,11 +820,11 @@ impl<'a> Iterator for TimeToSampleIteraor<'a> {
}
}
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),
}
}
}
@ -856,33 +874,10 @@ impl<'a> Iterator for SampleToChunkIterator<'a> {
}
}
// 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) =
@ -957,12 +952,14 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4p
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.
@ -971,20 +968,38 @@ fn create_sample_table(track: &Track, track_offset_time: i64) -> Option<Vec<mp4p
// 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();
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 (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)
});
sample.start_decode = decode_time;
sample.start_composition = start_composition;
sample.end_composition = end_composition;
// 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);
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.
@ -1215,6 +1230,7 @@ fn arg_validation() {
display_height: 0,
image_width: 0,
image_height: 0,
rotation: 0,
extra_data: mp4parse_byte_data::default(),
protected_data: Default::default(),
};
@ -1261,6 +1277,7 @@ fn arg_validation_with_parser() {
display_height: 0,
image_width: 0,
image_height: 0,
rotation: 0,
extra_data: mp4parse_byte_data::default(),
protected_data: Default::default(),
};
@ -1334,6 +1351,7 @@ fn arg_validation_with_data() {
display_height: 0,
image_width: 0,
image_height: 0,
rotation: 0,
extra_data: mp4parse_byte_data::default(),
protected_data: Default::default(),
};
@ -1368,6 +1386,7 @@ fn arg_validation_with_data() {
display_height: 0,
image_width: 0,
image_height: 0,
rotation: 0,
extra_data: mp4parse_byte_data::default(),
protected_data: Default::default(),
};
@ -1389,14 +1408,14 @@ fn arg_validation_with_data() {
#[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]
@ -1408,7 +1427,7 @@ fn media_time_overflow() {
#[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));
}

Просмотреть файл

@ -2,7 +2,7 @@
# 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

2
toolkit/library/gtest/rust/Cargo.lock сгенерированный
Просмотреть файл

@ -443,6 +443,7 @@ 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]]
@ -455,6 +456,7 @@ 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]]

2
toolkit/library/rust/Cargo.lock сгенерированный
Просмотреть файл

@ -441,6 +441,7 @@ 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]]
@ -449,6 +450,7 @@ 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]]