Bug 1057225 - Fix MP4 timestamp discontinuities due to rounding; r=edwin

This commit is contained in:
Anthony Jones 2014-08-29 14:29:11 +12:00
parent c6ef15ed08
commit 6199191782
5 changed files with 70 additions and 18 deletions

View File

@ -153,7 +153,7 @@ Index::ConvertByteRangesToTimeRanges(
// We need the entire moof in order to play anything // We need the entire moof in order to play anything
if (rangeFinder.Contains(moof.mRange)) { if (rangeFinder.Contains(moof.mRange)) {
if (rangeFinder.Contains(moof.mMdatRange)) { if (rangeFinder.Contains(moof.mMdatRange)) {
timeRanges.AppendElements(moof.mTimeRanges); Interval<Microseconds>::SemiNormalAppend(timeRanges, moof.mTimeRange);
} else { } else {
indexes.AppendElement(&moof.mIndex); indexes.AppendElement(&moof.mIndex);
} }
@ -199,7 +199,7 @@ Index::GetEvictionOffset(Microseconds aTime)
for (int i = 0; i < mMoofParser->mMoofs.Length(); i++) { for (int i = 0; i < mMoofParser->mMoofs.Length(); i++) {
Moof& moof = mMoofParser->mMoofs[i]; Moof& moof = mMoofParser->mMoofs[i];
if (!moof.mTimeRanges.IsEmpty() && moof.mTimeRanges[0].end > aTime) { if (moof.mTimeRange.Length() && moof.mTimeRange.end > aTime) {
offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart, offset = std::min(offset, uint64_t(std::min(moof.mRange.mStart,
moof.mMdatRange.mStart))); moof.mMdatRange.mStart)));
} }

View File

@ -21,7 +21,15 @@ MoofParser::RebuildFragmentedIndex(const nsTArray<MediaByteRange>& aByteRanges)
if (box.IsType("moov")) { if (box.IsType("moov")) {
ParseMoov(box); ParseMoov(box);
} else if (box.IsType("moof")) { } else if (box.IsType("moof")) {
mMoofs.AppendElement(Moof(box, mTrex, mMdhd)); Moof moof(box, mTrex, mMdhd);
if (!mMoofs.IsEmpty()) {
// Stitch time ranges together in the case of a (hopefully small) time
// range gap between moofs.
mMoofs.LastElement().FixRounding(moof);
}
mMoofs.AppendElement(moof);
} }
mOffset = box.NextOffset(); mOffset = box.NextOffset();
} }
@ -32,10 +40,7 @@ MoofParser::GetCompositionRange()
{ {
Interval<Microseconds> compositionRange; Interval<Microseconds> compositionRange;
for (size_t i = 0; i < mMoofs.Length(); i++) { for (size_t i = 0; i < mMoofs.Length(); i++) {
nsTArray<Interval<Microseconds>>& compositionRanges = mMoofs[i].mTimeRanges; compositionRange = compositionRange.Extents(mMoofs[i].mTimeRange);
for (int j = 0; j < compositionRanges.Length(); j++) {
compositionRange = compositionRange.Extents(compositionRanges[j]);
}
} }
return compositionRange; return compositionRange;
} }
@ -97,7 +102,8 @@ MoofParser::ParseMvex(Box& aBox)
} }
} }
Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd) : mRange(aBox.Range()) Moof::Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd) :
mRange(aBox.Range()), mMaxRoundingError(0)
{ {
for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) {
if (box.IsType("traf")) { if (box.IsType("traf")) {
@ -126,6 +132,28 @@ Moof::ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd)
} }
} }
void
Moof::FixRounding(const Moof& aMoof) {
Microseconds gap = aMoof.mTimeRange.start - mTimeRange.end;
if (gap > 0 && gap <= mMaxRoundingError) {
mTimeRange.end = aMoof.mTimeRange.start;
}
}
class CtsComparator
{
public:
bool Equals(Sample* const aA, Sample* const aB) const
{
return aA->mCompositionRange.start == aB->mCompositionRange.start;
}
bool
LessThan(Sample* const aA, Sample* const aB) const
{
return aA->mCompositionRange.start < aB->mCompositionRange.start;
}
};
void void
Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd) Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
{ {
@ -142,6 +170,10 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
} }
uint32_t sampleCount = reader->ReadU32(); uint32_t sampleCount = reader->ReadU32();
if (sampleCount == 0) {
return;
}
uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0); uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
bool hasFirstSampleFlags = flags & 4; bool hasFirstSampleFlags = flags & 4;
uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0; uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0;
@ -163,8 +195,8 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
offset += sampleSize; offset += sampleSize;
sample.mCompositionRange = Interval<Microseconds>( sample.mCompositionRange = Interval<Microseconds>(
((decodeTime + ctsOffset) * 1000000ll) / aMdhd.mTimescale, aMdhd.ToMicroseconds(decodeTime + ctsOffset),
((decodeTime + sampleDuration + ctsOffset) * 1000000ll) / aMdhd.mTimescale); aMdhd.ToMicroseconds(decodeTime + ctsOffset + sampleDuration));
decodeTime += sampleDuration; decodeTime += sampleDuration;
sample.mSync = !(sampleFlags & 0x1010000); sample.mSync = !(sampleFlags & 0x1010000);
@ -172,10 +204,22 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
mIndex.AppendElement(sample); mIndex.AppendElement(sample);
mMdatRange = mMdatRange.Extents(sample.mByteRange); mMdatRange = mMdatRange.Extents(sample.mByteRange);
Interval<Microseconds>::SemiNormalAppend(timeRanges,
sample.mCompositionRange);
} }
Interval<Microseconds>::Normalize(timeRanges, &mTimeRanges); mMaxRoundingError += aMdhd.ToMicroseconds(sampleCount);
nsTArray<Sample*> ctsOrder;
for (int i = 0; i < mIndex.Length(); i++) {
ctsOrder.AppendElement(&mIndex[i]);
}
ctsOrder.Sort(CtsComparator());
for (int i = 0; i < ctsOrder.Length(); i++) {
if (i + 1 < ctsOrder.Length()) {
ctsOrder[i]->mCompositionRange.end = ctsOrder[i + 1]->mCompositionRange.start;
}
}
mTimeRange = Interval<Microseconds>(ctsOrder[0]->mCompositionRange.start,
ctsOrder.LastElement()->mCompositionRange.end);
} }
Tkhd::Tkhd(Box& aBox) Tkhd::Tkhd(Box& aBox)

View File

@ -6,7 +6,6 @@
#define INDEX_H_ #define INDEX_H_
#include "media/stagefright/MediaSource.h" #include "media/stagefright/MediaSource.h"
#include "mozilla/Monitor.h"
#include "mp4_demuxer/mp4_demuxer.h" #include "mp4_demuxer/mp4_demuxer.h"
namespace mp4_demuxer namespace mp4_demuxer

View File

@ -44,6 +44,11 @@ public:
} }
Mdhd(Box& aBox); Mdhd(Box& aBox);
Microseconds ToMicroseconds(uint64_t aTimescaleUnits)
{
return aTimescaleUnits * 1000000ll / mTimescale;
}
uint64_t mCreationTime; uint64_t mCreationTime;
uint64_t mModificationTime; uint64_t mModificationTime;
uint32_t mTimescale; uint32_t mTimescale;
@ -102,13 +107,17 @@ class Moof
{ {
public: public:
Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd); Moof(Box& aBox, Trex& aTrex, Mdhd& aMdhd);
void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd); void FixRounding(const Moof& aMoof);
void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd);
mozilla::MediaByteRange mRange; mozilla::MediaByteRange mRange;
mozilla::MediaByteRange mMdatRange; mozilla::MediaByteRange mMdatRange;
nsTArray<Interval<Microseconds>> mTimeRanges; Interval<Microseconds> mTimeRange;
nsTArray<Sample> mIndex; nsTArray<Sample> mIndex;
private:
void ParseTraf(Box& aBox, Trex& aTrex, Mdhd& aMdhd);
void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd);
uint64_t mMaxRoundingError;
}; };
class MoofParser class MoofParser

View File

@ -253,7 +253,7 @@ MP4Demuxer::GetEvictionOffset(Microseconds aTime)
for (int i = 0; i < mPrivate->mIndexes.Length(); i++) { for (int i = 0; i < mPrivate->mIndexes.Length(); i++) {
offset = std::min(offset, mPrivate->mIndexes[i]->GetEvictionOffset(aTime)); offset = std::min(offset, mPrivate->mIndexes[i]->GetEvictionOffset(aTime));
} }
return offset; return offset == std::numeric_limits<uint64_t>::max() ? -1 : offset;
} }
} // namespace mp4_demuxer } // namespace mp4_demuxer