Bug 1201393. Make suspended MediaStreams implicitly always block. r=padenot
To make this work, we have to iterate over suspended MediaStreams in a few
more places. We don't need START_TIME_DELAYED anymore since blocking takes
care of that. I think it's good to allow suspended MediaStreams to notify
the main thread that they're finished; we might need that later when
we have non-AudioNode streams being suspended.
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -77,38 +77,35 @@ MediaStreamGraphImpl::FinishStream(Media
return;
STREAM_LOG(LogLevel::Debug, ("MediaStream %p will finish", aStream));
aStream->mFinished = true;
aStream->mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
SetStreamOrderDirty();
}
-static const GraphTime START_TIME_DELAYED = -1;
-
void
MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream)
{
+ aStream->mBufferStartTime = mProcessedTime;
// Check if we're adding a stream to a suspended context, in which case, we
- // add it to mSuspendedStreams, and delay setting mBufferStartTime
+ // add it to mSuspendedStreams
bool contextSuspended = false;
if (aStream->AsAudioNodeStream()) {
for (uint32_t i = 0; i < mSuspendedStreams.Length(); i++) {
if (aStream->AudioContextId() == mSuspendedStreams[i]->AudioContextId()) {
contextSuspended = true;
}
}
}
if (contextSuspended) {
- aStream->mBufferStartTime = START_TIME_DELAYED;
mSuspendedStreams.AppendElement(aStream);
STREAM_LOG(LogLevel::Debug, ("Adding media stream %p to the graph, in the suspended stream array", aStream));
} else {
- aStream->mBufferStartTime = mProcessedTime;
mStreams.AppendElement(aStream);
STREAM_LOG(LogLevel::Debug, ("Adding media stream %p to the graph", aStream));
}
SetStreamOrderDirty();
}
void
@@ -385,28 +382,26 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
}
stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime,
blockedTime);
// Advance mBlocked last so that AdvanceTimeVaryingValuesToCurrentTime
// can rely on the value of mBlocked.
stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime);
- if (!stream->IsSuspended()) {
- bool streamHasOutput = blockedTime < aNextCurrentTime - aPrevCurrentTime;
- NS_ASSERTION(!streamHasOutput || !stream->mNotifiedFinished,
- "Shouldn't have already notified of finish *and* have output!");
-
- if (streamHasOutput) {
- StreamNotifyOutput(stream);
- }
-
- if (stream->mFinished && !stream->mNotifiedFinished) {
- StreamReadyToFinish(stream);
- }
+ bool streamHasOutput = blockedTime < aNextCurrentTime - aPrevCurrentTime;
+ NS_ASSERTION(!streamHasOutput || !stream->mNotifiedFinished,
+ "Shouldn't have already notified of finish *and* have output!");
+
+ if (streamHasOutput) {
+ StreamNotifyOutput(stream);
+ }
+
+ if (stream->mFinished && !stream->mNotifiedFinished) {
+ StreamReadyToFinish(stream);
}
STREAM_LOG(LogLevel::Verbose,
("MediaStream %p bufferStartTime=%f blockedTime=%f", stream,
MediaTimeToSeconds(stream->mBufferStartTime),
MediaTimeToSeconds(blockedTime)));
}
}
@@ -845,16 +840,22 @@ MediaStreamGraphImpl::RecomputeBlockingA
bool explicitBlock = stream->mExplicitBlockerCount.GetAt(aTime, &end) > 0;
*aEnd = std::min(*aEnd, end);
if (explicitBlock) {
STREAM_LOG(LogLevel::Verbose, ("MediaStream %p is blocked due to explicit blocker", stream));
MarkStreamBlocking(stream);
continue;
}
+ if (StreamSuspended(stream)) {
+ STREAM_LOG(LogLevel::Verbose, ("MediaStream %p is blocked due to being suspended", stream));
+ MarkStreamBlocking(stream);
+ continue;
+ }
+
bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd);
if (underrun) {
// We'll block indefinitely
MarkStreamBlocking(stream);
*aEnd = std::min(*aEnd, aEndBlockingDecisions);
continue;
}
}
@@ -1243,19 +1244,19 @@ MediaStreamGraphImpl::ShouldUpdateMainTh
void
MediaStreamGraphImpl::PrepareUpdatesToMainThreadState(bool aFinalUpdate)
{
mMonitor.AssertCurrentThreadOwns();
// We don't want to frequently update the main thread about timing update
// when we are not running in realtime.
if (aFinalUpdate || ShouldUpdateMainThread()) {
- mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length());
- for (uint32_t i = 0; i < mStreams.Length(); ++i) {
- MediaStream* stream = mStreams[i];
+ mStreamUpdates.SetCapacity(mStreamUpdates.Length() + mStreams.Length() +
+ mSuspendedStreams.Length());
+ for (MediaStream* stream : Streams()) {
if (!stream->MainThreadNeedsUpdates()) {
continue;
}
StreamUpdate* update = mStreamUpdates.AppendElement();
update->mStream = stream;
update->mNextMainThreadCurrentTime =
GraphTimeToStreamTime(stream, mProcessedTime);
update->mNextMainThreadFinished = stream->mNotifiedFinished;
@@ -1317,19 +1318,18 @@ MediaStreamGraphImpl::ProduceDataForStre
t = next;
}
NS_ASSERTION(t == aTo, "Something went wrong with rounding to block boundaries");
}
bool
MediaStreamGraphImpl::AllFinishedStreamsNotified()
{
- for (uint32_t i = 0; i < mStreams.Length(); ++i) {
- MediaStream* s = mStreams[i];
- if (s->mFinished && !s->mNotifiedFinished) {
+ for (MediaStream* stream : Streams()) {
+ if (stream->mFinished && !stream->mNotifiedFinished) {
return false;
}
}
return true;
}
void
MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision)
@@ -1464,18 +1464,18 @@ MediaStreamGraphImpl::Process(GraphTime
bool
MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd)
{
{
MonitorAutoLock lock(mMemoryReportMonitor);
if (mNeedsMemoryReport) {
mNeedsMemoryReport = false;
- for (uint32_t i = 0; i < mStreams.Length(); ++i) {
- AudioNodeStream* stream = mStreams[i]->AsAudioNodeStream();
+ for (MediaStream* s : Streams()) {
+ AudioNodeStream* stream = s->AsAudioNodeStream();
if (stream) {
AudioNodeSizes usage;
stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage);
mAudioStreamSizes.AppendElement(usage);
}
}
lock.Notify();
@@ -1584,18 +1584,18 @@ public:
mGraph->Destroy();
} else {
// The graph is not empty. We must be in a forced shutdown, or a
// non-realtime graph that has finished processing. Some later
// AppendMessage will detect that the manager has been emptied, and
// delete it.
NS_ASSERTION(mGraph->mForceShutDown || !mGraph->mRealtime,
"Not in forced shutdown?");
- for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) {
- DOMMediaStream* s = mGraph->mStreams[i]->GetWrapper();
+ for (MediaStream* stream : mGraph->Streams()) {
+ DOMMediaStream* s = stream->GetWrapper();
if (s) {
s->NotifyMediaStreamGraphShutdown();
}
}
mGraph->mLifecycleState =
MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
}
@@ -3231,23 +3231,16 @@ MediaStreamGraphImpl::MoveStreams(AudioC
// It is posible to not find the stream here, if there has been two
// suspend/resume/close calls in a row.
auto i = from.IndexOf(stream);
if (i != from.NoIndex) {
from.RemoveElementAt(i);
to.AppendElement(stream);
}
- // If streams got added during a period where an AudioContext was suspended,
- // set their buffer start time to the appropriate value now:
- if (aAudioContextOperation == AudioContextOperation::Resume &&
- stream->mBufferStartTime == START_TIME_DELAYED) {
- stream->mBufferStartTime = mProcessedTime;
- }
-
stream->remove();
}
STREAM_LOG(LogLevel::Debug, ("Moving streams between suspended and running"
"state: mStreams: %d, mSuspendedStreams: %d\n", mStreams.Length(),
mSuspendedStreams.Length()));
#ifdef DEBUG
// The intersection of the two arrays should be null.
for (uint32_t i = 0; i < mStreams.Length(); i++) {
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -852,21 +852,16 @@ AudioContext::Suspend(ErrorResult& aRv)
if (mAudioContextState == AudioContextState::Suspended) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
Destination()->Suspend();
- MediaStream* ds = DestinationStream();
- if (ds) {
- ds->BlockStreamIfNeeded();
- }
-
mPromiseGripArray.AppendElement(promise);
Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
AudioContextOperation::Suspend, promise);
return promise.forget();
}
already_AddRefed<Promise>
@@ -938,20 +933,16 @@ AudioContext::Close(ErrorResult& aRv)
mPromiseGripArray.AppendElement(promise);
// This can be called when freeing a document, and the streams are dead at
// this point, so we need extra null-checks.
MediaStream* ds = DestinationStream();
if (ds) {
Graph()->ApplyAudioContextOperation(ds->AsAudioNodeStream(),
AudioContextOperation::Close, promise);
-
- if (ds) {
- ds->BlockStreamIfNeeded();
- }
}
return promise.forget();
}
void
AudioContext::UpdateNodeCount(int32_t aDelta)
{
bool firstNode = mNodeCount == 0;