--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -28,19 +28,21 @@ typedef enum mp4parse_error {
typedef enum mp4parse_track_type {
MP4PARSE_TRACK_TYPE_VIDEO = 0,
MP4PARSE_TRACK_TYPE_AUDIO = 1,
} mp4parse_track_type;
typedef enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
+ MP4PARSE_CODEC_FLAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
+ MP4PARSE_CODEC_MP3,
} mp4parse_codec;
typedef struct mp4parse_track_info {
mp4parse_track_type track_type;
mp4parse_codec codec;
uint32_t track_id;
uint64_t duration;
int64_t media_time;
--- 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 = { version = "0.5.0" }
+byteorder = "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
@@ -45,15 +45,18 @@ box_database!(
AVCSampleEntry 0x61766331, // "avc1"
AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
AVCConfigurationBox 0x61766343, // "avcC"
MP4AudioSampleEntry 0x6d703461, // "mp4a"
ESDBox 0x65736473, // "esds"
VP8SampleEntry 0x76703038, // "vp08"
VP9SampleEntry 0x76703039, // "vp09"
VPCodecConfigurationBox 0x76706343, // "vpcC"
+ FLACSampleEntry 0x664c6143, // "fLaC"
+ FLACSpecificBox 0x64664c61, // "dfLa"
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"
+ QTWaveAtom 0x77617665, // "wave" - quicktime atom
);
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -6,16 +6,17 @@
#![cfg_attr(feature = "fuzz", feature(plugin))]
#![cfg_attr(feature = "fuzz", plugin(afl_plugin))]
#[cfg(feature = "fuzz")]
extern crate afl;
extern crate byteorder;
use byteorder::ReadBytesExt;
use std::io::{Read, Take};
+use std::io::Cursor;
use std::cmp;
mod boxes;
use boxes::BoxType;
// Unit tests.
#[cfg(test)]
mod tests;
@@ -200,18 +201,27 @@ struct SampleDescriptionBox {
pub enum SampleEntry {
Audio(AudioSampleEntry),
Video(VideoSampleEntry),
Unknown,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
+pub struct ES_Descriptor {
+ pub audio_codec: CodecType,
+ pub audio_sample_rate: Option<u32>,
+ pub codec_specific_config: Vec<u8>,
+}
+
+#[allow(non_camel_case_types)]
+#[derive(Debug, Clone)]
pub enum AudioCodecSpecific {
- ES_Descriptor(Vec<u8>),
+ ES_Descriptor(ES_Descriptor),
+ FLACSpecificBox(FLACSpecificBox),
OpusSpecificBox(OpusSpecificBox),
}
#[derive(Debug, Clone)]
pub struct AudioSampleEntry {
data_reference_index: u16,
pub channelcount: u16,
pub samplesize: u16,
@@ -242,16 +252,29 @@ pub struct VPxConfigBox {
pub color_space: u8, // Really an enum
pub chroma_subsampling: u8,
transfer_function: u8,
video_full_range: bool,
pub codec_init: Vec<u8>, // Empty for vp8/vp9.
}
#[derive(Debug, Clone)]
+pub struct FLACMetadataBlock {
+ pub block_type: u8,
+ pub data: Vec<u8>,
+}
+
+/// Represet a FLACSpecificBox 'dfLa'
+#[derive(Debug, Clone)]
+pub struct FLACSpecificBox {
+ version: u8,
+ pub blocks: Vec<FLACMetadataBlock>,
+}
+
+#[derive(Debug, Clone)]
struct ChannelMappingTable {
stream_count: u8,
coupled_count: u8,
channel_mapping: Vec<u8>,
}
/// Represent an OpusSpecificBox 'dOps'
#[derive(Debug, Clone)]
@@ -280,31 +303,33 @@ pub struct MediaContext {
}
impl MediaContext {
pub fn new() -> MediaContext {
Default::default()
}
}
-#[derive(Debug)]
+#[derive(Debug, PartialEq)]
pub enum TrackType {
Audio,
Video,
Unknown,
}
impl Default for TrackType {
fn default() -> Self { TrackType::Unknown }
}
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CodecType {
Unknown,
+ MP3,
AAC,
+ FLAC,
Opus,
H264,
VP9,
VP8,
EncryptedVideo,
EncryptedAudio,
}
@@ -464,16 +489,27 @@ fn skip_box_content<T: Read>(src: &mut B
let header = src.get_header();
log!("{:?} (skipped)", header);
(header.size - header.offset) as usize
};
assert!(to_skip == src.bytes_left());
skip(src, to_skip)
}
+/// Skip over the remain data of a box.
+fn skip_box_remain<T: Read>(src: &mut BMFFBox<T>) -> Result<()> {
+ let remain = {
+ let header = src.get_header();
+ let len = src.bytes_left();
+ log!("remain {} (skipped) in {:?}", len, header);
+ len
+ };
+ skip(src, remain)
+}
+
macro_rules! check_parser_state {
( $src:expr ) => {
if $src.limit() > 0 {
log!("bad parser state: {} content bytes left", $src.limit());
return Err(Error::InvalidData("unread box content or bad parser sync"));
}
}
}
@@ -916,44 +952,53 @@ fn read_mdhd<T: Read>(src: &mut BMFFBox<
fn read_stco<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
let (_, _) = try!(read_fullbox_extra(src));
let offset_count = try!(be_u32(src));
let mut offsets = Vec::new();
for _ in 0..offset_count {
offsets.push(try!(be_u32(src)) as u64);
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(ChunkOffsetBox {
offsets: offsets,
})
}
/// Parse a co64 box.
fn read_co64<T: Read>(src: &mut BMFFBox<T>) -> Result<ChunkOffsetBox> {
let (_, _) = try!(read_fullbox_extra(src));
let offset_count = try!(be_u32(src));
let mut offsets = Vec::new();
for _ in 0..offset_count {
offsets.push(try!(be_u64(src)));
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(ChunkOffsetBox {
offsets: offsets,
})
}
/// Parse a stss box.
fn read_stss<T: Read>(src: &mut BMFFBox<T>) -> Result<SyncSampleBox> {
let (_, _) = try!(read_fullbox_extra(src));
let sample_count = try!(be_u32(src));
let mut samples = Vec::new();
for _ in 0..sample_count {
samples.push(try!(be_u32(src)));
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(SyncSampleBox {
samples: samples,
})
}
/// Parse a stsc box.
fn read_stsc<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleToChunkBox> {
let (_, _) = try!(read_fullbox_extra(src));
@@ -965,16 +1010,19 @@ fn read_stsc<T: Read>(src: &mut BMFFBox<
let sample_description_index = try!(be_u32(src));
samples.push(SampleToChunk {
first_chunk: first_chunk,
samples_per_chunk: samples_per_chunk,
sample_description_index: sample_description_index,
});
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(SampleToChunkBox {
samples: samples,
})
}
/// Parse a stsz box.
fn read_stsz<T: Read>(src: &mut BMFFBox<T>) -> Result<SampleSizeBox> {
let (_, _) = try!(read_fullbox_extra(src));
@@ -982,16 +1030,19 @@ fn read_stsz<T: Read>(src: &mut BMFFBox<
let sample_count = try!(be_u32(src));
let mut sample_sizes = Vec::new();
if sample_size == 0 {
for _ in 0..sample_count {
sample_sizes.push(try!(be_u32(src)));
}
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(SampleSizeBox {
sample_size: sample_size,
sample_sizes: sample_sizes,
})
}
/// Parse a stts box.
fn read_stts<T: Read>(src: &mut BMFFBox<T>) -> Result<TimeToSampleBox> {
@@ -1002,16 +1053,19 @@ fn read_stts<T: Read>(src: &mut BMFFBox<
let sample_count = try!(be_u32(src));
let sample_delta = try!(be_u32(src));
samples.push(Sample {
sample_count: sample_count,
sample_delta: sample_delta,
});
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(TimeToSampleBox {
samples: samples,
})
}
/// Parse a VPx Config Box.
fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
let (version, _) = try!(read_fullbox_extra(src));
@@ -1041,21 +1095,187 @@ fn read_vpcc<T: Read>(src: &mut BMFFBox<
color_space: color_space,
chroma_subsampling: chroma_subsampling,
transfer_function: transfer_function,
video_full_range: video_full_range,
codec_init: codec_init,
})
}
+fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
+ let temp = try!(src.read_u8());
+ let block_type = temp & 0x7f;
+ let length = try!(be_u24(src));
+ if length as usize > src.bytes_left() {
+ return Err(Error::InvalidData(
+ "FLACMetadataBlock larger than parent box"));
+ }
+ let data = try!(read_buf(src, length as usize));
+ Ok(FLACMetadataBlock {
+ block_type: block_type,
+ data: data,
+ })
+}
+
+fn read_esds<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+ // Tags for elementary stream description
+ const ESDESCR_TAG: u8 = 0x03;
+ const DECODER_CONFIG_TAG: u8 = 0x04;
+ const DECODER_SPECIFIC_TAG: u8 = 0x05;
+
+ let frequency_table =
+ vec![(0x1, 96000), (0x1, 88200), (0x2, 64000), (0x3, 48000),
+ (0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050),
+ (0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000),
+ (0xc, 7350)];
+
+ let (_, _) = try!(read_fullbox_extra(src));
+
+ let esds_size = src.head.size - src.head.offset - 4;
+ if esds_size > BUF_SIZE_LIMIT {
+ return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
+ }
+ let esds_array = try!(read_buf(src, esds_size as usize));
+
+ // Parsing DecoderConfig descriptor to get the object_profile_indicator
+ // for correct codec type and audio sample rate.
+ let (object_profile_indicator, sample_frequency) = {
+ let mut object_profile: u8 = 0;
+ let mut sample_frequency = None;
+
+ // clone a esds cursor for parsing.
+ let esds = &mut Cursor::new(&esds_array);
+ let next_tag = try!(esds.read_u8());
+
+ if next_tag != ESDESCR_TAG {
+ return Err(Error::Unsupported("fail to parse ES descriptor"));
+ }
+
+ let esds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ let esds_end = if esds_extend >= 0x80 {
+ // skip remaining extension.
+ try!(skip(esds, 2));
+ esds.position() + try!(esds.read_u8()) as u64
+ } else {
+ esds.position() + esds_extend as u64
+ };
+ try!(skip(esds, 2));
+
+ let esds_flags = try!(esds.read_u8());
+
+ // Stream dependency flag, first bit from left most.
+ if esds_flags & 0x80 > 0 {
+ // Skip uninteresting fields.
+ try!(skip(esds, 2));
+ }
+
+ // Url flag, second bit from left most.
+ if esds_flags & 0x40 > 0 {
+ // Skip uninteresting fields.
+ let skip_es_len: usize = try!(esds.read_u8()) as usize + 2;
+ try!(skip(esds, skip_es_len));
+ }
+
+ // find DecoderConfig descriptor (tag = DECODER_CONFIG_TAG)
+ if esds_end > esds.position() {
+ let next_tag = try!(esds.read_u8());
+ if next_tag == DECODER_CONFIG_TAG {
+ let dcds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ if dcds_extend >= 0x80 {
+ // skip remains extension and length.
+ try!(skip(esds, 3));
+ }
+
+ object_profile = try!(esds.read_u8());
+
+ // Skip uninteresting fields.
+ try!(skip(esds, 12));
+ }
+ }
+
+
+ // find DecoderSpecific descriptor (tag = DECODER_SPECIFIC_TAG)
+ if esds_end > esds.position() {
+ let next_tag = try!(esds.read_u8());
+ if next_tag == DECODER_SPECIFIC_TAG {
+ let dsds_extend = try!(esds.read_u8());
+ // extension tag start from 0x80.
+ if dsds_extend >= 0x80 {
+ // skip remains extension and length.
+ try!(skip(esds, 3));
+ }
+
+ let audio_specific_config = try!(be_u16(esds));
+
+ let sample_index = (audio_specific_config & 0x07FF) >> 7;
+
+ sample_frequency =
+ frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
+ }
+ }
+
+ (object_profile, sample_frequency)
+ };
+
+ let codec = match object_profile_indicator {
+ 0x40 | 0x41 => CodecType::AAC,
+ 0x6B => CodecType::MP3,
+ _ => CodecType::Unknown,
+ };
+
+ if codec == CodecType::Unknown {
+ return Err(Error::Unsupported("unknown audio codec"));
+ }
+
+ Ok(ES_Descriptor {
+ audio_codec: codec,
+ audio_sample_rate: sample_frequency,
+ codec_specific_config: esds_array,
+ })
+}
+
+/// Parse `FLACSpecificBox`.
+fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> {
+ let (version, flags) = try!(read_fullbox_extra(src));
+ if version != 0 {
+ return Err(Error::Unsupported("unknown dfLa (FLAC) version"));
+ }
+ if flags != 0 {
+ return Err(Error::InvalidData("no-zero dfLa (FLAC) flags"));
+ }
+ let mut blocks = Vec::new();
+ while src.bytes_left() > 0 {
+ let block = try!(read_flac_metadata(src));
+ blocks.push(block);
+ }
+ // The box must have at least one meta block, and the first block
+ // must be the METADATA_BLOCK_STREAMINFO
+ if blocks.is_empty() {
+ return Err(Error::InvalidData("FLACSpecificBox missing metadata"));
+ } else if blocks[0].block_type != 0 {
+ println!("flac metadata block:\n {:?}", blocks[0]);
+ return Err(Error::InvalidData(
+ "FLACSpecificBox must have STREAMINFO metadata first"));
+ } else if blocks[0].data.len() != 34 {
+ return Err(Error::InvalidData(
+ "FLACSpecificBox STREAMINFO block is the wrong size"));
+ }
+ Ok(FLACSpecificBox {
+ version: version,
+ blocks: blocks,
+ })
+}
+
/// Parse `OpusSpecificBox`.
fn read_dops<T: Read>(src: &mut BMFFBox<T>) -> Result<OpusSpecificBox> {
let version = try!(src.read_u8());
if version != 0 {
- return Err(Error::Unsupported("unknown dOps version"));
+ return Err(Error::Unsupported("unknown dOps (Opus) version"));
}
let output_channel_count = try!(src.read_u8());
let pre_skip = try!(be_u16(src));
let input_sample_rate = try!(be_u32(src));
let output_gain = try!(be_i16(src));
let channel_mapping_family = try!(src.read_u8());
@@ -1144,17 +1364,17 @@ fn read_hdlr<T: Read>(src: &mut BMFFBox<
let _name = try!(read_null_terminated_string(src, bytes_left));
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> {
+fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
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,
};
@@ -1217,22 +1437,39 @@ fn read_video_desc<T: Read>(src: &mut BM
data_reference_index: data_reference_index,
width: width,
height: height,
codec_specific: codec_specific,
}))
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
}
+fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
+ let mut codec_specific = None;
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ match b.head.name {
+ BoxType::ESDBox => {
+ let esds = try!(read_esds(&mut b));
+ codec_specific = Some(esds);
+ },
+ _ => try!(skip_box_content(&mut b)),
+ }
+ }
+
+ codec_specific.ok_or_else(|| Error::InvalidData("malformed audio 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> {
+fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
track.codec_type = match name {
// TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg).
BoxType::MP4AudioSampleEntry => CodecType::AAC,
+ BoxType::FLACSampleEntry => CodecType::FLAC,
BoxType::OpusSampleEntry => CodecType::Opus,
BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio,
_ => CodecType::Unknown,
};
// Skip uninteresting fields.
try!(skip(src, 6));
@@ -1252,47 +1489,63 @@ fn read_audio_desc<T: Read>(src: &mut BM
// Skip uninteresting fields.
try!(skip(src, 4));
let samplerate = try!(be_u32(src));
match version {
0 => (),
+ 1 => {
+ // Quicktime sound sample description version 1.
+ // Skip uninteresting fields.
+ try!(skip(src, 16));
+ },
_ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
}
// Skip chan/etc. for now.
let mut codec_specific = None;
let mut iter = src.box_iter();
while let Some(mut b) = try!(iter.next_box()) {
match b.head.name {
BoxType::ESDBox => {
if (name != BoxType::MP4AudioSampleEntry &&
name != BoxType::ProtectedAudioSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed audio sample entry"));
- }
- let (_, _) = try!(read_fullbox_extra(&mut b.content));
- let esds_size = b.head.size - b.head.offset - 4;
- if esds_size > BUF_SIZE_LIMIT {
- return Err(Error::InvalidData("esds box exceeds BUF_SIZE_LIMIT"));
}
- let esds = try!(read_buf(&mut b.content, esds_size as usize));
- // TODO(kinetik): Parse esds box? For now we just stash the data.
+
+ let esds = try!(read_esds(&mut b));
+ track.codec_type = esds.audio_codec;
codec_specific = Some(AudioCodecSpecific::ES_Descriptor(esds));
}
+ BoxType::FLACSpecificBox => {
+ if name != BoxType::FLACSampleEntry ||
+ codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed audio sample entry"));
+ }
+ let dfla = try!(read_dfla(&mut b));
+ track.codec_type = CodecType::FLAC;
+ codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla));
+ }
BoxType::OpusSpecificBox => {
if name != BoxType::OpusSampleEntry ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed audio sample entry"));
}
let dops = try!(read_dops(&mut b));
+ track.codec_type = CodecType::Opus;
codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
}
+ BoxType::QTWaveAtom => {
+ let qt_esds = try!(read_qt_wave_atom(&mut b));
+ track.codec_type = qt_esds.audio_codec;
+ codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
+ }
_ => try!(skip_box_content(&mut b)),
}
check_parser_state!(b.content);
}
codec_specific
.map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
data_reference_index: data_reference_index,
@@ -1306,48 +1559,53 @@ fn read_audio_desc<T: Read>(src: &mut BM
/// Parse a stsd box.
fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
let (_, _) = try!(read_fullbox_extra(src));
let description_count = try!(be_u32(src));
let mut descriptions = Vec::new();
- // TODO(kinetik): check if/when more than one desc per track? do we need to support?
- let mut iter = src.box_iter();
- while let Some(mut b) = try!(iter.next_box()) {
- let description = match track.track_type {
- TrackType::Video => read_video_desc(&mut b, track),
- TrackType::Audio => read_audio_desc(&mut b, track),
- TrackType::Unknown => Err(Error::Unsupported("unknown track type")),
- };
- let description = match description {
- Ok(desc) => desc,
- Err(Error::Unsupported(_)) => {
- // read_{audio,video}_desc may have returned Unsupported
- // after partially reading the box content, so we can't
- // simply use skip_box_content here.
- let to_skip = b.bytes_left();
- try!(skip(&mut b, to_skip));
- SampleEntry::Unknown
+ {
+ // TODO(kinetik): check if/when more than one desc per track? do we need to support?
+ let mut iter = src.box_iter();
+ while let Some(mut b) = try!(iter.next_box()) {
+ let description = match track.track_type {
+ TrackType::Video => read_video_sample_entry(&mut b, track),
+ TrackType::Audio => read_audio_sample_entry(&mut b, track),
+ TrackType::Unknown => Err(Error::Unsupported("unknown track type")),
+ };
+ let description = match description {
+ Ok(desc) => desc,
+ Err(Error::Unsupported(_)) => {
+ // read_{audio,video}_desc may have returned Unsupported
+ // after partially reading the box content, so we can't
+ // simply use skip_box_content here.
+ let to_skip = b.bytes_left();
+ try!(skip(&mut b, to_skip));
+ SampleEntry::Unknown
+ }
+ Err(e) => return Err(e),
+ };
+ if track.data.is_none() {
+ track.data = Some(description.clone());
+ } else {
+ log!("** don't know how to handle multiple descriptions **");
}
- Err(e) => return Err(e),
- };
- if track.data.is_none() {
- track.data = Some(description.clone());
- } else {
- log!("** don't know how to handle multiple descriptions **");
- }
- descriptions.push(description);
- check_parser_state!(b.content);
- if descriptions.len() == description_count as usize {
- break;
+ descriptions.push(description);
+ check_parser_state!(b.content);
+ if descriptions.len() == description_count as usize {
+ break;
+ }
}
}
+ // Padding could be added in some contents.
+ try!(skip_box_remain(src));
+
Ok(SampleDescriptionBox {
descriptions: descriptions,
})
}
/// Skip a number of bytes that we don't care to parse.
fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
const BUF_SIZE: usize = 64 * 1024;
@@ -1419,15 +1677,21 @@ fn be_i32<T: ReadBytesExt>(src: &mut T)
fn be_i64<T: ReadBytesExt>(src: &mut T) -> Result<i64> {
src.read_i64::<byteorder::BigEndian>().map_err(From::from)
}
fn be_u16<T: ReadBytesExt>(src: &mut T) -> Result<u16> {
src.read_u16::<byteorder::BigEndian>().map_err(From::from)
}
+fn be_u24<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
+ src.read_uint::<byteorder::BigEndian>(3)
+ .map(|v| v as u32)
+ .map_err(From::from)
+}
+
fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
src.read_u32::<byteorder::BigEndian>().map_err(From::from)
}
fn be_u64<T: ReadBytesExt>(src: &mut T) -> Result<u64> {
src.read_u64::<byteorder::BigEndian>().map_err(From::from)
}
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -437,16 +437,112 @@ fn read_hdlr_zero_length_name() {
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::HandlerBox);
assert_eq!(stream.head.size, 32);
let parsed = super::read_hdlr(&mut stream).unwrap();
assert_eq!(parsed.handler_type, 0x76696465); // vide
}
+fn flac_streaminfo() -> Vec<u8> {
+ vec![
+ 0x10, 0x00, 0x10, 0x00, 0x00, 0x0a, 0x11, 0x00,
+ 0x38, 0x32, 0x0a, 0xc4, 0x42, 0xf0, 0x00, 0xc9,
+ 0xdf, 0xae, 0xb5, 0x66, 0xfc, 0x02, 0x15, 0xa3,
+ 0xb1, 0x54, 0x61, 0x47, 0x0f, 0xfb, 0x05, 0x00,
+ 0x33, 0xad,
+ ]
+}
+
+#[test]
+fn read_flac() {
+ let mut stream = make_box(BoxSize::Auto, b"fLaC", |s| {
+ s.append_repeated(0, 6) // reserved
+ .B16(1) // data reference index
+ .B32(0) // reserved
+ .B32(0) // reserved
+ .B16(2) // channel count
+ .B16(16) // bits per sample
+ .B16(0) // pre_defined
+ .B16(0) // reserved
+ .B32(44100 << 16) // Sample rate
+ .append_bytes(&make_dfla(FlacBlockType::StreamInfo, true,
+ &flac_streaminfo(), FlacBlockLength::Correct)
+ .into_inner())
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ let r = super::read_audio_sample_entry(&mut stream, &mut track);
+ r.unwrap();
+}
+
+#[derive(Clone, Copy)]
+enum FlacBlockType {
+ StreamInfo = 0,
+ _Padding = 1,
+ _Application = 2,
+ _Seektable = 3,
+ _Comment = 4,
+ _Cuesheet = 5,
+ _Picture = 6,
+ _Reserved,
+ _Invalid = 127,
+}
+
+enum FlacBlockLength {
+ Correct,
+ Incorrect(usize),
+}
+
+fn make_dfla(block_type: FlacBlockType, last: bool, data: &Vec<u8>,
+ data_length: FlacBlockLength) -> Cursor<Vec<u8>> {
+ assert!(data.len() < 1<<24);
+ make_fullbox(BoxSize::Auto, b"dfLa", 0, |s| {
+ let flag = match last {
+ true => 1,
+ false => 0,
+ };
+ let size = match data_length {
+ FlacBlockLength::Correct => (data.len() as u32) & 0xffffff,
+ FlacBlockLength::Incorrect(size) => {
+ assert!(size < 1<<24);
+ (size as u32) & 0xffffff
+ }
+ };
+ let block_type = (block_type as u32) & 0x7f;
+ s.B32(flag << 31 | block_type << 24 | size)
+ .append_bytes(data)
+ })
+}
+
+#[test]
+fn read_dfla() {
+ let mut stream = make_dfla(FlacBlockType::StreamInfo, true,
+ &flac_streaminfo(), FlacBlockLength::Correct);
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FLACSpecificBox);
+ let dfla = super::read_dfla(&mut stream).unwrap();
+ assert_eq!(dfla.version, 0);
+}
+
+#[test]
+fn long_flac_metadata() {
+ let streaminfo = flac_streaminfo();
+ let mut stream = make_dfla(FlacBlockType::StreamInfo, true,
+ &streaminfo,
+ FlacBlockLength::Incorrect(streaminfo.len() + 4));
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::FLACSpecificBox);
+ let r = super::read_dfla(&mut stream);
+ assert!(r.is_err());
+}
+
#[test]
fn read_opus() {
let mut stream = make_box(BoxSize::Auto, b"Opus", |s| {
s.append_repeated(0, 6)
.B16(1) // data reference index
.B32(0)
.B32(0)
.B16(2) // channel count
@@ -454,17 +550,17 @@ fn read_opus() {
.B16(0)
.B16(0)
.B32(48000 << 16) // Sample rate is always 48 kHz for Opus.
.append_bytes(&make_dops().into_inner())
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
- let r = super::read_audio_desc(&mut stream, &mut track);
+ let r = super::read_audio_sample_entry(&mut stream, &mut track);
assert!(r.is_ok());
}
fn make_dops() -> Cursor<Vec<u8>> {
make_box(BoxSize::Auto, b"dOps", |s| {
s.B8(0) // version
.B8(2) // channel count
.B16(348) // pre-skip
@@ -542,17 +638,17 @@ fn avcc_limit() {
.append_repeated(0, 4)
.B32(0xffffffff)
.append_bytes(b"avcC")
.append_repeated(0, 100)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
- match super::read_video_desc(&mut stream, &mut track) {
+ match super::read_video_sample_entry(&mut stream, &mut track) {
Err(Error::InvalidData(s)) => assert_eq!(s, "avcC box exceeds BUF_SIZE_LIMIT"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn esds_limit() {
@@ -568,17 +664,17 @@ fn esds_limit() {
.B32(48000 << 16)
.B32(0xffffffff)
.append_bytes(b"esds")
.append_repeated(0, 100)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
- match super::read_audio_desc(&mut stream, &mut track) {
+ match super::read_audio_sample_entry(&mut stream, &mut track) {
Err(Error::InvalidData(s)) => assert_eq!(s, "esds box exceeds BUF_SIZE_LIMIT"),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn esds_limit_2() {
@@ -594,17 +690,17 @@ fn esds_limit_2() {
.B32(48000 << 16)
.B32(8)
.append_bytes(b"esds")
.append_repeated(0, 4)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
let mut track = super::Track::new(0);
- match super::read_audio_desc(&mut stream, &mut track) {
+ match super::read_audio_sample_entry(&mut stream, &mut track) {
Err(Error::UnexpectedEOF) => (),
Ok(_) => assert!(false, "expected an error result"),
_ => assert!(false, "expected a different error result"),
}
}
#[test]
fn read_elst_zero_entries() {
@@ -656,8 +752,109 @@ fn invalid_pascal_string() {
// the 1 byte length prefix).
let pstr = "\x20xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
let mut stream = Cursor::new(pstr);
// Reader wants to limit the total read length to 32 bytes, so any
// returned string must be no longer than 31 bytes.
let s = super::read_fixed_length_pascal_string(&mut stream, 32).unwrap();
assert_eq!(s.len(), 31);
}
+
+#[test]
+fn skip_padding_in_boxes() {
+ // Padding data could be added in the end of these boxes. Parser needs to skip
+ // them instead of returning error.
+ let box_names = vec![b"stts", b"stsc", b"stsz", b"stco", b"co64", b"stss"];
+
+ for name in box_names {
+ let mut stream = make_fullbox(BoxSize::Auto, name, 1, |s| {
+ s.append_repeated(0, 100) // add padding data
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ match name {
+ b"stts" => {
+ super::read_stts(&mut stream).expect("fail to skip padding: stts");
+ },
+ b"stsc" => {
+ super::read_stsc(&mut stream).expect("fail to skip padding: stsc");
+ },
+ b"stsz" => {
+ super::read_stsz(&mut stream).expect("fail to skip padding: stsz");
+ },
+ b"stco" => {
+ super::read_stco(&mut stream).expect("fail to skip padding: stco");
+ },
+ b"co64" => {
+ super::read_co64(&mut stream).expect("fail to skip padding: co64");
+ },
+ b"stss" => {
+ super::read_stss(&mut stream).expect("fail to skip padding: stss");
+ },
+ _ => (),
+ }
+ }
+}
+
+#[test]
+fn skip_padding_in_stsd() {
+ // Padding data could be added in the end of stsd boxes. Parser needs to skip
+ // them instead of returning error.
+ let avc = make_box(BoxSize::Auto, b"avc1", |s| {
+ s.append_repeated(0, 6)
+ .B16(1)
+ .append_repeated(0, 16)
+ .B16(320)
+ .B16(240)
+ .append_repeated(0, 14)
+ .append_repeated(0, 32)
+ .append_repeated(0, 4)
+ .B32(0xffffffff)
+ .append_bytes(b"avcC")
+ .append_repeated(0, 100)
+ }).into_inner();
+ let mut stream = make_fullbox(BoxSize::Auto, b"stsd", 0, |s| {
+ s.B32(1)
+ .append_bytes(avc.as_slice())
+ .append_repeated(0, 100) // add padding data
+ });
+
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ super::read_stsd(&mut stream, &mut super::Track::new(0))
+ .expect("fail to skip padding: stsd");
+}
+
+#[test]
+fn read_qt_wave_atom() {
+ let esds = make_fullbox(BoxSize::Auto, b"esds", 0, |s| {
+ s.B8(0x03) // elementary stream descriptor tag
+ .B8(0x0b) // esds length
+ .append_repeated(0, 2)
+ .B8(0x00) // flags
+ .B8(0x04) // decoder config descriptor tag
+ .B8(0x0d) // dcds length
+ .B8(0x6b) // mp3
+ .append_repeated(0, 12)
+ }).into_inner();
+ let wave = make_box(BoxSize::Auto, b"wave", |s| {
+ s.append_bytes(esds.as_slice())
+ }).into_inner();
+ let mut stream = make_box(BoxSize::Auto, b"mp4a", |s| {
+ s.append_repeated(0, 6)
+ .B16(1) // data_reference_count
+ .B16(1) // verion: qt -> 1
+ .append_repeated(0, 6)
+ .B16(2)
+ .B16(16)
+ .append_repeated(0, 4)
+ .B32(48000 << 16)
+ .append_repeated(0, 16)
+ .append_bytes(wave.as_slice())
+ });
+
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ let mut track = super::Track::new(0);
+ super::read_audio_sample_entry(&mut stream, &mut track)
+ .expect("fail to read qt wave atom");
+ assert_eq!(track.codec_type, super::CodecType::MP3);
+}
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -65,20 +65,28 @@ fn public_api() {
let tkhd = track.tkhd.unwrap();
assert_eq!(tkhd.disabled, false);
assert_eq!(tkhd.duration, 62);
assert_eq!(tkhd.width, 0);
assert_eq!(tkhd.height, 0);
// track.data part
assert_eq!(match a.codec_specific {
- mp4::AudioCodecSpecific::ES_Descriptor(v) => {
- assert!(v.len() > 0);
+ mp4::AudioCodecSpecific::ES_Descriptor(esds) => {
+ assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
+ assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
"ES"
}
+ mp4::AudioCodecSpecific::FLACSpecificBox(flac) => {
+ // STREAMINFO block must be present and first.
+ assert!(flac.blocks.len() > 0);
+ assert!(flac.blocks[0].block_type == 0);
+ assert!(flac.blocks[0].data.len() == 34);
+ "FLAC"
+ }
mp4::AudioCodecSpecific::OpusSpecificBox(opus) => {
// We don't enter in here, we just check if fields are public.
assert!(opus.version > 0);
"Opus"
}
}, "ES");
assert!(a.samplesize > 0);
assert!(a.samplerate > 0);
--- a/media/libstagefright/binding/mp4parse_capi/build.rs
+++ b/media/libstagefright/binding/mp4parse_capi/build.rs
@@ -1,11 +1,12 @@
extern crate cheddar;
fn main() {
+ println!("cargo:rerun-if-changed=src/lib.rs");
// Generate mp4parse.h.
cheddar::Cheddar::new().expect("could not read manifest")
.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")
.insert_code("// file, You can obtain one at https://mozilla.org/MPL/2.0/.")
.run_build("include/mp4parse.h");
}
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -47,16 +47,17 @@ 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;
+use mp4parse::CodecType;
// 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
// https://github.com/Sean1708/rusty-cheddar/pull/35 is fixed. Importing
// the members into the module namespace avoids doubling up on the
// namespacing on the Rust side.
use mp4parse_error::*;
use mp4parse_track_type::*;
@@ -79,19 +80,21 @@ pub enum mp4parse_track_type {
MP4PARSE_TRACK_TYPE_AUDIO = 1,
}
#[repr(C)]
#[derive(PartialEq, Debug)]
pub enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
+ MP4PARSE_CODEC_FLAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
+ MP4PARSE_CODEC_MP3,
}
#[repr(C)]
pub struct mp4parse_track_info {
pub track_type: mp4parse_track_type,
pub codec: mp4parse_codec,
pub track_id: u32,
pub duration: u64,
@@ -337,52 +340,61 @@ pub unsafe extern fn mp4parse_get_track_
TrackType::Audio => MP4PARSE_TRACK_TYPE_AUDIO,
TrackType::Unknown => return MP4PARSE_ERROR_UNSUPPORTED,
};
info.codec = match context.tracks[track_index].data {
Some(SampleEntry::Audio(ref audio)) => match audio.codec_specific {
AudioCodecSpecific::OpusSpecificBox(_) =>
mp4parse_codec::MP4PARSE_CODEC_OPUS,
+ AudioCodecSpecific::FLACSpecificBox(_) =>
+ mp4parse_codec::MP4PARSE_CODEC_FLAC,
+ AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC =>
+ mp4parse_codec::MP4PARSE_CODEC_AAC,
+ AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 =>
+ mp4parse_codec::MP4PARSE_CODEC_MP3,
AudioCodecSpecific::ES_Descriptor(_) =>
- mp4parse_codec::MP4PARSE_CODEC_AAC,
+ mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
},
Some(SampleEntry::Video(ref video)) => match video.codec_specific {
VideoCodecSpecific::VPxConfig(_) =>
mp4parse_codec::MP4PARSE_CODEC_VP9,
VideoCodecSpecific::AVCConfig(_) =>
mp4parse_codec::MP4PARSE_CODEC_AVC,
},
_ => mp4parse_codec::MP4PARSE_CODEC_UNKNOWN,
};
let track = &context.tracks[track_index];
if let (Some(track_timescale),
- Some(context_timescale),
- Some(track_duration)) = (track.timescale,
- context.timescale,
- track.duration) {
+ Some(context_timescale)) = (track.timescale,
+ context.timescale) {
let media_time =
match track.media_time.map_or(Some(0), |media_time| {
track_time_to_us(media_time, track_timescale) }) {
Some(time) => time as i64,
None => return MP4PARSE_ERROR_INVALID,
};
let empty_duration =
match track.empty_duration.map_or(Some(0), |empty_duration| {
media_time_to_us(empty_duration, context_timescale) }) {
Some(time) => time as i64,
None => return MP4PARSE_ERROR_INVALID,
};
info.media_time = media_time - empty_duration;
- match track_time_to_us(track_duration, track_timescale) {
- Some(duration) => info.duration = duration,
- None => return MP4PARSE_ERROR_INVALID,
+ if let Some(track_duration) = track.duration {
+ match track_time_to_us(track_duration, track_timescale) {
+ Some(duration) => info.duration = duration,
+ None => return MP4PARSE_ERROR_INVALID,
+ }
+ } else {
+ // Duration unknown; stagefright returns 0 for this.
+ info.duration = 0
}
} else {
return MP4PARSE_ERROR_INVALID
}
info.track_id = match track.track_id {
Some(track_id) => track_id,
None => return MP4PARSE_ERROR_INVALID,
@@ -422,34 +434,49 @@ pub unsafe extern fn mp4parse_get_track_
};
(*info).channels = audio.channelcount;
(*info).bit_depth = audio.samplesize;
(*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
match audio.codec_specific {
AudioCodecSpecific::ES_Descriptor(ref v) => {
- if v.len() > std::u32::MAX as usize {
+ if v.codec_specific_config.len() > std::u32::MAX as usize {
return MP4PARSE_ERROR_INVALID;
}
- (*info).codec_specific_config.length = v.len() as u32;
- (*info).codec_specific_config.data = v.as_ptr();
+ (*info).codec_specific_config.length = v.codec_specific_config.len() as u32;
+ (*info).codec_specific_config.data = v.codec_specific_config.as_ptr();
+ if let Some(rate) = v.audio_sample_rate {
+ (*info).sample_rate = rate;
+ }
+ }
+ AudioCodecSpecific::FLACSpecificBox(ref flac) => {
+ // Return the STREAMINFO metadata block in the codec_specific.
+ let streaminfo = &flac.blocks[0];
+ if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
+ return MP4PARSE_ERROR_INVALID;
+ }
+ (*info).codec_specific_config.length = streaminfo.data.len() as u32;
+ (*info).codec_specific_config.data = streaminfo.data.as_ptr();
}
AudioCodecSpecific::OpusSpecificBox(ref opus) => {
let mut v = Vec::new();
match serialize_opus_header(opus, &mut v) {
Err(_) => {
return MP4PARSE_ERROR_INVALID;
}
Ok(_) => {
let header = (*parser).opus_header_mut();
header.insert(track_index, v);
match header.get(&track_index) {
None => {}
Some(v) => {
+ if v.len() > std::u32::MAX as usize {
+ return MP4PARSE_ERROR_INVALID;
+ }
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
}
}
}
}
}
}