From 5477c03e0c52f72832c129710583b8c8c39ea98f Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 19 Jun 2015 13:45:09 -0700 Subject: [PATCH] Bug 1175768 - Throttle NotifyDataArrived. r=jya --- dom/media/AbstractMediaDecoder.h | 3 +- dom/media/MediaDecoder.cpp | 4 +- dom/media/MediaDecoder.h | 3 +- dom/media/MediaDecoderReader.cpp | 54 +++++++++++++++++++ dom/media/MediaDecoderReader.h | 32 +++++++++-- dom/media/MediaDecoderStateMachine.h | 4 +- dom/media/MediaResource.cpp | 3 +- dom/media/mediasource/SourceBuffer.cpp | 4 +- dom/media/mediasource/SourceBufferDecoder.cpp | 2 +- dom/media/mediasource/SourceBufferDecoder.h | 2 +- dom/media/mediasource/TrackBuffer.cpp | 8 ++- dom/media/omx/MediaOmxReader.cpp | 2 +- dom/media/webaudio/BufferDecoder.cpp | 6 --- dom/media/webaudio/BufferDecoder.h | 2 +- 14 files changed, 101 insertions(+), 28 deletions(-) diff --git a/dom/media/AbstractMediaDecoder.h b/dom/media/AbstractMediaDecoder.h index 58617f3139f..8933d3b4d9f 100644 --- a/dom/media/AbstractMediaDecoder.h +++ b/dom/media/AbstractMediaDecoder.h @@ -117,7 +117,8 @@ public: // Called by the reader's MediaResource as data arrives over the network. // Must be called on the main thread. - virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) = 0; + virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset, + bool aThrottleUpdates) = 0; // Set by Reader if the current audio track can be offloaded virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {} diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index 9e79bafd1c1..9341fe1e26c 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -1313,11 +1313,11 @@ size_t MediaDecoder::SizeOfAudioQueue() { return 0; } -void MediaDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset) { +void MediaDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) { MOZ_ASSERT(NS_IsMainThread()); if (mDecoderStateMachine) { - mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset); + mDecoderStateMachine->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates); } // ReadyState computation depends on MediaDecoder::CanPlayThrough, which diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index d1745a12fa6..a92fa11d9cb 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -434,7 +434,8 @@ public: // Called as data arrives on the stream and is read into the cache. Called // on the main thread only. - virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) override; + virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset, + bool aThrottleUpdates) override; // Called by MediaResource when the principal of the resource has // changed. Called on main thread only. diff --git a/dom/media/MediaDecoderReader.cpp b/dom/media/MediaDecoderReader.cpp index ec04454c9df..f463f6b229b 100644 --- a/dom/media/MediaDecoderReader.cpp +++ b/dom/media/MediaDecoderReader.cpp @@ -70,8 +70,11 @@ MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder, : new MediaTaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK), /* aSupportsTailDispatch = */ true)) , mWatchManager(this, mTaskQueue) + , mTimer(new MediaTimer()) , mBuffered(mTaskQueue, TimeIntervals(), "MediaDecoderReader::mBuffered (Canonical)") , mDuration(mTaskQueue, NullableTimeUnit(), "MediaDecoderReader::mDuration (Mirror)") + , mThrottleDuration(TimeDuration::FromMilliseconds(500)) + , mLastThrottledNotify(TimeStamp::Now() - mThrottleDuration) , mIgnoreAudioOutputFormat(false) , mStartTime(-1) , mHitAudioDecodeError(false) @@ -173,6 +176,54 @@ MediaDecoderReader::UpdateBuffered() mBuffered = GetBuffered(); } +void +MediaDecoderReader::ThrottledNotifyDataArrived(const Interval& aInterval) +{ + MOZ_ASSERT(OnTaskQueue()); + NS_ENSURE_TRUE_VOID(!mShutdown); + + if (mThrottledInterval.isNothing()) { + mThrottledInterval.emplace(aInterval); + } else if (!mThrottledInterval.ref().Contiguous(aInterval)) { + DoThrottledNotify(); + mThrottledInterval.emplace(aInterval); + } else { + mThrottledInterval = Some(mThrottledInterval.ref().Span(aInterval)); + } + + // If it's been long enough since our last update, do it. + if (TimeStamp::Now() - mLastThrottledNotify > mThrottleDuration) { + DoThrottledNotify(); + } else if (!mThrottledNotify.Exists()) { + // Otherwise, schedule an update if one isn't scheduled already. + nsRefPtr self = this; + mThrottledNotify.Begin( + mTimer->WaitUntil(mLastThrottledNotify + mThrottleDuration, __func__) + ->Then(TaskQueue(), __func__, + [self] () -> void { + self->mThrottledNotify.Complete(); + NS_ENSURE_TRUE_VOID(!self->mShutdown); + self->DoThrottledNotify(); + }, + [self] () -> void { + self->mThrottledNotify.Complete(); + NS_WARNING("throttle callback rejected"); + }) + ); + } +} + +void +MediaDecoderReader::DoThrottledNotify() +{ + MOZ_ASSERT(OnTaskQueue()); + mLastThrottledNotify = TimeStamp::Now(); + mThrottledNotify.DisconnectIfExists(); + Interval interval = mThrottledInterval.ref(); + mThrottledInterval.reset(); + NotifyDataArrived(interval); +} + media::TimeIntervals MediaDecoderReader::GetBuffered() { @@ -362,6 +413,8 @@ MediaDecoderReader::Shutdown() mBaseAudioPromise.RejectIfExists(END_OF_STREAM, __func__); mBaseVideoPromise.RejectIfExists(END_OF_STREAM, __func__); + mThrottledNotify.DisconnectIfExists(); + ReleaseMediaResources(); mDuration.DisconnectIfConnected(); mBuffered.DisconnectAll(); @@ -383,6 +436,7 @@ MediaDecoderReader::Shutdown() p = ShutdownPromise::CreateAndResolve(true, __func__); } + mTimer = nullptr; mDecoder = nullptr; return p; diff --git a/dom/media/MediaDecoderReader.h b/dom/media/MediaDecoderReader.h index a3095c5fa11..d17d0c99f91 100644 --- a/dom/media/MediaDecoderReader.h +++ b/dom/media/MediaDecoderReader.h @@ -11,7 +11,9 @@ #include "MediaData.h" #include "MediaPromise.h" #include "MediaQueue.h" +#include "MediaTimer.h" #include "AudioCompactor.h" +#include "Intervals.h" #include "TimeUnits.h" namespace mozilla { @@ -247,19 +249,32 @@ public: protected: friend class TrackBuffer; virtual void NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { } - void NotifyDataArrived(uint32_t aLength, int64_t aOffset) + + void NotifyDataArrived(const media::Interval& aInfo) { MOZ_ASSERT(OnTaskQueue()); NS_ENSURE_TRUE_VOID(!mShutdown); - NotifyDataArrivedInternal(aLength, aOffset); + NotifyDataArrivedInternal(aInfo.Length(), aInfo.mStart); UpdateBuffered(); } + // Invokes NotifyDataArrived while throttling the calls to occur at most every mThrottleDuration ms. + void ThrottledNotifyDataArrived(const media::Interval& aInterval); + void DoThrottledNotify(); + public: - void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset) + // In situations where these notifications come from stochastic network + // activity, we can save significant recomputation by throttling the delivery + // of these updates to the reader implementation. We don't want to do this + // throttling when the update comes from MSE code, since that code needs the + // updates to be observable immediately, and is generally less + // trigger-happy with notifications anyway. + void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) { RefPtr r = - NS_NewRunnableMethodWithArgs(this, &MediaDecoderReader::NotifyDataArrived, aLength, aOffset); + NS_NewRunnableMethodWithArg>(this, aThrottleUpdates ? &MediaDecoderReader::ThrottledNotifyDataArrived + : &MediaDecoderReader::NotifyDataArrived, + media::Interval(aOffset, aOffset + aLength)); TaskQueue()->Dispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess); } @@ -358,6 +373,9 @@ protected: // State-watching manager. WatchManager mWatchManager; + // MediaTimer. + nsRefPtr mTimer; + // Buffered range. Canonical mBuffered; public: @@ -370,6 +388,12 @@ protected: // Duration, mirrored from the state machine task queue. Mirror mDuration; + // State for ThrottledNotifyDataArrived. + MediaPromiseRequestHolder mThrottledNotify; + const TimeDuration mThrottleDuration; + TimeStamp mLastThrottledNotify; + Maybe> mThrottledInterval; + // Whether we should accept media that we know we can't play // directly, because they have a number of channel higher than // what we support. diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index c60ed7113c1..bcfbc819597 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -266,9 +266,9 @@ public: return 0; } - void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset) + void DispatchNotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) { - mReader->DispatchNotifyDataArrived(aLength, aOffset); + mReader->DispatchNotifyDataArrived(aLength, aOffset, aThrottleUpdates); } AbstractCanonical* CanonicalBuffered() { return mReader->CanonicalBuffered(); } diff --git a/dom/media/MediaResource.cpp b/dom/media/MediaResource.cpp index adce7ac8983..329b03b4e39 100644 --- a/dom/media/MediaResource.cpp +++ b/dom/media/MediaResource.cpp @@ -463,7 +463,8 @@ ChannelMediaResource::CopySegmentToCache(nsIInputStream *aInStream, { CopySegmentClosure* closure = static_cast(aClosure); - closure->mResource->mDecoder->NotifyDataArrived(aCount, closure->mResource->mOffset); + closure->mResource->mDecoder->NotifyDataArrived(aCount, closure->mResource->mOffset, + /* aThrottleUpdates = */ true); // Keep track of where we're up to. RESOURCE_LOG("%p [ChannelMediaResource]: CopySegmentToCache at mOffset [%lld] add " diff --git a/dom/media/mediasource/SourceBuffer.cpp b/dom/media/mediasource/SourceBuffer.cpp index bc9aa2d5a67..3148fd571cf 100644 --- a/dom/media/mediasource/SourceBuffer.cpp +++ b/dom/media/mediasource/SourceBuffer.cpp @@ -303,7 +303,7 @@ SourceBuffer::Ended() mContentManager->Ended(); // We want the MediaSourceReader to refresh its buffered range as it may // have been modified (end lined up). - mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++); + mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false); } SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType) @@ -498,7 +498,7 @@ SourceBuffer::AppendDataCompletedWithSuccess(bool aHasActiveTracks) // Tell our parent decoder that we have received new data. // The information provided do not matter much so long as it is monotonically // increasing. - mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++); + mMediaSource->GetDecoder()->NotifyDataArrived(1, mReportedOffset++, /* aThrottleUpdates = */ false); // Send progress event. mMediaSource->GetDecoder()->NotifyBytesDownloaded(); } diff --git a/dom/media/mediasource/SourceBufferDecoder.cpp b/dom/media/mediasource/SourceBufferDecoder.cpp index cb82b881f22..9a34cde2885 100644 --- a/dom/media/mediasource/SourceBufferDecoder.cpp +++ b/dom/media/mediasource/SourceBufferDecoder.cpp @@ -197,7 +197,7 @@ SourceBufferDecoder::GetOwner() } void -SourceBufferDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset) +SourceBufferDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) { } diff --git a/dom/media/mediasource/SourceBufferDecoder.h b/dom/media/mediasource/SourceBufferDecoder.h index 0f4d5200fe4..df2e3d4f0f8 100644 --- a/dom/media/mediasource/SourceBufferDecoder.h +++ b/dom/media/mediasource/SourceBufferDecoder.h @@ -47,7 +47,7 @@ public: virtual void FirstFrameLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) final override; virtual void NotifyBytesConsumed(int64_t aBytes, int64_t aOffset) final override; - virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) final override; + virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset, bool aThrottleUpdates) final override; virtual void NotifyDecodedFrames(uint32_t aParsed, uint32_t aDecoded, uint32_t aDropped) final override; virtual void NotifyWaitingForResourcesStatusChanged() final override; virtual void OnReadMetadataCompleted() final override; diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index f385bb09d4c..124eb1bdb19 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -294,8 +294,7 @@ nsRefPtr TrackBuffer::UpdateBufferedRanges(Interval aByteRange, bool aNotifyParent) { if (aByteRange.Length()) { - mCurrentDecoder->GetReader()->NotifyDataArrived(uint32_t(aByteRange.Length()), - aByteRange.mStart); + mCurrentDecoder->GetReader()->NotifyDataArrived(aByteRange); } // Recalculate and cache our new buffered range. @@ -325,7 +324,7 @@ TrackBuffer::UpdateBufferedRanges(Interval aByteRange, bool aNotifyPare // specific SourceBufferDecoder's data stream. Pass bogus values here to // force parent decoder's state machine to recompute end time for // infinite length media. - parent->NotifyDataArrived(0, 0); + parent->NotifyDataArrived(0, 0, /* aThrottleUpdates = */ false); parent->NotifyBytesDownloaded(); }); AbstractThread::MainThread()->Dispatch(task.forget()); @@ -762,8 +761,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder) MSE_DEBUG("Initializing subdecoder %p reader %p", aDecoder, reader); - reader->NotifyDataArrived(uint32_t(mLastAppendRange.Length()), - mLastAppendRange.mStart); + reader->NotifyDataArrived(mLastAppendRange); // HACK WARNING: // We only reach this point once we know that we have a complete init segment. diff --git a/dom/media/omx/MediaOmxReader.cpp b/dom/media/omx/MediaOmxReader.cpp index 2911ef8f6aa..c4a0facef0e 100644 --- a/dom/media/omx/MediaOmxReader.cpp +++ b/dom/media/omx/MediaOmxReader.cpp @@ -96,7 +96,7 @@ private: while (mLength) { uint32_t length = std::min(mLength, UINT32_MAX); - mOmxReader->NotifyDataArrived(length, mOffset); + mOmxReader->NotifyDataArrived(Interval(mOffset, mOffset + length)); mLength -= length; mOffset += length; } diff --git a/dom/media/webaudio/BufferDecoder.cpp b/dom/media/webaudio/BufferDecoder.cpp index bbbe89259cc..2a119dfd968 100644 --- a/dom/media/webaudio/BufferDecoder.cpp +++ b/dom/media/webaudio/BufferDecoder.cpp @@ -153,12 +153,6 @@ BufferDecoder::NotifyWaitingForResourcesStatusChanged() // ignore } -void -BufferDecoder::NotifyDataArrived(uint32_t aLength, int64_t aOffset) -{ - // ignore -} - MediaDecoderOwner* BufferDecoder::GetOwner() { diff --git a/dom/media/webaudio/BufferDecoder.h b/dom/media/webaudio/BufferDecoder.h index b800f0d23e8..cf07293ab73 100644 --- a/dom/media/webaudio/BufferDecoder.h +++ b/dom/media/webaudio/BufferDecoder.h @@ -69,7 +69,7 @@ public: virtual void NotifyWaitingForResourcesStatusChanged() final override; - virtual void NotifyDataArrived(uint32_t aLength, int64_t aOffset) final override; + virtual void NotifyDataArrived(uint32_t, int64_t, bool) final override {}; private: virtual ~BufferDecoder();