From 512dcfac01d5395855cf2aff96ecd62ededb05aa Mon Sep 17 00:00:00 2001 From: trav90 Date: Wed, 25 Jul 2018 22:37:39 -0500 Subject: [EME] Add support for sbgp and sgpd boxes in the traf box --- media/libstagefright/binding/Index.cpp | 46 +++++++ media/libstagefright/binding/MoofParser.cpp | 152 +++++++++++++++++++++ .../binding/include/mp4_demuxer/Index.h | 3 + .../binding/include/mp4_demuxer/MoofParser.h | 51 +++++++ 4 files changed, 252 insertions(+) (limited to 'media') 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 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 SampleIterator::GetNext() return sample.forget(); } +CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() +{ + nsTArray& 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()) { + 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()) { + 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 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 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 mEntries; +}; + +struct CencSampleEncryptionInfoEntry final +{ +public: + CencSampleEncryptionInfoEntry() { } + + bool Init(BoxReader& aReader); + + bool mIsEncrypted = false; + uint8_t mIVSize = 0; + nsTArray mKeyId; +}; + +class Sgpd final : public Atom // SampleGroupDescription box. +{ +public: + explicit Sgpd(Box& aBox); + + AtomType mGroupingType; + nsTArray mEntries; +}; + class AuxInfo { public: AuxInfo(int64_t aMoofOffset, Saiz& aSaiz, Saio& aSaio); @@ -182,6 +230,9 @@ public: Interval mTimeRange; FallibleTArray mIndex; + nsTArray mSampleEncryptionInfoEntries; + nsTArray mSampleToGroupEntries; + nsTArray mSaizs; nsTArray mSaios; -- cgit v1.2.3 From 165a8d5057687bbcfc14ccdc084579372faf9860 Mon Sep 17 00:00:00 2001 From: trav90 Date: Wed, 25 Jul 2018 22:39:12 -0500 Subject: [EME] Add support for sbgp and sgpd boxes occuring in the sampletable --- media/libstagefright/binding/Index.cpp | 32 +++++++++++++++++----- media/libstagefright/binding/MoofParser.cpp | 21 +++++++++++--- .../binding/include/mp4_demuxer/MoofParser.h | 8 ++++-- 3 files changed, 48 insertions(+), 13 deletions(-) (limited to 'media') diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index a3a3f9847..eb039b5d6 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -181,9 +181,16 @@ CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() 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* sampleToGroupEntries = + currentMoof->mFragmentSampleToGroupEntries.Length() != 0 + ? ¤tMoof->mFragmentSampleToGroupEntries + : &mIndex->mMoofParser->mTrackSampleToGroupEntries; + uint32_t seen = 0; - for (SampleToGroupEntry& entry : currentMoof->mSampleToGroupEntries) { + for (SampleToGroupEntry& entry : *sampleToGroupEntries) { if (seen + entry.mSampleCount > mCurrentSample) { sampleToGroupEntry = &entry; break; @@ -200,20 +207,31 @@ CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry() // 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; } - uint32_t group_index = sampleToGroupEntry->mGroupDescriptionIndex; + nsTArray* entries = + &mIndex->mMoofParser->mTrackSampleEncryptionInfoEntries; + + uint32_t groupIndex = sampleToGroupEntry->mGroupDescriptionIndex; - if (group_index > SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) { - group_index -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase; + // 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 indexed - return group_index > currentMoof->mSampleEncryptionInfoEntries.Length() + // The group_index is one based. + return groupIndex > entries->Length() ? nullptr - : ¤tMoof->mSampleEncryptionInfoEntries.ElementAt(group_index - 1); + : &entries->ElementAt(groupIndex - 1); } Sample* SampleIterator::Get() diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 970bc2325..4abad747b 100644 --- a/media/libstagefright/binding/MoofParser.cpp +++ b/media/libstagefright/binding/MoofParser.cpp @@ -332,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); + } } } } @@ -497,6 +509,7 @@ 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); @@ -506,14 +519,14 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, S } else if (box.IsType("sgpd")) { Sgpd sgpd(box); if (sgpd.IsValid() && sgpd.mGroupingType == "seig") { - mSampleEncryptionInfoEntries.Clear(); - mSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries); + mFragmentSampleEncryptionInfoEntries.Clear(); + mFragmentSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries); } } else if (box.IsType("sbgp")) { Sbgp sbgp(box); if (sbgp.IsValid() && sbgp.mGroupingType == "seig") { - mSampleToGroupEntries.Clear(); - mSampleToGroupEntries.AppendElements(sbgp.mEntries); + mFragmentSampleToGroupEntries.Clear(); + mFragmentSampleToGroupEntries.AppendElements(sbgp.mEntries); } } else if (box.IsType("saiz")) { mSaizs.AppendElement(Saiz(box, aSinf.mDefaultEncryptionType)); diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h index 47bd27287..bb895555c 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -230,8 +230,8 @@ public: Interval mTimeRange; FallibleTArray mIndex; - nsTArray mSampleEncryptionInfoEntries; - nsTArray mSampleToGroupEntries; + nsTArray mFragmentSampleEncryptionInfoEntries; + nsTArray mFragmentSampleToGroupEntries; nsTArray mSaizs; nsTArray mSaios; @@ -297,6 +297,10 @@ public: Tfdt mTfdt; Edts mEdts; Sinf mSinf; + + nsTArray mTrackSampleEncryptionInfoEntries; + nsTArray mTrackSampleToGroupEntries; + nsTArray& Moofs() { return mMoofs; } private: void ScanForMetadata(mozilla::MediaByteRange& aFtyp, -- cgit v1.2.3 From b7143013045c9a37e33de59b6646301af17427c4 Mon Sep 17 00:00:00 2001 From: trav90 Date: Wed, 25 Jul 2018 22:56:45 -0500 Subject: [EME] Add error handling in MoofParser for sbgp and sgpd boxes --- media/libstagefright/binding/MoofParser.cpp | 75 ++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 17 deletions(-) (limited to 'media') diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp index 4abad747b..771be3428 100644 --- a/media/libstagefright/binding/MoofParser.cpp +++ b/media/libstagefright/binding/MoofParser.cpp @@ -1086,7 +1086,10 @@ Sbgp::Sbgp(Box& aBox) return; } - uint32_t flags = reader->ReadU32(); + uint32_t flags; + if (!reader->ReadU32(flags)) { + return; + } const uint8_t version = flags >> 24; flags = flags & 0xffffff; @@ -1098,13 +1101,22 @@ Sbgp::Sbgp(Box& aBox) return; } - mGroupingType = reader->ReadU32(); + uint32_t groupType; + if (!reader->ReadU32(groupType)) { + return; + } + mGroupingType = groupType; if (version == 1) { - mGroupingTypeParam = reader->Read32(); + if (reader->ReadU32(mGroupingTypeParam)) { + false; + } } - uint32_t count = reader->ReadU32(); + uint32_t count; + if (!reader->ReadU32(count)) { + return; + } // Make sure we can read all the entries. need = sizeof(uint32_t) * 2 * count; @@ -1115,8 +1127,12 @@ Sbgp::Sbgp(Box& aBox) } for (uint32_t i = 0; i < count; i++) { - uint32_t sampleCount = reader->ReadU32(); - uint32_t groupDescriptionIndex = reader->ReadU32(); + uint32_t sampleCount; + uint32_t groupDescriptionIndex; + if (!reader->ReadU32(sampleCount) || + !reader->ReadU32(groupDescriptionIndex)) { + return; + } SampleToGroupEntry entry(sampleCount, groupDescriptionIndex); mEntries.AppendElement(entry); @@ -1134,7 +1150,10 @@ Sgpd::Sgpd(Box& aBox) return; } - uint32_t flags = reader->ReadU32(); + uint32_t flags; + if (!reader->ReadU32(flags)) { + return; + } const uint8_t version = flags >> 24; flags = flags & 0xffffff; @@ -1145,19 +1164,28 @@ Sgpd::Sgpd(Box& aBox) return; } - mGroupingType = reader->ReadU32(); + 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) { - defaultLength = reader->ReadU32(); + if (!reader->ReadU32(defaultLength)) { + return; + } if (defaultLength < entrySize && defaultLength != 0) { return; } } - uint32_t count = reader->ReadU32(); + uint32_t count; + if (!reader->ReadU32(count)) { + return; + } // Make sure we have sufficient remaining bytes to read the entries. need = @@ -1170,7 +1198,10 @@ Sgpd::Sgpd(Box& aBox) } for (uint32_t i = 0; i < count; ++i) { if (version == 1 && defaultLength == 0) { - uint32_t descriptionLength = reader->ReadU32(); + uint32_t descriptionLength; + if (!reader->ReadU32(descriptionLength)) { + return; + } if (descriptionLength < entrySize) { return; } @@ -1190,16 +1221,26 @@ Sgpd::Sgpd(Box& aBox) bool CencSampleEncryptionInfoEntry::Init(BoxReader& aReader) { // Skip a reserved byte. - aReader->ReadU8(); - - uint8_t possiblePatternInfo = aReader->ReadU8(); - uint8_t flag = aReader->ReadU8(); + uint8_t skip; + if (!aReader->ReadU8(skip)) { + return false; + } - mIVSize = aReader->ReadU8(); + 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) { - mKeyId.AppendElement(aReader->ReadU8()); + if (!aReader->ReadU8(key)) { + return false; + } + mKeyId.AppendElement(key); } mIsEncrypted = flag != 0; -- cgit v1.2.3