Bug 1066427 - Add Common Encryption support to the MP4 demuxer for non-fragmented MP4 files - r=kentuckyfriedtakahe

This commit is contained in:
Edwin Flores 2014-09-24 10:04:48 +12:00
parent ad9da9149f
commit af6576a9da
4 changed files with 357 additions and 2 deletions

View File

@ -358,7 +358,9 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
mLastTrack(NULL), mLastTrack(NULL),
mFileMetaData(new MetaData), mFileMetaData(new MetaData),
mFirstSINF(NULL), mFirstSINF(NULL),
mIsDrm(false) { mIsDrm(false),
mDrmScheme(0)
{
} }
MPEG4Extractor::~MPEG4Extractor() { MPEG4Extractor::~MPEG4Extractor() {
@ -956,6 +958,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
break; break;
} }
case FOURCC('s', 'c', 'h', 'm'):
{
if (!mDataSource->getUInt32(data_offset, &mDrmScheme)) {
return ERROR_IO;
}
*offset += chunk_size;
break;
}
case FOURCC('t', 'e', 'n', 'c'): case FOURCC('t', 'e', 'n', 'c'):
{ {
if (chunk_size < 32) { if (chunk_size < 32) {
@ -1431,6 +1443,34 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
break; break;
} }
case FOURCC('s', 'a', 'i', 'z'):
{
status_t err =
mLastTrack->sampleTable->setSampleAuxiliaryInformationSizeParams(
data_offset, chunk_data_size, mDrmScheme);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
case FOURCC('s', 'a', 'i', 'o'):
{
status_t err =
mLastTrack->sampleTable->setSampleAuxiliaryInformationOffsetParams(
data_offset, chunk_data_size, mDrmScheme);
if (err != OK) {
return err;
}
*offset += chunk_size;
break;
}
// @xyz // @xyz
case FOURCC('\xA9', 'x', 'y', 'z'): case FOURCC('\xA9', 'x', 'y', 'z'):
{ {
@ -3407,6 +3447,28 @@ status_t MPEG4Source::read(
mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); mBuffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);
} }
if (mSampleTable->hasCencInfo()) {
Vector<uint16_t> clearSizes;
Vector<uint32_t> cipherSizes;
uint8_t iv[16];
status_t err = mSampleTable->getSampleCencInfo(
mCurrentSampleIndex, clearSizes, cipherSizes, iv);
if (err != OK) {
return err;
}
const auto& meta = mBuffer->meta_data();
meta->setData(kKeyPlainSizes, 0, clearSizes.array(),
clearSizes.size() * sizeof(uint16_t));
meta->setData(kKeyEncryptedSizes, 0, cipherSizes.array(),
cipherSizes.size() * sizeof(uint32_t));
meta->setData(kKeyCryptoIV, 0, iv, sizeof(iv));
meta->setInt32(kKeyCryptoDefaultIVSize, mDefaultIVSize);
meta->setInt32(kKeyCryptoMode, mCryptoMode);
meta->setData(kKeyCryptoKey, 0, mCryptoKey, 16);
}
++mCurrentSampleIndex; ++mCurrentSampleIndex;
*out = mBuffer; *out = mBuffer;

View File

@ -39,6 +39,8 @@ const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
// static // static
const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2'); const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
const uint32_t kAuxTypeCenc = FOURCC('c', 'e', 'n', 'c');
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
struct SampleTable::CompositionDeltaLookup { struct SampleTable::CompositionDeltaLookup {
@ -127,7 +129,11 @@ SampleTable::SampleTable(const sp<DataSource> &source)
mNumSyncSamples(0), mNumSyncSamples(0),
mSyncSamples(NULL), mSyncSamples(NULL),
mLastSyncSampleIndex(0), mLastSyncSampleIndex(0),
mSampleToChunkEntries(NULL) { mSampleToChunkEntries(NULL),
mCencInfo(NULL),
mCencInfoCount(0),
mCencDefaultSize(0)
{
mSampleIterator = new SampleIterator(this); mSampleIterator = new SampleIterator(this);
} }
@ -150,6 +156,15 @@ SampleTable::~SampleTable() {
delete[] mTimeToSample; delete[] mTimeToSample;
mTimeToSample = NULL; mTimeToSample = NULL;
if (mCencInfo) {
for (uint32_t i = 0; i < mCencInfoCount; i++) {
if (mCencInfo[i].mSubsamples) {
delete[] mCencInfo[i].mSubsamples;
}
}
delete[] mCencInfo;
}
delete mSampleIterator; delete mSampleIterator;
mSampleIterator = NULL; mSampleIterator = NULL;
} }
@ -432,6 +447,224 @@ status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size)
return OK; return OK;
} }
static status_t
validateCencBoxHeader(
sp<DataSource>& data_source, off64_t& data_offset,
uint8_t* out_version, uint32_t* out_aux_type) {
*out_aux_type = 0;
if (data_source->readAt(data_offset++, out_version, 1) < 1) {
ALOGE("error reading sample aux info header");
return ERROR_IO;
}
uint32_t flags;
if (!data_source->getUInt24(data_offset, &flags)) {
ALOGE("error reading sample aux info flags");
return ERROR_IO;
}
data_offset += 3;
if (flags & 1) {
uint32_t aux_type;
uint32_t aux_param;
if (!data_source->getUInt32(data_offset, &aux_type) ||
!data_source->getUInt32(data_offset + 4, &aux_param)) {
ALOGE("error reading aux info type");
return ERROR_IO;
}
data_offset += 8;
*out_aux_type = aux_type;
}
return OK;
}
status_t
SampleTable::setSampleAuxiliaryInformationSizeParams(
off64_t data_offset, size_t data_size, uint32_t drm_scheme) {
off64_t data_end = data_offset + data_size;
uint8_t version;
uint32_t aux_type;
status_t err = validateCencBoxHeader(
mDataSource, data_offset, &version, &aux_type);
if (err != OK) {
return err;
}
if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) {
// Quietly skip aux types we don't care about.
return OK;
}
if (!mCencSizes.isEmpty() || mCencDefaultSize) {
ALOGE("duplicate cenc saiz box");
return ERROR_MALFORMED;
}
if (version) {
ALOGV("unsupported cenc saiz version");
return ERROR_UNSUPPORTED;
}
if (mDataSource->readAt(
data_offset++, &mCencDefaultSize, sizeof(mCencDefaultSize))
< sizeof(mCencDefaultSize)) {
return ERROR_IO;
}
if (!mDataSource->getUInt32(data_offset, &mCencInfoCount)) {
return ERROR_IO;
}
data_offset += 4;
if (!mCencDefaultSize) {
mCencSizes.insertAt(0, 0, mCencInfoCount);
if (mDataSource->readAt(
data_offset, mCencSizes.editArray(), mCencInfoCount)
< mCencInfoCount) {
return ERROR_IO;
}
data_offset += mCencInfoCount;
}
CHECK(data_offset == data_end);
return parseSampleCencInfo();
}
status_t
SampleTable::setSampleAuxiliaryInformationOffsetParams(
off64_t data_offset, size_t data_size, uint32_t drm_scheme) {
off64_t data_end = data_offset + data_size;
uint8_t version;
uint32_t aux_type;
status_t err = validateCencBoxHeader(mDataSource, data_offset,
&version, &aux_type);
if (err != OK) {
return err;
}
if (aux_type && aux_type != kAuxTypeCenc && drm_scheme != kAuxTypeCenc) {
// Quietly skip aux types we don't care about.
return OK;
}
if (!mCencOffsets.isEmpty()) {
ALOGE("duplicate cenc saio box");
return ERROR_MALFORMED;
}
uint32_t cencOffsetCount;
if (!mDataSource->getUInt32(data_offset, &cencOffsetCount)) {
ALOGE("error reading cenc aux info offset count");
return ERROR_IO;
}
data_offset += 4;
mCencOffsets.setCapacity(cencOffsetCount);
if (!version) {
for (uint32_t i = 0; i < cencOffsetCount; i++) {
uint32_t tmp;
if (!mDataSource->getUInt32(data_offset, &tmp)) {
ALOGE("error reading cenc aux info offsets");
return ERROR_IO;
}
mCencOffsets.push(tmp);
data_offset += 4;
}
} else {
for (uint32_t i = 0; i < cencOffsetCount; i++) {
if (!mDataSource->getUInt64(data_offset, &mCencOffsets.editItemAt(i))) {
ALOGE("error reading cenc aux info offsets");
return ERROR_IO;
}
data_offset += 8;
}
}
CHECK(data_offset == data_end);
return parseSampleCencInfo();
}
status_t
SampleTable::parseSampleCencInfo() {
if (!mCencDefaultSize && !mCencInfoCount || mCencOffsets.isEmpty()) {
// We don't have all the cenc information we need yet. Quietly fail and
// hope we get the data we need later in the track header.
ALOGV("Got half of cenc saio/saiz pair. Deferring parse until we get the other half.");
return OK;
}
if (!mCencSizes.isEmpty() && mCencOffsets.size() > 1 &&
mCencSizes.size() != mCencOffsets.size()) {
return ERROR_MALFORMED;
}
mCencInfo = new SampleCencInfo[mCencInfoCount];
for (uint32_t i = 0; i < mCencInfoCount; i++) {
mCencInfo[i].mSubsamples = nullptr;
}
uint64_t nextOffset = mCencOffsets[0];
for (uint32_t i = 0; i < mCencInfoCount; i++) {
uint8_t size = mCencDefaultSize ? mCencDefaultSize : mCencSizes[i];
uint64_t offset = mCencOffsets.size() == 1 ? nextOffset : mCencOffsets[i];
nextOffset = offset + size;
auto& info = mCencInfo[i];
if (size < IV_BYTES) {
ALOGE("cenc aux info too small");
return ERROR_MALFORMED;
}
if (mDataSource->readAt(offset, info.mIV, IV_BYTES) < IV_BYTES) {
ALOGE("couldn't read init vector");
return ERROR_IO;
}
offset += IV_BYTES;
if (size == IV_BYTES) {
info.mSubsampleCount = 0;
continue;
}
if (size < IV_BYTES + sizeof(info.mSubsampleCount)) {
ALOGE("subsample count overflows sample aux info buffer");
return ERROR_MALFORMED;
}
if (!mDataSource->getUInt16(offset, &info.mSubsampleCount)) {
ALOGE("error reading sample cenc info subsample count");
return ERROR_IO;
}
offset += sizeof(info.mSubsampleCount);
if (size < IV_BYTES + sizeof(info.mSubsampleCount) + info.mSubsampleCount * 6) {
ALOGE("subsample descriptions overflow sample aux info buffer");
return ERROR_MALFORMED;
}
info.mSubsamples = new SampleCencInfo::SubsampleSizes[info.mSubsampleCount];
for (uint16_t j = 0; j < info.mSubsampleCount; j++) {
auto& subsample = info.mSubsamples[j];
if (!mDataSource->getUInt16(offset, &subsample.mClearBytes) ||
!mDataSource->getUInt32(offset + sizeof(subsample.mClearBytes),
&subsample.mCipherBytes)) {
ALOGE("error reading cenc subsample aux info");
return ERROR_IO;
}
offset += 6;
}
}
return OK;
}
uint32_t SampleTable::countChunkOffsets() const { uint32_t SampleTable::countChunkOffsets() const {
return mNumChunkOffsets; return mNumChunkOffsets;
} }
@ -838,6 +1071,31 @@ uint32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex); return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);
} }
status_t
SampleTable::getSampleCencInfo(
uint32_t sample_index, Vector<uint16_t>& clear_sizes,
Vector<uint32_t>& cipher_sizes, uint8_t iv[]) {
CHECK(clear_sizes.isEmpty() && cipher_sizes.isEmpty());
if (sample_index >= mCencInfoCount) {
ALOGE("cenc info requested for out of range sample index");
return ERROR_MALFORMED;
}
auto& info = mCencInfo[sample_index];
clear_sizes.setCapacity(info.mSubsampleCount);
cipher_sizes.setCapacity(info.mSubsampleCount);
for (uint32_t i = 0; i < info.mSubsampleCount; i++) {
clear_sizes.push(info.mSubsamples[i].mClearBytes);
cipher_sizes.push(info.mSubsamples[i].mCipherBytes);
}
memcpy(iv, info.mIV, IV_BYTES);
return OK;
}
} // namespace stagefright } // namespace stagefright
#undef LOG_TAG #undef LOG_TAG

View File

@ -131,6 +131,7 @@ private:
bool mIsDrm; bool mIsDrm;
TrackExtends mTrackExtends; TrackExtends mTrackExtends;
uint32_t mDrmScheme;
status_t parseDrmSINF(off64_t *offset, off64_t data_offset); status_t parseDrmSINF(off64_t *offset, off64_t data_offset);

View File

@ -24,6 +24,7 @@
#include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaErrors.h>
#include <utils/RefBase.h> #include <utils/RefBase.h>
#include <utils/threads.h> #include <utils/threads.h>
#include <utils/Vector.h>
namespace stagefright { namespace stagefright {
@ -53,6 +54,14 @@ public:
status_t setSyncSampleParams(off64_t data_offset, size_t data_size); status_t setSyncSampleParams(off64_t data_offset, size_t data_size);
status_t setSampleAuxiliaryInformationSizeParams(off64_t aDataOffset,
size_t aDataSize,
uint32_t aDrmScheme);
status_t setSampleAuxiliaryInformationOffsetParams(off64_t aDataOffset,
size_t aDataSize,
uint32_t aDrmScheme);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
uint32_t countChunkOffsets() const; uint32_t countChunkOffsets() const;
@ -84,6 +93,13 @@ public:
status_t findThumbnailSample(uint32_t *sample_index); status_t findThumbnailSample(uint32_t *sample_index);
bool hasCencInfo() const { return !!mCencInfo; }
status_t getSampleCencInfo(uint32_t aSampleIndex,
Vector<uint16_t>& aClearSizes,
Vector<uint32_t>& aCipherSizes,
uint8_t aIV[]);
protected: protected:
~SampleTable(); ~SampleTable();
@ -137,6 +153,22 @@ private:
}; };
SampleToChunkEntry *mSampleToChunkEntries; SampleToChunkEntry *mSampleToChunkEntries;
enum { IV_BYTES = 16 };
struct SampleCencInfo {
uint8_t mIV[IV_BYTES];
uint16_t mSubsampleCount;
struct SubsampleSizes {
uint16_t mClearBytes;
uint32_t mCipherBytes;
} * mSubsamples;
} * mCencInfo;
uint32_t mCencInfoCount;
uint8_t mCencDefaultSize;
Vector<uint8_t> mCencSizes;
Vector<uint64_t> mCencOffsets;
friend struct SampleIterator; friend struct SampleIterator;
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size); status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
@ -146,6 +178,8 @@ private:
void buildSampleEntriesTable(); void buildSampleEntriesTable();
status_t parseSampleCencInfo();
SampleTable(const SampleTable &); SampleTable(const SampleTable &);
SampleTable &operator=(const SampleTable &); SampleTable &operator=(const SampleTable &);
}; };