Bug 1393045 - support QT AudioSampleEntry v2. r=kinetik
authorAlfredo.Yang <ayang@mozilla.com>
Wed, 08 Nov 2017 09:21:58 +0800
changeset 443947 78f97c364749691c184361eef850a59198cf011f
parent 443946 8dbc7c83b48762894fd1e02fa1032bfc1a3fdb73
child 443948 901652a2298bb1e2a436dfbeba959899eb84adf8
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1393045
milestone58.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 1393045 - support QT AudioSampleEntry v2. r=kinetik MozReview-Commit-ID: GchJJBrdKYm
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/mp4parse/src/boxes.rs
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -132,9 +132,11 @@ box_database!(
     QTWaveAtom                        0x77617665, // "wave" - quicktime atom
     ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
     SchemeInformationBox              0x73636869, // "schi"
     TrackEncryptionBox                0x74656e63, // "tenc"
     ProtectionSchemeInformationBox    0x73696e66, // "sinf"
     OriginalFormatBox                 0x66726d61, // "frma"
     MP3AudioSampleEntry               0x2e6d7033, // ".mp3" - from F4V.
     CompositionOffsetBox              0x63747473, // "ctts"
+    AudioChannelLayoutAtom            0x6368616E, // "chan" - quicktime atom
+    LPCMAudioSampleEntry              0x6C70636D, // "lpcm" - quicktime atom
 );
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -314,24 +314,25 @@ pub struct ES_Descriptor {
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone)]
 pub enum AudioCodecSpecific {
     ES_Descriptor(ES_Descriptor),
     FLACSpecificBox(FLACSpecificBox),
     OpusSpecificBox(OpusSpecificBox),
     MP3,
+    LPCM,
 }
 
 #[derive(Debug, Clone)]
 pub struct AudioSampleEntry {
     data_reference_index: u16,
-    pub channelcount: u16,
+    pub channelcount: u32,
     pub samplesize: u16,
-    pub samplerate: u32,
+    pub samplerate: f64,
     pub codec_specific: AudioCodecSpecific,
     pub protection_info: Vec<ProtectionSchemeInfoBox>,
 }
 
 #[derive(Debug, Clone)]
 pub enum VideoCodecSpecific {
     AVCConfig(Vec<u8>),
     VPxConfig(VPxConfigBox),
@@ -459,16 +460,17 @@ pub enum CodecType {
     Opus,
     H264,   // 14496-10
     MP4V,   // 14496-2
     VP10,
     VP9,
     VP8,
     EncryptedVideo,
     EncryptedAudio,
+    LPCM,   // QT
 }
 
 impl Default for CodecType {
     fn default() -> Self { CodecType::Unknown }
 }
 
 /// The media's global (mvhd) timescale in units per second.
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -1831,35 +1833,41 @@ fn read_audio_sample_entry<T: Read>(src:
     // uses it, need to work out if we have to support it.  Without checking
     // here and reading extra fields after samplerate (or bailing with an
     // error), the parser loses sync completely.
     let version = be_u16(src)?;
 
     // Skip uninteresting fields.
     skip(src, 6)?;
 
-    let channelcount = be_u16(src)?;
+    let mut channelcount = be_u16(src)? as u32;
     let samplesize = be_u16(src)?;
 
     // Skip uninteresting fields.
     skip(src, 4)?;
 
-    let samplerate = be_u32(src)?;
+    let mut samplerate = (be_u32(src)? >> 16) as f64; // 16.16 fixed point;
 
     match version {
         0 => (),
         1 => {
             // Quicktime sound sample description version 1.
             // Skip uninteresting fields.
             skip(src, 16)?;
         },
+        2 => {
+            // Quicktime sound sample description version 2.
+            skip(src, 4)?;
+            samplerate = f64::from_bits(be_u64(src)?);
+            channelcount = be_u32(src)?;
+            skip(src, 20)?;
+        }
         _ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
     }
 
-    // Skip chan/etc. for now.
     let mut codec_type = CodecType::Unknown;
     let mut codec_specific = None;
     if name == BoxType::MP3AudioSampleEntry {
         codec_type = CodecType::MP3;
         codec_specific = Some(AudioCodecSpecific::MP3);
     }
     let mut protection_info = Vec::new();
     let mut iter = src.box_iter();
@@ -1903,16 +1911,25 @@ fn read_audio_sample_entry<T: Read>(src:
                 if name != BoxType::ProtectedAudioSampleEntry {
                     return Err(Error::InvalidData("malformed audio sample entry"));
                 }
                 let sinf = read_sinf(&mut b)?;
                 log!("{:?} (sinf)", sinf);
                 codec_type = CodecType::EncryptedAudio;
                 vec_push(&mut protection_info, sinf)?;
             }
+            BoxType::AudioChannelLayoutAtom => {
+                if name != BoxType::LPCMAudioSampleEntry {
+                    return Err(Error::InvalidData("malformed audio sample entry"));
+                }
+                // skip 'chan' for now.
+                skip_box_content(&mut b)?;
+                codec_type = CodecType::LPCM;
+                codec_specific = Some(AudioCodecSpecific::LPCM);
+            }
             _ => {
                 log!("Unsupported audio codec, box {:?} found", b.head.name);
                 skip_box_content(&mut b)?;
             }
         }
         check_parser_state!(b.content);
     }
 
--- a/media/libstagefright/binding/mp4parse/src/tests.rs
+++ b/media/libstagefright/binding/mp4parse/src/tests.rs
@@ -1123,8 +1123,53 @@ fn read_invalid_pssh() {
     let mut stream = iter.next_box().unwrap().unwrap();
     let mut context = super::MediaContext::new();
 
     match super::read_moov(&mut stream, &mut context) {
         Err(Error::InvalidData(s)) => assert_eq!(s, "read_buf size exceeds BUF_SIZE_LIMIT"),
         _ => panic!("unexpected result with invalid descriptor"),
     }
 }
+
+#[test]
+fn read_stsd_lpcm() {
+    // Extract from sample converted by ffmpeg.
+    // "ffmpeg -i ./gizmo-short.mp4 -acodec pcm_s16le -ar 96000 -vcodec copy -f mov gizmo-short.mov"
+    let lpcm =
+        vec![
+                              0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x10, 0xff,
+            0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x48, 0x40, 0xf7, 0x70, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+            0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x00,
+            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x63,
+            0x68, 0x61, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x64, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00,
+        ];
+
+    let mut stream = make_box(BoxSize::Auto, b"lpcm", |s| {
+        s.append_bytes(lpcm.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_audio_sample_entry(&mut stream).unwrap();
+
+    assert_eq!(codec_type, super::CodecType::LPCM);
+
+    match sample_entry {
+        super::SampleEntry::Audio(a) => {
+            assert_eq!(a.samplerate, 96000.0);
+            assert_eq!(a.channelcount, 1);
+            match a.codec_specific {
+                super::AudioCodecSpecific::LPCM => (),
+                _ => panic!("it should be LPCM!"),
+            }
+        },
+        _ => panic!("it should be a audio sample entry!"),
+    }
+
+}
+
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -94,19 +94,22 @@ fn public_api() {
                     mp4::AudioCodecSpecific::OpusSpecificBox(opus) => {
                         // We don't enter in here, we just check if fields are public.
                         assert!(opus.version > 0);
                         "Opus"
                     }
                     mp4::AudioCodecSpecific::MP3 => {
                         "MP3"
                     }
+                    mp4::AudioCodecSpecific::LPCM => {
+                        "LPCM"
+                    }
                 }, "ES");
                 assert!(a.samplesize > 0);
-                assert!(a.samplerate > 0);
+                assert!(a.samplerate > 0.0);
             }
             Some(mp4::SampleEntry::Unknown) | None => {}
         }
     }
 }
 
 #[test]
 fn public_audio_tenc() {
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -427,17 +427,17 @@ pub unsafe extern fn mp4parse_get_track_
             AudioCodecSpecific::OpusSpecificBox(_) =>
                 mp4parse_codec::OPUS,
             AudioCodecSpecific::FLACSpecificBox(_) =>
                 mp4parse_codec::FLAC,
             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::AAC =>
                 mp4parse_codec::AAC,
             AudioCodecSpecific::ES_Descriptor(ref esds) if esds.audio_codec == CodecType::MP3 =>
                 mp4parse_codec::MP3,
-            AudioCodecSpecific::ES_Descriptor(_) =>
+            AudioCodecSpecific::ES_Descriptor(_) | AudioCodecSpecific::LPCM =>
                 mp4parse_codec::UNKNOWN,
             AudioCodecSpecific::MP3 =>
                 mp4parse_codec::MP3,
         },
         Some(SampleEntry::Video(ref video)) => match video.codec_specific {
             VideoCodecSpecific::VPxConfig(_) =>
                 mp4parse_codec::VP9,
             VideoCodecSpecific::AVCConfig(_) =>
@@ -516,19 +516,19 @@ pub unsafe extern fn mp4parse_get_track_
         None => return mp4parse_status::INVALID,
     };
 
     let audio = match *audio {
         SampleEntry::Audio(ref x) => x,
         _ => return mp4parse_status::INVALID,
     };
 
-    (*info).channels = audio.channelcount;
+    (*info).channels = audio.channelcount as u16;
     (*info).bit_depth = audio.samplesize;
-    (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point
+    (*info).sample_rate = audio.samplerate as u32;
 
     match audio.codec_specific {
         AudioCodecSpecific::ES_Descriptor(ref v) => {
             if v.codec_esds.len() > std::u32::MAX as usize {
                 return mp4parse_status::INVALID;
             }
             (*info).extra_data.length = v.codec_esds.len() as u32;
             (*info).extra_data.data = v.codec_esds.as_ptr();
@@ -567,17 +567,17 @@ pub unsafe extern fn mp4parse_get_track_
                             return mp4parse_status::INVALID;
                         }
                         (*info).extra_data.length = v.len() as u32;
                         (*info).extra_data.data = v.as_ptr();
                     }
                 }
             }
         }
-        AudioCodecSpecific::MP3 => (),
+        AudioCodecSpecific::MP3 | AudioCodecSpecific::LPCM => (),
     }
 
     if let Some(p) = audio.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=cfeeab0036e14658f28f8df16c7ddede46ccf79a
+VER=17debc745a3c97ddaed61dde8caabce461d5001a
 
 # Accept version or commit from the command line.
 if test -n "$1"; then
   VER=$1
 fi
 
 echo "Fetching sources..."
 rm -rf _upstream