diff options
Diffstat (limited to 'media/libstagefright/binding')
-rw-r--r-- | media/libstagefright/binding/Index.cpp | 64 | ||||
-rw-r--r-- | media/libstagefright/binding/MoofParser.cpp | 206 | ||||
-rw-r--r-- | media/libstagefright/binding/include/mp4_demuxer/Index.h | 3 | ||||
-rw-r--r-- | media/libstagefright/binding/include/mp4_demuxer/MoofParser.h | 55 |
4 files changed, 328 insertions, 0 deletions
diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index 9d6ab5028..eb039b5d6 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -143,6 +143,11 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext() writer->mCrypto.mValid = true; writer->mCrypto.mIVSize = ivSize; + CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry(); + if (sampleInfo) { + writer->mCrypto.mKeyId.AppendElements(sampleInfo->mKeyId); + } + if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) { return nullptr; } @@ -170,6 +175,65 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext() return sample.forget(); } +CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() +{ + nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs(); + Moof* currentMoof = &moofs[mCurrentMoof]; + SampleToGroupEntry* sampleToGroupEntry = nullptr; + + // Default to using the sample to group entries for the fragment, otherwise + // fall back to the sample to group entries for the track. + nsTArray<SampleToGroupEntry>* sampleToGroupEntries = + currentMoof->mFragmentSampleToGroupEntries.Length() != 0 + ? ¤tMoof->mFragmentSampleToGroupEntries + : &mIndex->mMoofParser->mTrackSampleToGroupEntries; + + uint32_t seen = 0; + + for (SampleToGroupEntry& entry : *sampleToGroupEntries) { + if (seen + entry.mSampleCount > mCurrentSample) { + sampleToGroupEntry = &entry; + break; + } + seen += entry.mSampleCount; + } + + // ISO-14496-12 Section 8.9.2.3 and 8.9.4 : group description index + // (1) ranges from 1 to the number of sample group entries in the track + // level SampleGroupDescription Box, or (2) takes the value 0 to + // indicate that this sample is a member of no group, in this case, the + // sample is associated with the default values specified in + // TrackEncryption Box, or (3) starts at 0x10001, i.e. the index value + // 1, with the value 1 in the top 16 bits, to reference fragment-local + // SampleGroupDescription Box. + + // According to the spec, ISO-14496-12, the sum of the sample counts in this + // box should be equal to the total number of samples, and, if less, the + // reader should behave as if an extra SampleToGroupEntry existed, with + // groupDescriptionIndex 0. + + if (!sampleToGroupEntry || sampleToGroupEntry->mGroupDescriptionIndex == 0) { + return nullptr; + } + + nsTArray<CencSampleEncryptionInfoEntry>* entries = + &mIndex->mMoofParser->mTrackSampleEncryptionInfoEntries; + + uint32_t groupIndex = sampleToGroupEntry->mGroupDescriptionIndex; + + // If the first bit is set to a one, then we should use the sample group + // descriptions from the fragment. + if (groupIndex > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { + groupIndex -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; + entries = ¤tMoof->mFragmentSampleEncryptionInfoEntries; + } + + // The group_index is one based. + return groupIndex > entries->Length() + ? nullptr + : &entries->ElementAt(groupIndex - 1); +} + Sample* SampleIterator::Get() { if (!mIndex->mMoofParser) { diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 491967a12..771be3428 100644 --- a/media/libstagefright/binding/MoofParser.cpp +++ b/media/libstagefright/binding/MoofParser.cpp @@ -27,6 +27,8 @@ namespace mp4_demuxer using namespace stagefright; using namespace mozilla; +const uint32_t kKeyIdSize = 16; + bool MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges) { @@ -330,6 +332,18 @@ MoofParser::ParseStbl(Box& aBox) for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { if (box.IsType("stsd")) { ParseStsd(box); + } else if (box.IsType("sgpd")) { + Sgpd sgpd(box); + if (sgpd.IsValid() && sgpd.mGroupingType == "seig") { + mTrackSampleEncryptionInfoEntries.Clear(); + mTrackSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries); + } + } else if (box.IsType("sbgp")) { + Sbgp sbgp(box); + if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { + mTrackSampleToGroupEntries.Clear(); + mTrackSampleToGroupEntries.AppendElements(sbgp.mEntries); + } } } } @@ -495,12 +509,25 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, S MOZ_ASSERT(aDecodeTime); Tfhd tfhd(aTrex); Tfdt tfdt; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { if (box.IsType("tfhd")) { tfhd = Tfhd(box, aTrex); } else if (!aTrex.mTrackId || tfhd.mTrackId == aTrex.mTrackId) { if (box.IsType("tfdt")) { tfdt = Tfdt(box); + } else if (box.IsType("sgpd")) { + Sgpd sgpd(box); + if (sgpd.IsValid() && sgpd.mGroupingType == "seig") { + mFragmentSampleEncryptionInfoEntries.Clear(); + mFragmentSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries); + } + } else if (box.IsType("sbgp")) { + Sbgp sbgp(box); + if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { + mFragmentSampleToGroupEntries.Clear(); + mFragmentSampleToGroupEntries.AppendElements(sbgp.mEntries); + } } else if (box.IsType("saiz")) { mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType)); } else if (box.IsType("saio")) { @@ -1050,5 +1077,184 @@ Saio::Saio(Box& aBox, AtomType aDefaultType) mValid = true; } +Sbgp::Sbgp(Box& aBox) +{ + BoxReader reader(aBox); + + if (!reader->CanReadType<uint32_t>()) { + LOG(Sbgp, "Incomplete Box (missing flags)"); + return; + } + + uint32_t flags; + if (!reader->ReadU32(flags)) { + return; + } + const uint8_t version = flags >> 24; + flags = flags & 0xffffff; + + // Make sure we have enough bytes to read as far as the count. + uint32_t need = (version == 1 ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2; + if (reader->Remaining() < need) { + LOG(Sbgp, "Incomplete Box (have:%lld, need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + + uint32_t groupType; + if (!reader->ReadU32(groupType)) { + return; + } + mGroupingType = groupType; + + if (version == 1) { + if (reader->ReadU32(mGroupingTypeParam)) { + false; + } + } + + uint32_t count; + if (!reader->ReadU32(count)) { + return; + } + + // Make sure we can read all the entries. + need = sizeof(uint32_t) * 2 * count; + if (reader->Remaining() < need) { + LOG(Sbgp, "Incomplete Box (have:%lld, need:%lld). Failed to read entries", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + + for (uint32_t i = 0; i < count; i++) { + uint32_t sampleCount; + uint32_t groupDescriptionIndex; + if (!reader->ReadU32(sampleCount) || + !reader->ReadU32(groupDescriptionIndex)) { + return; + } + + SampleToGroupEntry entry(sampleCount, groupDescriptionIndex); + mEntries.AppendElement(entry); + } + + mValid = true; +} + +Sgpd::Sgpd(Box& aBox) +{ + BoxReader reader(aBox); + + if (!reader->CanReadType<uint32_t>()) { + LOG(Sgpd, "Incomplete Box (missing flags)"); + return; + } + + uint32_t flags; + if (!reader->ReadU32(flags)) { + return; + } + const uint8_t version = flags >> 24; + flags = flags & 0xffffff; + + uint32_t need = ((flags & 1) ? sizeof(uint32_t) : 0) + sizeof(uint32_t) * 2; + if (reader->Remaining() < need) { + LOG(Sgpd, "Incomplete Box (have:%lld need:%lld)", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + + uint32_t groupType; + if (!reader->ReadU32(groupType)) { + return; + } + mGroupingType = groupType; + + const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize; + uint32_t defaultLength = 0; + + if (version == 1) { + if (!reader->ReadU32(defaultLength)) { + return; + } + if (defaultLength < entrySize && defaultLength != 0) { + return; + } + } + + uint32_t count; + if (!reader->ReadU32(count)) { + return; + } + + // Make sure we have sufficient remaining bytes to read the entries. + need = + count * (sizeof(uint32_t) * (version == 1 && defaultLength == 0 ? 2 : 1) + + kKeyIdSize * sizeof(uint8_t)); + if (reader->Remaining() < need) { + LOG(Sgpd, "Incomplete Box (have:%lld need:%lld). Failed to read entries", + (uint64_t)reader->Remaining(), (uint64_t)need); + return; + } + for (uint32_t i = 0; i < count; ++i) { + if (version == 1 && defaultLength == 0) { + uint32_t descriptionLength; + if (!reader->ReadU32(descriptionLength)) { + return; + } + if (descriptionLength < entrySize) { + return; + } + } + + CencSampleEncryptionInfoEntry entry; + bool valid = entry.Init(reader); + if (!valid) { + return; + } + mEntries.AppendElement(entry); + } + + mValid = true; +} + +bool CencSampleEncryptionInfoEntry::Init(BoxReader& aReader) +{ + // Skip a reserved byte. + uint8_t skip; + if (!aReader->ReadU8(skip)) { + return false; + } + + uint8_t possiblePatternInfo; + uint8_t flag; + if (!aReader->ReadU8(possiblePatternInfo) || + !aReader->ReadU8(flag) || + !aReader->ReadU8(mIVSize)) { + return false; + } + + // Read the key id. + uint8_t key; + for (uint32_t i = 0; i < kKeyIdSize; ++i) { + if (!aReader->ReadU8(key)) { + return false; + } + mKeyId.AppendElement(key); + } + + mIsEncrypted = flag != 0; + + if (mIsEncrypted) { + if (mIVSize != 8 && mIVSize != 16) { + return false; + } + } else if (mIVSize != 0) { + return false; + } + + return true; +} + #undef LOG } diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h index d566c9459..ca254f73d 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/Index.h +++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h @@ -32,6 +32,9 @@ public: Microseconds GetNextKeyframeTime(); private: Sample* Get(); + + CencSampleEncryptionInfoEntry* GetSampleEncryptionEntry(); + void Next(); RefPtr<Index> mIndex; friend class Index; diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h index 814f806fc..bb895555c 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -17,6 +17,7 @@ typedef int64_t Microseconds; class Box; class BoxContext; +class BoxReader; class Moof; class Mvhd : public Atom @@ -160,6 +161,53 @@ public: FallibleTArray<uint64_t> mOffsets; }; +struct SampleToGroupEntry +{ +public: + static const uint32_t kTrackGroupDescriptionIndexBase = 0; + static const uint32_t kFragmentGroupDescriptionIndexBase = 0x10000; + + SampleToGroupEntry(uint32_t aSampleCount, uint32_t aGroupDescriptionIndex) + : mSampleCount(aSampleCount) + , mGroupDescriptionIndex(aGroupDescriptionIndex) + { + } + + uint32_t mSampleCount; + uint32_t mGroupDescriptionIndex; +}; + +class Sbgp final : public Atom // SampleToGroup box. +{ +public: + explicit Sbgp(Box& aBox); + + AtomType mGroupingType; + uint32_t mGroupingTypeParam; + nsTArray<SampleToGroupEntry> mEntries; +}; + +struct CencSampleEncryptionInfoEntry final +{ +public: + CencSampleEncryptionInfoEntry() { } + + bool Init(BoxReader& aReader); + + bool mIsEncrypted = false; + uint8_t mIVSize = 0; + nsTArray<uint8_t> mKeyId; +}; + +class Sgpd final : public Atom // SampleGroupDescription box. +{ +public: + explicit Sgpd(Box& aBox); + + AtomType mGroupingType; + nsTArray<CencSampleEncryptionInfoEntry> mEntries; +}; + class AuxInfo { public: AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio); @@ -182,6 +230,9 @@ public: Interval<Microseconds> mTimeRange; FallibleTArray<Sample> mIndex; + nsTArray<CencSampleEncryptionInfoEntry> mFragmentSampleEncryptionInfoEntries; + nsTArray<SampleToGroupEntry> mFragmentSampleToGroupEntries; + nsTArray<Saiz> mSaizs; nsTArray<Saio> mSaios; @@ -246,6 +297,10 @@ public: Tfdt mTfdt; Edts mEdts; Sinf mSinf; + + nsTArray<CencSampleEncryptionInfoEntry> mTrackSampleEncryptionInfoEntries; + nsTArray<SampleToGroupEntry> mTrackSampleToGroupEntries; + nsTArray<Moof>& Moofs() { return mMoofs; } private: void ScanForMetadata(mozilla::MediaByteRange& aFtyp, |