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
Родитель 23be8a3362
Коммит 1d26f60c37
5 изменённых файлов: 70 добавлений и 18 удалений

Просмотреть файл

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

Просмотреть файл

@ -21,7 +21,15 @@ MoofParser::RebuildFragmentedIndex(const nsTArray<MediaByteRange>& aByteRanges)
if (box.IsType("moov")) {
ParseMoov(box);
} 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();
}
@ -32,10 +40,7 @@ MoofParser::GetCompositionRange()
{
Interval<Microseconds> compositionRange;
for (size_t i = 0; i < mMoofs.Length(); i++) {
nsTArray<Interval<Microseconds>>& compositionRanges = mMoofs[i].mTimeRanges;
for (int j = 0; j < compositionRanges.Length(); j++) {
compositionRange = compositionRange.Extents(compositionRanges[j]);
}
compositionRange = compositionRange.Extents(mMoofs[i].mTimeRange);
}
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()) {
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
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();
if (sampleCount == 0) {
return;
}
uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0);
bool hasFirstSampleFlags = flags & 4;
uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0;
@ -163,8 +195,8 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
offset += sampleSize;
sample.mCompositionRange = Interval<Microseconds>(
((decodeTime + ctsOffset) * 1000000ll) / aMdhd.mTimescale,
((decodeTime + sampleDuration + ctsOffset) * 1000000ll) / aMdhd.mTimescale);
aMdhd.ToMicroseconds(decodeTime + ctsOffset),
aMdhd.ToMicroseconds(decodeTime + ctsOffset + sampleDuration));
decodeTime += sampleDuration;
sample.mSync = !(sampleFlags & 0x1010000);
@ -172,10 +204,22 @@ Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt, Mdhd& aMdhd)
mIndex.AppendElement(sample);
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)

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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