Bug 1306164 - Update rust parser for fragmented file. r=rillian
MozReview-Commit-ID: BzRxyH9tYZL
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -60,16 +60,20 @@ typedef struct 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;
} mp4parse_track_video_info;
+typedef struct mp4parse_fragment_info {
+ uint64_t fragment_duration;
+} mp4parse_fragment_info;
+
typedef struct mp4parse_parser mp4parse_parser;
typedef struct mp4parse_io {
intptr_t (*read)(uint8_t* buffer, uintptr_t size, void* userdata);
void* userdata;
} mp4parse_io;
/// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
@@ -88,16 +92,18 @@ mp4parse_error mp4parse_get_track_count(
mp4parse_error mp4parse_get_track_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_info* info);
/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
mp4parse_error mp4parse_get_track_audio_info(mp4parse_parser* parser, uint32_t track_index, mp4parse_track_audio_info* info);
/// 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_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info);
+
mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented);
#ifdef __cplusplus
}
#endif
--- a/media/libstagefright/binding/mp4parse/Cargo.toml
+++ b/media/libstagefright/binding/mp4parse/Cargo.toml
@@ -13,16 +13,16 @@ license = "MPL-2.0"
repository = "https://github.com/mozilla/mp4parse-rust"
# Avoid complaints about trying to package test files.
exclude = [
"*.mp4",
]
[dependencies]
-byteorder = "0.5.0"
+byteorder = { version = "0.5.0" }
[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/boxes.rs
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -50,9 +50,10 @@ box_database!(
VP8SampleEntry 0x76703038, // "vp08"
VP9SampleEntry 0x76703039, // "vp09"
VPCodecConfigurationBox 0x76706343, // "vpcC"
OpusSampleEntry 0x4f707573, // "Opus"
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"
+ MovieExtendsHeaderBox 0x6d656864, // "mehd"
);
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -260,23 +260,28 @@ pub struct OpusSpecificBox {
output_channel_count: u8,
pre_skip: u16,
input_sample_rate: u32,
output_gain: i16,
channel_mapping_family: u8,
channel_mapping_table: Option<ChannelMappingTable>,
}
+#[derive(Debug)]
+pub struct MovieExtendsBox {
+ pub fragment_duration: Option<MediaScaledTime>,
+}
+
/// Internal data structures.
#[derive(Debug, Default)]
pub struct MediaContext {
pub timescale: Option<MediaTimeScale>,
- pub has_mvex: bool,
/// Tracks found in the file.
pub tracks: Vec<Track>,
+ pub mvex: Option<MovieExtendsBox>,
}
impl MediaContext {
pub fn new() -> MediaContext {
Default::default()
}
}
@@ -286,16 +291,32 @@ pub enum TrackType {
Video,
Unknown,
}
impl Default for TrackType {
fn default() -> Self { TrackType::Unknown }
}
+#[derive(Debug)]
+pub enum CodecType {
+ Unknown,
+ AAC,
+ Opus,
+ H264,
+ VP9,
+ VP8,
+ EncryptedVideo,
+ EncryptedAudio,
+}
+
+impl Default for CodecType {
+ fn default() -> Self { CodecType::Unknown }
+}
+
/// The media's global (mvhd) timescale in units per second.
#[derive(Debug, Copy, Clone, PartialEq)]
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);
@@ -328,17 +349,17 @@ impl EmptySampleTableBoxes {
pub struct Track {
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 track_id: Option<u32>,
- pub mime_type: String,
+ 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.
}
impl Track {
fn new(id: usize) -> Track {
Track { id: id, ..Default::default() }
@@ -534,26 +555,54 @@ fn read_moov<T: Read>(f: &mut BMFFBox<T>
log!("{:?}", mvhd);
}
BoxType::TrackBox => {
let mut track = Track::new(context.tracks.len());
try!(read_trak(&mut b, &mut track));
context.tracks.push(track);
}
BoxType::MovieExtendsBox => {
- context.has_mvex = true;
- try!(skip_box_content(&mut b));
+ let mvex = try!(read_mvex(&mut b));
+ log!("{:?}", mvex);
+ context.mvex = Some(mvex);
}
_ => try!(skip_box_content(&mut b)),
};
check_parser_state!(b.content);
}
Ok(())
}
+fn read_mvex<T: Read>(src: &mut BMFFBox<T>) -> Result<MovieExtendsBox> {
+ let mut iter = src.box_iter();
+ let mut fragment_duration = None;
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::MovieExtendsHeaderBox => {
+ let duration = try!(read_mehd(&mut b));
+ fragment_duration = Some(duration);
+ },
+ _ => try!(skip_box_content(&mut b)),
+ }
+ }
+ Ok(MovieExtendsBox {
+ fragment_duration: fragment_duration,
+ })
+}
+
+fn read_mehd<T: Read>(src: &mut BMFFBox<T>) -> Result<MediaScaledTime> {
+ let (version, _) = try!(read_fullbox_extra(src));
+ let fragment_duration = match version {
+ 1 => try!(be_u64(src)),
+ 0 => try!(be_u32(src)) as u64,
+ _ => return Err(Error::InvalidData("unhandled mehd version")),
+ };
+ Ok(MediaScaledTime(fragment_duration))
+}
+
fn read_trak<T: Read>(f: &mut BMFFBox<T>, track: &mut Track) -> Result<()> {
let mut iter = f.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::TrackHeaderBox => {
let tkhd = try!(read_tkhd(&mut b));
track.track_id = Some(tkhd.track_id);
track.tkhd = Some(tkhd.clone());
@@ -1097,22 +1146,22 @@ fn read_hdlr<T: Read>(src: &mut BMFFBox<
Ok(HandlerBox {
handler_type: handler_type,
})
}
/// Parse an video description inside an stsd box.
fn read_video_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
- track.mime_type = match name {
- BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => String::from("video/avc"),
- BoxType::VP8SampleEntry => String::from("video/vp8"),
- BoxType::VP9SampleEntry => String::from("video/vp9"),
- BoxType::ProtectedVisualSampleEntry => String::from("video/crypto"),
- _ => return Err(Error::Unsupported("unhandled video sample entry type")),
+ track.codec_type = match name {
+ BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264,
+ BoxType::VP8SampleEntry => CodecType::VP8,
+ BoxType::VP9SampleEntry => CodecType::VP9,
+ BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
+ _ => CodecType::Unknown,
};
// Skip uninteresting fields.
try!(skip(src, 6));
let data_reference_index = try!(be_u16(src));
// Skip uninteresting fields.
@@ -1171,23 +1220,22 @@ fn read_video_desc<T: Read>(src: &mut BM
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
}
/// Parse an audio description inside an stsd box.
fn read_audio_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
- track.mime_type = match name {
+ track.codec_type = match name {
// TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg).
- BoxType::MP4AudioSampleEntry => String::from("audio/mp4a-latm"),
- // TODO(kinetik): stagefright doesn't have a MIME mapping for this, revisit.
- BoxType::OpusSampleEntry => String::from("audio/opus"),
- BoxType::ProtectedAudioSampleEntry => String::from("audio/crypto"),
- _ => return Err(Error::Unsupported("unhandled audio sample entry type")),
+ BoxType::MP4AudioSampleEntry => CodecType::AAC,
+ BoxType::OpusSampleEntry => CodecType::Opus,
+ BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio,
+ _ => CodecType::Unknown,
};
// Skip uninteresting fields.
try!(skip(src, 6));
let data_reference_index = try!(be_u16(src));
// XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -132,16 +132,23 @@ pub struct mp4parse_track_video_info {
pub display_height: u32,
pub image_width: u16,
pub image_height: u16,
// TODO(kinetik):
// extra_data
// codec_specific_config
}
+#[repr(C)]
+pub struct mp4parse_fragment_info {
+ pub fragment_duration: u64,
+ // TODO:
+ // info in trex box.
+}
+
// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us
// use more than one member, so we introduce *another* wrapper.
struct Wrap {
context: MediaContext,
io: mp4parse_io,
poisoned: bool,
opus_header: HashMap<u32, Vec<u8>>,
}
@@ -487,28 +494,54 @@ pub unsafe extern fn mp4parse_get_track_
return MP4PARSE_ERROR_INVALID;
}
(*info).image_width = video.width;
(*info).image_height = video.height;
MP4PARSE_OK
}
+#[no_mangle]
+pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_info) -> mp4parse_error {
+ if parser.is_null() || info.is_null() || (*parser).poisoned() {
+ return MP4PARSE_ERROR_BADARG;
+ }
+
+ let context = (*parser).context();
+ let info: &mut mp4parse_fragment_info = &mut *info;
+
+ info.fragment_duration = 0;
+
+ let duration = match context.mvex {
+ Some(ref mvex) => mvex.fragment_duration,
+ None => return MP4PARSE_ERROR_INVALID,
+ };
+
+ if let (Some(time), Some(scale)) = (duration, context.timescale) {
+ info.fragment_duration = match media_time_to_us(time, scale) {
+ Some(time_us) => time_us as u64,
+ None => return MP4PARSE_ERROR_INVALID,
+ }
+ }
+
+ 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 {
+ if context.mvex.is_none() {
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(_) => {},