Bug 1363669 - update rust mp4 parser for mp4v. r=kinetik
authorAlfredo Yang <ayang@mozilla.com>
Thu, 25 May 2017 14:33:36 +0800
changeset 409202 17a953825a4e915c425ced37e57812f9d8cd9f10
parent 409201 fa4b5a89201b3d27a62580467eff8c89f837d6ce
child 409203 54d7b5816adb8f926f0f0ae62f13380778e73f55
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1363669
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1363669 - update rust mp4 parser for mp4v. r=kinetik MozReview-Commit-ID: 3HhKbIxTBPV
media/libstagefright/binding/include/mp4parse.h
media/libstagefright/binding/mp4parse/src/boxes.rs
media/libstagefright/binding/mp4parse/src/lib.rs
media/libstagefright/binding/mp4parse/src/tests.rs
media/libstagefright/binding/mp4parse/tests/public.rs
media/libstagefright/binding/mp4parse_capi/src/lib.rs
media/libstagefright/binding/update-rust.sh
--- 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