Bug 1300219 - Update mp4parse to v0.5.0. r=kinetik

Result of running the update script and updating gecko's
integration crate for the layout change.

MozReview-Commit-ID: GaIMFKmPmtf

--HG--
extra : rebase_source : 0d3a2f1d211840879e562cb56afcc9ef7e38c730
This commit is contained in:
Ralph Giles 2016-09-02 14:27:50 -07:00
Родитель 3985298455
Коммит 104d30ab53
11 изменённых файлов: 237 добавлений и 152 удалений

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

@ -93,6 +93,8 @@ mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t t
/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
mp4parse_error mp4parse_get_track_video_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_video_info* info);
mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented);
#ifdef __cplusplus

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

@ -1,25 +1,28 @@
[package]
name = "mp4parse"
version = "0.4.0"
version = "0.5.0"
authors = [
"Ralph Giles <giles@mozilla.com>",
"Matthew Gregan <kinetik@flim.org>",
]
description = "Parser for ISO base media file format (mp4)"
documentation = "https://mp4parse-docs.surge.sh/mp4parse/"
license = "MPL-2.0"
repository = "https://github.com/mozilla/mp4parse-rust"
# Cargo includes random files from the working directory
# by default! Avoid bloating the package with test files.
# Avoid complaints about trying to package test files.
exclude = [
"*.mp4",
]
[dependencies]
byteorder = { version = "0.5.0", path = "../byteorder" }
[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
[dependencies]
byteorder = { path = "../byteorder" }

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

@ -54,4 +54,5 @@ box_database!(
OpusSpecificBox 0x644f7073, // "dOps"
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
MovieExtendsBox 0x6d766578, // "mvex"
);

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

@ -13,10 +13,6 @@ use byteorder::ReadBytesExt;
use std::io::{Read, Take};
use std::cmp;
// Expose C api wrapper.
pub mod capi;
pub use capi::*;
mod boxes;
use boxes::BoxType;
@ -109,18 +105,18 @@ struct FileTypeBox {
/// Movie header box 'mvhd'.
#[derive(Debug)]
struct MovieHeaderBox {
timescale: u32,
pub timescale: u32,
duration: u64,
}
/// Track header box 'tkhd'
#[derive(Debug, Clone)]
struct TrackHeaderBox {
pub struct TrackHeaderBox {
track_id: u32,
disabled: bool,
duration: u64,
width: u32,
height: u32,
pub disabled: bool,
pub duration: u64,
pub width: u32,
pub height: u32,
}
/// Edit list box 'elst'
@ -201,7 +197,7 @@ struct SampleDescriptionBox {
}
#[derive(Debug, Clone)]
enum SampleEntry {
pub enum SampleEntry {
Audio(AudioSampleEntry),
Video(VideoSampleEntry),
Unknown,
@ -209,45 +205,45 @@ enum SampleEntry {
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
enum AudioCodecSpecific {
pub enum AudioCodecSpecific {
ES_Descriptor(Vec<u8>),
OpusSpecificBox(OpusSpecificBox),
}
#[derive(Debug, Clone)]
struct AudioSampleEntry {
pub struct AudioSampleEntry {
data_reference_index: u16,
channelcount: u16,
samplesize: u16,
samplerate: u32,
codec_specific: AudioCodecSpecific,
pub channelcount: u16,
pub samplesize: u16,
pub samplerate: u32,
pub codec_specific: AudioCodecSpecific,
}
#[derive(Debug, Clone)]
enum VideoCodecSpecific {
pub enum VideoCodecSpecific {
AVCConfig(Vec<u8>),
VPxConfig(VPxConfigBox),
}
#[derive(Debug, Clone)]
struct VideoSampleEntry {
pub struct VideoSampleEntry {
data_reference_index: u16,
width: u16,
height: u16,
codec_specific: VideoCodecSpecific,
pub width: u16,
pub height: u16,
pub codec_specific: VideoCodecSpecific,
}
/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9).
#[derive(Debug, Clone)]
struct VPxConfigBox {
pub struct VPxConfigBox {
profile: u8,
level: u8,
bit_depth: u8,
color_space: u8, // Really an enum
chroma_subsampling: u8,
pub bit_depth: u8,
pub color_space: u8, // Really an enum
pub chroma_subsampling: u8,
transfer_function: u8,
video_full_range: bool,
codec_init: Vec<u8>, // Empty for vp8/vp9.
pub codec_init: Vec<u8>, // Empty for vp8/vp9.
}
#[derive(Debug, Clone)]
@ -259,8 +255,8 @@ struct ChannelMappingTable {
/// Represent an OpusSpecificBox 'dOps'
#[derive(Debug, Clone)]
struct OpusSpecificBox {
version: u8,
pub struct OpusSpecificBox {
pub version: u8,
output_channel_count: u8,
pre_skip: u16,
input_sample_rate: u32,
@ -270,73 +266,80 @@ struct OpusSpecificBox {
}
/// Internal data structures.
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct MediaContext {
timescale: Option<MediaTimeScale>,
pub timescale: Option<MediaTimeScale>,
pub has_mvex: bool,
/// Tracks found in the file.
tracks: Vec<Track>,
pub tracks: Vec<Track>,
}
impl MediaContext {
pub fn new() -> MediaContext {
MediaContext {
timescale: None,
tracks: Vec::new(),
}
Default::default()
}
}
#[derive(Debug)]
enum TrackType {
pub enum TrackType {
Audio,
Video,
Unknown,
}
impl Default for TrackType {
fn default() -> Self { TrackType::Unknown }
}
/// The media's global (mvhd) timescale.
#[derive(Debug, Copy, Clone)]
struct MediaTimeScale(u64);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct MediaTimeScale(pub u64);
/// A time scaled by the media's global (mvhd) timescale.
#[derive(Debug, Copy, Clone)]
struct MediaScaledTime(u64);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct MediaScaledTime(pub u64);
/// The track's local (mdhd) timescale.
#[derive(Debug, Copy, Clone)]
struct TrackTimeScale(u64, usize);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TrackTimeScale(pub u64, pub usize);
/// A time scaled by the track's local (mdhd) timescale.
#[derive(Debug, Copy, Clone)]
struct TrackScaledTime(u64, usize);
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TrackScaledTime(pub u64, pub usize);
#[derive(Debug)]
struct Track {
/// A fragmented file contains no sample data in stts, stsc, and stco.
#[derive(Debug, Default)]
pub struct EmptySampleTableBoxes {
pub empty_stts : bool,
pub empty_stsc : bool,
pub empty_stco : bool,
}
/// Check boxes contain data.
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,
track_type: TrackType,
empty_duration: Option<MediaScaledTime>,
media_time: Option<TrackScaledTime>,
timescale: Option<TrackTimeScale>,
duration: Option<TrackScaledTime>,
track_id: Option<u32>,
mime_type: String,
data: Option<SampleEntry>,
tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this.
pub track_type: TrackType,
pub empty_duration: Option<MediaScaledTime>,
pub media_time: Option<TrackScaledTime>,
pub timescale: Option<TrackTimeScale>,
pub duration: Option<TrackScaledTime>,
pub track_id: Option<u32>,
pub mime_type: String,
pub empty_sample_boxes: EmptySampleTableBoxes,
pub data: Option<SampleEntry>,
pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this.
}
impl Track {
fn new(id: usize) -> Track {
Track {
id: id,
track_type: TrackType::Unknown,
empty_duration: None,
media_time: None,
timescale: None,
duration: None,
track_id: None,
mime_type: String::new(),
data: None,
tkhd: None,
}
Track { id: id, ..Default::default() }
}
}
@ -454,7 +457,7 @@ macro_rules! check_parser_state {
/// Read the contents of a box, including sub boxes.
///
/// Metadata is accumulated in the passed-through MediaContext struct,
/// Metadata is accumulated in the passed-through `MediaContext` struct,
/// which can be examined later.
pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
let mut found_ftyp = false;
@ -533,6 +536,10 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>, context: &mut MediaContext) -> Result<
try!(read_trak(&mut b, &mut track));
context.tracks.push(track);
}
BoxType::MovieExtendsBox => {
context.has_mvex = true;
try!(skip_box_content(&mut b));
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
@ -654,10 +661,12 @@ fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
}
BoxType::TimeToSampleBox => {
let stts = try!(read_stts(&mut b));
track.empty_sample_boxes.empty_stts = stts.samples.is_empty();
log!("{:?}", stts);
}
BoxType::SampleToChunkBox => {
let stsc = try!(read_stsc(&mut b));
track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty();
log!("{:?}", stsc);
}
BoxType::SampleSizeBox => {
@ -666,6 +675,7 @@ fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
}
BoxType::ChunkOffsetBox => {
let stco = try!(read_stco(&mut b));
track.empty_sample_boxes.empty_stco = stco.offsets.is_empty();
log!("{:?}", stco);
}
BoxType::ChunkLargeOffsetBox => {
@ -985,7 +995,7 @@ fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
})
}
/// Parse OpusSpecificBox.
/// Parse `OpusSpecificBox`.
fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
let version = try!(src.read_u8());
if version != 0 {
@ -1024,13 +1034,13 @@ fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
})
}
/// Re-serialize the Opus codec-specific config data as an OpusHead packet.
/// Re-serialize the Opus codec-specific config data as an `OpusHead` packet.
///
/// Some decoders expect the initialization data in the format used by the
/// Ogg and WebM encapsulations. To support this we prepend the 'OpusHead'
/// Ogg and WebM encapsulations. To support this we prepend the `OpusHead`
/// tag and byte-swap the data from big- to little-endian relative to the
/// dOps box.
fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> {
pub fn serialize_opus_header<W: byteorder::WriteBytesExt + std::io::Write>(opus: &OpusSpecificBox, dst: &mut W) -> Result<()> {
match dst.write(b"OpusHead") {
Err(e) => return Err(Error::from(e)),
Ok(bytes) => {
@ -1151,16 +1161,14 @@ fn read_video_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<S
check_parser_state!(b.content);
}
if codec_specific.is_none() {
return Err(Error::InvalidData("malformed video sample entry"));
}
Ok(SampleEntry::Video(VideoSampleEntry {
data_reference_index: data_reference_index,
width: width,
height: height,
codec_specific: codec_specific.unwrap(),
}))
codec_specific
.map(|codec_specific| SampleEntry::Video(VideoSampleEntry {
data_reference_index: data_reference_index,
width: width,
height: height,
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
}
/// Parse an audio description inside an stsd box.
@ -1235,17 +1243,15 @@ fn read_audio_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<S
check_parser_state!(b.content);
}
if codec_specific.is_none() {
return Err(Error::InvalidData("malformed audio sample entry"));
}
Ok(SampleEntry::Audio(AudioSampleEntry {
data_reference_index: data_reference_index,
channelcount: channelcount,
samplesize: samplesize,
samplerate: samplerate,
codec_specific: codec_specific.unwrap(),
}))
codec_specific
.map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
data_reference_index: data_reference_index,
channelcount: channelcount,
samplesize: samplesize,
samplerate: samplerate,
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
}
/// Parse a stsd box.
@ -1352,17 +1358,6 @@ fn read_fixed_length_pascal_string<T: Read>(src: &mut T, size: usize) -> Result<
String::from_utf8(buf).map_err(From::from)
}
fn media_time_to_ms(time: MediaScaledTime, scale: MediaTimeScale) -> u64 {
assert!(scale.0 != 0);
time.0 * 1000000 / scale.0
}
fn track_time_to_ms(time: TrackScaledTime, scale: TrackTimeScale) -> u64 {
assert!(time.1 == scale.1);
assert!(scale.0 != 0);
time.0 * 1000000 / scale.0
}
fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> {
src.read_i16::<byteorder::BigEndian>().map_err(From::from)
}

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

@ -6,7 +6,9 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
use std::io::Cursor;
use super::*;
use super::read_mp4;
use super::MediaContext;
use super::Error;
extern crate test_assembler;
use self::test_assembler::*;

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

@ -0,0 +1,28 @@
[package]
name = "mp4parse_capi"
version = "0.5.0"
authors = [
"Ralph Giles <giles@mozilla.com>",
"Matthew Gregan <kinetik@flim.org>",
]
description = "Parser for ISO base media file format (mp4)"
documentation = "https://mp4parse-docs.surge.sh/mp4parse/"
license = "MPL-2.0"
repository = "https://github.com/mozilla/mp4parse-rust"
# Avoid complaints about trying to package test files.
exclude = [
"*.mp4",
]
[dependencies]
"mp4parse" = {version = "0.5.0", path = "../mp4parse"}
[features]
fuzz = ["mp4parse/fuzz"]
# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
[profile.release]
debug-assertions = true

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

@ -3,7 +3,6 @@ extern crate cheddar;
fn main() {
// Generate mp4parse.h.
cheddar::Cheddar::new().expect("could not read manifest")
.module("capi").expect("invalid module path")
.insert_code("// THIS FILE IS AUTOGENERATED BY mp4parse-rust/build.rs - DO NOT EDIT\n\n")
.insert_code("// This Source Code Form is subject to the terms of the Mozilla Public\n")
.insert_code("// License, v. 2.0. If a copy of the MPL was not distributed with this\n")

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

@ -5,7 +5,7 @@
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//! extern crate mp4parse_capi;
//! use std::io::Read;
//!
//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
@ -17,14 +17,16 @@
//! }
//! }
//!
//! let mut file = std::fs::File::open("examples/minimal.mp4").unwrap();
//! let io = mp4parse::mp4parse_io { read: buf_read,
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
//! let io = mp4parse_capi::mp4parse_io {
//! read: buf_read,
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void
//! };
//! unsafe {
//! let parser = mp4parse::mp4parse_new(&io);
//! let rv = mp4parse::mp4parse_read(parser);
//! assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//! mp4parse::mp4parse_free(parser);
//! let parser = mp4parse_capi::mp4parse_new(&io);
//! let rv = mp4parse_capi::mp4parse_read(parser);
//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK);
//! mp4parse_capi::mp4parse_free(parser);
//! }
//! ```
@ -32,21 +34,24 @@
// 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/.
use std;
extern crate mp4parse;
use std::io::Read;
use std::collections::HashMap;
// Symbols we need from our rust api.
use MediaContext;
use TrackType;
use read_mp4;
use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;
use AudioCodecSpecific;
use VideoCodecSpecific;
use serialize_opus_header;
use mp4parse::MediaContext;
use mp4parse::TrackType;
use mp4parse::read_mp4;
use mp4parse::Error;
use mp4parse::SampleEntry;
use mp4parse::AudioCodecSpecific;
use mp4parse::VideoCodecSpecific;
use mp4parse::MediaTimeScale;
use mp4parse::MediaScaledTime;
use mp4parse::TrackTimeScale;
use mp4parse::TrackScaledTime;
use mp4parse::serialize_opus_header;
// rusty-cheddar's C enum generation doesn't namespace enum members by
// prefixing them, so we're forced to do it in our member names until
@ -266,13 +271,24 @@ pub unsafe extern fn mp4parse_get_track_count(parser: *const mp4parse_parser, co
let context = (*parser).context();
// Make sure the track count fits in a u32.
if context.tracks.len() >= u32::max_value() as usize {
if context.tracks.len() > u32::max_value() as usize {
return MP4PARSE_ERROR_INVALID;
}
*count = context.tracks.len() as u32;
MP4PARSE_OK
}
fn media_time_to_ms(time: MediaScaledTime, scale: MediaTimeScale) -> u64 {
assert!(scale.0 != 0);
time.0 * 1000000 / scale.0
}
fn track_time_to_ms(time: TrackScaledTime, scale: TrackTimeScale) -> u64 {
assert!(time.1 == scale.1);
assert!(scale.0 != 0);
time.0 * 1000000 / scale.0
}
/// 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 {
@ -310,22 +326,28 @@ pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track
_ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
};
// Maybe context & track should just have a single simple is_valid() instead?
if context.timescale.is_none() ||
context.tracks[track_index].timescale.is_none() ||
context.tracks[track_index].duration.is_none() ||
context.tracks[track_index].track_id.is_none() {
return MP4PARSE_ERROR_INVALID;
let track = &context.tracks[track_index];
if let (Some(track_timescale),
Some(context_timescale),
Some(track_duration)) = (track.timescale,
context.timescale,
track.duration) {
info.media_time = track.media_time.map_or(0, |media_time| {
track_time_to_ms(media_time, track_timescale) as i64
}) - track.empty_duration.map_or(0, |empty_duration| {
media_time_to_ms(empty_duration, context_timescale) as i64
});
info.duration = track_time_to_ms(track_duration, track_timescale);
} else {
return MP4PARSE_ERROR_INVALID
}
let track = &context.tracks[track_index];
info.media_time = track.media_time.map_or(0, |media_time| {
track_time_to_ms(media_time, track.timescale.unwrap()) as i64
}) - track.empty_duration.map_or(0, |empty_duration| {
media_time_to_ms(empty_duration, context.timescale.unwrap()) as i64
});
info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
info.track_id = track.track_id.unwrap();
info.track_id = match track.track_id {
Some(track_id) => track_id,
None => return MP4PARSE_ERROR_INVALID,
};
MP4PARSE_OK
}
@ -438,6 +460,32 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser,
MP4PARSE_OK
}
// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
#[no_mangle]
pub unsafe extern fn mp4parse_is_fragmented(parser: *mut mp4parse_parser, track_id: u32, fragmented: *mut u8) -> mp4parse_error {
if parser.is_null() || (*parser).poisoned() {
return MP4PARSE_ERROR_BADARG;
}
let context = (*parser).context_mut();
let tracks = &context.tracks;
(*fragmented) = false as u8;
if !context.has_mvex {
return MP4PARSE_OK;
}
// check sample tables.
let mut iter = tracks.iter();
match iter.find(|track| track.track_id == Some(track_id)) {
Some(track) if track.empty_sample_boxes.all_empty() => (*fragmented) = true as u8,
Some(_) => {},
None => return MP4PARSE_ERROR_BADARG,
}
MP4PARSE_OK
}
#[cfg(test)]
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
panic!("panic_read shouldn't be called in these tests");
@ -614,7 +662,7 @@ fn get_track_count_poisoned_parser() {
#[test]
fn arg_validation_with_data() {
unsafe {
let mut file = std::fs::File::open("examples/minimal.mp4").unwrap();
let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
let io = mp4parse_io { read: valid_read,
userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
let parser = mp4parse_new(&io);

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

@ -2,7 +2,7 @@
name = "gkrust"
version = "0.1.0"
dependencies = [
"mp4parse 0.4.0",
"mp4parse_capi 0.5.0",
]
[[package]]
@ -11,8 +11,15 @@ version = "0.5.3"
[[package]]
name = "mp4parse"
version = "0.4.0"
version = "0.5.0"
dependencies = [
"byteorder 0.5.3",
]
[[package]]
name = "mp4parse_capi"
version = "0.5.0"
dependencies = [
"mp4parse 0.5.0",
]

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

@ -6,7 +6,7 @@ license = "MPL-2.0"
description = "Rust code for libxul"
[dependencies]
mp4parse = { path = "../../../media/libstagefright/binding/mp4parse" }
mp4parse_capi = { path = "../../../media/libstagefright/binding/mp4parse_capi" }
[lib]
path = "lib.rs"

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

@ -2,4 +2,4 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
extern crate mp4parse;
extern crate mp4parse_capi;