diff options
-rw-r--r-- | dom/media/fmp4/MP4Demuxer.cpp | 7 | ||||
-rw-r--r-- | media/libstagefright/binding/Index.cpp | 46 | ||||
-rw-r--r-- | media/libstagefright/binding/MoofParser.cpp | 152 | ||||
-rw-r--r-- | media/libstagefright/binding/include/mp4_demuxer/Index.h | 3 | ||||
-rw-r--r-- | media/libstagefright/binding/include/mp4_demuxer/MoofParser.h | 51 |
5 files changed, 258 insertions, 1 deletions
diff --git a/dom/media/fmp4/MP4Demuxer.cpp b/dom/media/fmp4/MP4Demuxer.cpp index 70b176699..a871d73b9 100644 --- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -350,7 +350,12 @@ MP4TrackDemuxer::GetNextSample() nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mMode = mInfo->mCrypto.mMode; writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize; - writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); + + // The length of the key will be zero if no key was specified in the sample + // information, meaning we should fall back to the default key. + if (writer->mCrypto.mKeyId.Length() == 0) { + writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); + } } return sample.forget(); } diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index 9d6ab5028..a3a3f9847 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,47 @@ already_AddRefed<MediaRawData> SampleIterator::GetNext() return sample.forget(); } +CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() +{ + nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs(); + Moof* currentMoof = &moofs[mCurrentMoof]; + SampleToGroupEntry* sampleToGroupEntry = nullptr; + + uint32_t seen = 0; + + for (SampleToGroupEntry& entry : currentMoof->mSampleToGroupEntries) { + 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. + + if (!sampleToGroupEntry || sampleToGroupEntry->mGroupDescriptionIndex == 0) { + return nullptr; + } + + uint32_t group_index = sampleToGroupEntry->mGroupDescriptionIndex; + + if (group_index > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { + group_index -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; + } + + // The group_index is one indexed + return group_index > currentMoof->mSampleEncryptionInfoEntries.Length() + ? nullptr + : ¤tMoof->mSampleEncryptionInfoEntries.ElementAt(group_index - 1); +} + Sample* SampleIterator::Get() { if (!mIndex->mMoofParser) { diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 491967a12..970bc2325 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) { @@ -501,6 +503,18 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, S } 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") { + mSampleEncryptionInfoEntries.Clear(); + mSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries); + } + } else if (box.IsType("sbgp")) { + Sbgp sbgp(box); + if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { + mSampleToGroupEntries.Clear(); + mSampleToGroupEntries.AppendElements(sbgp.mEntries); + } } else if (box.IsType("saiz")) { mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType)); } else if (box.IsType("saio")) { @@ -1050,5 +1064,143 @@ 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 = reader->ReadU32(); + 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; + } + + mGroupingType = reader->ReadU32(); + + if (version == 1) { + mGroupingTypeParam = reader->Read32(); + } + + uint32_t count = reader->ReadU32(); + + // 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 = reader->ReadU32(); + uint32_t groupDescriptionIndex = reader->ReadU32(); + + 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 = reader->ReadU32(); + 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; + } + + mGroupingType = reader->ReadU32(); + + const uint32_t entrySize = sizeof(uint32_t) + kKeyIdSize; + uint32_t defaultLength = 0; + + if (version == 1) { + defaultLength = reader->ReadU32(); + if (defaultLength < entrySize && defaultLength != 0) { + return; + } + } + + uint32_t count = reader->ReadU32(); + + // 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 = reader->ReadU32(); + 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. + aReader->ReadU8(); + + uint8_t possiblePatternInfo = aReader->ReadU8(); + uint8_t flag = aReader->ReadU8(); + + mIVSize = aReader->ReadU8(); + + // Read the key id. + for (uint32_t i = 0; i < kKeyIdSize; ++i) { + mKeyId.AppendElement(aReader->ReadU8()); + } + + 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..47bd27287 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> mSampleEncryptionInfoEntries; + nsTArray<SampleToGroupEntry> mSampleToGroupEntries; + nsTArray<Saiz> mSaizs; nsTArray<Saio> mSaios; |