--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -9,16 +9,18 @@
#include <media/stagefright/foundation/ABitReader.h>
#include "media/stagefright/MetaData.h"
#include "media/stagefright/MediaDefs.h"
#include "media/stagefright/Utils.h"
#include "mozilla/ArrayUtils.h"
#include "include/ESDS.h"
#ifdef MOZ_RUST_MP4PARSE
+// OpusDecoder header is really needed only by MP4 in rust
+#include "OpusDecoder.h"
#include "mp4parse.h"
#endif
using namespace stagefright;
namespace mp4_demuxer
{
@@ -183,20 +185,83 @@ MP4VideoInfo::Update(const MetaData* aMe
mCodecSpecificConfig->AppendElements(cdata, size);
}
}
}
}
#ifdef MOZ_RUST_MP4PARSE
+static void
+UpdateTrackProtectedInfo(mozilla::TrackInfo& aConfig,
+ const mp4parser_sinf_info& aSinf)
+{
+ if (aSinf.is_encrypted != 0) {
+ aConfig.mCrypto.mValid = true;
+ aConfig.mCrypto.mMode = aSinf.is_encrypted;
+ aConfig.mCrypto.mIVSize = aSinf.iv_size;
+ aConfig.mCrypto.mKeyId.AppendElements(aSinf.kid.data, aSinf.kid.length);
+ }
+}
+
+void
+MP4AudioInfo::Update(const mp4parse_track_info* track,
+ const mp4parse_track_audio_info* audio)
+{
+ UpdateTrackProtectedInfo(*this, audio->protected_data);
+
+ if (track->codec == MP4PARSE_CODEC_OPUS) {
+ mMimeType = NS_LITERAL_CSTRING("audio/opus");
+ // The Opus decoder expects the container's codec delay or
+ // pre-skip value, in microseconds, as a 64-bit int at the
+ // start of the codec-specific config blob.
+ MOZ_ASSERT(audio->codec_specific_config.data);
+ MOZ_ASSERT(audio->codec_specific_config.length >= 12);
+ uint16_t preskip =
+ LittleEndian::readUint16(audio->codec_specific_config.data + 10);
+ OpusDataDecoder::AppendCodecDelay(mCodecSpecificConfig,
+ mozilla::FramesToUsecs(preskip, 48000).value());
+ } else if (track->codec == MP4PARSE_CODEC_AAC) {
+ mMimeType = MEDIA_MIMETYPE_AUDIO_AAC;
+ } else if (track->codec == MP4PARSE_CODEC_FLAC) {
+ mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
+ } else if (track->codec == MP4PARSE_CODEC_MP3) {
+ mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
+ }
+
+ mRate = audio->sample_rate;
+ mChannels = audio->channels;
+ mBitDepth = audio->bit_depth;
+ mDuration = track->duration;
+ mMediaTime = track->media_time;
+ mTrackId = track->track_id;
+
+ // TODO: mProfile (kKeyAACProfile in stagefright)
+
+ const uint8_t* cdata = audio->codec_specific_config.data;
+ size_t size = audio->codec_specific_config.length;
+ if (size > 0) {
+ mCodecSpecificConfig->AppendElements(cdata, size);
+
+ if (size > 1) {
+ ABitReader br(cdata, size);
+ mExtendedProfile = br.getBits(5);
+
+ if (mExtendedProfile == 31) { // AAC-ELD => additional 6 bits
+ mExtendedProfile = 32 + br.getBits(6);
+ }
+ }
+ }
+}
+
void
MP4VideoInfo::Update(const mp4parse_track_info* track,
const mp4parse_track_video_info* video)
{
+ UpdateTrackProtectedInfo(*this, video->protected_data);
if (track->codec == MP4PARSE_CODEC_AVC) {
mMimeType = MEDIA_MIMETYPE_VIDEO_AVC;
} else if (track->codec == MP4PARSE_CODEC_VP9) {
mMimeType = NS_LITERAL_CSTRING("video/vp9");
}
mTrackId = track->track_id;
mDuration = track->duration;
mMediaTime = track->media_time;
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -20,18 +20,16 @@
#include "mp4_demuxer/Stream.h"
#include "MediaPrefs.h"
#include <limits>
#include <stdint.h>
#include <vector>
#ifdef MOZ_RUST_MP4PARSE
-// OpusDecoder header is really needed only by MP4 in rust
-#include "OpusDecoder.h"
#include "mp4parse.h"
struct FreeMP4Parser { void operator()(mp4parse_parser* aPtr) { mp4parse_free(aPtr); } };
#endif
using namespace stagefright;
namespace mp4_demuxer
@@ -289,25 +287,29 @@ MP4Metadata::GetTrackInfo(mozilla::Track
MOZ_DIAGNOSTIC_ASSERT(infoRust->mKind == info->mKind);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mLabel == info->mLabel);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mLanguage == info->mLanguage);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mEnabled == info->mEnabled);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mTrackId == info->mTrackId);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mMimeType == info->mMimeType);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mDuration == info->mDuration);
MOZ_DIAGNOSTIC_ASSERT(infoRust->mMediaTime == info->mMediaTime);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mValid == info->mCrypto.mValid);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mMode == info->mCrypto.mMode);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mIVSize == info->mCrypto.mIVSize);
+ MOZ_DIAGNOSTIC_ASSERT(infoRust->mCrypto.mKeyId == info->mCrypto.mKeyId);
switch (aType) {
case mozilla::TrackInfo::kAudioTrack: {
AudioInfo *audioRust = infoRust->GetAsAudioInfo(), *audio = info->GetAsAudioInfo();
MOZ_DIAGNOSTIC_ASSERT(audioRust->mRate == audio->mRate);
MOZ_DIAGNOSTIC_ASSERT(audioRust->mChannels == audio->mChannels);
MOZ_DIAGNOSTIC_ASSERT(audioRust->mBitDepth == audio->mBitDepth);
// TODO: These fields aren't implemented in the Rust demuxer yet.
//MOZ_DIAGNOSTIC_ASSERT(audioRust->mProfile != audio->mProfile);
- //MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile != audio->mExtendedProfile);
+ MOZ_DIAGNOSTIC_ASSERT(audioRust->mExtendedProfile == audio->mExtendedProfile);
break;
}
case mozilla::TrackInfo::kVideoTrack: {
VideoInfo *videoRust = infoRust->GetAsVideoInfo(), *video = info->GetAsVideoInfo();
MOZ_DIAGNOSTIC_ASSERT(videoRust->mDisplay == video->mDisplay);
MOZ_DIAGNOSTIC_ASSERT(videoRust->mImage == video->mImage);
MOZ_DIAGNOSTIC_ASSERT(*videoRust->mExtraData == *video->mExtraData);
// mCodecSpecificConfig is for video/mp4-es, not video/avc. Since video/mp4-es
@@ -794,46 +796,18 @@ MP4MetadataRust::GetTrackInfo(mozilla::T
switch (aType) {
case TrackInfo::TrackType::kAudioTrack: {
mp4parse_track_audio_info audio;
auto rv = mp4parse_get_track_audio_info(mRustParser.get(), trackIndex.value(), &audio);
if (rv != MP4PARSE_OK) {
MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_audio_info returned error %d", rv));
return nullptr;
}
- auto track = mozilla::MakeUnique<mozilla::AudioInfo>();
- if (info.codec == MP4PARSE_CODEC_OPUS) {
- track->mMimeType = NS_LITERAL_CSTRING("audio/opus");
- // The Opus decoder expects the container's codec delay or
- // pre-skip value, in microseconds, as a 64-bit int at the
- // start of the codec-specific config blob.
- MOZ_ASSERT(audio.codec_specific_config.data);
- MOZ_ASSERT(audio.codec_specific_config.length >= 12);
- uint16_t preskip =
- LittleEndian::readUint16(audio.codec_specific_config.data + 10);
- MOZ_LOG(sLog, LogLevel::Debug,
- ("Copying opus pre-skip value of %d as CodecDelay.",(int)preskip));
- OpusDataDecoder::AppendCodecDelay(track->mCodecSpecificConfig,
- mozilla::FramesToUsecs(preskip, 48000).value());
- } else if (info.codec == MP4PARSE_CODEC_AAC) {
- track->mMimeType = MEDIA_MIMETYPE_AUDIO_AAC;
- } else if (info.codec == MP4PARSE_CODEC_FLAC) {
- track->mMimeType = MEDIA_MIMETYPE_AUDIO_FLAC;
- } else if (info.codec == MP4PARSE_CODEC_MP3) {
- track->mMimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
- }
- track->mCodecSpecificConfig->AppendElements(
- audio.codec_specific_config.data,
- audio.codec_specific_config.length);
- track->mRate = audio.sample_rate;
- track->mChannels = audio.channels;
- track->mBitDepth = audio.bit_depth;
- track->mDuration = info.duration;
- track->mMediaTime = info.media_time;
- track->mTrackId = info.track_id;
+ auto track = mozilla::MakeUnique<MP4AudioInfo>();
+ track->Update(&info, &audio);
e = Move(track);
}
break;
case TrackInfo::TrackType::kVideoTrack: {
mp4parse_track_video_info video;
auto rv = mp4parse_get_track_video_info(mRustParser.get(), trackIndex.value(), &video);
if (rv != MP4PARSE_OK) {
MOZ_LOG(sLog, LogLevel::Warning, ("mp4parse_get_track_video_info returned error %d", rv));
--- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h
@@ -68,16 +68,21 @@ private:
class MP4AudioInfo : public mozilla::AudioInfo
{
public:
MP4AudioInfo() = default;
void Update(const stagefright::MetaData* aMetaData,
const char* aMimeType);
+#ifdef MOZ_RUST_MP4PARSE
+ void Update(const mp4parse_track_info* track,
+ const mp4parse_track_audio_info* audio);
+#endif
+
virtual bool IsValid() const override;
};
class MP4VideoInfo : public mozilla::VideoInfo
{
public:
MP4VideoInfo() = default;
--- a/media/libstagefright/binding/include/mp4parse.h
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -52,29 +52,38 @@ typedef struct mp4parse_byte_data {
uint32_t length;
uint8_t const* data;
} mp4parse_byte_data;
typedef struct mp4parse_pssh_info {
mp4parse_byte_data data;
} mp4parse_pssh_info;
+typedef struct mp4parser_sinf_info {
+ uint32_t is_encrypted;
+ uint8_t iv_size;
+ mp4parse_byte_data kid;
+} mp4parser_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_config;
+ mp4parser_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;
uint16_t image_height;
mp4parse_byte_data extra_data;
+ mp4parser_sinf_info protected_data;
} mp4parse_track_video_info;
typedef struct mp4parse_fragment_info {
uint64_t fragment_duration;
} mp4parse_fragment_info;
typedef struct mp4parse_parser mp4parse_parser;
--- a/media/libstagefright/binding/mp4parse/src/boxes.rs
+++ b/media/libstagefright/binding/mp4parse/src/boxes.rs
@@ -55,9 +55,13 @@ box_database!(
OpusSampleEntry 0x4f707573, // "Opus"
OpusSpecificBox 0x644f7073, // "dOps"
ProtectedVisualSampleEntry 0x656e6376, // "encv" - Need to check official name in spec.
ProtectedAudioSampleEntry 0x656e6361, // "enca" - Need to check official name in spec.
MovieExtendsBox 0x6d766578, // "mvex"
MovieExtendsHeaderBox 0x6d656864, // "mehd"
QTWaveAtom 0x77617665, // "wave" - quicktime atom
ProtectionSystemSpecificHeaderBox 0x70737368, // "pssh"
+ SchemeInformationBox 0x73636869, // "schi"
+ TrackEncryptionBox 0x74656e63, // "tenc"
+ ProtectionSchemeInformationBox 0x73696e66, // "sinf"
+ OriginalFormatBox 0x66726d61, // "frma"
);
--- a/media/libstagefright/binding/mp4parse/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse/src/lib.rs
@@ -203,16 +203,17 @@ pub enum SampleEntry {
Video(VideoSampleEntry),
Unknown,
}
#[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>,
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum AudioCodecSpecific {
@@ -223,30 +224,32 @@ pub enum AudioCodecSpecific {
#[derive(Debug, Clone)]
pub struct AudioSampleEntry {
data_reference_index: u16,
pub channelcount: u16,
pub samplesize: u16,
pub samplerate: u32,
pub codec_specific: AudioCodecSpecific,
+ pub protection_info: Vec<ProtectionSchemeInfoBox>,
}
#[derive(Debug, Clone)]
pub enum VideoCodecSpecific {
AVCConfig(Vec<u8>),
VPxConfig(VPxConfigBox),
}
#[derive(Debug, Clone)]
pub struct VideoSampleEntry {
data_reference_index: u16,
pub width: u16,
pub height: u16,
pub codec_specific: VideoCodecSpecific,
+ pub protection_info: Vec<ProtectionSchemeInfoBox>,
}
/// Represent a Video Partition Codec Configuration 'vpcC' box (aka vp9).
#[derive(Debug, Clone)]
pub struct VPxConfigBox {
profile: u8,
level: u8,
pub bit_depth: u8,
@@ -301,16 +304,29 @@ pub struct ProtectionSystemSpecificHeade
pub system_id: ByteData,
pub kid: Vec<ByteData>,
pub data: ByteData,
// The entire pssh box (include header) required by Gecko.
pub box_content: ByteData,
}
+#[derive(Debug, Default, Clone)]
+pub struct TrackEncryptionBox {
+ pub is_encrypted: u32,
+ pub iv_size: u8,
+ pub kid: Vec<u8>,
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct ProtectionSchemeInfoBox {
+ pub code_name: String,
+ pub tenc: Option<TrackEncryptionBox>,
+}
+
/// Internal data structures.
#[derive(Debug, Default)]
pub struct MediaContext {
pub timescale: Option<MediaTimeScale>,
/// Tracks found in the file.
pub tracks: Vec<Track>,
pub mvex: Option<MovieExtendsBox>,
pub psshs: Vec<ProtectionSystemSpecificHeaderBox>
@@ -1228,23 +1244,26 @@ fn read_ds_descriptor(data: &[u8], esds:
(0x4, 44100), (0x5, 32000), (0x6, 24000), (0x7, 22050),
(0x8, 16000), (0x9, 12000), (0xa, 11025), (0xb, 8000),
(0xc, 7350)];
let des = &mut Cursor::new(data);
let audio_specific_config = be_u16(des)?;
+ let audio_object_type = audio_specific_config >> 11;
+
let sample_index = (audio_specific_config & 0x07FF) >> 7;
let channel_counts = (audio_specific_config & 0x007F) >> 3;
let sample_frequency =
frequency_table.iter().find(|item| item.0 == sample_index).map(|x| x.1);
+ esds.audio_object_type = Some(audio_object_type);
esds.audio_sample_rate = sample_frequency;
esds.audio_channel_count = Some(channel_counts);
Ok(())
}
fn read_dc_descriptor(data: &[u8], esds: &mut ES_Descriptor) -> Result<()> {
let des = &mut Cursor::new(data);
@@ -1471,54 +1490,65 @@ fn read_video_sample_entry<T: Read>(src:
let _compressorname = read_fixed_length_pascal_string(src, 32)?;
// Skip uninteresting fields.
skip(src, 4)?;
// Skip clap/pasp/etc. for now.
let mut codec_specific = None;
+ let mut protection_info = Vec::new();
let mut iter = src.box_iter();
while let Some(mut b) = iter.next_box()? {
match b.head.name {
BoxType::AVCConfigurationBox => {
if (name != BoxType::AVCSampleEntry &&
name != BoxType::AVC3SampleEntry &&
name != BoxType::ProtectedVisualSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed video sample entry"));
}
let avcc_size = b.head.size - b.head.offset;
if avcc_size > BUF_SIZE_LIMIT {
return Err(Error::InvalidData("avcC box exceeds BUF_SIZE_LIMIT"));
}
let avcc = read_buf(&mut b.content, avcc_size as usize)?;
+ log!("{:?} (avcc)", avcc);
// TODO(kinetik): Parse avcC box? For now we just stash the data.
codec_specific = Some(VideoCodecSpecific::AVCConfig(avcc));
}
BoxType::VPCodecConfigurationBox => { // vpcC
if (name != BoxType::VP8SampleEntry &&
name != BoxType::VP9SampleEntry) ||
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::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);
+ }
_ => skip_box_content(&mut b)?,
}
check_parser_state!(b.content);
}
codec_specific
.map(|codec_specific| SampleEntry::Video(VideoSampleEntry {
data_reference_index: data_reference_index,
width: width,
height: height,
codec_specific: codec_specific,
+ protection_info: protection_info,
}))
.ok_or_else(|| Error::InvalidData("malformed video sample entry"))
}
fn read_qt_wave_atom<T: Read>(src: &mut BMFFBox<T>) -> Result<ES_Descriptor> {
let mut codec_specific = None;
let mut iter = src.box_iter();
while let Some(mut b) = iter.next_box()? {
@@ -1532,24 +1562,16 @@ fn read_qt_wave_atom<T: Read>(src: &mut
}
codec_specific.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
}
/// Parse an audio description inside an stsd box.
fn read_audio_sample_entry<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleEntry> {
let name = src.get_header().name;
- track.codec_type = match name {
- // TODO(kinetik): stagefright inspects ESDS to detect MP3 (audio/mpeg).
- BoxType::MP4AudioSampleEntry => CodecType::AAC,
- BoxType::FLACSampleEntry => CodecType::FLAC,
- BoxType::OpusSampleEntry => CodecType::Opus,
- BoxType::ProtectedAudioSampleEntry => CodecType::EncryptedAudio,
- _ => CodecType::Unknown,
- };
// Skip uninteresting fields.
skip(src, 6)?;
let data_reference_index = be_u16(src)?;
// XXX(kinetik): This is "reserved" in BMFF, but some old QT MOV variant
// uses it, need to work out if we have to support it. Without checking
@@ -1575,16 +1597,17 @@ fn read_audio_sample_entry<T: Read>(src:
// Skip uninteresting fields.
skip(src, 16)?;
},
_ => return Err(Error::Unsupported("unsupported non-isom audio sample entry")),
}
// Skip chan/etc. for now.
let mut codec_specific = None;
+ let mut protection_info = Vec::new();
let mut iter = src.box_iter();
while let Some(mut b) = iter.next_box()? {
match b.head.name {
BoxType::ESDBox => {
if (name != BoxType::MP4AudioSampleEntry &&
name != BoxType::ProtectedAudioSampleEntry) ||
codec_specific.is_some() {
return Err(Error::InvalidData("malformed audio sample entry"));
@@ -1612,28 +1635,38 @@ fn read_audio_sample_entry<T: Read>(src:
track.codec_type = CodecType::Opus;
codec_specific = Some(AudioCodecSpecific::OpusSpecificBox(dops));
}
BoxType::QTWaveAtom => {
let qt_esds = read_qt_wave_atom(&mut b)?;
track.codec_type = qt_esds.audio_codec;
codec_specific = Some(AudioCodecSpecific::ES_Descriptor(qt_esds));
}
+ BoxType::ProtectionSchemeInformationBox => {
+ if name != BoxType::ProtectedAudioSampleEntry {
+ return Err(Error::InvalidData("malformed audio sample entry"));
+ }
+ let sinf = read_sinf(&mut b)?;
+ log!("{:?} (sinf)", sinf);
+ track.codec_type = CodecType::EncryptedAudio;
+ protection_info.push(sinf);
+ }
_ => skip_box_content(&mut b)?,
}
check_parser_state!(b.content);
}
codec_specific
.map(|codec_specific| SampleEntry::Audio(AudioSampleEntry {
data_reference_index: data_reference_index,
channelcount: channelcount,
samplesize: samplesize,
samplerate: samplerate,
codec_specific: codec_specific,
+ protection_info: protection_info,
}))
.ok_or_else(|| Error::InvalidData("malformed audio sample entry"))
}
/// Parse a stsd box.
fn read_stsd<T: Read>(src: &mut BMFFBox<T>, track: &mut Track) -> Result<SampleDescriptionBox> {
let (_, _) = read_fullbox_extra(src)?;
@@ -1677,16 +1710,75 @@ fn read_stsd<T: Read>(src: &mut BMFFBox<
// Padding could be added in some contents.
skip_box_remain(src)?;
Ok(SampleDescriptionBox {
descriptions: descriptions,
})
}
+fn read_sinf<T: Read>(src: &mut BMFFBox<T>) -> Result<ProtectionSchemeInfoBox> {
+ let mut sinf = ProtectionSchemeInfoBox::default();
+
+ let mut iter = src.box_iter();
+ while let Some(mut b) = iter.next_box()? {
+ match b.head.name {
+ BoxType::OriginalFormatBox => {
+ let frma = read_frma(&mut b)?;
+ sinf.code_name = frma;
+ },
+ BoxType::SchemeInformationBox => {
+ // We only need tenc box in schi box so far.
+ sinf.tenc = read_schi(&mut b)?;
+ },
+ _ => skip_box_content(&mut b)?,
+ }
+ check_parser_state!(b.content);
+ }
+
+ Ok(sinf)
+}
+
+fn read_schi<T: Read>(src: &mut BMFFBox<T>) -> Result<Option<TrackEncryptionBox>> {
+ let mut tenc = None;
+ let mut iter = src.box_iter();
+ while let Some(mut b) = iter.next_box()? {
+ match b.head.name {
+ BoxType::TrackEncryptionBox => {
+ if tenc.is_some() {
+ return Err(Error::InvalidData("tenc box should be only one at most in sinf box"));
+ }
+ tenc = Some(read_tenc(&mut b)?);
+ },
+ _ => skip_box_content(&mut b)?,
+ }
+ }
+
+ Ok(tenc)
+}
+
+fn read_tenc<T: Read>(src: &mut BMFFBox<T>) -> Result<TrackEncryptionBox> {
+ let (_, _) = read_fullbox_extra(src)?;
+
+ let default_is_encrypted = be_u24(src)?;
+ let default_iv_size = src.read_u8()?;
+ let default_kid = read_buf(src, 16)?;
+
+ Ok(TrackEncryptionBox {
+ is_encrypted: default_is_encrypted,
+ iv_size: default_iv_size,
+ kid: default_kid,
+ })
+}
+
+fn read_frma<T: Read>(src: &mut BMFFBox<T>) -> Result<String> {
+ let code_name = read_buf(src, 4)?;
+ String::from_utf8(code_name).map_err(From::from)
+}
+
/// Skip a number of bytes that we don't care to parse.
fn skip<T: Read>(src: &mut T, mut bytes: usize) -> Result<()> {
const BUF_SIZE: usize = 64 * 1024;
let mut buf = vec![0; BUF_SIZE];
while bytes > 0 {
let buf_size = cmp::min(bytes, BUF_SIZE);
let len = src.take(buf_size as u64).read(&mut buf)?;
if len == 0 {
new file mode 100644
index 0000000000000000000000000000000000000000..b827aa49aac417fce998e4d5541ddc8dd9438815
GIT binary patch
literal 1000
zc$}3{&2G~`5MIXtap=JnMFOcs=BFwL9FkJQg+mbq%1@;LqDTk{ZN2N*syDWFHZJtQ
zGjQh#5S$PXz=0Rw72v`Fi3i}q%sNpv0U<H+dVY7lnQs|m%c*%;<xs|qF^|_tk8Hsq
zL;qN+r9^#~^c^U7q{w-AD=%c&*QFRGQiKP&$->+H>o>w~C+@6ATQ_$jE$d!4js`+*
z#FypONO3SyV<Z1im1T`aq3TSq8S%ec<=Jm4-fY#pzsde7KAvq=%NM8vB`26`m+%)F
zZQ~@ZrgPi98(_Yg=36I?=KE+rGyAp=^=4mIyV>~uym;P!&+mOy$S}>O#FU=~WA`lC
zk3ZW`l!-!5@=%V3ZkI>si)}s>g(cm4uJU0@3xS#;{k&-6%R<Z48E;{vi?Ff1b71xg
zwBLaN;ncj5!-W5_Qi}zono-%B1Oa6Dr!doA+&p2!ix21=X&rxk)Fa;d(2TK=YQ<Hx
zL2Z{H$rKeQh%*MbfU%soF%)Z%gI7;h+U~Jy8UxOn=tJW8OXwf6S;3m5bo`4js06`+
z%m;L5Ovm5w_C~_c%ukgx6H3mw?}Kluc~uoIV3ZeNP_Mo<B>}GpZZF$a@AEp1uXK8G
or`KIaf2#t>C=(p~{PF3-PV4=*Yo{N6{dJEh$0_`|Ge05w4LiP>jsO4v
new file mode 100644
index 0000000000000000000000000000000000000000..5db21d091bc41d7b0bd9a5f0de118fef576c6eb5
GIT binary patch
literal 1094
zc$}3|&1(}u6rafkKYLgqhahR$LdAk^Xlhq49xC-CeguM$3L?sMXEwWZC%eo{w(3DV
zdG_B3c=8}#1P>m(3W9<lRxcg|^(+|Q%r<1(rUf56``&NfoA-Wi-XMf(fqIZeGVu^X
zm}GuK+my%<@S#932Kt)TS7p*-G$PLBC}vJuB((1{>TE<Rbgr~7tT+pGufFJBytL{H
zmNgbUw@cZQcZQvFIgyG9NMwEFNs@sP^DLxjn^@nC!sZ(b-X66RFQa{>1gpt2b|19O
z*{J4$S@=0hnEg>!SVvzLYS`dU$Nth2CCED1chtJcV`;}b*Sb4_<$v+FI-L&wV&EL3
zN`kJjSgKtojD`Pln1bfd>G%!}4}9;bwmWakmK)MojQm+dS+VXa;G{`<G&Y0510MAP
zFe({0NsBb+Xzr^~!GZ#nslJGv+{TeqYcbfrrING|v&njXw#QYV(qm}52}X0Th+R@0
z4&iU7MC#F$Q9g!K@NidfYk6k4i7F>S52}@eFVOpH`S_<JA71^kF)Gi0KKvF!+cIxv
zDiECL_pY0ke#mupAJ}e@n1{Vc-?GwsIXnB&t!b;Uj%g}VnTd@Au=ESK0{G#MCLqR7
z3i!0_rc{@eEb8hrTbDYi-ddk3J^kC98LefuIB9qtf~09&Ea*o0b2>3li7q@%i8*Xq
sy+N|TJ6msf^~S;?;H6Z`P<!l+pFdtdZ<U^XJ3n*t>!0iTzywC<A8D7YlK=n!
--- a/media/libstagefright/binding/mp4parse/tests/public.rs
+++ b/media/libstagefright/binding/mp4parse/tests/public.rs
@@ -4,20 +4,24 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
extern crate mp4parse as mp4;
use std::io::{Cursor, Read};
use std::fs::File;
+static MINI_MP4: &'static str = "tests/minimal.mp4";
+static AUDIO_EME_MP4: &'static str = "tests/bipbop-cenc-audioinit.mp4";
+static VIDEO_EME_MP4: &'static str = "tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4";
+
// Taken from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
#[test]
fn public_api() {
- let mut fd = File::open("tests/minimal.mp4").expect("Unknown file");
+ let mut fd = File::open(MINI_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
let mut c = Cursor::new(&buf);
let mut context = mp4::MediaContext::new();
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000)));
for track in context.tracks {
@@ -68,16 +72,17 @@ fn public_api() {
assert_eq!(tkhd.width, 0);
assert_eq!(tkhd.height, 0);
// track.data part
assert_eq!(match a.codec_specific {
mp4::AudioCodecSpecific::ES_Descriptor(esds) => {
assert_eq!(esds.audio_codec, mp4::CodecType::AAC);
assert_eq!(esds.audio_sample_rate.unwrap(), 48000);
+ assert_eq!(esds.audio_object_type.unwrap(), 2);
"ES"
}
mp4::AudioCodecSpecific::FLACSpecificBox(flac) => {
// STREAMINFO block must be present and first.
assert!(flac.blocks.len() > 0);
assert_eq!(flac.blocks[0].block_type, 0);
assert_eq!(flac.blocks[0].data.len(), 34);
"FLAC"
@@ -92,37 +97,107 @@ fn public_api() {
assert!(a.samplerate > 0);
}
Some(mp4::SampleEntry::Unknown) | None => {}
}
}
}
#[test]
-fn public_cenc() {
- let mut fd = File::open("tests/bipbop_480wp_1001kbps-cenc-video-key1-init.mp4").expect("Unknown file");
+fn public_audio_tenc() {
+ let kid =
+ vec![0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04,
+ 0x7e, 0x57, 0x1d, 0x04, 0x7e, 0x57, 0x1d, 0x04];
+
+ let mut fd = File::open(AUDIO_EME_MP4).expect("Unknown file");
+ let mut buf = Vec::new();
+ fd.read_to_end(&mut buf).expect("File error");
+
+ 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) => {
+ 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);
+ } else {
+ assert!(false, "Invalid test condition");
+ }
+ },
+ _=> {
+ assert!(false, "Invalid test condition");
+ },
+ }
+ },
+ _ => {
+ assert!(false, "Invalid test condition");
+ }
+ }
+ }
+}
+
+#[test]
+fn public_video_cenc() {
+ let system_id =
+ vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+ 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
+
+ let kid =
+ vec![0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03,
+ 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11];
+
+ let pssh_box =
+ vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x77, 0xef, 0xec,
+ 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
+ 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00, 0x00, 0x01,
+ 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03,
+ 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11,
+ 0x00, 0x00, 0x00, 0x00];
+
+ let mut fd = File::open(VIDEO_EME_MP4).expect("Unknown file");
let mut buf = Vec::new();
fd.read_to_end(&mut buf).expect("File error");
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) => {
+ 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);
+ } else {
+ assert!(false, "Invalid test condition");
+ }
+ },
+ _=> {
+ assert!(false, "Invalid test condition");
+ },
+ }
+ },
+ _ => {
+ assert!(false, "Invalid test condition");
+ }
+ }
}
- let system_id = vec![0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b];
-
- let kid = vec![0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x11];
-
- let pssh_box = vec![0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, 0x01, 0x00, 0x00, 0x00, 0x10, 0x77,
- 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, 0x00, 0x00,
- 0x00, 0x01, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57, 0x1d, 0x03, 0x7e, 0x57,
- 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00];
-
for pssh in context.psshs {
assert_eq!(pssh.system_id, system_id);
for kid_id in pssh.kid {
assert_eq!(kid_id, kid);
}
assert_eq!(pssh.data.len(), 0);
assert_eq!(pssh.box_content, pssh_box);
}
--- a/media/libstagefright/binding/mp4parse_capi/src/lib.rs
+++ b/media/libstagefright/binding/mp4parse_capi/src/lib.rs
@@ -77,29 +77,38 @@ pub enum mp4parse_error {
#[repr(C)]
#[derive(PartialEq, Debug)]
pub enum mp4parse_track_type {
MP4PARSE_TRACK_TYPE_VIDEO = 0,
MP4PARSE_TRACK_TYPE_AUDIO = 1,
}
+impl Default for mp4parse_track_type {
+ fn default() -> Self { mp4parse_track_type::MP4PARSE_TRACK_TYPE_VIDEO }
+}
+
#[repr(C)]
#[derive(PartialEq, Debug)]
pub enum mp4parse_codec {
MP4PARSE_CODEC_UNKNOWN,
MP4PARSE_CODEC_AAC,
MP4PARSE_CODEC_FLAC,
MP4PARSE_CODEC_OPUS,
MP4PARSE_CODEC_AVC,
MP4PARSE_CODEC_VP9,
MP4PARSE_CODEC_MP3,
}
+impl Default for mp4parse_codec {
+ fn default() -> Self { mp4parse_codec::MP4PARSE_CODEC_UNKNOWN }
+}
+
#[repr(C)]
+#[derive(Default)]
pub struct mp4parse_track_info {
pub track_type: mp4parse_track_type,
pub codec: mp4parse_codec,
pub track_id: u32,
pub duration: u64,
pub media_time: i64, // wants to be u64? understand how elst adjustment works
// TODO(kinetik): include crypto guff
}
@@ -127,38 +136,48 @@ impl mp4parse_byte_data {
}
#[repr(C)]
#[derive(Default)]
pub struct mp4parse_pssh_info {
pub data: mp4parse_byte_data,
}
+#[repr(C)]
#[derive(Default)]
+pub struct mp4parser_sinf_info {
+ pub is_encrypted: u32,
+ pub iv_size: u8,
+ pub kid: mp4parse_byte_data,
+}
+
#[repr(C)]
+#[derive(Default)]
pub struct mp4parse_track_audio_info {
pub channels: u16,
pub bit_depth: u16,
pub sample_rate: u32,
- // TODO(kinetik):
- // int32_t profile;
- // int32_t extended_profile; // check types
+ pub profile: u16,
pub codec_specific_config: mp4parse_byte_data,
+ pub protected_data: mp4parser_sinf_info,
}
#[repr(C)]
+#[derive(Default)]
pub struct mp4parse_track_video_info {
pub display_width: u32,
pub display_height: u32,
pub image_width: u16,
pub image_height: u16,
pub extra_data: mp4parse_byte_data,
+ pub protected_data: mp4parser_sinf_info,
}
#[repr(C)]
+#[derive(Default)]
pub struct mp4parse_fragment_info {
pub fragment_duration: u64,
// TODO:
// info in trex box.
}
// Even though mp4parse_parser is opaque to C, rusty-cheddar won't let us
// use more than one member, so we introduce *another* wrapper.
@@ -341,16 +360,19 @@ fn track_time_to_us(time: TrackScaledTim
/// Fill the supplied `mp4parse_track_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern fn mp4parse_get_track_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_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();
+
let context = (*parser).context_mut();
let track_index: usize = track_index as usize;
let info: &mut mp4parse_track_info = &mut *info;
if track_index >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
@@ -424,16 +446,19 @@ pub unsafe extern fn mp4parse_get_track_
/// Fill the supplied `mp4parse_track_audio_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern fn mp4parse_get_track_audio_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_audio_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();
+
let context = (*parser).context_mut();
if track_index as usize >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
let track = &context.tracks[track_index as usize];
@@ -464,16 +489,19 @@ pub unsafe extern fn mp4parse_get_track_
(*info).codec_specific_config.length = v.codec_esds.len() as u32;
(*info).codec_specific_config.data = v.codec_esds.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;
+ }
}
AudioCodecSpecific::FLACSpecificBox(ref flac) => {
// Return the STREAMINFO metadata block in the codec_specific.
let streaminfo = &flac.blocks[0];
if streaminfo.block_type != 0 || streaminfo.data.len() != 34 {
return MP4PARSE_ERROR_INVALID;
}
(*info).codec_specific_config.length = streaminfo.data.len() as u32;
@@ -498,26 +526,40 @@ pub unsafe extern fn mp4parse_get_track_
(*info).codec_specific_config.data = v.as_ptr();
}
}
}
}
}
}
+ match audio.protection_info.iter().find(|sinf| sinf.tenc.is_some()) {
+ Some(ref 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));
+ }
+ },
+ _ => {},
+ }
+
MP4PARSE_OK
}
/// Fill the supplied `mp4parse_track_video_info` with metadata for `track`.
#[no_mangle]
pub unsafe extern fn mp4parse_get_track_video_info(parser: *mut mp4parse_parser, track_index: u32, info: *mut mp4parse_track_video_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();
+
let context = (*parser).context_mut();
if track_index as usize >= context.tracks.len() {
return MP4PARSE_ERROR_BADARG;
}
let track = &context.tracks[track_index as usize];
@@ -547,26 +589,40 @@ 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) => {
+ 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));
+ }
+ },
+ _ => {},
+ }
+
MP4PARSE_OK
}
/// Fill the supplied `mp4parse_fragment_info` with metadata from fragmented file.
#[no_mangle]
pub unsafe extern fn mp4parse_get_fragment_info(parser: *mut mp4parse_parser, info: *mut mp4parse_fragment_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();
+
let context = (*parser).context();
let info: &mut mp4parse_fragment_info = &mut *info;
info.fragment_duration = 0;
let duration = match context.mvex {
Some(ref mvex) => mvex.fragment_duration,
None => return MP4PARSE_ERROR_INVALID,
@@ -615,16 +671,19 @@ pub unsafe extern fn mp4parse_is_fragmen
/// pssh box size (in 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();
+
let context = (*parser).context_mut();
let pssh_data = (*parser).pssh_data_mut();
let info: &mut mp4parse_pssh_info = &mut *info;
pssh_data.clear();
for pssh in &context.psshs {
let mut data_len = Vec::new();
match data_len.write_u32::<byteorder::NativeEndian>(pssh.box_content.len() as u32) {
@@ -738,16 +797,17 @@ fn arg_validation() {
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(std::ptr::null_mut(), 0, &mut dummy_info));
let mut dummy_video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
extra_data: mp4parse_byte_data::default(),
+ protected_data: Default::default(),
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(std::ptr::null_mut(), 0, &mut dummy_video));
let mut dummy_audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(std::ptr::null_mut(), 0, &mut dummy_audio));
}
}
@@ -783,16 +843,17 @@ fn arg_validation_with_parser() {
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_info(parser, 0, &mut dummy_info));
let mut dummy_video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
extra_data: mp4parse_byte_data::default(),
+ protected_data: Default::default(),
};
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 0, &mut dummy_video));
let mut dummy_audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 0, &mut dummy_audio));
mp4parse_free(parser);
}
@@ -855,16 +916,17 @@ fn arg_validation_with_data() {
assert_eq!(info.media_time, 21333);
let mut video = mp4parse_track_video_info {
display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
extra_data: mp4parse_byte_data::default(),
+ protected_data: Default::default(),
};
assert_eq!(MP4PARSE_OK, mp4parse_get_track_video_info(parser, 0, &mut video));
assert_eq!(video.display_width, 320);
assert_eq!(video.display_height, 240);
assert_eq!(video.image_width, 320);
assert_eq!(video.image_height, 240);
let mut audio = Default::default();
@@ -887,17 +949,19 @@ fn arg_validation_with_data() {
assert_eq!(info.track_id, 0);
assert_eq!(info.duration, 0);
assert_eq!(info.media_time, 0);
let mut video = mp4parse_track_video_info { display_width: 0,
display_height: 0,
image_width: 0,
image_height: 0,
- extra_data: mp4parse_byte_data::default(),};
+ extra_data: mp4parse_byte_data::default(),
+ protected_data: Default::default(),
+ };
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_video_info(parser, 3, &mut video));
assert_eq!(video.display_width, 0);
assert_eq!(video.display_height, 0);
assert_eq!(video.image_width, 0);
assert_eq!(video.image_height, 0);
let mut audio = Default::default();
assert_eq!(MP4PARSE_ERROR_BADARG, mp4parse_get_track_audio_info(parser, 3, &mut audio));