Bug 1201393. Make suspended MediaStreams implicitly always block. r=padenot draft
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 03 Sep 2015 23:53:35 +1200
changeset 290051 6bc36caead9a87c6aa19a336ca5da1b75b4047dc
parent 290050 4248871f6cd79586451b596d900eb338e03e8674
child 290052 7ce390766d1c88cba9e5863bdac9e2a1b94fbdb9
push id5089
push userrocallahan@mozilla.com
push dateThu, 03 Sep 2015 12:15:06 +0000
reviewerspadenot
bugs1201393
milestone43.0a1
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.
dom/media/MediaStreamGraph.cpp
dom/media/webaudio/AudioContext.cpp
--- 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;