Bug 1367128: P2. Add methods to trim index. r=gerald
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 02 Jun 2017 21:15:10 +0200
changeset 410453 564b0921883d4c42e9cccc632a85483f0ded802b
parent 410452 fe9f05834bbab6d5642a04e482a9cbf71bcaf688
child 410454 34cf246b51d8c2a07f1b29265b09bdf01862472e
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1367128
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1367128: P2. Add methods to trim index. r=gerald The MoofParser's used by the MP4Demuxer, can grow very large as it keeps adding new tables and is never reset. Once we have demuxed all data present in the MP4 media segment, we no longer require the samples table for that media segment and we can drop it from the index. Unfortunately. some websites (in particular some using live video) use media segments containing a single sample in order to (incorrectly) reduce latency. While this is a very bad approach from a performance perspective it does happen in the wild. MozReview-Commit-ID: I66jxcScmKM
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/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -78,16 +78,22 @@ RangeFinder::Contains(MediaByteRange aBy
   return false;
 }
 
 SampleIterator::SampleIterator(Index* aIndex)
   : mIndex(aIndex)
   , mCurrentMoof(0)
   , mCurrentSample(0)
 {
+  mIndex->RegisterIterator(this);
+}
+
+SampleIterator::~SampleIterator()
+{
+  mIndex->UnregisterIterator(this);
 }
 
 already_AddRefed<MediaRawData> SampleIterator::GetNext()
 {
   Sample* s(Get());
   if (!s) {
     return nullptr;
   }
@@ -382,21 +388,46 @@ Index::Index(const IndiceWrapper& aIndic
   }
 }
 
 Index::~Index() {}
 
 void
 Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges)
 {
+  UpdateMoofIndex(aByteRanges, false);
+}
+
+void
+Index::UpdateMoofIndex(const MediaByteRangeSet& aByteRanges, bool aCanEvict)
+{
   if (!mMoofParser) {
     return;
   }
-
-  mMoofParser->RebuildFragmentedIndex(aByteRanges);
+  size_t moofs = mMoofParser->Moofs().Length();
+  bool canEvict = aCanEvict && moofs > 1;
+  if (canEvict) {
+    // Check that we can trim the mMoofParser. We can only do so if all
+    // iterators have demuxed all possible samples.
+    for (const SampleIterator* iterator : mIterators) {
+      if ((iterator->mCurrentSample == 0 && iterator->mCurrentMoof == moofs) ||
+          iterator->mCurrentMoof == moofs - 1) {
+        continue;
+      }
+      canEvict = false;
+      break;
+    }
+  }
+  mMoofParser->RebuildFragmentedIndex(aByteRanges, &canEvict);
+  if (canEvict) {
+    // The moofparser got trimmed. Adjust all registered iterators.
+    for (SampleIterator* iterator : mIterators) {
+      iterator->mCurrentMoof -= moofs - 1;
+    }
+  }
 }
 
 Microseconds
 Index::GetEndCompositionIfBuffered(const MediaByteRangeSet& aByteRanges)
 {
   FallibleTArray<Sample>* index;
   if (mMoofParser) {
     if (!mMoofParser->ReachedEnd() || mMoofParser->Moofs().IsEmpty()) {
@@ -554,9 +585,22 @@ Index::GetEvictionOffset(Microseconds aT
       const Sample& sample = mIndex[i];
       if (aTime >= sample.mCompositionRange.end) {
         offset = std::min(offset, uint64_t(sample.mByteRange.mEnd));
       }
     }
   }
   return offset;
 }
+
+void
+Index::RegisterIterator(SampleIterator* aIterator)
+{
+  mIterators.AppendElement(aIterator);
 }
+
+void
+Index::UnregisterIterator(SampleIterator* aIterator)
+{
+  mIterators.RemoveElement(aIterator);
+}
+
+}
--- a/media/libstagefright/binding/MoofParser.cpp
+++ b/media/libstagefright/binding/MoofParser.cpp
@@ -26,24 +26,39 @@ namespace mp4_demuxer
 {
 
 using namespace stagefright;
 using namespace mozilla;
 
 const uint32_t kKeyIdSize = 16;
 
 bool
-MoofParser::RebuildFragmentedIndex(
-  const MediaByteRangeSet& aByteRanges)
+MoofParser::RebuildFragmentedIndex(const MediaByteRangeSet& aByteRanges)
 {
   BoxContext context(mSource, aByteRanges);
   return RebuildFragmentedIndex(context);
 }
 
 bool
+MoofParser::RebuildFragmentedIndex(
+  const MediaByteRangeSet& aByteRanges, bool* aCanEvict)
+{
+  MOZ_ASSERT(aCanEvict);
+  if (*aCanEvict && mMoofs.Length() > 1) {
+    MOZ_ASSERT(mMoofs.Length() == mMediaRanges.Length());
+    mMoofs.RemoveElementsAt(0, mMoofs.Length() - 1);
+    mMediaRanges.RemoveElementsAt(0, mMediaRanges.Length() - 1);
+    *aCanEvict = true;
+  } else {
+    *aCanEvict = false;
+  }
+  return RebuildFragmentedIndex(aByteRanges);
+}
+
+bool
 MoofParser::RebuildFragmentedIndex(BoxContext& aContext)
 {
   bool foundValidMoof = false;
   bool foundMdat = false;
 
   for (Box box(&aContext, mOffset); box.IsAvailable(); box = box.Next()) {
     if (box.IsType("moov") && mInitRange.IsEmpty()) {
       mInitRange = MediaByteRange(0, box.Range().mEnd);
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -22,27 +22,28 @@ class Index;
 class IndiceWrapper;
 
 typedef int64_t Microseconds;
 
 class SampleIterator
 {
 public:
   explicit SampleIterator(Index* aIndex);
+  ~SampleIterator();
   already_AddRefed<mozilla::MediaRawData> GetNext();
   void Seek(Microseconds aTime);
   Microseconds GetNextKeyframeTime();
-
 private:
   Sample* Get();
 
   CencSampleEncryptionInfoEntry* GetSampleEncryptionEntry();
 
   void Next();
   RefPtr<Index> mIndex;
+  friend class Index;
   size_t mCurrentMoof;
   size_t mCurrentSample;
 };
 
 class Index
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Index)
@@ -93,33 +94,38 @@ public:
     Interval<Microseconds> mTime;
   };
 
   Index(const IndiceWrapper& aIndices,
         Stream* aSource,
         uint32_t aTrackId,
         bool aIsAudio);
 
+  void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges,
+                       bool aCanEvict);
   void UpdateMoofIndex(const mozilla::MediaByteRangeSet& aByteRanges);
   Microseconds GetEndCompositionIfBuffered(
     const mozilla::MediaByteRangeSet& aByteRanges);
   mozilla::media::TimeIntervals ConvertByteRangesToTimeRanges(
     const mozilla::MediaByteRangeSet& aByteRanges);
   uint64_t GetEvictionOffset(Microseconds aTime);
   bool IsFragmented() { return mMoofParser; }
 
   friend class SampleIterator;
 
 private:
   ~Index();
+  void RegisterIterator(SampleIterator* aIterator);
+  void UnregisterIterator(SampleIterator* aIterator);
 
   Stream* mSource;
   FallibleTArray<Sample> mIndex;
   FallibleTArray<MP4DataOffset> mDataOffset;
   nsAutoPtr<MoofParser> mMoofParser;
+  nsTArray<SampleIterator*> mIterators;
 
   // ConvertByteRangesToTimeRanges cache
   mozilla::MediaByteRangeSet mLastCachedRanges;
   mozilla::media::TimeIntervals mLastBufferedRanges;
   bool mIsAudio;
 };
 }
 
--- a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h
@@ -257,16 +257,21 @@ public:
     , mIsAudio(aIsAudio)
     , mLastDecodeTime(0)
   {
     // Setting the mTrex.mTrackId to 0 is a nasty work around for calculating
     // the composition range for MSE. We need an array of tracks.
   }
   bool RebuildFragmentedIndex(
     const mozilla::MediaByteRangeSet& aByteRanges);
+  // If *aCanEvict is set to true. then will remove all moofs already parsed
+  // from index then rebuild the index. *aCanEvict is set to true upon return if
+  // some moofs were removed.
+  bool RebuildFragmentedIndex(
+    const mozilla::MediaByteRangeSet& aByteRanges, bool* aCanEvict);
   bool RebuildFragmentedIndex(BoxContext& aContext);
   Interval<Microseconds> GetCompositionRange(
     const mozilla::MediaByteRangeSet& aByteRanges);
   bool ReachedEnd();
   void ParseMoov(Box& aBox);
   void ParseTrak(Box& aBox);
   void ParseMdia(Box& aBox, Tkhd& aTkhd);
   void ParseMvex(Box& aBox);