Bug 1181097 - Properly handle the case of resuming an AudioContext with an MSG that is already switching to an AudioCallbackDriver. r=roc, a=lmandel
authorPaul Adenot <paul@paul.cx>
Mon, 13 Jul 2015 19:16:53 +0200
changeset 275378 07cda54dec6032d7e0b64b51fd974e8240568a69
parent 275377 86026c031ded8d9d535731d8a9725fb80b95985b
child 275379 9190dedf78a2e06e218ee262e872da8c0f24541e
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, lmandel
bugs1181097
milestone40.0
Bug 1181097 - Properly handle the case of resuming an AudioContext with an MSG that is already switching to an AudioCallbackDriver. r=roc, a=lmandel
dom/media/GraphDriver.h
dom/media/MediaStreamGraph.cpp
dom/media/test/crashtests/audiocontext-double-suspend.html
dom/media/test/crashtests/crashtests.list
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -114,16 +114,21 @@ public:
    * latency, we will get a callback approximately every 10ms. */
   virtual uint32_t IterationDuration() = 0;
 
   /* Return whether we are switching or not. */
   bool Switching() {
     return mNextDriver || mPreviousDriver;
   }
 
+  GraphDriver* NextDriver()
+  {
+    return mNextDriver;
+  }
+
   /**
    * If we are running a real time graph, get the current time stamp to schedule
    * video frames. This has to be reimplemented by real time drivers.
    */
   virtual TimeStamp GetCurrentTimeStamp() {
     return mCurrentTimeStamp;
   }
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -3370,25 +3370,31 @@ MediaStreamGraphImpl::ApplyAudioContextO
 
   MoveStreams(aOperation, streamSet);
   MOZ_ASSERT(!streamSet.getFirst(),
       "Streams should be removed from the list after having been moved.");
 
   // If we have suspended the last AudioContext, and we don't have other
   // streams that have audio, this graph will automatically switch to a
   // SystemCallbackDriver, because it can't find a MediaStream that has an audio
-  // track. When resuming, force switching to an AudioCallbackDriver. It would
-  // have happened at the next iteration anyways, but doing this now save
-  // some time.
+  // track. When resuming, force switching to an AudioCallbackDriver (if we're
+  // not already switching). It would have happened at the next iteration
+  // anyways, but doing this now save some time.
   if (aOperation == AudioContextOperation::Resume) {
     if (!CurrentDriver()->AsAudioCallbackDriver()) {
-      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+      AudioCallbackDriver* driver;
+      if (CurrentDriver()->Switching()) {
+        MOZ_ASSERT(CurrentDriver()->NextDriver()->AsAudioCallbackDriver());
+        driver = CurrentDriver()->NextDriver()->AsAudioCallbackDriver();
+      } else {
+        driver = new AudioCallbackDriver(this);
+        mMixer.AddCallback(driver);
+        CurrentDriver()->SwitchAtNextIteration(driver);
+      }
       driver->EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
-      mMixer.AddCallback(driver);
-      CurrentDriver()->SwitchAtNextIteration(driver);
     } else {
       // We are resuming a context, but we are already using an
       // AudioCallbackDriver, we can resolve the promise now.
       AudioContextOperationCompleted(aStream, aPromise, aOperation);
     }
   }
   // Close, suspend: check if we are going to switch to a
   // SystemAudioCallbackDriver, and pass the promise to the AudioCallbackDriver
@@ -3406,19 +3412,24 @@ MediaStreamGraphImpl::ApplyAudioContextO
           !tracks.IsEnded(); tracks.Next()) {
         audioTrackPresent = true;
       }
     }
     if (!audioTrackPresent && CurrentDriver()->AsAudioCallbackDriver()) {
       CurrentDriver()->AsAudioCallbackDriver()->
         EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
 
-      SystemClockDriver* driver = new SystemClockDriver(this);
-      mMixer.RemoveCallback(CurrentDriver()->AsAudioCallbackDriver());
-      CurrentDriver()->SwitchAtNextIteration(driver);
+      SystemClockDriver* driver;
+      if (CurrentDriver()->NextDriver()) {
+        MOZ_ASSERT(!CurrentDriver()->NextDriver()->AsAudioCallbackDriver());
+      } else {
+        driver = new SystemClockDriver(this);
+        mMixer.RemoveCallback(CurrentDriver()->AsAudioCallbackDriver());
+        CurrentDriver()->SwitchAtNextIteration(driver);
+      }
     } else {
       // We are closing or suspending an AudioContext, but something else is
       // using the audio stream, we can resolve the promise now.
       AudioContextOperationCompleted(aStream, aPromise, aOperation);
     }
   }
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/audiocontext-double-suspend.html
@@ -0,0 +1,5 @@
+<script>
+var ac = new AudioContext();
+ac.resume();
+ac.resume();
+</script>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -73,12 +73,13 @@ load buffer-source-ended-1.html
 HTTP load media-element-source-seek-1.html
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
 load 1080986.html
 load 1158427.html
 load 1157994.html
 load 1122218.html
+load audiocontext-double-suspend.html
 include ../../mediasource/test/crashtests/crashtests.list
 
 # This needs to run at the end to avoid leaking busted state into other tests.
 skip-if(B2G||winWidget||OSX==1006||OSX==1010&&isDebugBuild) load 691096-1.html