Bug 1393045 - support QT AudioSampleEntry v2. r?kinetik draft
authorAlfredo.Yang <ayang@mozilla.com>
Wed, 08 Nov 2017 09:21:58 +0800
changeset 694656 f98008cb456b57d8e8d38e211874d7f76a3f5f90
parent 694565 40df5dd35fdb7ce3652fe4448ac8961c075c928e
child 739384 e720bb7126b44dce54aa3b6f5ad52e7a35765125
push id88185
push userbmo:ayang@mozilla.com
push dateWed, 08 Nov 2017 01:22:31 +0000
reviewerskinetik
bugs1393045
milestone58.0a1
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