diff --git a/dom/media/mediasource/SourceBufferDecoder.cpp b/dom/media/mediasource/SourceBufferDecoder.cpp index 8e409fe366d4..33c14a94ad42 100644 --- a/dom/media/mediasource/SourceBufferDecoder.cpp +++ b/dom/media/mediasource/SourceBufferDecoder.cpp @@ -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(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 tr = new dom::TimeRanges(); tr->Add(0, mTrimmedOffset); aBuffered->Intersection(tr); + BuildCacheFromTimeRanges(aBuffered); return NS_OK; } diff --git a/dom/media/mediasource/SourceBufferDecoder.h b/dom/media/mediasource/SourceBufferDecoder.h index af94f7365e6c..4bfbbfb5a22b 100644 --- a/dom/media/mediasource/SourceBufferDecoder.h +++ b/dom/media/mediasource/SourceBufferDecoder.h @@ -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 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 mCacheBufferedRanges; + bool mCacheBufferedRangeStale; + #ifdef MOZ_EME nsRefPtr mCDMProxy; #endif diff --git a/dom/media/mediasource/SourceBufferResource.h b/dom/media/mediasource/SourceBufferResource.h index 00fa49bd882f..8cb4d259a1f2 100644 --- a/dom/media/mediasource/SourceBufferResource.h +++ b/dom/media/mediasource/SourceBufferResource.h @@ -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 diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 0541afe70b17..08648fe9f8b0 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -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(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;