Bug 1034444 - Make MP4Reader::GetBuffered() accurate; r=edwin,cpearce
authorAnthony Jones <ajones@mozilla.com>
Mon, 02 Jun 2014 17:38:04 +0200
changeset 215439 4bd579e7389af3767ada254e5a89bbb074c45e1f
parent 215438 70f3b01cb736ea9f21d692e537058ad3859a9c69
child 215440 7c60cb7a9403d4320b58b14dd200882bb1de80b8
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin, cpearce
bugs1034444
milestone33.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 1034444 - Make MP4Reader::GetBuffered() accurate; r=edwin,cpearce
content/media/MediaResource.h
content/media/fmp4/MP4Reader.cpp
content/media/fmp4/MP4Reader.h
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
media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
media/libstagefright/moz.build
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -142,16 +142,20 @@ public:
   }
 
   // Clears byte range values.
   void Clear() {
     mStart = 0;
     mEnd = 0;
   }
 
+  bool Contains(const MediaByteRange& aByteRange) const {
+    return aByteRange.mStart >= mStart && aByteRange.mEnd <= mEnd;
+  }
+
   int64_t mStart, mEnd;
 };
 
 // Represents a section of contiguous media, with a start and end offset, and
 // a timestamp representing the start time.
 class TimestampedMediaByteRange : public MediaByteRange {
 public:
   TimestampedMediaByteRange() : MediaByteRange(), mStartTime(-1) {}
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -8,16 +8,17 @@
 #include "MediaResource.h"
 #include "nsSize.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "SharedThreadPool.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/dom/TimeRanges.h"
 
 using mozilla::layers::Image;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* GetDemuxerLog() {
   static PRLogModuleInfo* log = nullptr;
@@ -548,9 +549,28 @@ MP4Reader::Seek(int64_t aTime,
   if (mDemuxer->HasValidAudio()) {
     mDemuxer->SeekAudio(
       mQueuedVideoSample ? mQueuedVideoSample->composition_timestamp : aTime);
   }
 
   return NS_OK;
 }
 
+nsresult
+MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
+{
+  nsTArray<MediaByteRange> ranges;
+  if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  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);
+  }
+
+  return NS_OK;
+}
+
 } // namespace mozilla
--- a/content/media/fmp4/MP4Reader.h
+++ b/content/media/fmp4/MP4Reader.h
@@ -47,16 +47,19 @@ public:
 
   virtual nsresult Seek(int64_t aTime,
                         int64_t aStartTime,
                         int64_t aEndTime,
                         int64_t aCurrentTime) MOZ_OVERRIDE;
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
+  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
+                               int64_t aStartTime) MOZ_OVERRIDE;
+
 private:
 
   // Destroys all decoder resources.
   void Shutdown();
 
   // Initializes mLayersBackendType if possible.
   void InitLayersBackendType();
 
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/Index.cpp
@@ -0,0 +1,121 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mp4_demuxer/Index.h"
+#include "mp4_demuxer/Interval.h"
+#include "media/stagefright/MediaSource.h"
+#include "MediaResource.h"
+
+using namespace stagefright;
+using namespace mozilla;
+
+namespace mp4_demuxer
+{
+
+class MOZ_STACK_CLASS RangeFinder
+{
+public:
+  // Given that we're processing this in order we don't use a binary search
+  // to find the apropriate time range. Instead we search linearly from the
+  // last used point.
+  RangeFinder(const nsTArray<mozilla::MediaByteRange>& ranges)
+    : mRanges(ranges), mIndex(0)
+  {
+    // Ranges must be normalised for this to work
+  }
+
+  bool Contains(MediaByteRange aByteRange);
+
+private:
+  const nsTArray<MediaByteRange>& mRanges;
+  size_t mIndex;
+};
+
+bool
+RangeFinder::Contains(MediaByteRange aByteRange)
+{
+  if (!mRanges.Length()) {
+    return false;
+  }
+
+  if (mRanges[mIndex].Contains(aByteRange)) {
+    return true;
+  }
+
+  if (aByteRange.mStart < mRanges[mIndex].mStart) {
+    // Search backwards
+    do {
+      if (!mIndex) {
+        return false;
+      }
+      --mIndex;
+      if (mRanges[mIndex].Contains(aByteRange)) {
+        return true;
+      }
+    } while (aByteRange.mStart < mRanges[mIndex].mStart);
+
+    return false;
+  }
+
+  while (aByteRange.mEnd > mRanges[mIndex].mEnd) {
+    if (mIndex == mRanges.Length() - 1) {
+      return false;
+    }
+    ++mIndex;
+    if (mRanges[mIndex].Contains(aByteRange)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void
+Index::Init(const stagefright::Vector<MediaSource::Indice>& aIndex)
+{
+  MOZ_ASSERT(mIndex.IsEmpty());
+  if (!aIndex.isEmpty()) {
+    mIndex.AppendElements(&aIndex[0], aIndex.size());
+  }
+}
+
+void
+Index::ConvertByteRangesToTimeRanges(
+  const nsTArray<MediaByteRange>& aByteRanges,
+  nsTArray<Interval<Microseconds>>* aTimeRanges)
+{
+  nsTArray<Interval<Microseconds>> timeRanges;
+  RangeFinder rangeFinder(aByteRanges);
+
+  bool hasSync = false;
+  for (size_t i = 0; i < mIndex.Length(); i++) {
+    const MediaSource::Indice& indice = mIndex[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;
+    }
+
+    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));
+    }
+  }
+
+  // This fixes up when the compositon order differs from the byte range order
+  Interval<Microseconds>::Normalize(timeRanges, aTimeRanges);
+}
+}
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef INDEX_H_
+#define INDEX_H_
+
+#include "media/stagefright/MediaSource.h"
+#include "mp4_demuxer/mp4_demuxer.h"
+
+namespace mp4_demuxer
+{
+
+template <typename T> class Interval;
+
+class Index
+{
+public:
+  Index() {}
+
+  void Init(
+    const stagefright::Vector<stagefright::MediaSource::Indice>& aIndex);
+  void ConvertByteRangesToTimeRanges(
+    const nsTArray<mozilla::MediaByteRange>& aByteRanges,
+    nsTArray<Interval<Microseconds>>* aTimeRanges);
+
+private:
+  nsTArray<stagefright::MediaSource::Indice> mIndex;
+};
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4_demuxer/Interval.h
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef INTERVAL_H_
+#define INTERVAL_H_
+
+#include "nsTArray.h"
+
+namespace mp4_demuxer
+{
+
+template <typename T>
+struct Interval
+{
+  Interval() : start(0), end(0) {}
+  Interval(T aStart, T aEnd) : start(aStart), end(aEnd)
+  {
+    MOZ_ASSERT(aStart <= aEnd);
+  }
+  T Length() { return end - start; }
+  Interval Intersection(const Interval& aOther) const
+  {
+    T s = start > aOther.start ? start : aOther.start;
+    T e = end < aOther.end ? end : aOther.end;
+    if (s > e) {
+      return Interval();
+    }
+    return Interval(s, e);
+  }
+  bool Contains(const Interval& aOther) const
+  {
+    return aOther.start >= start && aOther.end <= end;
+  }
+  bool operator==(const Interval& aOther) const
+  {
+    return start == aOther.start && end == aOther.end;
+  }
+  bool operator!=(const Interval& aOther) const { return !(*this == aOther); }
+
+  T start;
+  T end;
+
+  static void Normalize(const nsTArray<Interval<T>>& aIntervals,
+                        nsTArray<Interval<T>>* aNormalized)
+  {
+    if (!aNormalized || !aIntervals.Length()) {
+      MOZ_ASSERT(aNormalized);
+      return;
+    }
+    MOZ_ASSERT(aNormalized->IsEmpty());
+
+    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;
+      }
+      if (current.end >= sorted[i].start) {
+        current.end = sorted[i].end;
+      } 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)
+  {
+    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()) {
+        aIntersection->AppendElement(i);
+      }
+      if (a0[i0].end < a1[i1].end) {
+        i0++;
+        // Assert that the array is sorted
+        MOZ_ASSERT(i0 == a0.Length() || a0[i0 - 1].start < a0[i0].start);
+      } 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)
+  {
+    for (size_t i = 1; i < aIntervals.Length(); i++) {
+      if (aIntervals[i - 1].end >= aIntervals[i].start) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  struct Compare
+  {
+    bool Equals(const Interval<T>& a0, const Interval<T>& a1) const
+    {
+      return a0.start == a1.start && a0.end == a1.end;
+    }
+
+    bool LessThan(const Interval<T>& a0, const Interval<T>& a1) const
+    {
+      return a0.start < a1.start;
+    }
+  };
+};
+}
+
+#endif
--- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h
@@ -1,18 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MP4_DEMUXER_H_
 #define MP4_DEMUXER_H_
 
 #include "nsAutoPtr.h"
-#include "mozilla/gfx/Rect.h"
+#include "nsTArray.h"
 #include "mp4_demuxer/DecoderData.h"
+#include "mp4_demuxer/Interval.h"
+
+namespace mozilla { class MediaByteRange; }
 
 namespace mp4_demuxer
 {
 
 struct StageFrightPrivate;
 typedef int64_t Microseconds;
 
 class Stream
@@ -47,16 +50,20 @@ public:
   // DemuxAudioSample and DemuxVideoSample functions
   // return nullptr on end of stream or error.
   MP4Sample* DemuxAudioSample();
   MP4Sample* DemuxVideoSample();
 
   const AudioDecoderConfig& AudioConfig() { return mAudioConfig; }
   const VideoDecoderConfig& VideoConfig() { return mVideoConfig; }
 
+  void ConvertByteRangesToTime(
+    const nsTArray<mozilla::MediaByteRange>& aByteRanges,
+    nsTArray<Interval<Microseconds> >* aIntervals);
+
 private:
   AudioDecoderConfig mAudioConfig;
   VideoDecoderConfig mVideoConfig;
 
   nsAutoPtr<StageFrightPrivate> mPrivate;
 };
 }
 
--- a/media/libstagefright/binding/mp4_demuxer.cpp
+++ b/media/libstagefright/binding/mp4_demuxer.cpp
@@ -3,33 +3,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "include/MPEG4Extractor.h"
 #include "media/stagefright/DataSource.h"
 #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>
 
 using namespace stagefright;
 
 namespace mp4_demuxer
 {
 
 struct StageFrightPrivate
 {
   sp<MPEG4Extractor> mExtractor;
 
   sp<MediaSource> mAudio;
   MediaSource::ReadOptions mAudioOptions;
+  Index mAudioIndex;
 
   sp<MediaSource> mVideo;
   MediaSource::ReadOptions mVideoOptions;
+  Index mVideoIndex;
 };
 
 class DataSourceAdapter : public DataSource
 {
 public:
   DataSourceAdapter(Stream* aSource) : mSource(aSource) {}
 
   ~DataSourceAdapter() {}
@@ -41,17 +45,17 @@ public:
     MOZ_ASSERT(((ssize_t)size) >= 0);
     size_t bytesRead;
     if (!mSource->ReadAt(offset, data, size, &bytesRead))
       return ERROR_IO;
 
     if (bytesRead == 0)
       return ERROR_END_OF_STREAM;
 
-    MOZ_ASSERT(((ssize_t)bytesRead) >= 0);
+    MOZ_ASSERT(((ssize_t)bytesRead) > 0);
     return bytesRead;
   }
 
   virtual status_t getSize(off64_t* size)
   {
     if (!mSource->Length(size))
       return ERROR_UNSUPPORTED;
     return NO_ERROR;
@@ -92,20 +96,24 @@ MP4Demuxer::Init()
     if (metaData == nullptr || !metaData->findCString(kKeyMIMEType, &mimeType)) {
       continue;
     }
 
     if (!mPrivate->mAudio.get() && !strncmp(mimeType, "audio/", 6)) {
       mPrivate->mAudio = e->getTrack(i);
       mPrivate->mAudio->start();
       mAudioConfig.Update(metaData, mimeType);
+      auto index = mPrivate->mAudio->exportIndex();
+      mPrivate->mAudioIndex.Init(index);
     } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) {
       mPrivate->mVideo = e->getTrack(i);
       mPrivate->mVideo->start();
       mVideoConfig.Update(metaData, mimeType);
+      auto index = mPrivate->mVideo->exportIndex();
+      mPrivate->mVideoIndex.Init(index);
     }
   }
 
   return mPrivate->mAudio.get() || mPrivate->mVideo.get();
 }
 
 bool
 MP4Demuxer::HasValidAudio()
@@ -179,9 +187,43 @@ MP4Demuxer::DemuxVideoSample()
     return nullptr;
   }
 
   sample->Update();
 
   return sample.forget();
 }
 
+void
+MP4Demuxer::ConvertByteRangesToTime(
+  const nsTArray<mozilla::MediaByteRange>& aByteRanges,
+  nsTArray<Interval<Microseconds>>* aIntervals)
+{
+  if (!HasValidVideo() && !HasValidAudio()) {
+    return;
+  }
+
+  nsTArray<Interval<Microseconds>> video;
+  if (HasValidVideo()) {
+    nsTArray<Interval<Microseconds>> ranges;
+    if (!HasValidAudio()) {
+      mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges,
+                                                          aIntervals);
+      return;
+    }
+    mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges, &video);
+  }
+
+  nsTArray<Interval<Microseconds>> audio;
+  if (HasValidAudio()) {
+    nsTArray<Interval<Microseconds>> ranges;
+    if (!HasValidVideo()) {
+      mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges,
+                                                          aIntervals);
+      return;
+    }
+    mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges, &audio);
+  }
+
+  Interval<Microseconds>::Intersection(audio, video, aIntervals);
+}
+
 } // namespace mp4_demuxer
--- a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
@@ -104,16 +104,27 @@ struct MediaSource : public virtual RefB
     // are to be returned exclusively in response to read calls.
     // This will be called after a successful start() and before the
     // first read() call.
     // Callee assumes ownership of the buffers if no error is returned.
     virtual status_t setBuffers(const Vector<MediaBuffer *> &buffers) {
         return ERROR_UNSUPPORTED;
     }
 
+    struct Indice
+    {
+      uint64_t start_offset;
+      uint64_t end_offset;
+      uint64_t start_composition;
+      uint64_t end_composition;
+      bool sync;
+    };
+
+    virtual Vector<Indice> exportIndex() = 0;
+
 protected:
     virtual ~MediaSource();
 
 private:
     MediaSource(const MediaSource &);
     MediaSource &operator=(const MediaSource &);
 };
 
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -53,16 +53,17 @@ public:
 
     virtual status_t start(MetaData *params = NULL);
     virtual status_t stop();
 
     virtual sp<MetaData> getFormat();
 
     virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
     virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
+    virtual Vector<Indice> exportIndex();
 
 protected:
     virtual ~MPEG4Source();
 
 private:
     Mutex mLock;
 
     sp<MetaData> mFormat;
@@ -3624,16 +3625,45 @@ status_t MPEG4Source::fragmentedRead(
 
         *out = mBuffer;
         mBuffer = NULL;
 
         return OK;
     }
 }
 
+Vector<MediaSource::Indice> MPEG4Source::exportIndex()
+{
+  Vector<Indice> index;
+  for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples();
+          sampleIndex++) {
+      off64_t offset;
+      size_t size;
+      uint32_t compositionTime;
+      uint32_t duration;
+      bool isSyncSample;
+      if (mSampleTable->getMetaDataForSample(sampleIndex, &offset, &size,
+                                             &compositionTime, &duration,
+                                             &isSyncSample) != OK) {
+          ALOGE("Unexpected sample table problem");
+          continue;
+      }
+
+      Indice indice;
+      indice.start_offset = offset;
+      indice.end_offset = offset + size;
+      indice.start_composition = (compositionTime * 1000000ll) / mTimescale,
+      indice.end_composition = ((compositionTime + duration) * 1000000ll) /
+              mTimescale;
+      indice.sync = isSyncSample;
+      index.add(indice);
+  }
+  return index;
+}
+
 MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
         const char *mimePrefix) {
     for (Track *track = mFirstTrack; track != NULL; track = track->next) {
         const char *mime;
         if (track->meta != NULL
                 && track->meta->findCString(kKeyMIMEType, &mime)
                 && !strncasecmp(mime, mimePrefix, strlen(mimePrefix))) {
             return track;
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleIterator.cpp
@@ -134,27 +134,18 @@ status_t SampleIterator::seekTo(uint32_t
     }
 
     status_t err;
     if ((err = findSampleTime(sampleIndex, &mCurrentSampleTime)) != OK) {
         ALOGE("findSampleTime return error");
         return err;
     }
 
-    if (sampleIndex + 1 == mTable->mNumSampleSizes) {
-        mCurrentSampleDuration = 0;
-    }
-    else {
-        uint32_t nextSampleTime;
-        if ((err = findSampleTime(sampleIndex + 1, &nextSampleTime)) != OK ) {
-            ALOGE("findSampleTime return error");
-            return err;
-        }
-        mCurrentSampleDuration = nextSampleTime - mCurrentSampleTime;
-    }
+    // mTTSDuration is set by findSampleTime()
+    mCurrentSampleDuration = mTTSDuration;
 
     mCurrentSampleIndex = sampleIndex;
 
     mInitialized = true;
 
     return OK;
 }
 
--- a/media/libstagefright/moz.build
+++ b/media/libstagefright/moz.build
@@ -43,32 +43,34 @@ if CONFIG['OS_TARGET'] != 'Android':
         'system/core/libcutils/strdup16to8.c',
         'system/core/liblog/logd_write.c',
         'system/core/liblog/logprint.c',
     ]
 
 EXPORTS.mp4_demuxer += [
     'binding/include/mp4_demuxer/AnnexB.h',
     'binding/include/mp4_demuxer/DecoderData.h',
+    'binding/include/mp4_demuxer/Interval.h',
     'binding/include/mp4_demuxer/mp4_demuxer.h',
 ]
 
 SOURCES += [
     'frameworks/av/media/libstagefright/foundation/hexdump.cpp',
     'frameworks/av/media/libstagefright/MetaData.cpp',
     'system/core/libutils/RefBase.cpp',
     'system/core/libutils/String16.cpp',
     'system/core/libutils/String8.cpp',
     'system/core/libutils/VectorImpl.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'binding/Adts.cpp',
     'binding/AnnexB.cpp',
     'binding/DecoderData.cpp',
+    'binding/Index.cpp',
     'binding/mp4_demuxer.cpp',
     'frameworks/av/media/libstagefright/DataSource.cpp',
     'frameworks/av/media/libstagefright/ESDS.cpp',
     'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp',
     'frameworks/av/media/libstagefright/foundation/ABitReader.cpp',
     'frameworks/av/media/libstagefright/foundation/ABuffer.cpp',
     'frameworks/av/media/libstagefright/foundation/AString.cpp',
     'frameworks/av/media/libstagefright/id3/ID3.cpp',