--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -33,16 +33,17 @@ typedef enum 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_MP4V,
} 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/src/boxes.rs
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -111,16 +111,17 @@ box_database!(
SampleSizeBox 0x7374737a, // "stsz"
ChunkOffsetBox 0x7374636f, // "stco"
ChunkLargeOffsetBox 0x636f3634, // "co64"
SyncSampleBox 0x73747373, // "stss"
AVCSampleEntry 0x61766331, // "avc1"
AVC3SampleEntry 0x61766333, // "avc3" - Need to check official name in spec.
AVCConfigurationBox 0x61766343, // "avcC"
MP4AudioSampleEntry 0x6d703461, // "mp4a"
+ MP4VideoSampleEntry 0x6d703476, // "mp4v"
ESDBox 0x65736473, // "esds"
VP8SampleEntry 0x76703038, // "vp08"
VP9SampleEntry 0x76703039, // "vp09"
VPCodecConfigurationBox 0x76706343, // "vpcC"
FLACSampleEntry 0x664c6143, // "fLaC"
FLACSpecificBox 0x64664c61, // "dfLa"
OpusSampleEntry 0x4f707573, // "Opus"
OpusSpecificBox 0x644f7073, // "dOps"
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -275,16 +275,17 @@ pub struct AudioSampleEntry {
pub codec_specific: AudioCodecSpecific,
pub protection_info: Vec<ProtectionSchemeInfoBox>,
}
#[derive(Debug, Clone)]
pub enum VideoCodecSpecific {
AVCConfig(Vec<u8>),
VPxConfig(VPxConfigBox),
+ ESDSConfig(Vec<u8>),
}
#[derive(Debug, Clone)]
pub struct VideoSampleEntry {
data_reference_index: u16,
pub width: u16,
pub height: u16,
pub codec_specific: VideoCodecSpecific,
@@ -295,16 +296,17 @@ pub struct VideoSampleEntry {
#[derive(Debug, Clone)]
pub struct VPxConfigBox {
profile: u8,
level: u8,
pub bit_depth: u8,
pub color_space: u8, // Really an enum
pub chroma_subsampling: u8,
transfer_function: u8,
+ matrix: Option<u8>, // Available in 'VP Codec ISO Media File Format' version 1 only.
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>,
@@ -395,17 +397,19 @@ impl Default for TrackType {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CodecType {
Unknown,
MP3,
AAC,
FLAC,
Opus,
- H264,
+ H264, // 14496-10
+ MP4V, // 14496-2
+ VP10,
VP9,
VP8,
EncryptedVideo,
EncryptedAudio,
}
impl Default for CodecType {
fn default() -> Self { CodecType::Unknown }
@@ -1243,42 +1247,58 @@ fn read_stts<T: Read>(src: &mut BMFFBox<
Ok(TimeToSampleBox {
samples: samples,
})
}
/// Parse a VPx Config Box.
fn read_vpcc<T: Read>(src: &mut BMFFBox<T>) -> Result<VPxConfigBox> {
let (version, _) = read_fullbox_extra(src)?;
- if version != 0 {
+ let supported_versions = [0, 1];
+ if ! supported_versions.contains(&version) {
return Err(Error::Unsupported("unknown vpcC version"));
}
let profile = src.read_u8()?;
let level = src.read_u8()?;
- let (bit_depth, color_space) = {
- let byte = src.read_u8()?;
- ((byte >> 4) & 0x0f, byte & 0x0f)
- };
- let (chroma_subsampling, transfer_function, video_full_range) = {
- let byte = src.read_u8()?;
- ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1)
- };
+ let (bit_depth, color_space, chroma_subsampling, transfer_function, matrix, video_full_range) =
+ if version == 0 {
+ let (bit_depth, color_space) = {
+ let byte = src.read_u8()?;
+ ((byte >> 4) & 0x0f, byte & 0x0f)
+ };
+ let (chroma_subsampling, transfer_function, video_full_range) = {
+ let byte = src.read_u8()?;
+ ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1)
+ };
+ (bit_depth, color_space, chroma_subsampling, transfer_function, None, video_full_range)
+ } else {
+ let (bit_depth, chroma_subsampling, video_full_range) = {
+ let byte = src.read_u8()?;
+ ((byte >> 4) & 0x0f, (byte >> 1) & 0x07, (byte & 1) == 1)
+ };
+ let color_space = src.read_u8()?;
+ let transfer_function = src.read_u8()?;
+ let matrix = src.read_u8()?;
+
+ (bit_depth, color_space, chroma_subsampling, transfer_function, Some(matrix), video_full_range)
+ };
let codec_init_size = be_u16(src)?;
let codec_init = read_buf(src, codec_init_size as usize)?;
// TODO(rillian): validate field value ranges.
Ok(VPxConfigBox {
profile: profile,
level: level,
bit_depth: bit_depth,
color_space: color_space,
chroma_subsampling: chroma_subsampling,
transfer_function: transfer_function,
+ matrix: matrix,
video_full_range: video_full_range,
codec_init: codec_init,
})
}
fn read_flac_metadata<T: Read>(src: &mut BMFFBox<T>) -> Result<FLACMetadataBlock> {
let temp = src.read_u8()?;
let block_type = temp & 0x7f;
@@ -1635,16 +1655,17 @@ fn read_hdlr<T: Read>(src: &mut BMFFBox<
})
}
/// Parse an video description inside an stsd box.
fn read_video_sample_entry<T: Read>(src: &mut BMFFBox<T>) -> Result<(CodecType, SampleEntry)> {
let name = src.get_header().name;
let codec_type = match name {
BoxType::AVCSampleEntry | BoxType::AVC3SampleEntry => CodecType::H264,
+ BoxType::MP4VideoSampleEntry => CodecType::MP4V,
BoxType::VP8SampleEntry => CodecType::VP8,
BoxType::VP9SampleEntry => CodecType::VP9,
BoxType::ProtectedVisualSampleEntry => CodecType::EncryptedVideo,
_ => CodecType::Unknown,
};
// Skip uninteresting fields.
skip(src, 6)?;
@@ -1684,16 +1705,25 @@ fn read_video_sample_entry<T: Read>(src:
name != BoxType::VP9SampleEntry &&
name != BoxType::ProtectedVisualSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed video sample entry"));
}
let vpcc = read_vpcc(&mut b)?;
codec_specific = Some(VideoCodecSpecific::VPxConfig(vpcc));
}
+ BoxType::ESDBox => {
+ if name != BoxType::MP4VideoSampleEntry || codec_specific.is_some() {
+ return Err(Error::InvalidData("malformed video sample entry"));
+ }
+ let (_, _) = read_fullbox_extra(&mut b.content)?;
+ let esds_size = b.head.size - b.head.offset - 4;
+ let esds = read_buf(&mut b.content, esds_size as usize)?;
+ codec_specific = Some(VideoCodecSpecific::ESDSConfig(esds));
+ }
BoxType::ProtectionSchemeInformationBox => {
if name != BoxType::ProtectedVisualSampleEntry {
return Err(Error::InvalidData("malformed video sample entry"));
}
let sinf = read_sinf(&mut b)?;
log!("{:?} (sinf)", sinf);
protection_info.push(sinf);
}
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -366,33 +366,63 @@ fn read_mvhd_unknown_duration() {
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
assert_eq!(stream.head.size, 108);
let parsed = super::read_mvhd(&mut stream).unwrap();
assert_eq!(parsed.timescale, 1234);
assert_eq!(parsed.duration, ::std::u64::MAX);
}
#[test]
-fn read_vpcc() {
+fn read_vpcc_version_0() {
let data_length = 12u16;
let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 0, |s| {
s.B8(2)
.B8(0)
.B8(0x82)
.B8(0)
.B16(data_length)
.append_repeated(42, data_length as usize)
});
let mut iter = super::BoxIter::new(&mut stream);
let mut stream = iter.next_box().unwrap().unwrap();
assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox);
let r = super::read_vpcc(&mut stream);
assert!(r.is_ok());
}
+// TODO: it'd be better to find a real sample here.
+#[test]
+fn read_vpcc_version_1() {
+ let data_length = 12u16;
+ let mut stream = make_fullbox(BoxSize::Auto, b"vpcC", 1, |s| {
+ s.B8(2) // profile
+ .B8(0) // level
+ .B8(0b1000_011_0) // bitdepth (4 bits), chroma (3 bits), video full range (1 bit)
+ .B8(1) // color primaries
+ .B8(1) // transfer characteristics
+ .B8(1) // matrix
+ .B16(data_length)
+ .append_repeated(42, data_length as usize)
+ });
+
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+ assert_eq!(stream.head.name, BoxType::VPCodecConfigurationBox);
+ let r = super::read_vpcc(&mut stream);
+ match r {
+ Ok(vpcc) => {
+ assert_eq!(vpcc.bit_depth, 8);
+ assert_eq!(vpcc.chroma_subsampling, 3);
+ assert_eq!(vpcc.video_full_range, false);
+ assert_eq!(vpcc.matrix.unwrap(), 1);
+ },
+ _ => panic!("vpcc parsing error"),
+ }
+}
+
#[test]
fn read_hdlr() {
let mut stream = make_fullbox(BoxSize::Short(45), b"hdlr", 0, |s| {
s.B32(0)
.append_bytes(b"vide")
.B32(0)
.B32(0)
.B32(0)
@@ -866,16 +896,65 @@ fn read_esds() {
assert_eq!(es.audio_object_type, Some(2));
assert_eq!(es.audio_sample_rate, Some(24000));
assert_eq!(es.audio_channel_count, Some(6));
assert_eq!(es.codec_esds, aac_esds);
assert_eq!(es.decoder_specific_data, aac_dc_descriptor);
}
#[test]
+fn read_stsd_mp4v() {
+ let mp4v =
+ vec![
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xd0, 0x01, 0xe0, 0x00, 0x48,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x18, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x4c, 0x65, 0x73, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x3e, 0x00, 0x00, 0x1f, 0x04, 0x36, 0x20, 0x11, 0x01, 0x77, 0x00,
+ 0x00, 0x03, 0xe8, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x05, 0x27, 0x00, 0x00,
+ 0x01, 0xb0, 0x05, 0x00, 0x00, 0x01, 0xb5, 0x0e, 0xcf, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x86, 0xe0, 0x00, 0x2e, 0xa6, 0x60,
+ 0x16, 0xf4, 0x01, 0xf4, 0x24, 0xc8, 0x01, 0xe5, 0x16, 0x84, 0x3c, 0x14,
+ 0x63, 0x06, 0x01, 0x02,
+ ];
+
+ let esds_specific_data = &mp4v[90 ..];
+ println!("esds_specific_data {:?}", esds_specific_data);
+
+ let mut stream = make_box(BoxSize::Auto, b"mp4v", |s| {
+ s.append_bytes(mp4v.as_slice())
+ });
+ let mut iter = super::BoxIter::new(&mut stream);
+ let mut stream = iter.next_box().unwrap().unwrap();
+
+ let (codec_type, sample_entry) = super::read_video_sample_entry(&mut stream).unwrap();
+
+ assert_eq!(codec_type, super::CodecType::MP4V);
+
+ match sample_entry {
+ super::SampleEntry::Video(v) => {
+ assert_eq!(v.width, 720);
+ assert_eq!(v.height, 480);
+ match v.codec_specific {
+ super::VideoCodecSpecific::ESDSConfig(esds_data) => {
+ assert_eq!(esds_data, esds_specific_data.to_vec());
+ },
+ _ => panic!("it should be ESDSConfig!"),
+ }
+ },
+ _ => panic!("it should be a video sample entry!"),
+ }
+
+}
+
+#[test]
fn read_esds_one_byte_extension_descriptor() {
let esds =
vec![
0x00, 0x03, 0x80, 0x1b, 0x00, 0x00, 0x00, 0x04,
0x80, 0x12, 0x40, 0x15, 0x00, 0x06, 0x00, 0x00,
0x01, 0xfe, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x05,
0x80, 0x02, 0x11, 0x90, 0x06, 0x01, 0x02,
];
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -51,16 +51,20 @@ fn public_api() {
mp4::VideoCodecSpecific::VPxConfig(vpx) => {
// We don't enter in here, we just check if fields are public.
assert!(vpx.bit_depth > 0);
assert!(vpx.color_space > 0);
assert!(vpx.chroma_subsampling > 0);
assert!(!vpx.codec_init.is_empty());
"VPx"
}
+ mp4::VideoCodecSpecific::ESDSConfig(mp4v) => {
+ assert!(!mp4v.is_empty());
+ "MP4V"
+ }
}, "AVC");
}
Some(mp4::SampleEntry::Audio(a)) => {
// track part
assert_eq!(track.duration, Some(mp4::TrackScaledTime(2944, 1)));
assert_eq!(track.empty_duration, Some(mp4::MediaScaledTime(0)));
assert_eq!(track.media_time, Some(mp4::TrackScaledTime(1024, 1)));
assert_eq!(track.timescale, Some(mp4::TrackTimeScale(48000, 1)));
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -89,16 +89,17 @@ impl Default for mp4parse_track_type {
pub enum mp4parse_codec {
UNKNOWN,
AAC,
FLAC,
OPUS,
AVC,
VP9,
MP3,
+ MP4V,
}
impl Default for mp4parse_codec {
fn default() -> Self { mp4parse_codec::UNKNOWN }
}
#[repr(C)]
#[derive(Default)]
@@ -428,16 +429,18 @@ pub unsafe extern fn mp4parse_get_track_
AudioCodecSpecific::MP3 =>
mp4parse_codec::MP3,
},
Some(SampleEntry::Video(ref video)) => match video.codec_specific {
VideoCodecSpecific::VPxConfig(_) =>
mp4parse_codec::VP9,
VideoCodecSpecific::AVCConfig(_) =>
mp4parse_codec::AVC,
+ VideoCodecSpecific::ESDSConfig(_) =>
+ mp4parse_codec::MP4V,
},
_ => mp4parse_codec::UNKNOWN,
};
let track = &context.tracks[track_index];
if let (Some(track_timescale),
Some(context_timescale)) = (track.timescale,
@@ -620,18 +623,21 @@ pub unsafe extern fn mp4parse_get_track_
_ => 0,
};
} else {
return mp4parse_status::INVALID;
}
(*info).image_width = video.width;
(*info).image_height = video.height;
- if let VideoCodecSpecific::AVCConfig(ref avc) = video.codec_specific {
- (*info).extra_data.set_data(avc);
+ match video.codec_specific {
+ VideoCodecSpecific::AVCConfig(ref data) | VideoCodecSpecific::ESDSConfig(ref data) => {
+ (*info).extra_data.set_data(data);
+ },
+ _ => {}
}
if let Some(p) = video.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
if let Some(ref tenc) = p.tenc {
(*info).protected_data.is_encrypted = tenc.is_encrypted;
(*info).protected_data.iv_size = tenc.iv_size;
(*info).protected_data.kid.set_data(&(tenc.kid));
}
--- a/media/libstagefright/binding/update-rust.sh
+++ b/media/libstagefright/binding/update-rust.sh
@@ -1,13 +1,13 @@
#!/bin/sh -e
# Script to update mp4parse-rust sources to latest upstream
# Default version.
-VER=70adbd200fe6af69290c4272fe859dd75e82e37a
+VER=70b2008dc9fd5cd09fb5b047e72616c5cf52c1d7
# Accept version or commit from the command line.
if test -n "$1"; then
VER=$1
fi
echo "Fetching sources..."
rm -rf _upstream