Bug 1367128: P2. Add methods to trim index. r?alwu
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
--- 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);