Bug 1347834 - update rust mp4 parser for audio codec specific data. r=kinetik draft
authorAlfredo.Yang <ayang@mozilla.com>
Fri, 17 Mar 2017 11:13:22 +0800
changeset 500349 353b9b287dd1f6160d883dda5852c2f57fe85256
parent 500348 3f4f27c0a2bc9f0d200ee646d68666c0c8adda59
child 549615 0e4d441cede40f325b350f609460d81f155b3102
push id49698
push userbmo:ayang@mozilla.com
push dateFri, 17 Mar 2017 03:34:37 +0000
reviewerskinetik
bugs1347834
milestone55.0a1
Bug 1347834 - update rust mp4 parser for audio codec specific data. r=kinetik MozReview-Commit-ID: 29yu6VsFIE6
media/libstagefright/binding/include/mp4parse.h
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
@@ -73,16 +73,17 @@ typedef struct mp4parse_sinf_info {
 	mp4parse_byte_data kid;
 } mp4parse_sinf_info;
 
 typedef struct mp4parse_track_audio_info {
 	uint16_t channels;
 	uint16_t bit_depth;
 	uint32_t sample_rate;
 	uint16_t profile;
+	mp4parse_byte_data codec_specific_data;
 	mp4parse_byte_data codec_specific_config;
 	mp4parse_sinf_info protected_data;
 } mp4parse_track_audio_info;
 
 typedef struct mp4parse_track_video_info {
 	uint32_t display_width;
 	uint32_t display_height;
 	uint16_t image_width;
@@ -103,17 +104,17 @@ typedef struct mp4parse_io {
 } mp4parse_io;
 
 /// Allocate an `mp4parse_parser*` to read from the supplied `mp4parse_io`.
 mp4parse_parser* mp4parse_new(mp4parse_io const* io);
 
 /// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
 void mp4parse_free(mp4parse_parser* parser);
 
-/// Enable mp4_parser log.
+/// Enable `mp4_parser` log.
 void mp4parse_log(bool enable);
 
 /// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
 mp4parse_error mp4parse_read(mp4parse_parser* parser);
 
 /// Return the number of tracks parsed by previous `mp4parse_read()` call.
 mp4parse_error mp4parse_get_track_count(mp4parse_parser const* parser, uint32_t* count);
 
@@ -131,20 +132,21 @@ mp4parse_error mp4parse_get_indice_table
 /// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
 mp4parse_error mp4parse_get_fragment_info(mp4parse_parser* parser, mp4parse_fragment_info* info);
 
 /// A fragmented file needs mvex table and contains no data in stts, stsc, and stco boxes.
 mp4parse_error mp4parse_is_fragmented(mp4parse_parser* parser, uint32_t track_id, uint8_t* fragmented);
 
 /// Get 'pssh' system id and 'pssh' box content for eme playback.
 ///
-/// The data format in 'info' passing to gecko is:
-///   system_id
-///   pssh box size (in native endian)
-///   pssh box content (including header)
+/// The data format of the `info` struct passed to gecko is:
+///
+/// - system id (16 byte uuid)
+/// - pssh box size (32-bit native endian)
+/// - pssh box content (including header)
 mp4parse_error mp4parse_get_pssh_info(mp4parse_parser* parser, mp4parse_pssh_info* info);
 
 
 
 #ifdef __cplusplus
 }
 #endif
 
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -233,16 +233,17 @@ pub enum SampleEntry {
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone, Default)]
 pub struct ES_Descriptor {
     pub audio_codec: CodecType,
     pub audio_object_type: Option<u16>,
     pub audio_sample_rate: Option<u32>,
     pub audio_channel_count: Option<u16>,
     pub codec_esds: Vec<u8>,
+    pub decoder_specific_data: Vec<u8>, // Data in DECODER_SPECIFIC_TAG
 }
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone)]
 pub enum AudioCodecSpecific {
     ES_Descriptor(ES_Descriptor),
     FLACSpecificBox(FLACSpecificBox),
     OpusSpecificBox(OpusSpecificBox),
@@ -1407,16 +1408,18 @@ fn read_ds_descriptor(data: &[u8], esds:
         channel_counts += read_surround_channel_count(bit_reader, num_side_channel)?;
         channel_counts += read_surround_channel_count(bit_reader, num_back_channel)?;
         channel_counts += read_surround_channel_count(bit_reader, num_lfe_channel)?;
     }
 
     esds.audio_object_type = Some(audio_object_type);
     esds.audio_sample_rate = sample_frequency;
     esds.audio_channel_count = Some(channel_counts);
+    assert!(esds.decoder_specific_data.is_empty());
+    esds.decoder_specific_data.extend(data.iter());
 
     Ok(())
 }
 
 fn read_surround_channel_count(bit_reader: &mut BitReader, channels: u8) -> Result<u16> {
     let mut count = 0;
     for _ in 0..channels {
         let is_cpe: bool = ReadInto::read(bit_reader, 1)?;
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -858,30 +858,33 @@ fn read_esds() {
     let aac_esds =
         vec![
             0x03, 0x24, 0x00, 0x00, 0x00, 0x04, 0x1c, 0x40,
             0x15, 0x00, 0x12, 0x00, 0x00, 0x01, 0xf4, 0x00,
             0x00, 0x01, 0xf4, 0x00, 0x05, 0x0d, 0x13, 0x00,
             0x05, 0x88, 0x05, 0x00, 0x48, 0x21, 0x10, 0x00,
             0x56, 0xe5, 0x98, 0x06, 0x01, 0x02,
         ];
+    let aac_dc_descriptor = &aac_esds[22 .. 35];
+
     let mut stream = make_box(BoxSize::Auto, b"esds", |s| {
         s.B32(0) // reserved
          .append_bytes(aac_esds.as_slice())
     });
     let mut iter = super::BoxIter::new(&mut stream);
     let mut stream = iter.next_box().unwrap().unwrap();
 
     let es = super::read_esds(&mut stream).unwrap();
 
     assert_eq!(es.audio_codec, super::CodecType::AAC);
     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_null_terminated_string() {
     let tests = vec![
         vec![0u8],                         // Short null-terminated string.
         vec![65u8, 0u8],                   // Normal null-terminated string.
         vec![],                            // Empty string (no data).
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -117,22 +117,22 @@ fn public_audio_tenc() {
     let mut c = Cursor::new(&buf);
     let mut context = mp4::MediaContext::new();
     mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
     for track in context.tracks {
         assert_eq!(track.codec_type, mp4::CodecType::EncryptedAudio);
         match track.data {
             Some(mp4::SampleEntry::Audio(a)) => {
                 match a.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
-                    Some(ref p) => {
+                    Some(p) => {
                         assert_eq!(p.code_name, "mp4a");
                         if let Some(ref tenc) = p.tenc {
                             assert!(tenc.is_encrypted > 0);
-                            assert!(tenc.iv_size ==  16);
-                            assert!(tenc.kid == kid);
+                            assert_eq!(tenc.iv_size, 16);
+                            assert_eq!(tenc.kid, kid);
                         } else {
                             assert!(false, "Invalid test condition");
                         }
                     },
                     _=> {
                         assert!(false, "Invalid test condition");
                     },
                 }
@@ -170,22 +170,22 @@ fn public_video_cenc() {
     let mut c = Cursor::new(&buf);
     let mut context = mp4::MediaContext::new();
     mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
     for track in context.tracks {
         assert_eq!(track.codec_type, mp4::CodecType::EncryptedVideo);
         match track.data {
             Some(mp4::SampleEntry::Video(v)) => {
                 match v.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
-                    Some(ref p) => {
+                    Some(p) => {
                         assert_eq!(p.code_name, "avc1");
                         if let Some(ref tenc) = p.tenc {
                             assert!(tenc.is_encrypted > 0);
-                            assert!(tenc.iv_size ==  16);
-                            assert!(tenc.kid == kid);
+                            assert_eq!(tenc.iv_size, 16);
+                            assert_eq!(tenc.kid, kid);
                         } else {
                             assert!(false, "Invalid test condition");
                         }
                     },
                     _=> {
                         assert!(false, "Invalid test condition");
                     },
                 }
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -142,21 +142,21 @@ impl Default for mp4parse_byte_data {
             length: 0,
             data: std::ptr::null(),
             indices: std::ptr::null(),
         }
     }
 }
 
 impl mp4parse_byte_data {
-    fn set_data(&mut self, data: &Vec<u8>) {
+    fn set_data(&mut self, data: &[u8]) {
         self.length = data.len() as u32;
         self.data = data.as_ptr();
     }
-    fn set_indices(&mut self, data: &Vec<mp4parse_indice>) {
+    fn set_indices(&mut self, data: &[mp4parse_indice]) {
         self.length = data.len() as u32;
         self.indices = data.as_ptr();
     }
 }
 
 #[repr(C)]
 #[derive(Default)]
 pub struct mp4parse_pssh_info {
@@ -173,16 +173,21 @@ pub struct mp4parse_sinf_info {
 
 #[repr(C)]
 #[derive(Default)]
 pub struct mp4parse_track_audio_info {
     pub channels: u16,
     pub bit_depth: u16,
     pub sample_rate: u32,
     pub profile: u16,
+    // TODO:
+    //  codec_specific_data is AudioInfo.mCodecSpecificConfig,
+    //  codec_specific_config is AudioInfo.mExtraData.
+    //  It'd be better to change name same as AudioInfo.
+    pub codec_specific_data: mp4parse_byte_data,
     pub codec_specific_config: mp4parse_byte_data,
     pub protected_data: mp4parse_sinf_info,
 }
 
 #[repr(C)]
 #[derive(Default)]
 pub struct mp4parse_track_video_info {
     pub display_width: u32,
@@ -301,17 +306,17 @@ pub unsafe extern fn mp4parse_new(io: *c
 
 /// Free an `mp4parse_parser*` allocated by `mp4parse_new()`.
 #[no_mangle]
 pub unsafe extern fn mp4parse_free(parser: *mut mp4parse_parser) {
     assert!(!parser.is_null());
     let _ = Box::from_raw(parser);
 }
 
-/// Enable mp4_parser log.
+/// Enable `mp4_parser` log.
 #[no_mangle]
 pub unsafe extern fn mp4parse_log(enable: bool) {
     mp4parse::set_debug_mode(enable);
 }
 
 /// Run the `mp4parse_parser*` allocated by `mp4parse_new()` until EOF or error.
 #[no_mangle]
 pub unsafe extern fn mp4parse_read(parser: *mut mp4parse_parser) -> mp4parse_error {
@@ -520,16 +525,18 @@ pub unsafe extern fn mp4parse_get_track_
 
     match audio.codec_specific {
         AudioCodecSpecific::ES_Descriptor(ref v) => {
             if v.codec_esds.len() > std::u32::MAX as usize {
                 return MP4PARSE_ERROR_INVALID;
             }
             (*info).codec_specific_config.length = v.codec_esds.len() as u32;
             (*info).codec_specific_config.data = v.codec_esds.as_ptr();
+            (*info).codec_specific_data.length = v.decoder_specific_data.len() as u32;
+            (*info).codec_specific_data.data = v.decoder_specific_data.as_ptr();
             if let Some(rate) = v.audio_sample_rate {
                 (*info).sample_rate = rate;
             }
             if let Some(channels) = v.audio_channel_count {
                 (*info).channels = channels;
             }
             if let Some(profile) = v.audio_object_type {
                 (*info).profile = profile;
@@ -565,17 +572,17 @@ pub unsafe extern fn mp4parse_get_track_
                     }
                 }
             }
         }
         AudioCodecSpecific::MP3 => (),
     }
 
     match audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
-        Some(ref p) => {
+        Some(p) => {
             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));
             }
         },
         _ => {},
     }
@@ -628,17 +635,17 @@ pub unsafe extern fn mp4parse_get_track_
     match video.codec_specific {
         VideoCodecSpecific::AVCConfig(ref avc) => {
             (*info).extra_data.set_data(avc);
         },
         _ => {},
     }
 
     match video.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
-        Some(ref p) => {
+        Some(p) => {
             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));
             }
         },
         _ => {},
     }
@@ -927,27 +934,27 @@ fn create_sample_table(track: &Track, tr
                     start_decode: 0,
                     sync: !has_sync_table,
                 }
             );
         }
     }
 
     // Mark the sync sample in sample_table according to 'stss'.
-    match &track.stss {
-        &Some(ref v) => {
+    match track.stss {
+        Some(ref v) => {
             for iter in &v.samples {
                 sample_table[(iter - 1) as usize].sync = true;
             }
         },
         _ => {}
     }
 
-    let ctts_iter = match &track.ctts {
-        &Some(ref v) => Some(v.samples.as_slice().iter()),
+    let ctts_iter = match track.ctts {
+        Some(ref v) => Some(v.samples.as_slice().iter()),
         _ => None,
     };
 
     let mut ctts_offset_iter = TimeOffsetIterator {
         cur_sample_range: (0 .. 0),
         cur_offset: 0,
         ctts_iter: ctts_iter,
     };
@@ -1001,17 +1008,17 @@ fn create_sample_table(track: &Track, tr
             }
         });
 
         let iter = sort_table.iter();
         for i in 0 .. (iter.len() - 1) {
             let current_index = sort_table[i] as usize;
             let peek_index = sort_table[i + 1] as usize;
             let next_start_composition_time = sample_table[peek_index].start_composition;
-            let ref mut sample = sample_table[current_index];
+            let sample = &mut sample_table[current_index];
             sample.end_composition = next_start_composition_time;
         }
     }
 
     Some(sample_table)
 }
 
 /// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
@@ -1067,20 +1074,21 @@ pub unsafe extern fn mp4parse_is_fragmen
         None => return MP4PARSE_ERROR_BADARG,
     }
 
     MP4PARSE_OK
 }
 
 /// Get 'pssh' system id and 'pssh' box content for eme playback.
 ///
-/// The data format in 'info' passing to gecko is:
-///   system_id
-///   pssh box size (in native endian)
-///   pssh box content (including header)
+/// The data format of the `info` struct passed to gecko is:
+///
+/// - system id (16 byte uuid)
+/// - pssh box size (32-bit native endian)
+/// - pssh box content (including header)
 #[no_mangle]
 pub unsafe extern fn mp4parse_get_pssh_info(parser: *mut mp4parse_parser, info: *mut mp4parse_pssh_info) -> mp4parse_error {
     if parser.is_null() || info.is_null() || (*parser).poisoned() {
         return MP4PARSE_ERROR_BADARG;
     }
 
     // Initialize fields to default values to ensure all fields are always valid.
     *info = Default::default();
@@ -1098,17 +1106,17 @@ pub unsafe extern fn mp4parse_get_pssh_i
             },
             _ => (),
         }
         pssh_data.extend_from_slice(pssh.system_id.as_slice());
         pssh_data.extend_from_slice(data_len.as_slice());
         pssh_data.extend_from_slice(pssh.box_content.as_slice());
     }
 
-    info.data.set_data(&pssh_data);
+    info.data.set_data(pssh_data);
 
     MP4PARSE_OK
 }
 
 #[cfg(test)]
 extern fn panic_read(_: *mut u8, _: usize, _: *mut std::os::raw::c_void) -> isize {
     panic!("panic_read shouldn't be called in these tests");
 }
--- 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=a422808043e6a21ee98729dd8bfe1e8234897d18
+VER=6cd06d46565f55a8259d4ad7f083c52d6335750f
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream