Bug 1201393. Make suspended MediaStreams implicitly always block. r=padenot
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 04 Sep 2015 17:01:01 +1200
changeset 261215 61079e59ef351bdb9dbbfff2bb408838c5d66431
parent 261214 2d0794169172b3d89e863dddd53054b5f612fa3a
child 261216 321303b7d4287fd9987f30a717ae90f6cb998e92
push id64690
push userrocallahan@mozilla.com
push dateTue, 08 Sep 2015 04:29:49 +0000
treeherdermozilla-inbound@321303b7d428 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1201393
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
@@ -390,28 +387,26 @@ MediaStreamGraphImpl::UpdateCurrentTimeF
     // can rely on the value of mBlocked.
     stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime);
 
     STREAM_LOG(LogLevel::Verbose,
                ("MediaStream %p bufferStartTime=%f blockedTime=%f", stream,
                 MediaTimeToSeconds(stream->mBufferStartTime),
                 MediaTimeToSeconds(blockedTime)));
 
-    if (mSuspendedStreams.IndexOf(stream) == mSuspendedStreams.NoIndex) {
-      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);
     }
   }
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
 {
@@ -846,16 +841,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;
     }
   }
@@ -1244,19 +1245,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 : AllStreams()) {
       if (!stream->MainThreadNeedsUpdates()) {
         continue;
       }
       StreamUpdate* update = mStreamUpdates.AppendElement();
       update->mStream = stream;
       update->mNextMainThreadCurrentTime =
         GraphTimeToStreamTime(stream, mProcessedTime);
       update->mNextMainThreadFinished = stream->mNotifiedFinished;
@@ -1310,19 +1311,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 : AllStreams()) {
+    if (stream->mFinished && !stream->mNotifiedFinished) {
       return false;
     }
   }
   return true;
 }
 
 void
 MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision)
@@ -1457,18 +1457,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 : AllStreams()) {
+        AudioNodeStream* stream = s->AsAudioNodeStream();
         if (stream) {
           AudioNodeSizes usage;
           stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage);
           mAudioStreamSizes.AppendElement(usage);
         }
       }
 
       lock.Notify();
@@ -1577,18 +1577,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->AllStreams()) {
+        DOMMediaStream* s = stream->GetWrapper();
         if (s) {
           s->NotifyMediaStreamGraphShutdown();
         }
       }
 
       mGraph->mLifecycleState =
         MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
     }
@@ -3224,23 +3224,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>
@@ -892,21 +887,16 @@ AudioContext::Resume(ErrorResult& aRv)
 
   if (mAudioContextState == AudioContextState::Running) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
 
   Destination()->Resume();
 
-  MediaStream* ds = DestinationStream();
-  if (ds) {
-    ds->UnblockStreamIfNeeded();
-  }
-
   mPromiseGripArray.AppendElement(promise);
   Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
                                       AudioContextOperation::Resume, promise);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
@@ -938,20 +928,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;