diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index ff1c7cf94b2f..6e477c611eee 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -1149,12 +1149,25 @@ MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecisions) bool ensureNextIteration = false; // Grab pending stream input and compute blocking time + // TODO: Ensure that heap memory allocations isn't going to be a problem. + // Maybe modify code to use nsAutoTArray as out parameters. + nsTArray> promises; + for (MediaStream* stream : mStreams) { + if (SourceMediaStream* is = stream->AsSourceStream()) { + promises.AppendElements( + is->PullNewData(aEndBlockingDecisions, &ensureNextIteration)); + } + } + + // Wait until all PullEnabled stream's listeners have completed. + if (!promises.IsEmpty()) { + AwaitAll(promises); + } + for (MediaStream* stream : mStreams) { if (SourceMediaStream* is = stream->AsSourceStream()) { - is->PullNewData(aEndBlockingDecisions, &ensureNextIteration); is->ExtractPendingInput(); } - if (stream->mFinished) { // The stream's not suspended, and since it's finished, underruns won't // stop it playing out. So there's no blocking other than what we impose @@ -2694,43 +2707,48 @@ SourceMediaStream::SetPullEnabled(bool aEnabled) } } -void +nsTArray> SourceMediaStream::PullNewData(StreamTime aDesiredUpToTime, bool* aEnsureNextIteration) { + // 2 is the average number of listeners per SourceMediaStream. + nsTArray> promises(2); MutexAutoLock lock(mMutex); - if (mPullEnabled && !mFinished && !mListeners.IsEmpty()) { - // Compute how much stream time we'll need assuming we don't block - // the stream at all. - StreamTime t = GraphTimeToStreamTime(aDesiredUpToTime); - StreamTime current = mTracks.GetEnd(); - LOG(LogLevel::Verbose, - ("Calling NotifyPull aStream=%p t=%f current end=%f", - this, - GraphImpl()->MediaTimeToSeconds(t), - GraphImpl()->MediaTimeToSeconds(current))); - if (t > current) { - *aEnsureNextIteration = true; + if (!mPullEnabled || mFinished) { + return promises; + } + // Compute how much stream time we'll need assuming we don't block + // the stream at all. + StreamTime t = GraphTimeToStreamTime(aDesiredUpToTime); + StreamTime current = mTracks.GetEnd(); + LOG(LogLevel::Verbose, + ("Calling NotifyPull aStream=%p t=%f current end=%f", + this, + GraphImpl()->MediaTimeToSeconds(t), + GraphImpl()->MediaTimeToSeconds(current))); + if (t <= current) { + return promises; + } + *aEnsureNextIteration = true; #ifdef DEBUG - if (mListeners.Length() == 0) { - LOG( - LogLevel::Error, - ("No listeners in NotifyPull aStream=%p desired=%f current end=%f", - this, - GraphImpl()->MediaTimeToSeconds(t), - GraphImpl()->MediaTimeToSeconds(current))); - DumpTrackInfo(); - } + if (mListeners.Length() == 0) { + LOG( + LogLevel::Error, + ("No listeners in NotifyPull aStream=%p desired=%f current end=%f", + this, + GraphImpl()->MediaTimeToSeconds(t), + GraphImpl()->MediaTimeToSeconds(current))); + DumpTrackInfo(); + } #endif - for (uint32_t j = 0; j < mListeners.Length(); ++j) { - MediaStreamListener* l = mListeners[j]; - { - MutexAutoUnlock unlock(mMutex); - l->NotifyPull(GraphImpl(), t); - } - } + for (uint32_t j = 0; j < mListeners.Length(); ++j) { + MediaStreamListener* l = mListeners[j]; + { + MutexAutoUnlock unlock(mMutex); + promises.AppendElement(l->AsyncNotifyPull(GraphImpl(), t)); } } + return promises; } void diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index dfeb4979bc1c..049710e01d18 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -12,6 +12,7 @@ #include "StreamTracks.h" #include "VideoSegment.h" #include "mozilla/LinkedList.h" +#include "mozilla/MozPromise.h" #include "mozilla/Mutex.h" #include "mozilla/TaskQueue.h" #include "nsAutoPtr.h" @@ -702,7 +703,9 @@ public: * Call all MediaStreamListeners to request new data via the NotifyPull API * (if enabled). */ - void PullNewData(StreamTime aDesiredUpToTime, bool* aEnsureNextIteration); + typedef MozPromise NotifyPullPromise; + nsTArray> PullNewData(StreamTime aDesiredUpToTime, + bool* aEnsureNextIteration); /** * Extract any state updates pending in the stream, and apply them. diff --git a/dom/media/MediaStreamListener.h b/dom/media/MediaStreamListener.h index e7c738eadad6..ccbc32588f78 100644 --- a/dom/media/MediaStreamListener.h +++ b/dom/media/MediaStreamListener.h @@ -39,7 +39,8 @@ class VideoSegment; * callback to notify of the initial blocking state. Also, if a listener is * attached to a stream that has already finished, we'll call NotifyFinished. */ -class MediaStreamListener { +class MediaStreamListener +{ protected: // Protected destructor, to discourage deletion outside of Release(): virtual ~MediaStreamListener() {} @@ -60,6 +61,14 @@ public: * some reason, then data before aDesiredTime may not be played immediately. */ virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {} + virtual RefPtr AsyncNotifyPull( + MediaStreamGraph* aGraph, + StreamTime aDesiredTime) + { + NotifyPull(aGraph, aDesiredTime); + return SourceMediaStream::NotifyPullPromise::CreateAndResolve(true, + __func__); + } enum Blocking { BLOCKED,