Bug 1125581: Cache buffered TimeRanges and only recalculate as necessary. r=mattwoodrow

Also change the relationship between SourceBufferResource object and its callee.
Data is now required to be removed or added via the SourceBufferDecoder.
This fixes a potential race between the time we add data to the resource and
the time we retrieve the buffered time ranges.
This commit is contained in:
Jean-Yves Avenard 2015-01-26 11:26:20 +11:00
Родитель 86fa73c5fa
Коммит b6679d18f5
4 изменённых файлов: 127 добавлений и 21 удалений

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

@ -43,6 +43,8 @@ SourceBufferDecoder::SourceBufferDecoder(MediaResource* aResource,
, mMediaDuration(-1)
, mRealMediaDuration(0)
, mTrimmedOffset(-1)
, mCacheMonitor("SourceBufferDecoder")
, mCacheBufferedRangeStale(true)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(SourceBufferDecoder);
@ -202,7 +204,47 @@ SourceBufferDecoder::SetRealMediaDuration(int64_t aDuration)
void
SourceBufferDecoder::Trim(int64_t aDuration)
{
MonitorAutoLock mon(mCacheMonitor);
mTrimmedOffset = (double)aDuration / USECS_PER_S;
mCacheBufferedRangeStale = true;
}
void SourceBufferDecoder::AppendData(LargeDataBuffer* aData)
{
MonitorAutoLock mon(mCacheMonitor);
mCacheBufferedRangeStale = true;
int64_t appendOffset = GetResource()->GetLength();
GetResource()->AppendData(aData);
{
MonitorAutoUnlock mon(mCacheMonitor);
NotifyDataArrived(reinterpret_cast<const char*>(aData->Elements()),
aData->Length(), appendOffset);
}
}
uint32_t
SourceBufferDecoder::EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold)
{
MonitorAutoLock mon(mCacheMonitor);
mCacheBufferedRangeStale = true;
return GetResource()->EvictData(aPlaybackOffset, aThreshold);
}
// Remove data from resource before the given offset.
void
SourceBufferDecoder::EvictBefore(uint64_t aOffset)
{
MonitorAutoLock mon(mCacheMonitor);
mCacheBufferedRangeStale = true;
GetResource()->EvictBefore(aOffset);
}
// Remove all data from the attached resource
uint32_t SourceBufferDecoder::EvictAll()
{
MonitorAutoLock mon(mCacheMonitor);
mCacheBufferedRangeStale = true;
return GetResource()->EvictAll();
}
void
@ -241,19 +283,49 @@ SourceBufferDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, in
mParentDecoder->NotifyDataArrived(nullptr, 0, 0);
}
void
SourceBufferDecoder::BuildTimeRangesFromCache(dom::TimeRanges* aBuffered)
{
for (size_t i = 0; i < mCacheBufferedRanges.Length(); i++) {
aBuffered->Add(mCacheBufferedRanges[i].mStart, mCacheBufferedRanges[i].mEnd);
}
}
void
SourceBufferDecoder::BuildCacheFromTimeRanges(dom::TimeRanges* aBuffered)
{
mCacheBufferedRanges.Clear();
for (uint32_t i = 0; i < aBuffered->Length(); i++) {
double start;
aBuffered->Start(i, &start);
double end;
aBuffered->End(i, &end);
mCacheBufferedRanges.AppendElement(TimeRange(start, end));
}
mCacheBufferedRangeStale = false;
}
nsresult
SourceBufferDecoder::GetBuffered(dom::TimeRanges* aBuffered)
{
MonitorAutoLock mon(mCacheMonitor);
if (!mCacheBufferedRangeStale) {
BuildTimeRangesFromCache(aBuffered);
return NS_OK;
}
nsresult rv = mReader->GetBuffered(aBuffered);
if (NS_FAILED(rv)) {
return rv;
}
if (!WasTrimmed()) {
BuildCacheFromTimeRanges(aBuffered);
return NS_OK;
}
nsRefPtr<dom::TimeRanges> tr = new dom::TimeRanges();
tr->Add(0, mTrimmedOffset);
aBuffered->Intersection(tr);
BuildCacheFromTimeRanges(aBuffered);
return NS_OK;
}

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

@ -14,12 +14,14 @@
#ifdef MOZ_EME
#include "mozilla/CDMProxy.h"
#endif
#include "mozilla/Monitor.h"
#include "mozilla/ReentrantMonitor.h"
namespace mozilla {
class MediaResource;
class MediaDecoderReader;
class LargeDataBuffer;
namespace dom {
@ -30,8 +32,6 @@ class TimeRanges;
class SourceBufferDecoder MOZ_FINAL : public AbstractMediaDecoder
{
public:
// This class holds a weak pointer to MediaResource. It's the responsibility
// of the caller to manage the memory of the MediaResource object.
SourceBufferDecoder(MediaResource* aResource, AbstractMediaDecoder* aParentDecoder,
int64_t aTimestampOffset /* microseconds */);
@ -71,6 +71,23 @@ public:
// AbstractMediaDecoder, which does not supply this interface.
nsresult GetBuffered(dom::TimeRanges* aBuffered);
// This mirrors SourceBufferResource data management, while holding the lock
// to manage mCacheBuffered.
void AppendData(LargeDataBuffer* aData);
void Ended()
{
GetResource()->Ended();
}
uint32_t EvictData(uint64_t aPlaybackOffset, uint32_t aThreshold);
// Remove data from resource before the given offset.
void EvictBefore(uint64_t aOffset);
// Remove all data from the resource
uint32_t EvictAll();
int64_t GetSize()
{
return GetResource()->GetSize();
}
void SetReader(MediaDecoderReader* aReader)
{
MOZ_ASSERT(!mReader);
@ -143,6 +160,8 @@ public:
private:
virtual ~SourceBufferDecoder();
void BuildTimeRangesFromCache(dom::TimeRanges* aBuffered);
void BuildCacheFromTimeRanges(dom::TimeRanges* aBuffered);
// Our TrackBuffer's task queue, this is only non-null during initialization.
RefPtr<MediaTaskQueue> mTaskQueue;
@ -160,6 +179,23 @@ private:
// in seconds
double mTrimmedOffset;
Monitor mCacheMonitor;
// We use our own version of TimeRanges.
// dom::TimeRanges is normalized, and keep it so. We don't need to normalize
// our cache.
// dom::TimeRanges is also marked as not thread-safe and will error when
// accessed from a different thread that the one that created it.
struct TimeRange
{
TimeRange(double aStart, double aEnd)
: mStart(aStart),
mEnd(aEnd) {}
double mStart;
double mEnd;
};
nsAutoTArray<TimeRange,4> mCacheBufferedRanges;
bool mCacheBufferedRangeStale;
#ifdef MOZ_EME
nsRefPtr<CDMProxy> mCDMProxy;
#endif

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

@ -46,6 +46,8 @@ class SourceBuffer;
class SourceBufferResource MOZ_FINAL : public MediaResource
{
// When used in combination with a SourceBufferDecoder, data management is to
// be handled by owning SourceBufferDecoder.
public:
explicit SourceBufferResource(const nsACString& aType);
virtual nsresult Close() MOZ_OVERRIDE;
@ -111,7 +113,8 @@ public:
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
// Used by SourceBuffer.
// Used by SourceBuffer, and only to be called by SourceBufferDecoder owner if
// present.
void AppendData(LargeDataBuffer* aData);
void Ended();
// Remove data from resource if it holds more than the threshold

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

@ -207,13 +207,8 @@ TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDurat
return false;
}
SourceBufferResource* resource = mCurrentDecoder->GetResource();
int64_t appendOffset = resource->GetLength();
resource->AppendData(aData);
mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
// XXX: For future reference: NDA call must run on the main thread.
mCurrentDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData->Elements()),
aData->Length(), appendOffset);
mCurrentDecoder->AppendData(aData);
mParentDecoder->NotifyBytesDownloaded();
mParentDecoder->NotifyTimeRangesChanged();
@ -260,7 +255,7 @@ TrackBuffer::EvictData(double aPlaybackTime,
int64_t totalSize = 0;
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
totalSize += mDecoders[i]->GetResource()->GetSize();
totalSize += mDecoders[i]->GetSize();
}
int64_t toEvict = totalSize - aThreshold;
@ -297,8 +292,8 @@ TrackBuffer::EvictData(double aPlaybackTime,
MSE_DEBUG("TrackBuffer(%p)::EvictData evicting all before start "
"bufferedStart=%f bufferedEnd=%f aPlaybackTime=%f size=%lld",
this, buffered->GetStartTime(), buffered->GetEndTime(),
aPlaybackTime, decoders[i]->GetResource()->GetSize());
toEvict -= decoders[i]->GetResource()->EvictAll();
aPlaybackTime, decoders[i]->GetSize());
toEvict -= decoders[i]->EvictAll();
} else {
// To ensure we don't evict data past the current playback position
// we apply a threshold of a few seconds back and evict data up to
@ -309,9 +304,9 @@ TrackBuffer::EvictData(double aPlaybackTime,
MSE_DEBUG("TrackBuffer(%p)::EvictData evicting some bufferedEnd=%f"
"aPlaybackTime=%f time=%f, playbackOffset=%lld size=%lld",
this, buffered->GetEndTime(), aPlaybackTime, time,
playbackOffset, decoders[i]->GetResource()->GetSize());
playbackOffset, decoders[i]->GetSize());
if (playbackOffset > 0) {
toEvict -= decoders[i]->GetResource()->EvictData(playbackOffset,
toEvict -= decoders[i]->EvictData(playbackOffset,
toEvict);
}
}
@ -324,14 +319,14 @@ TrackBuffer::EvictData(double aPlaybackTime,
decoders[i]->GetBuffered(buffered);
MSE_DEBUG("TrackBuffer(%p):EvictData maybe remove empty decoders=%d "
"size=%lld start=%f end=%f",
this, i, decoders[i]->GetResource()->GetSize(),
this, i, decoders[i]->GetSize(),
buffered->GetStartTime(), buffered->GetEndTime());
if (decoders[i] == mCurrentDecoder
|| mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
continue;
}
if (decoders[i]->GetResource()->GetSize() == 0 ||
if (decoders[i]->GetSize() == 0 ||
buffered->GetStartTime() < 0.0 ||
buffered->GetEndTime() < 0.0) {
MSE_DEBUG("TrackBuffer(%p):EvictData remove empty decoders=%d", this, i);
@ -358,7 +353,7 @@ TrackBuffer::EvictBefore(double aTime)
int64_t endOffset = mInitializedDecoders[i]->ConvertToByteOffset(aTime);
if (endOffset > 0) {
MSE_DEBUG("TrackBuffer(%p)::EvictBefore decoder=%u offset=%lld", this, i, endOffset);
mInitializedDecoders[i]->GetResource()->EvictBefore(endOffset);
mInitializedDecoders[i]->EvictBefore(endOffset);
}
}
}
@ -545,7 +540,7 @@ TrackBuffer::DiscardDecoder()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
mCurrentDecoder->Ended();
}
mCurrentDecoder = nullptr;
}
@ -555,7 +550,7 @@ TrackBuffer::EndCurrentDecoder()
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
if (mCurrentDecoder) {
mCurrentDecoder->GetResource()->Ended();
mCurrentDecoder->Ended();
}
}
@ -751,8 +746,8 @@ TrackBuffer::RangeRemoval(int64_t aStart, int64_t aEnd)
decoders[i]->Trim(aStart);
if (aStart <= buffered->GetStartTime()) {
// We've completely emptied it, can clear the data.
int64_t size = decoders[i]->GetResource()->GetSize();
decoders[i]->GetResource()->EvictData(size, size);
int64_t size = decoders[i]->GetSize();
decoders[i]->EvictData(size, size);
if (decoders[i] == mCurrentDecoder ||
mParentDecoder->IsActiveReader(decoders[i]->GetReader())) {
continue;