Bug 1318792 - Adds support for sbgp and sgpd boxes in the traf box r?cpearce
Implements support for parsing sbgp and sgpd boxes in the moofparser
Adds a rudimentary system for mapping samples to their samplegroupdescriptions
MozReview-Commit-ID: GlhCzL51wmn
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -355,17 +355,20 @@ MP4TrackDemuxer::GetNextSample()
break;
}
}
}
if (sample->mCrypto.mValid) {
nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
writer->mCrypto.mMode = mInfo->mCrypto.mMode;
writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
- writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
+
+ if (writer->mCrypto.mKeyId.Length() == 0) {
+ writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
+ }
}
return sample.forget();
}
RefPtr<MP4TrackDemuxer::SamplesPromise>
MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
{
EnsureUpToDateIndex();
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -132,16 +132,25 @@ already_AddRefed<MediaRawData> SampleIte
if (!mIndex->mSource->ReadAt(s->mCencRange.mStart, cenc.Elements(), cenc.Length(),
&bytesRead) || bytesRead != cenc.Length()) {
return nullptr;
}
ByteReader reader(cenc);
writer->mCrypto.mValid = true;
writer->mCrypto.mIVSize = ivSize;
+ nsTArray<uint8_t> keyIdArray;
+
+ CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry();
+ if (sampleInfo) {
+ keyIdArray.AppendElements(sampleInfo->mKeyId);
+ }
+
+ writer->mCrypto.mKeyId = keyIdArray;
+
if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) {
return nullptr;
}
if (reader.CanRead16()) {
uint16_t count = reader.ReadU16();
if (reader.Remaining() < count * 6) {
@@ -159,16 +168,58 @@ already_AddRefed<MediaRawData> SampleIte
}
}
Next();
return sample.forget();
}
+CencSampleEncryptionInfoEntry* SampleIterator::GetSampleEncryptionEntry()
+{
+ nsTArray<Moof>& moofs = mIndex->mMoofParser->Moofs();
+ Moof* currentMoof = &moofs[mCurrentMoof];
+ SampleToGroupEntry* sampleToGroupEntry = nullptr;
+
+ uint32_t seen = 0;
+
+ for (uint32_t i = 0; i < currentMoof->mSampleToGroupEntries.Length(); ++i) {
+ SampleToGroupEntry* entry = ¤tMoof->mSampleToGroupEntries.ElementAt(i);
+ 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) {
MOZ_ASSERT(!mCurrentMoof);
return mCurrentSample < mIndex->mIndex.Length()
? &mIndex->mIndex[mCurrentSample]
: nullptr;
}
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -22,16 +22,18 @@ extern mozilla::LogModule* GetDemuxerLog
#endif
namespace mp4_demuxer
{
using namespace stagefright;
using namespace mozilla;
+const uint32_t kKeyIdSize = 16;
+
bool
MoofParser::RebuildFragmentedIndex(
const MediaByteRangeSet& aByteRanges)
{
BoxContext context(mSource, aByteRanges);
return RebuildFragmentedIndex(context);
}
@@ -481,16 +483,28 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex,
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.mGroupingType == "seig") {
+ mSampleEncryptionInfoEntries.Clear();
+ mSampleEncryptionInfoEntries.AppendElements(sgpd.mEntries);
+ }
+ } else if (box.IsType("sbgp")) {
+ Sbgp sbgp(box);
+ if (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")) {
mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType));
}
}
}
if (aTrex.mTrackId && tfhd.mTrackId != aTrex.mTrackId) {
@@ -901,10 +915,134 @@ Saio::Saio(Box& aBox, AtomType aDefaultT
} else {
for (size_t i = 0; i < count; i++) {
MOZ_ALWAYS_TRUE(mOffsets.AppendElement(reader->ReadU64(), fallible));
}
}
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);
+ }
+}
+
+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();
+ MOZ_ASSERT(defaultLength == 0 || defaultLength >= entrySize);
+ }
+
+ 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);
+ printf("Failed :'(\n");
+ return;
+ }
+ for (uint32_t i = 0; i < count; ++i) {
+ if (version == 1 && defaultLength == 0) {
+ uint32_t descriptionLength = reader->ReadU32();
+ MOZ_ASSERT(descriptionLength >= entrySize);
+ }
+
+ CencSampleEncryptionInfoEntry entry(reader);
+ mEntries.AppendElement(entry);
+ }
+}
+
+CencSampleEncryptionInfoEntry::CencSampleEncryptionInfoEntry(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) {
+ MOZ_ASSERT(mIVSize == 8 || mIVSize == 16);
+ } else {
+ MOZ_ASSERT(mIVSize == 0);
+ }
+}
+
#undef LOG
}
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -27,16 +27,19 @@ class SampleIterator
public:
explicit SampleIterator(Index* aIndex);
already_AddRefed<mozilla::MediaRawData> GetNext();
void Seek(Microseconds aTime);
Microseconds GetNextKeyframeTime();
private:
Sample* Get();
+
+ CencSampleEncryptionInfoEntry* GetSampleEncryptionEntry();
+
void Next();
RefPtr<Index> mIndex;
size_t mCurrentMoof;
size_t mCurrentSample;
};
class Index
{
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -12,16 +12,17 @@
#include "mp4_demuxer/Interval.h"
#include "MediaResource.h"
namespace mp4_demuxer {
typedef int64_t Microseconds;
class Box;
class BoxContext;
+class BoxReader;
class Moof;
class Mvhd : public Atom
{
public:
Mvhd()
: mCreationTime(0)
, mModificationTime(0)
@@ -155,16 +156,60 @@ class Saio final : public Atom
public:
Saio(Box& aBox, AtomType aDefaultType);
AtomType mAuxInfoType;
uint32_t mAuxInfoTypeParameter;
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:
+ explicit CencSampleEncryptionInfoEntry(BoxReader& aReader);
+
+ bool mIsEncrypted;
+ uint8_t mIVSize;
+ 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);
private:
int64_t mMoofOffset;
Saiz& mSaiz;
Saio& mSaio;
@@ -177,16 +222,19 @@ public:
bool GetAuxInfo(AtomType aType, nsTArray<MediaByteRange>* aByteRanges);
void FixRounding(const Moof& aMoof);
mozilla::MediaByteRange mRange;
mozilla::MediaByteRange mMdatRange;
Interval<Microseconds> mTimeRange;
FallibleTArray<Sample> mIndex;
+ nsTArray<CencSampleEncryptionInfoEntry> mSampleEncryptionInfoEntries;
+ nsTArray<SampleToGroupEntry> mSampleToGroupEntries;
+
nsTArray<Saiz> mSaizs;
nsTArray<Saio> mSaios;
private:
// aDecodeTime is updated to the end of the parsed TRAF on return.
void ParseTraf(Box& aBox, Trex& aTrex, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, Sinf& aSinf, uint64_t* aDecodeTime, bool aIsAudio);
// aDecodeTime is updated to the end of the parsed TRUN on return.
bool ParseTrun(Box& aBox, Tfhd& aTfhd, Mvhd& aMvhd, Mdhd& aMdhd, Edts& aEdts, uint64_t* aDecodeTime, bool aIsAudio);