зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
3985298455
Коммит
104d30ab53
|
@ -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`.
|
/// 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_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
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mp4parse"
|
name = "mp4parse"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Ralph Giles <giles@mozilla.com>",
|
"Ralph Giles <giles@mozilla.com>",
|
||||||
"Matthew Gregan <kinetik@flim.org>",
|
"Matthew Gregan <kinetik@flim.org>",
|
||||||
]
|
]
|
||||||
|
|
||||||
description = "Parser for ISO base media file format (mp4)"
|
description = "Parser for ISO base media file format (mp4)"
|
||||||
|
documentation = "https://mp4parse-docs.surge.sh/mp4parse/"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
|
|
||||||
repository = "https://github.com/mozilla/mp4parse-rust"
|
repository = "https://github.com/mozilla/mp4parse-rust"
|
||||||
|
|
||||||
# Cargo includes random files from the working directory
|
# Avoid complaints about trying to package test files.
|
||||||
# by default! Avoid bloating the package with test files.
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"*.mp4",
|
"*.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.
|
# Somewhat heavy-handed, but we want at least -Z force-overflow-checks=on.
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
byteorder = { path = "../byteorder" }
|
|
||||||
|
|
|
@ -54,4 +54,5 @@ box_database!(
|
||||||
OpusSpecificBox 0x644f7073, // "dOps"
|
OpusSpecificBox 0x644f7073, // "dOps"
|
||||||
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
|
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
|
||||||
ProtectedAudioSampleEntry 0x656e6361, // "enca" - 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::io::{Read, Take};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
// Expose C api wrapper.
|
|
||||||
pub mod capi;
|
|
||||||
pub use capi::*;
|
|
||||||
|
|
||||||
mod boxes;
|
mod boxes;
|
||||||
use boxes::BoxType;
|
use boxes::BoxType;
|
||||||
|
|
||||||
|
@ -109,18 +105,18 @@ struct FileTypeBox {
|
||||||
/// Movie header box 'mvhd'.
|
/// Movie header box 'mvhd'.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MovieHeaderBox {
|
struct MovieHeaderBox {
|
||||||
timescale: u32,
|
pub timescale: u32,
|
||||||
duration: u64,
|
duration: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track header box 'tkhd'
|
/// Track header box 'tkhd'
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TrackHeaderBox {
|
pub struct TrackHeaderBox {
|
||||||
track_id: u32,
|
track_id: u32,
|
||||||
disabled: bool,
|
pub disabled: bool,
|
||||||
duration: u64,
|
pub duration: u64,
|
||||||
width: u32,
|
pub width: u32,
|
||||||
height: u32,
|
pub height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Edit list box 'elst'
|
/// Edit list box 'elst'
|
||||||
|
@ -201,7 +197,7 @@ struct SampleDescriptionBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum SampleEntry {
|
pub enum SampleEntry {
|
||||||
Audio(AudioSampleEntry),
|
Audio(AudioSampleEntry),
|
||||||
Video(VideoSampleEntry),
|
Video(VideoSampleEntry),
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@ -209,45 +205,45 @@ enum SampleEntry {
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum AudioCodecSpecific {
|
pub enum AudioCodecSpecific {
|
||||||
ES_Descriptor(Vec<u8>),
|
ES_Descriptor(Vec<u8>),
|
||||||
OpusSpecificBox(OpusSpecificBox),
|
OpusSpecificBox(OpusSpecificBox),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct AudioSampleEntry {
|
pub struct AudioSampleEntry {
|
||||||
data_reference_index: u16,
|
data_reference_index: u16,
|
||||||
channelcount: u16,
|
pub channelcount: u16,
|
||||||
samplesize: u16,
|
pub samplesize: u16,
|
||||||
samplerate: u32,
|
pub samplerate: u32,
|
||||||
codec_specific: AudioCodecSpecific,
|
pub codec_specific: AudioCodecSpecific,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum VideoCodecSpecific {
|
pub enum VideoCodecSpecific {
|
||||||
AVCConfig(Vec<u8>),
|
AVCConfig(Vec<u8>),
|
||||||
VPxConfig(VPxConfigBox),
|
VPxConfig(VPxConfigBox),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct VideoSampleEntry {
|
pub struct VideoSampleEntry {
|
||||||
data_reference_index: u16,
|
data_reference_index: u16,
|
||||||
width: u16,
|
pub width: u16,
|
||||||
height: u16,
|
pub height: u16,
|
||||||
codec_specific: VideoCodecSpecific,
|
pub codec_specific: VideoCodecSpecific,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9).
|
/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9).
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct VPxConfigBox {
|
pub struct VPxConfigBox {
|
||||||
profile: u8,
|
profile: u8,
|
||||||
level: u8,
|
level: u8,
|
||||||
bit_depth: u8,
|
pub bit_depth: u8,
|
||||||
color_space: u8, // Really an enum
|
pub color_space: u8, // Really an enum
|
||||||
chroma_subsampling: u8,
|
pub chroma_subsampling: u8,
|
||||||
transfer_function: u8,
|
transfer_function: u8,
|
||||||
video_full_range: bool,
|
video_full_range: bool,
|
||||||
codec_init: Vec<u8>, // Empty for vp8/vp9.
|
pub codec_init: Vec<u8>, // Empty for vp8/vp9.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -259,8 +255,8 @@ struct ChannelMappingTable {
|
||||||
|
|
||||||
/// Represent an OpusSpecificBox 'dOps'
|
/// Represent an OpusSpecificBox 'dOps'
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct OpusSpecificBox {
|
pub struct OpusSpecificBox {
|
||||||
version: u8,
|
pub version: u8,
|
||||||
output_channel_count: u8,
|
output_channel_count: u8,
|
||||||
pre_skip: u16,
|
pre_skip: u16,
|
||||||
input_sample_rate: u32,
|
input_sample_rate: u32,
|
||||||
|
@ -270,73 +266,80 @@ struct OpusSpecificBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal data structures.
|
/// Internal data structures.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MediaContext {
|
pub struct MediaContext {
|
||||||
timescale: Option<MediaTimeScale>,
|
pub timescale: Option<MediaTimeScale>,
|
||||||
|
pub has_mvex: bool,
|
||||||
/// Tracks found in the file.
|
/// Tracks found in the file.
|
||||||
tracks: Vec<Track>,
|
pub tracks: Vec<Track>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaContext {
|
impl MediaContext {
|
||||||
pub fn new() -> MediaContext {
|
pub fn new() -> MediaContext {
|
||||||
MediaContext {
|
Default::default()
|
||||||
timescale: None,
|
|
||||||
tracks: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum TrackType {
|
pub enum TrackType {
|
||||||
Audio,
|
Audio,
|
||||||
Video,
|
Video,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TrackType {
|
||||||
|
fn default() -> Self { TrackType::Unknown }
|
||||||
|
}
|
||||||
|
|
||||||
/// The media's global (mvhd) timescale.
|
/// The media's global (mvhd) timescale.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
struct MediaTimeScale(u64);
|
pub struct MediaTimeScale(pub u64);
|
||||||
|
|
||||||
/// A time scaled by the media's global (mvhd) timescale.
|
/// A time scaled by the media's global (mvhd) timescale.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
struct MediaScaledTime(u64);
|
pub struct MediaScaledTime(pub u64);
|
||||||
|
|
||||||
/// The track's local (mdhd) timescale.
|
/// The track's local (mdhd) timescale.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
struct TrackTimeScale(u64, usize);
|
pub struct TrackTimeScale(pub u64, pub usize);
|
||||||
|
|
||||||
/// A time scaled by the track's local (mdhd) timescale.
|
/// A time scaled by the track's local (mdhd) timescale.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
struct TrackScaledTime(u64, usize);
|
pub struct TrackScaledTime(pub u64, pub usize);
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// A fragmented file contains no sample data in stts, stsc, and stco.
|
||||||
struct Track {
|
#[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,
|
id: usize,
|
||||||
track_type: TrackType,
|
pub track_type: TrackType,
|
||||||
empty_duration: Option<MediaScaledTime>,
|
pub empty_duration: Option<MediaScaledTime>,
|
||||||
media_time: Option<TrackScaledTime>,
|
pub media_time: Option<TrackScaledTime>,
|
||||||
timescale: Option<TrackTimeScale>,
|
pub timescale: Option<TrackTimeScale>,
|
||||||
duration: Option<TrackScaledTime>,
|
pub duration: Option<TrackScaledTime>,
|
||||||
track_id: Option<u32>,
|
pub track_id: Option<u32>,
|
||||||
mime_type: String,
|
pub mime_type: String,
|
||||||
data: Option<SampleEntry>,
|
pub empty_sample_boxes: EmptySampleTableBoxes,
|
||||||
tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this.
|
pub data: Option<SampleEntry>,
|
||||||
|
pub tkhd: Option<TrackHeaderBox>, // TODO(kinetik): find a nicer way to export this.
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Track {
|
impl Track {
|
||||||
fn new(id: usize) -> Track {
|
fn new(id: usize) -> Track {
|
||||||
Track {
|
Track { id: id, ..Default::default() }
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,7 +457,7 @@ macro_rules! check_parser_state {
|
||||||
|
|
||||||
/// Read the contents of a box, including sub boxes.
|
/// 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.
|
/// which can be examined later.
|
||||||
pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
|
pub fn read_mp4<T: Read>(f: &mut T, context: &mut MediaContext) -> Result<()> {
|
||||||
let mut found_ftyp = false;
|
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));
|
try!(read_trak(&mut b, &mut track));
|
||||||
context.tracks.push(track);
|
context.tracks.push(track);
|
||||||
}
|
}
|
||||||
|
BoxType::MovieExtendsBox => {
|
||||||
|
context.has_mvex = true;
|
||||||
|
try!(skip_box_content(&mut b));
|
||||||
|
}
|
||||||
_ => try!(skip_box_content(&mut b)),
|
_ => try!(skip_box_content(&mut b)),
|
||||||
};
|
};
|
||||||
check_parser_state!(b.content);
|
check_parser_state!(b.content);
|
||||||
|
@ -654,10 +661,12 @@ fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
|
||||||
}
|
}
|
||||||
BoxType::TimeToSampleBox => {
|
BoxType::TimeToSampleBox => {
|
||||||
let stts = try!(read_stts(&mut b));
|
let stts = try!(read_stts(&mut b));
|
||||||
|
track.empty_sample_boxes.empty_stts = stts.samples.is_empty();
|
||||||
log!("{:?}", stts);
|
log!("{:?}", stts);
|
||||||
}
|
}
|
||||||
BoxType::SampleToChunkBox => {
|
BoxType::SampleToChunkBox => {
|
||||||
let stsc = try!(read_stsc(&mut b));
|
let stsc = try!(read_stsc(&mut b));
|
||||||
|
track.empty_sample_boxes.empty_stsc = stsc.samples.is_empty();
|
||||||
log!("{:?}", stsc);
|
log!("{:?}", stsc);
|
||||||
}
|
}
|
||||||
BoxType::SampleSizeBox => {
|
BoxType::SampleSizeBox => {
|
||||||
|
@ -666,6 +675,7 @@ fn read_stbl<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
|
||||||
}
|
}
|
||||||
BoxType::ChunkOffsetBox => {
|
BoxType::ChunkOffsetBox => {
|
||||||
let stco = try!(read_stco(&mut b));
|
let stco = try!(read_stco(&mut b));
|
||||||
|
track.empty_sample_boxes.empty_stco = stco.offsets.is_empty();
|
||||||
log!("{:?}", stco);
|
log!("{:?}", stco);
|
||||||
}
|
}
|
||||||
BoxType::ChunkLargeOffsetBox => {
|
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> {
|
fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
|
||||||
let version = try!(src.read_u8());
|
let version = try!(src.read_u8());
|
||||||
if version != 0 {
|
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
|
/// 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
|
/// tag and byte-swap the data from big- to little-endian relative to the
|
||||||
/// dOps box.
|
/// 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") {
|
match dst.write(b"OpusHead") {
|
||||||
Err(e) => return Err(Error::from(e)),
|
Err(e) => return Err(Error::from(e)),
|
||||||
Ok(bytes) => {
|
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);
|
check_parser_state!(b.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
if codec_specific.is_none() {
|
codec_specific
|
||||||
return Err(Error::InvalidData("malformed video sample entry"));
|
.map(|codec_specific| SampleEntry::Video(VideoSampleEntry {
|
||||||
}
|
data_reference_index: data_reference_index,
|
||||||
|
width: width,
|
||||||
Ok(SampleEntry::Video(VideoSampleEntry {
|
height: height,
|
||||||
data_reference_index: data_reference_index,
|
codec_specific: codec_specific,
|
||||||
width: width,
|
}))
|
||||||
height: height,
|
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
|
||||||
codec_specific: codec_specific.unwrap(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an audio description inside an stsd box.
|
/// 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);
|
check_parser_state!(b.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
if codec_specific.is_none() {
|
codec_specific
|
||||||
return Err(Error::InvalidData("malformed audio sample entry"));
|
.map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
|
||||||
}
|
data_reference_index: data_reference_index,
|
||||||
|
channelcount: channelcount,
|
||||||
Ok(SampleEntry::Audio(AudioSampleEntry {
|
samplesize: samplesize,
|
||||||
data_reference_index: data_reference_index,
|
samplerate: samplerate,
|
||||||
channelcount: channelcount,
|
codec_specific: codec_specific,
|
||||||
samplesize: samplesize,
|
}))
|
||||||
samplerate: samplerate,
|
.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
|
||||||
codec_specific: codec_specific.unwrap(),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a stsd box.
|
/// 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)
|
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> {
|
fn be_i16<T: ReadBytesExt>(src: &mut T) -> Result<i16> {
|
||||||
src.read_i16::<byteorder::BigEndian>().map_err(From::from)
|
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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use super::*;
|
use super::read_mp4;
|
||||||
|
use super::MediaContext;
|
||||||
|
use super::Error;
|
||||||
extern crate test_assembler;
|
extern crate test_assembler;
|
||||||
use self::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() {
|
fn main() {
|
||||||
// Generate mp4parse.h.
|
// Generate mp4parse.h.
|
||||||
cheddar::Cheddar::new().expect("could not read manifest")
|
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 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("// 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")
|
.insert_code("// License, v. 2.0. If a copy of the MPL was not distributed with this\n")
|
|
@ -5,7 +5,7 @@
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! extern crate mp4parse;
|
//! extern crate mp4parse_capi;
|
||||||
//! use std::io::Read;
|
//! use std::io::Read;
|
||||||
//!
|
//!
|
||||||
//! extern fn buf_read(buf: *mut u8, size: usize, userdata: *mut std::os::raw::c_void) -> isize {
|
//! 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 mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
|
||||||
//! let io = mp4parse::mp4parse_io { read: buf_read,
|
//! let io = mp4parse_capi::mp4parse_io {
|
||||||
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
|
//! read: buf_read,
|
||||||
|
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void
|
||||||
|
//! };
|
||||||
//! unsafe {
|
//! unsafe {
|
||||||
//! let parser = mp4parse::mp4parse_new(&io);
|
//! let parser = mp4parse_capi::mp4parse_new(&io);
|
||||||
//! let rv = mp4parse::mp4parse_read(parser);
|
//! let rv = mp4parse_capi::mp4parse_read(parser);
|
||||||
//! assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
|
//! assert_eq!(rv, mp4parse_capi::mp4parse_error::MP4PARSE_OK);
|
||||||
//! mp4parse::mp4parse_free(parser);
|
//! mp4parse_capi::mp4parse_free(parser);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
@ -32,21 +34,24 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use std;
|
extern crate mp4parse;
|
||||||
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Symbols we need from our rust api.
|
// Symbols we need from our rust api.
|
||||||
use MediaContext;
|
use mp4parse::MediaContext;
|
||||||
use TrackType;
|
use mp4parse::TrackType;
|
||||||
use read_mp4;
|
use mp4parse::read_mp4;
|
||||||
use Error;
|
use mp4parse::Error;
|
||||||
use media_time_to_ms;
|
use mp4parse::SampleEntry;
|
||||||
use track_time_to_ms;
|
use mp4parse::AudioCodecSpecific;
|
||||||
use SampleEntry;
|
use mp4parse::VideoCodecSpecific;
|
||||||
use AudioCodecSpecific;
|
use mp4parse::MediaTimeScale;
|
||||||
use VideoCodecSpecific;
|
use mp4parse::MediaScaledTime;
|
||||||
use serialize_opus_header;
|
use mp4parse::TrackTimeScale;
|
||||||
|
use mp4parse::TrackScaledTime;
|
||||||
|
use mp4parse::serialize_opus_header;
|
||||||
|
|
||||||
// rusty-cheddar's C enum generation doesn't namespace enum members by
|
// 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
|
// 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();
|
let context = (*parser).context();
|
||||||
|
|
||||||
// Make sure the track count fits in a u32.
|
// 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;
|
return MP4PARSE_ERROR_INVALID;
|
||||||
}
|
}
|
||||||
*count = context.tracks.len() as u32;
|
*count = context.tracks.len() as u32;
|
||||||
MP4PARSE_OK
|
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`.
|
/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
|
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,
|
_ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maybe context & track should just have a single simple is_valid() instead?
|
let track = &context.tracks[track_index];
|
||||||
if context.timescale.is_none() ||
|
|
||||||
context.tracks[track_index].timescale.is_none() ||
|
if let (Some(track_timescale),
|
||||||
context.tracks[track_index].duration.is_none() ||
|
Some(context_timescale),
|
||||||
context.tracks[track_index].track_id.is_none() {
|
Some(track_duration)) = (track.timescale,
|
||||||
return MP4PARSE_ERROR_INVALID;
|
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.track_id = match track.track_id {
|
||||||
info.media_time = track.media_time.map_or(0, |media_time| {
|
Some(track_id) => track_id,
|
||||||
track_time_to_ms(media_time, track.timescale.unwrap()) as i64
|
None => return MP4PARSE_ERROR_INVALID,
|
||||||
}) - 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();
|
|
||||||
|
|
||||||
MP4PARSE_OK
|
MP4PARSE_OK
|
||||||
}
|
}
|
||||||
|
@ -438,6 +460,32 @@ pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser,
|
||||||
MP4PARSE_OK
|
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)]
|
#[cfg(test)]
|
||||||
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
|
extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
|
||||||
panic!("panic_read shouldn't be called in these tests");
|
panic!("panic_read shouldn't be called in these tests");
|
||||||
|
@ -614,7 +662,7 @@ fn get_track_count_poisoned_parser() {
|
||||||
#[test]
|
#[test]
|
||||||
fn arg_validation_with_data() {
|
fn arg_validation_with_data() {
|
||||||
unsafe {
|
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,
|
let io = mp4parse_io { read: valid_read,
|
||||||
userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
|
userdata: &mut file as *mut _ as *mut std::os::raw::c_void };
|
||||||
let parser = mp4parse_new(&io);
|
let parser = mp4parse_new(&io);
|
|
@ -2,7 +2,7 @@
|
||||||
name = "gkrust"
|
name = "gkrust"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mp4parse 0.4.0",
|
"mp4parse_capi 0.5.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -11,8 +11,15 @@ version = "0.5.3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mp4parse"
|
name = "mp4parse"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 0.5.3",
|
"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"
|
description = "Rust code for libxul"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mp4parse = { path = "../../../media/libstagefright/binding/mp4parse" }
|
mp4parse_capi = { path = "../../../media/libstagefright/binding/mp4parse_capi" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// 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/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
extern crate mp4parse;
|
extern crate mp4parse_capi;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче