Bug 974089 - Destroy WebAudio MediaStream when a source finishes. r=padenot
authorKarl Tomlinson <karlt+@karlt.net>
Wed, 10 Jun 2015 13:31:29 +0200
changeset 248565 7d4c3379ecb3cdf696f906e18f3560bb7d9c9705
parent 248564 f725700df16d2457b6cbb5c3eda11f6b19f4e632
child 248566 4055cc03c5c86c6a7a49aceb897fcf1129ae75e3
push id28899
push userryanvm@gmail.com
push dateFri, 12 Jun 2015 18:57:39 +0000
treeherdermozilla-central@203e1025a826 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs974089
milestone41.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 974089 - Destroy WebAudio MediaStream when a source finishes. r=padenot
dom/media/webaudio/AudioBufferSourceNode.cpp
dom/media/webaudio/AudioNode.cpp
dom/media/webaudio/AudioNode.h
dom/media/webaudio/OscillatorNode.cpp
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -638,17 +638,19 @@ AudioBufferSourceNode::Start(double aWhe
     ns->SetDoubleParameter(START, mContext->DOMTimeToStreamTime(aWhen));
   }
 }
 
 void
 AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
 {
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
-  MOZ_ASSERT(ns, "Why don't we have a stream here?");
+  if (!mStream) {
+    return;
+  }
 
   if (mBuffer) {
     float rate = mBuffer->SampleRate();
     nsRefPtr<ThreadSharedFloatArrayBufferList> data =
       mBuffer->GetThreadSharedChannelsForRate(aCx);
     ns->SetBuffer(data.forget());
     ns->SetInt32Parameter(SAMPLE_RATE, rate);
 
@@ -722,16 +724,18 @@ AudioBufferSourceNode::NotifyMainThreadS
     {
       // If it's not safe to run scripts right now, schedule this to run later
       if (!nsContentUtils::IsSafeToRunScript()) {
         nsContentUtils::AddScriptRunner(this);
         return NS_OK;
       }
 
       mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+      // Release stream resources.
+      mNode->DestroyMediaStream();
       return NS_OK;
     }
   private:
     nsRefPtr<AudioBufferSourceNode> mNode;
   };
 
   NS_DispatchToMainThread(new EndedEventDispatcher(this));
 
@@ -739,35 +743,42 @@ AudioBufferSourceNode::NotifyMainThreadS
   // Warning: The below line might delete this.
   MarkInactive();
 }
 
 void
 AudioBufferSourceNode::SendPlaybackRateToStream(AudioNode* aNode)
 {
   AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
+  if (!This->mStream) {
+    return;
+  }
   SendTimelineParameterToStream(This, PLAYBACKRATE, *This->mPlaybackRate);
 }
 
 void
 AudioBufferSourceNode::SendDetuneToStream(AudioNode* aNode)
 {
   AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
   SendTimelineParameterToStream(This, DETUNE, *This->mDetune);
 }
 
 void
 AudioBufferSourceNode::SendDopplerShiftToStream(double aDopplerShift)
 {
+  MOZ_ASSERT(mStream, "Should have disconnected panner if no stream");
   SendDoubleParameterToStream(DOPPLERSHIFT, aDopplerShift);
 }
 
 void
 AudioBufferSourceNode::SendLoopParametersToStream()
 {
+  if (!mStream) {
+    return;
+  }
   // Don't compute and set the loop parameters unnecessarily
   if (mLoop && mBuffer) {
     float rate = mBuffer->SampleRate();
     double length = (double(mBuffer->Length()) / mBuffer->SampleRate());
     double actualLoopStart, actualLoopEnd;
     if (mLoopStart >= 0.0 && mLoopEnd > 0.0 &&
         mLoopStart < mLoopEnd) {
       MOZ_ASSERT(mLoopStart != 0.0 || mLoopEnd != 0.0);
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -347,20 +347,22 @@ AudioNode::Disconnect(uint32_t aOutput, 
       if (input.mInputNode == this && input.mOutputPort == aOutput) {
         // Destroying the InputNode here sends a message to the graph thread
         // to disconnect the streams, which should be sent before the
         // RunAfterPendingUpdates() call below.
         dest->mInputNodes.RemoveElementAt(j);
         // Remove one instance of 'dest' from mOutputNodes. There could be
         // others, and it's not correct to remove them all since some of them
         // could be for different output ports.
-        nsCOMPtr<nsIRunnable> runnable =
-          new RunnableRelease(mOutputNodes[i].forget());
+        nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
         mOutputNodes.RemoveElementAt(i);
-        mStream->RunAfterPendingUpdates(runnable.forget());
+        if (mStream) {
+          nsRefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
+          mStream->RunAfterPendingUpdates(runnable.forget());
+        }
         break;
       }
     }
   }
 
   for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
     AudioParam* dest = mOutputParams[i];
     for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
--- a/dom/media/webaudio/AudioNode.h
+++ b/dom/media/webaudio/AudioNode.h
@@ -215,17 +215,17 @@ protected:
   void SendChannelMixingParametersToStream();
   static void SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex,
                                             const AudioParamTimeline& aValue);
 
 private:
   nsRefPtr<AudioContext> mContext;
 
 protected:
-  // Must be set in the constructor. Must not be null.
+  // Must be set in the constructor. Must not be null unless finished.
   // If MaxNumberOfInputs() is > 0, then mStream must be a ProcessedMediaStream.
   nsRefPtr<MediaStream> mStream;
 
 private:
   // For every InputNode, there is a corresponding entry in mOutputNodes of the
   // InputNode's mInputNode.
   nsTArray<InputNode> mInputNodes;
   // For every mOutputNode entry, there is a corresponding entry in mInputNodes
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -420,29 +420,38 @@ OscillatorNode::WrapObject(JSContext* aC
 {
   return OscillatorNodeBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
 {
   OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  if (!This->mStream) {
+    return;
+  }
   SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
 }
 
 void
 OscillatorNode::SendDetuneToStream(AudioNode* aNode)
 {
   OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
+  if (!This->mStream) {
+    return;
+  }
   SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
 }
 
 void
 OscillatorNode::SendTypeToStream()
 {
+  if (!mStream) {
+    return;
+  }
   if (mType == OscillatorType::Custom) {
     // The engine assumes we'll send the custom data before updating the type.
     SendPeriodicWaveToStream();
   }
   SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
 }
 
 void OscillatorNode::SendPeriodicWaveToStream()
@@ -524,16 +533,18 @@ OscillatorNode::NotifyMainThreadStreamFi
     {
       // If it's not safe to run scripts right now, schedule this to run later
       if (!nsContentUtils::IsSafeToRunScript()) {
         nsContentUtils::AddScriptRunner(this);
         return NS_OK;
       }
 
       mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
+      // Release stream resources.
+      mNode->DestroyMediaStream();
       return NS_OK;
     }
   private:
     nsRefPtr<OscillatorNode> mNode;
   };
 
   NS_DispatchToMainThread(new EndedEventDispatcher(this));