close
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mp4parse/src/boxes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ box_database!(
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.
Expand Down
79 changes: 77 additions & 2 deletions mp4parse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pub enum SampleEntry {
#[derive(Debug, Clone)]
pub enum AudioCodecSpecific {
ES_Descriptor(Vec<u8>),
FLACSpecificBox(FLACSpecificBox),
OpusSpecificBox(OpusSpecificBox),
}

Expand Down Expand Up @@ -246,6 +247,19 @@ pub struct VPxConfigBox {
pub codec_init: Vec<u8>, // Empty for vp8/vp9.
}

#[derive(Debug, Clone)]
struct FLACMetadataBlock {
block_type: u8,
data: Vec<u8>,
}

/// Represet a FLACSpecificBox 'dfLa'
#[derive(Debug, Clone)]
pub struct FLACSpecificBox {
version: u8,
blocks: Vec<FLACMetadataBlock>,
}

#[derive(Debug, Clone)]
struct ChannelMappingTable {
stream_count: u8,
Expand Down Expand Up @@ -285,7 +299,7 @@ impl MediaContext {
}
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum TrackType {
Audio,
Video,
Expand All @@ -300,6 +314,7 @@ impl Default for TrackType {
pub enum CodecType {
Unknown,
AAC,
FLAC,
Opus,
H264,
VP9,
Expand Down Expand Up @@ -1075,11 +1090,55 @@ fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
})
}

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,
})
}

/// Parse `FLACSpecificBox`.
fn read_dfla<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACSpecificBox> {
let version = try!(src.read_u8());
if version != 0 {
return Err(Error::Unsupported("unknown dfLa (FLAC) version"));
}
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());
Expand Down Expand Up @@ -1257,6 +1316,7 @@ fn read_audio_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<S
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,
Expand Down Expand Up @@ -1309,6 +1369,14 @@ fn read_audio_desc<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<S
// TODO(kinetik): Parse esds box? For now we just stash the data.
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));
codec_specific = Some(AudioCodecSpecific::FLACSpecificBox(dfla));
}
BoxType::OpusSpecificBox => {
if name != BoxType::OpusSampleEntry ||
codec_specific.is_some() {
Expand Down Expand Up @@ -1458,6 +1526,13 @@ 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> {
let a = try!(src.read_u8()) as u32;
let b = try!(src.read_u8()) as u32;
let c = try!(src.read_u8()) as u32;
Ok(a << 16 | b << 8 | c)
}

fn be_u32<T: ReadBytesExt>(src: &mut T) -> Result<u32> {
src.read_u32::<byteorder::BigEndian>().map_err(From::from)
}
Expand Down
97 changes: 97 additions & 0 deletions mp4parse/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,103 @@ fn read_hdlr_zero_length_name() {
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_desc(&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_box(BoxSize::Auto, b"dfLa", |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.B8(0) // version
.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| {
Expand Down
4 changes: 4 additions & 0 deletions mp4parse/tests/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ fn public_api() {
assert!(v.len() > 0);
"ES"
}
mp4::AudioCodecSpecific::FLACSpecificBox(_) => {
// No public fields.
"FLAC"
}
mp4::AudioCodecSpecific::OpusSpecificBox(opus) => {
// We don't enter in here, we just check if fields are public.
assert!(opus.version > 0);
Expand Down
6 changes: 6 additions & 0 deletions mp4parse_capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pub enum mp4parse_track_type {
pub enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
MP4PARSE_CODEC_FLAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
Expand Down Expand Up @@ -342,6 +343,8 @@ pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track
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(_) =>
mp4parse_codec::MP4PARSE_CODEC_AAC,
},
Expand Down Expand Up @@ -436,6 +439,9 @@ pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser,
(*info).codec_specific_config.length = v.len() as u32;
(*info).codec_specific_config.data = v.as_ptr();
}
AudioCodecSpecific::FLACSpecificBox(_) => {
return MP4PARSE_ERROR_UNSUPPORTED;
}
AudioCodecSpecific::OpusSpecificBox(ref opus) => {
let mut v = Vec::new();
match serialize_opus_header(opus, &mut v) {
Expand Down