Bug 1022434 - Extract crypto information from MP4 demuxer; r=cpearce

* * *
Bug 1022434 - "[EME] Expose decryption data in MP4Samples" []
This commit is contained in:
Anthony Jones 2014-07-17 16:27:13 +12:00
parent 0ff7099b5e
commit 438d2467e0
7 changed files with 193 additions and 20 deletions

View File

@ -56,6 +56,14 @@ Adts::ConvertEsdsToAdts(uint16_t aChannelCount, int8_t aFrequencyIndex,
header[6] = 0xfc;
aSample->Prepend(&header[0], ArrayLength(header));
if (aSample->crypto.valid) {
if (aSample->crypto.plain_sizes.Length() == 0) {
aSample->crypto.plain_sizes.AppendElement(kADTSHeaderSize);
aSample->crypto.encrypted_sizes.AppendElement(aSample->size - kADTSHeaderSize);
} else {
aSample->crypto.plain_sizes[0] += kADTSHeaderSize;
}
}
return true;
}

View File

@ -34,6 +34,94 @@ FindInt64(sp<MetaData>& mMetaData, uint32_t mKey)
return value;
}
template <typename T, size_t N>
static bool
FindData(sp<MetaData>& aMetaData, uint32_t aKey, mozilla::Vector<T, N>* aDest)
{
const void* data;
size_t size;
uint32_t type;
aDest->clear();
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
return false;
}
aDest->append(reinterpret_cast<const T*>(data), size / sizeof(T));
return true;
}
template <typename T>
static bool
FindData(sp<MetaData>& aMetaData, uint32_t aKey, nsTArray<T>* aDest)
{
const void* data;
size_t size;
uint32_t type;
aDest->Clear();
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(aKey, &type, &data, &size) || size % sizeof(T)) {
return false;
}
aDest->AppendElements(reinterpret_cast<const T*>(data), size / sizeof(T));
return true;
}
bool
CryptoFile::DoUpdate(sp<MetaData>& aMetaData)
{
const void* data;
size_t size;
uint32_t type;
// There's no point in checking that the type matches anything because it
// isn't set consistently in the MPEG4Extractor.
if (!aMetaData->findData(kKeyPssh, &type, &data, &size)) {
return false;
}
ByteReader reader(reinterpret_cast<const uint8_t*>(data), size);
while (reader.Remaining()) {
PsshInfo psshInfo;
if (!reader.ReadArray(psshInfo.uuid, 16)) {
return false;
}
if (!reader.CanReadType<uint32_t>()) {
return false;
}
auto length = reader.ReadType<uint32_t>();
if (!reader.ReadArray(psshInfo.data, length)) {
return false;
}
pssh.append(Move(psshInfo));
}
return true;
}
void
CryptoTrack::Update(sp<MetaData>& aMetaData)
{
valid = aMetaData->findInt32(kKeyCryptoMode, &mode) &&
aMetaData->findInt32(kKeyCryptoDefaultIVSize, &iv_size) &&
FindData(aMetaData, kKeyCryptoKey, &key);
}
void
CryptoSample::Update(sp<MetaData>& aMetaData)
{
CryptoTrack::Update(aMetaData);
valid = valid && FindData(aMetaData, kKeyPlainSizes, &plain_sizes) &&
FindData(aMetaData, kKeyEncryptedSizes, &encrypted_sizes) &&
FindData(aMetaData, kKeyCryptoIV, &iv);
}
void
AudioDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
{
@ -46,20 +134,18 @@ AudioDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
frequency_index = Adts::GetFrequencyIndex(samples_per_second);
aac_profile = FindInt32(aMetaData, kKeyAACProfile);
const void* data;
size_t size;
uint32_t type;
if (aMetaData->findData(kKeyESDS, &type, &data, &size)) {
extra_data.clear();
extra_data.append(reinterpret_cast<const uint8_t*>(data), size);
if (FindData(aMetaData, kKeyESDS, &extra_data)) {
ESDS esds(&extra_data[0], extra_data.length());
const void* data;
size_t size;
if (esds.getCodecSpecificInfo(&data, &size) == OK) {
audio_specific_config.append(reinterpret_cast<const uint8_t*>(data),
size);
}
}
crypto.Update(aMetaData);
}
bool
@ -78,18 +164,14 @@ VideoDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
display_width = FindInt32(aMetaData, kKeyDisplayWidth);
display_height = FindInt32(aMetaData, kKeyDisplayHeight);
const void* data;
size_t size;
uint32_t type;
if (aMetaData->findData(kKeyAVCC, &type, &data, &size) && size >= 7) {
extra_data.clear();
extra_data.append(reinterpret_cast<const uint8_t*>(data), size);
if (FindData(aMetaData, kKeyAVCC, &extra_data) && extra_data.length() >= 7) {
// Set size of the NAL length to 4. The demuxer formats its output with
// this NAL length size.
extra_data[4] |= 3;
annex_b = AnnexB::ConvertExtraDataToAnnexB(extra_data);
}
crypto.Update(aMetaData);
}
bool
@ -126,6 +208,8 @@ MP4Sample::Update()
is_sync_point = FindInt32(m, kKeyIsSyncFrame);
data = reinterpret_cast<uint8_t*>(mMediaBuffer->data());
size = mMediaBuffer->range_length();
crypto.Update(m);
}
void

View File

@ -6,6 +6,7 @@
#define BYTE_READER_H_
#include "mozilla/Vector.h"
#include "nsTArray.h"
namespace mp4_demuxer
{
@ -13,7 +14,7 @@ namespace mp4_demuxer
class ByteReader
{
public:
ByteReader(mozilla::Vector<uint8_t>& aData)
ByteReader(const mozilla::Vector<uint8_t>& aData)
: mPtr(&aData[0]), mRemaining(aData.length())
{
}
@ -72,6 +73,31 @@ public:
return result;
}
template <typename T> bool CanReadType() { return mRemaining >= sizeof(T); }
template <typename T> T ReadType()
{
auto ptr = Read(sizeof(T));
if (!ptr) {
MOZ_ASSERT(false);
return 0;
}
return *reinterpret_cast<const T*>(ptr);
}
template <typename T>
bool ReadArray(nsTArray<T>& aDest, size_t aLength)
{
auto ptr = Read(aLength * sizeof(T));
if (!ptr) {
return false;
}
aDest.Clear();
aDest.AppendElements(reinterpret_cast<const T*>(ptr), aLength);
return true;
}
private:
const uint8_t* mPtr;
size_t mRemaining;

View File

@ -7,6 +7,7 @@
#include "mozilla/Types.h"
#include "mozilla/Vector.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
namespace stagefright
@ -21,6 +22,51 @@ namespace mp4_demuxer
class MP4Demuxer;
struct PsshInfo
{
PsshInfo() {}
PsshInfo(PsshInfo&& aOther) : uuid(aOther.uuid), data(aOther.data) {}
nsTArray<uint8_t> uuid;
nsTArray<uint8_t> data;
};
class CryptoFile
{
public:
void Update(stagefright::sp<stagefright::MetaData>& aMetaData)
{
valid = DoUpdate(aMetaData);
}
bool valid;
mozilla::Vector<PsshInfo> pssh;
private:
bool DoUpdate(stagefright::sp<stagefright::MetaData>& aMetaData);
};
class CryptoTrack
{
public:
CryptoTrack() : valid(false) {}
void Update(stagefright::sp<stagefright::MetaData>& aMetaData);
bool valid;
int32_t mode;
int32_t iv_size;
nsTArray<uint8_t> key;
};
class CryptoSample : public CryptoTrack
{
public:
void Update(stagefright::sp<stagefright::MetaData>& aMetaData);
nsTArray<uint16_t> plain_sizes;
nsTArray<uint32_t> encrypted_sizes;
nsTArray<uint8_t> iv;
};
class AudioDecoderConfig
{
public:
@ -42,6 +88,7 @@ public:
int8_t frequency_index;
mozilla::Vector<uint8_t> extra_data;
mozilla::Vector<uint8_t> audio_specific_config;
CryptoTrack crypto;
void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
bool IsValid();
@ -69,6 +116,7 @@ public:
mozilla::Vector<uint8_t> extra_data; // Unparsed AVCDecoderConfig payload.
mozilla::Vector<uint8_t> annex_b; // Parsed version for sample prepend.
CryptoTrack crypto;
void Update(stagefright::sp<stagefright::MetaData>& aMetaData, const char* aMimeType);
bool IsValid();
@ -94,6 +142,8 @@ public:
uint8_t* data;
size_t size;
CryptoSample crypto;
void Prepend(const uint8_t* aData, size_t aSize);
private:

View File

@ -52,6 +52,7 @@ public:
MP4Sample* DemuxAudioSample();
MP4Sample* DemuxVideoSample();
const CryptoFile& Crypto() { return mCrypto; }
const AudioDecoderConfig& AudioConfig() { return mAudioConfig; }
const VideoDecoderConfig& VideoConfig() { return mVideoConfig; }
@ -62,6 +63,7 @@ public:
private:
AudioDecoderConfig mAudioConfig;
VideoDecoderConfig mVideoConfig;
CryptoFile mCrypto;
nsAutoPtr<StageFrightPrivate> mPrivate;
};

View File

@ -111,6 +111,8 @@ MP4Demuxer::Init()
mPrivate->mVideoIndex.Init(index);
}
}
sp<MetaData> metaData = e->getMetaData();
mCrypto.Update(metaData);
return mPrivate->mAudio.get() || mPrivate->mVideo.get();
}

View File

@ -137,12 +137,12 @@ private:
struct Sample {
off64_t offset;
size_t size;
uint32_t size;
uint32_t duration;
uint32_t ctsOffset;
uint8_t iv[16];
Vector<size_t> clearsizes;
Vector<size_t> encryptedsizes;
Vector<uint16_t> clearsizes;
Vector<uint32_t> encryptedsizes;
};
Vector<Sample> mCurrentSamples;
@ -1522,7 +1522,8 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
if (mPath.size() >= 2
&& mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a')) {
&& (mPath[mPath.size() - 2] == FOURCC('m', 'p', '4', 'a') ||
(mPath[mPath.size() - 2] == FOURCC('e', 'n', 'c', 'a')))) {
// Information from the ESDS must be relied on for proper
// setup of sample rate and channel count for MPEG4 Audio.
// The generic header appears to only contain generic