summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/libstagefright/binding/Index.cpp64
-rw-r--r--media/libstagefright/binding/MoofParser.cpp206
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/Index.h3
-rw-r--r--media/libstagefright/binding/include/mp4_demuxer/MoofParser.h55
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
+ ? &currentMoof->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 = &currentMoof->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,