Bug 1318792 - Adds support for sbgp and sgpd boxes in the traf box r?jya draft
authorJay Harris <jharris@mozilla.com>
Wed, 25 Jan 2017 12:10:35 +1300
changeset 468641 5aa017398e46279b4a2c57954cb740c3fdf17b24
parent 464800 bd0cd9af94d9334b862d9891013fed56fb9b3b7c
child 468642 3423c2875fc3041a82dd905355dfb8abe427a138
push id43545
push userbmo:jharris@mozilla.com
push dateTue, 31 Jan 2017 22:50:07 +0000
reviewersjya
bugs1318792
milestone53.0a1
Bug 1318792 - Adds support for sbgp and sgpd boxes in the traf box r?jya MozReview-Commit-ID: 7aHEaCTJm22
dom/media/fmp4/MP4Demuxer.cpp
media/libstagefright/binding/Index.cpp
media/libstagefright/binding/MoofParser.cpp
media/libstagefright/binding/include/mp4_demuxer/Index.h
media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
--- a/dom/media/fmp4/MP4Demuxer.cpp
+++ b/dom/media/fmp4/MP4Demuxer.cpp
@@ -355,17 +355,22 @@ 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);
+
+    // 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();
 }
 
 RefPtr<MP4TrackDemuxer::SamplesPromise>
 MP4TrackDemuxer::GetSamples(int32_t aNumSamples)
 {
   EnsureUpToDateIndex();
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -132,16 +132,21 @@ 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;
 
+    CencSampleEncryptionInfoEntry* sampleInfo = GetSampleEncryptionEntry();
+    if (sampleInfo) {
+      writer->mCrypto.mKeyId.AppendElements(sampleInfo->mKeyId);
+    }
+
     if (!reader.ReadArray(writer->mCrypto.mIV, ivSize)) {
       return nullptr;
     }
 
     if (reader.CanRead16()) {
       uint16_t count = reader.ReadU16();
 
       if (reader.Remaining() < count * 6) {
@@ -159,16 +164,57 @@ 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 (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
+         : &currentMoof->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.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")) {
         mSaios.AppendElement(Saio(box, aSinf.mDefaultEncryptionType));
       }
     }
   }
   if (aTrex.mTrackId && tfhd.mTrackId != aTrex.mTrackId) {
@@ -901,10 +915,157 @@ 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)
+{
+  mValid = true;
+  BoxReader reader(aBox);
+
+  if (!reader->CanReadType<uint32_t>()) {
+    LOG(Sbgp, "Incomplete Box (missing flags)");
+    mValid = false;
+    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);
+    mValid = false;
+    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);
+    mValid = false;
+    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)
+{
+  mValid = true;
+  BoxReader reader(aBox);
+
+  if (!reader->CanReadType<uint32_t>()) {
+    LOG(Sgpd, "Incomplete Box (missing flags)");
+    mValid = false;
+    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);
+
+    mValid = false;
+    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) {
+      mValid = false;
+      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);
+
+    mValid = false;
+    return;
+  }
+  for (uint32_t i = 0; i < count; ++i) {
+    if (version == 1 && defaultLength == 0) {
+      uint32_t descriptionLength = reader->ReadU32();
+      if (descriptionLength < entrySize) {
+        mValid = false;
+        return;
+      }
+    }
+
+    CencSampleEncryptionInfoEntry entry;
+    bool valid = entry.Init(reader);
+    if (!valid) {
+      mValid = false;
+      return;
+    }
+    mEntries.AppendElement(entry);
+  }
+}
+
+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
 }
--- 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,63 @@ 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:
+  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);
 
 private:
   int64_t mMoofOffset;
   Saiz& mSaiz;
   Saio& mSaio;
@@ -177,16 +225,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);