Bug 1393045 - support QT AudioSampleEntry v2. r?kinetik
MozReview-Commit-ID: GchJJBrdKYm
--- 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