Bug 1363669 - update rust mp4 parser for mp4v. r?kinetik draft
authorAlfredo Yang <ayang@mozilla.com>
Thu, 25 May 2017 14:33:36 +0800
changeset 584328 d7e010f17c5c4e4894425ec0011859ccf46381a6
parent 584327 d3561658d881ce80f0a1360a11e709d50f96545d
child 584971 3be9bb2886a4843799a23fd79faba3cafd255ea4
push id60692
push userayang@mozilla.com
push dateThu, 25 May 2017 08:49:50 +0000
reviewerskinetik
bugs1363669
milestone55.0a1
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