Bug 1049133 - Calculate eviction offset for MP4 instead of guessing; r=kinetik,edwin
authorAnthony Jones <ajones@mozilla.com>
Tue, 19 Aug 2014 14:13:55 +1200
changeset 200234 3998dcd6b14766d51f3364f5edfe742703ecf8a7
parent 200233 33fcf51e6d13321ea7667fe2bbac4cf564acf3fb
child 200235 28a26c0a23b042d550d096d2a3e5110702ab87f1
push id47845
push userajones@mozilla.com
push dateTue, 19 Aug 2014 02:14:22 +0000
treeherdermozilla-inbound@cdf1cbfe5c49 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, edwin
bugs1049133
milestone34.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 1049133 - Calculate eviction offset for MP4 instead of guessing; r=kinetik,edwin
content/media/MediaDecoderReader.h
content/media/fmp4/MP4Reader.cpp
content/media/fmp4/MP4Reader.h
content/media/mediasource/SubBufferDecoder.cpp
media/libstagefright/binding/Index.cpp
media/libstagefright/binding/include/mp4_demuxer/Index.h
media/libstagefright/binding/include/mp4_demuxer/Interval.h
media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
media/libstagefright/binding/mp4_demuxer.cpp
--- a/content/media/MediaDecoderReader.h
+++ b/content/media/MediaDecoderReader.h
@@ -154,16 +154,17 @@ public:
 
   // Returns the number of bytes of memory allocated by structures/frames in
   // the audio queue.
   size_t SizeOfAudioQueueInBytes() const;
 
   // Only used by WebMReader and MediaOmxReader for now, so stub here rather
   // than in every reader than inherits from MediaDecoderReader.
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
+  virtual int64_t GetEvictionOffset(double aTime) { return -1; }
 
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
   // Returns a pointer to the decoder.
   AbstractMediaDecoder* GetDecoder() {
     return mDecoder;
   }
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -104,17 +104,16 @@ private:
 };
 
 MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mAudio("MP4 audio decoder data", Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
   , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2))
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
-  , mTimeRangesMonitor("MP4Reader::TimeRanges")
   , mDemuxerInitialized(false)
   , mIsEncrypted(false)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(MP4Reader);
 }
 
 MP4Reader::~MP4Reader()
@@ -769,39 +768,46 @@ MP4Reader::NotifyDataArrived(const char*
   } else {
     UpdateIndex();
   }
 }
 
 void
 MP4Reader::UpdateIndex()
 {
-  nsTArray<MediaByteRange> ranges;
-  nsTArray<Interval<Microseconds>> timeRanges;
-
   MediaResource* resource = mDecoder->GetResource();
   resource->Pin();
+  nsTArray<MediaByteRange> ranges;
   if (NS_SUCCEEDED(resource->GetCachedRanges(ranges))) {
-    mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
+    mDemuxer->UpdateIndex(ranges);
   }
   resource->Unpin();
-
-  MonitorAutoLock mon(mTimeRangesMonitor);
-  mTimeRanges = timeRanges;
 }
 
+int64_t
+MP4Reader::GetEvictionOffset(double aTime)
+{
+  return mDemuxer->GetEvictionOffset(aTime * 1000000.0);
+}
 
 nsresult
 MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
 {
-  MonitorAutoLock mon(mTimeRangesMonitor);
-  for (size_t i = 0; i < mTimeRanges.Length(); i++) {
-    aBuffered->Add((mTimeRanges[i].start - aStartTime) / 1000000.0,
-                   (mTimeRanges[i].end - aStartTime) / 1000000.0);
+  MediaResource* resource = mDecoder->GetResource();
+  resource->Pin();
+  nsTArray<MediaByteRange> ranges;
+  if (NS_SUCCEEDED(resource->GetCachedRanges(ranges))) {
+    nsTArray<Interval<Microseconds>> timeRanges;
+    mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
+    for (size_t i = 0; i < timeRanges.Length(); i++) {
+      aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0,
+                     (timeRanges[i].end - aStartTime) / 1000000.0);
+    }
   }
+  resource->Unpin();
 
   return NS_OK;
 }
 
 bool MP4Reader::IsDormantNeeded()
 {
 #ifdef MOZ_GONK_MEDIACODEC
   return mVideo.mDecoder && mVideo.mDecoder->IsDormantNeeded();
--- a/content/media/fmp4/MP4Reader.h
+++ b/content/media/fmp4/MP4Reader.h
@@ -50,16 +50,18 @@ public:
                         int64_t aEndTime,
                         int64_t aCurrentTime) MOZ_OVERRIDE;
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength,
                                  int64_t aOffset) MOZ_OVERRIDE;
 
+  virtual int64_t GetEvictionOffset(double aTime) MOZ_OVERRIDE;
+
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
                                int64_t aStartTime) MOZ_OVERRIDE;
 
   // For Media Resource Management
   virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
   virtual bool IsDormantNeeded() MOZ_OVERRIDE;
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
 
@@ -176,18 +178,16 @@ private:
   uint64_t mLastReportedNumDecodedFrames;
 
   DecoderData& GetDecoderData(mp4_demuxer::TrackType aTrack);
   MediaDataDecoder* Decoder(mp4_demuxer::TrackType aTrack);
 
   layers::LayersBackend mLayersBackendType;
 
   nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
-  Monitor mTimeRangesMonitor;
-  nsTArray<mp4_demuxer::Interval<Microseconds>> mTimeRanges;
 
   // True if we've read the streams' metadata.
   bool mDemuxerInitialized;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 };
 
--- a/content/media/mediasource/SubBufferDecoder.cpp
+++ b/content/media/mediasource/SubBufferDecoder.cpp
@@ -107,21 +107,26 @@ SubBufferDecoder::GetBuffered(dom::TimeR
 {
   // XXX: Need mStartTime (from StateMachine) instead of passing 0.
   return mReader->GetBuffered(aBuffered, 0);
 }
 
 int64_t
 SubBufferDecoder::ConvertToByteOffset(double aTime)
 {
+  int64_t readerOffset = mReader->GetEvictionOffset(aTime);
+  if (readerOffset >= 0) {
+    return readerOffset;
+  }
+
   // Uses a conversion based on (aTime/duration) * length.  For the
   // purposes of eviction this should be adequate since we have the
   // byte threshold as well to ensure data actually gets evicted and
   // we ensure we don't evict before the current playable point.
-  if (mMediaDuration == -1) {
+  if (mMediaDuration <= 0) {
     return -1;
   }
   int64_t length = GetResource()->GetLength();
   MOZ_ASSERT(length > 0);
   int64_t offset = (aTime / (double(mMediaDuration) / USECS_PER_S)) * length;
   return offset;
 }
 
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mp4_demuxer/Index.h"
 #include "mp4_demuxer/Interval.h"
 #include "mp4_demuxer/MoofParser.h"
 #include "media/stagefright/MediaSource.h"
 #include "MediaResource.h"
 
+#include <algorithm>
+#include <limits>
+
 using namespace stagefright;
 using namespace mozilla;
 
 namespace mp4_demuxer
 {
 
 class MOZ_STACK_CLASS RangeFinder
 {
@@ -81,71 +84,105 @@ Index::Index(const stagefright::Vector<M
   } else {
     mIndex.AppendElements(&aIndex[0], aIndex.size());
   }
 }
 
 Index::~Index() {}
 
 void
+Index::UpdateMoofIndex(const nsTArray<MediaByteRange>& aByteRanges)
+{
+  if (!mMoofParser) {
+    return;
+  }
+
+  MonitorAutoLock mon(mMonitor);
+  mMoofParser->RebuildFragmentedIndex(aByteRanges);
+}
+
+void
 Index::ConvertByteRangesToTimeRanges(
   const nsTArray<MediaByteRange>& aByteRanges,
   nsTArray<Interval<Microseconds>>* aTimeRanges)
 {
+  MonitorAutoLock mon(mMonitor);
+
   RangeFinder rangeFinder(aByteRanges);
   nsTArray<Interval<Microseconds>> timeRanges;
 
-  nsTArray<stagefright::MediaSource::Indice> moofIndex;
-  nsTArray<stagefright::MediaSource::Indice>* index;
+  nsTArray<nsTArray<stagefright::MediaSource::Indice>*> indexes;
   if (mMoofParser) {
-    {
-      MonitorAutoLock mon(mMonitor);
-      mMoofParser->RebuildFragmentedIndex(aByteRanges);
+    // We take the index out of the moof parser and move it into a local
+    // variable so we don't get concurrency issues. It gets freed when we
+    // exit this function.
+    for (int i = 0; i < mMoofParser->mMoofs.Length(); i++) {
+      Moof& moof = mMoofParser->mMoofs[i];
 
-      // We take the index out of the moof parser and move it into a local
-      // variable so we don't get concurrency issues. It gets freed when we
-      // exit this function.
-      for (int i = 0; i < mMoofParser->mMoofs.Length(); i++) {
-        Moof& moof = mMoofParser->mMoofs[i];
-        if (rangeFinder.Contains(moof.mRange) &&
-            rangeFinder.Contains(moof.mMdatRange)) {
+      // We need the entire moof in order to play anything
+      if (rangeFinder.Contains(moof.mRange)) {
+        if (rangeFinder.Contains(moof.mMdatRange)) {
           timeRanges.AppendElements(moof.mTimeRanges);
         } else {
-          moofIndex.AppendElements(mMoofParser->mMoofs[i].mIndex);
+          indexes.AppendElement(&moof.mIndex);
         }
       }
     }
-    index = &moofIndex;
   } else {
-    index = &mIndex;
+    indexes.AppendElement(&mIndex);
   }
 
   bool hasSync = false;
-  for (size_t i = 0; i < index->Length(); i++) {
-    const MediaSource::Indice& indice = (*index)[i];
-    if (!rangeFinder.Contains(MediaByteRange(indice.start_offset,
-                                             indice.end_offset))) {
-      // We process the index in decode order so we clear hasSync when we hit
-      // a range that isn't buffered.
-      hasSync = false;
-      continue;
-    }
+  for (size_t i = 0; i < indexes.Length(); i++) {
+    nsTArray<stagefright::MediaSource::Indice>* index = indexes[i];
+    for (size_t j = 0; j < index->Length(); j++) {
+      const MediaSource::Indice& indice = (*index)[j];
+      if (!rangeFinder.Contains(
+             MediaByteRange(indice.start_offset, indice.end_offset))) {
+        // We process the index in decode order so we clear hasSync when we hit
+        // a range that isn't buffered.
+        hasSync = false;
+        continue;
+      }
 
-    hasSync |= indice.sync;
-    if (!hasSync) {
-      continue;
-    }
+      hasSync |= indice.sync;
+      if (!hasSync) {
+        continue;
+      }
 
-    // This is an optimisation for when the file is decoded in composition
-    // order. It means that Normalise() below doesn't need to do a sort.
-    size_t s = timeRanges.Length();
-    if (s && timeRanges[s - 1].end == indice.start_composition) {
-      timeRanges[s - 1].end = indice.end_composition;
-    } else {
-      timeRanges.AppendElement(Interval<Microseconds>(indice.start_composition,
-                                                      indice.end_composition));
+      Interval<Microseconds>::SemiNormalAppend(
+        timeRanges, Interval<Microseconds>(indice.start_composition,
+                                           indice.end_composition));
     }
   }
 
   // This fixes up when the compositon order differs from the byte range order
   Interval<Microseconds>::Normalize(timeRanges, aTimeRanges);
 }
+
+uint64_t
+Index::GetEvictionOffset(Microseconds aTime)
+{
+  uint64_t offset = std::numeric_limits<uint64_t>::max();
+  if (mMoofParser) {
+    // We need to keep the whole moof if we're keeping any of it because the
+    // parser doesn't keep parsed moofs.
+    for (int i = 0; i < mMoofParser->mMoofs.Length(); i++) {
+      Moof& moof = mMoofParser->mMoofs[i];
+
+      if (!moof.mTimeRanges.IsEmpty() && moof.mTimeRanges[0].end > aTime) {
+        offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart,
+                                                    moof.mMdatRange.mStart)));
+      }
+    }
+  } else {
+    // We've already parsed and stored the moov so we don't need to keep it.
+    // All we need to keep is the sample data itself.
+    for (size_t i = 0; i < mIndex.Length(); i++) {
+      const MediaSource::Indice& indice = mIndex[i];
+      if (aTime >= indice.start_composition) {
+        offset = std::min(offset, indice.start_offset);
+      }
+    }
+  }
+  return offset;
 }
+}
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -17,19 +17,21 @@ class MoofParser;
 
 class Index
 {
 public:
   Index(const stagefright::Vector<stagefright::MediaSource::Indice>& aIndex,
         Stream* aSource, uint32_t aTrackId);
   ~Index();
 
+  void UpdateMoofIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges);
   void ConvertByteRangesToTimeRanges(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges,
     nsTArray<Interval<Microseconds>>* aTimeRanges);
+  uint64_t GetEvictionOffset(Microseconds aTime);
 
 private:
   mozilla::Monitor mMonitor;
   nsTArray<stagefright::MediaSource::Indice> mIndex;
   nsAutoPtr<MoofParser> mMoofParser;
 };
 }
 
--- a/media/libstagefright/binding/include/mp4_demuxer/Interval.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Interval.h
@@ -56,17 +56,17 @@ struct Interval
                         nsTArray<Interval<T>>* aNormalized)
   {
     if (!aNormalized || !aIntervals.Length()) {
       MOZ_ASSERT(aNormalized);
       return;
     }
     MOZ_ASSERT(aNormalized->IsEmpty());
 
-    nsTArray<Interval<T> > sorted;
+    nsTArray<Interval<T>> sorted;
     sorted = aIntervals;
     sorted.Sort(Compare());
 
     Interval<T> current = sorted[0];
     for (size_t i = 1; i < sorted.Length(); i++) {
       MOZ_ASSERT(sorted[i].start <= sorted[i].end);
       if (current.Contains(sorted[i])) {
         continue;
@@ -76,19 +76,19 @@ struct Interval
       } else {
         aNormalized->AppendElement(current);
         current = sorted[i];
       }
     }
     aNormalized->AppendElement(current);
   }
 
-  static void Intersection(const nsTArray<Interval<T> >& a0,
-                           const nsTArray<Interval<T> >& a1,
-                           nsTArray<Interval<T> >* aIntersection)
+  static void Intersection(const nsTArray<Interval<T>>& a0,
+                           const nsTArray<Interval<T>>& a1,
+                           nsTArray<Interval<T>>* aIntersection)
   {
     MOZ_ASSERT(IsNormalized(a0));
     MOZ_ASSERT(IsNormalized(a1));
     size_t i0 = 0;
     size_t i1 = 0;
     while (i0 < a0.Length() && i1 < a1.Length()) {
       Interval i = a0[i0].Intersection(a1[i1]);
       if (i.Length()) {
@@ -101,17 +101,17 @@ struct Interval
       } else {
         i1++;
         // Assert that the array is sorted
         MOZ_ASSERT(i1 == a1.Length() || a1[i1 - 1].start < a1[i1].start);
       }
     }
   }
 
-  static bool IsNormalized(const nsTArray<Interval<T> >& aIntervals)
+  static bool IsNormalized(const nsTArray<Interval<T>>& aIntervals)
   {
     for (size_t i = 1; i < aIntervals.Length(); i++) {
       if (aIntervals[i - 1].end >= aIntervals[i].start) {
         return false;
       }
     }
     return true;
   }
--- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
@@ -54,19 +54,23 @@ public:
   // return nullptr on end of stream or error.
   MP4Sample* DemuxAudioSample();
   MP4Sample* DemuxVideoSample();
 
   const CryptoFile& Crypto() { return mCrypto; }
   const AudioDecoderConfig& AudioConfig() { return mAudioConfig; }
   const VideoDecoderConfig& VideoConfig() { return mVideoConfig; }
 
+  void UpdateIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges);
+
   void ConvertByteRangesToTime(
     const nsTArray<mozilla::MediaByteRange>& aByteRanges,
-    nsTArray<Interval<Microseconds> >* aIntervals);
+    nsTArray<Interval<Microseconds>>* aIntervals);
+
+  int64_t GetEvictionOffset(Microseconds aTime);
 
 private:
   AudioDecoderConfig mAudioConfig;
   VideoDecoderConfig mVideoConfig;
   CryptoFile mCrypto;
 
   nsAutoPtr<StageFrightPrivate> mPrivate;
   nsRefPtr<Stream> mSource;
--- a/media/libstagefright/binding/mp4_demuxer.cpp
+++ b/media/libstagefright/binding/mp4_demuxer.cpp
@@ -8,16 +8,17 @@
 #include "media/stagefright/MediaSource.h"
 #include "media/stagefright/MetaData.h"
 #include "mp4_demuxer/Adts.h"
 #include "mp4_demuxer/mp4_demuxer.h"
 #include "mp4_demuxer/Index.h"
 
 #include <stdint.h>
 #include <algorithm>
+#include <limits>
 
 using namespace stagefright;
 
 namespace mp4_demuxer
 {
 
 struct StageFrightPrivate
 {
@@ -193,16 +194,24 @@ MP4Demuxer::DemuxVideoSample()
   }
 
   sample->Update();
 
   return sample.forget();
 }
 
 void
+MP4Demuxer::UpdateIndex(const nsTArray<mozilla::MediaByteRange>& aByteRanges)
+{
+  for (int i = 0; i < mPrivate->mIndexes.Length(); i++) {
+    mPrivate->mIndexes[i]->UpdateMoofIndex(aByteRanges);
+  }
+}
+
+void
 MP4Demuxer::ConvertByteRangesToTime(
   const nsTArray<mozilla::MediaByteRange>& aByteRanges,
   nsTArray<Interval<Microseconds>>* aIntervals)
 {
   if (mPrivate->mIndexes.IsEmpty()) {
     return;
   }
 
@@ -213,9 +222,23 @@ MP4Demuxer::ConvertByteRangesToTime(
     mPrivate->mIndexes[i]->ConvertByteRangesToTimeRanges(aByteRanges, &ranges);
 
     nsTArray<Interval<Microseconds>> intersection;
     Interval<Microseconds>::Intersection(*aIntervals, ranges, &intersection);
     *aIntervals = intersection;
   }
 }
 
+int64_t
+MP4Demuxer::GetEvictionOffset(Microseconds aTime)
+{
+  if (mPrivate->mIndexes.IsEmpty()) {
+    return 0;
+  }
+
+  uint64_t offset = std::numeric_limits<uint64_t>::max();
+  for (int i = 0; i < mPrivate->mIndexes.Length(); i++) {
+    offset = std::min(offset, mPrivate->mIndexes[i]->GetEvictionOffset(aTime));
+  }
+  return offset;
+}
+
 } // namespace mp4_demuxer