mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1066427 - Add Common Encryption support to the MP4 demuxer for non-fragmented MP4 files - r=kentuckyfriedtakahe
This commit is contained in:
parent
ad9da9149f
commit
af6576a9da
@ -358,7 +358,9 @@ MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
|
||||
mLastTrack(NULL),
|
||||
mFileMetaData(new MetaData),
|
||||
mFirstSINF(NULL),
|
||||
mIsDrm(false) {
|
||||
mIsDrm(false),
|
||||
mDrmScheme(0)
|
||||
{
|
||||
}
|
||||
|
||||
MPEG4Extractor::~MPEG4Extractor() {
|
||||
@ -956,6 +958,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
|
||||
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'):
|
||||
{
|
||||
if (chunk_size < 32) {
|
||||
@ -1431,6 +1443,34 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
|
||||
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
|
||||
case FOURCC('\xA9', 'x', 'y', 'z'):
|
||||
{
|
||||
@ -3407,6 +3447,28 @@ status_t MPEG4Source::read(
|
||||
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;
|
||||
|
||||
*out = mBuffer;
|
||||
|
@ -39,6 +39,8 @@ const uint32_t SampleTable::kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
|
||||
// static
|
||||
const uint32_t SampleTable::kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
|
||||
|
||||
const uint32_t kAuxTypeCenc = FOURCC('c', 'e', 'n', 'c');
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct SampleTable::CompositionDeltaLookup {
|
||||
@ -127,7 +129,11 @@ SampleTable::SampleTable(const sp<DataSource> &source)
|
||||
mNumSyncSamples(0),
|
||||
mSyncSamples(NULL),
|
||||
mLastSyncSampleIndex(0),
|
||||
mSampleToChunkEntries(NULL) {
|
||||
mSampleToChunkEntries(NULL),
|
||||
mCencInfo(NULL),
|
||||
mCencInfoCount(0),
|
||||
mCencDefaultSize(0)
|
||||
{
|
||||
mSampleIterator = new SampleIterator(this);
|
||||
}
|
||||
|
||||
@ -150,6 +156,15 @@ SampleTable::~SampleTable() {
|
||||
delete[] mTimeToSample;
|
||||
mTimeToSample = NULL;
|
||||
|
||||
if (mCencInfo) {
|
||||
for (uint32_t i = 0; i < mCencInfoCount; i++) {
|
||||
if (mCencInfo[i].mSubsamples) {
|
||||
delete[] mCencInfo[i].mSubsamples;
|
||||
}
|
||||
}
|
||||
delete[] mCencInfo;
|
||||
}
|
||||
|
||||
delete mSampleIterator;
|
||||
mSampleIterator = NULL;
|
||||
}
|
||||
@ -432,6 +447,224 @@ status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size)
|
||||
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 {
|
||||
return mNumChunkOffsets;
|
||||
}
|
||||
@ -838,6 +1071,31 @@ uint32_t SampleTable::getCompositionTimeOffset(uint32_t 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
|
||||
|
||||
#undef LOG_TAG
|
||||
|
@ -131,6 +131,7 @@ private:
|
||||
|
||||
bool mIsDrm;
|
||||
TrackExtends mTrackExtends;
|
||||
uint32_t mDrmScheme;
|
||||
|
||||
status_t parseDrmSINF(off64_t *offset, off64_t data_offset);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <media/stagefright/MediaErrors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/threads.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
namespace stagefright {
|
||||
|
||||
@ -53,6 +54,14 @@ public:
|
||||
|
||||
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;
|
||||
@ -84,6 +93,13 @@ public:
|
||||
|
||||
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:
|
||||
~SampleTable();
|
||||
|
||||
@ -137,6 +153,22 @@ private:
|
||||
};
|
||||
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;
|
||||
|
||||
status_t getSampleSize_l(uint32_t sample_index, size_t *sample_size);
|
||||
@ -146,6 +178,8 @@ private:
|
||||
|
||||
void buildSampleEntriesTable();
|
||||
|
||||
status_t parseSampleCencInfo();
|
||||
|
||||
SampleTable(const SampleTable &);
|
||||
SampleTable &operator=(const SampleTable &);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user