зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
86fa73c5fa
Коммит
b6679d18f5
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче