Bug 914030 - Shut down an AudioDestinationNode's graph on destruction, if not already. r=ehsan, a=akeybl
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 10 Sep 2013 17:05:22 +1200
changeset 154093 dde022e033510d8c2d5e4ec832e3762113e94189
parent 154092 0cd6256aacfed10eacc34201131a4278ccec0c30
child 154094 2f1d4aea750edf61955f25bfd5d0f89af8af8be0
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersehsan, akeybl
bugs914030
milestone25.0
Bug 914030 - Shut down an AudioDestinationNode's graph on destruction, if not already. r=ehsan, a=akeybl This means that the graph will be shutdown properly, even if the AudioDestinationNode is unlinked before AudioContext::Shutdown() is called. Making MediaStreamGraph::DestroyNonRealtimeInstance() idempotent also makes AudioContext::Shutdown() idempotent.
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
content/media/webaudio/AudioContext.h
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioDestinationNode.h
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -2334,19 +2334,22 @@ MediaStreamGraph::CreateNonRealtimeInsta
   MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
   return graph;
 }
 
 void
 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
-  MOZ_ASSERT(aGraph != gGraph, "Should not destroy the global graph here");
+  MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
+  if (graph->mForceShutDown)
+    return; // already done
+
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(0);
   }
   graph->ForceShutDown();
 }
 
 SourceMediaStream*
@@ -2403,16 +2406,22 @@ MediaStreamGraph::CreateAudioNodeStream(
     stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
                                            aEngine->NodeMainThread()->ChannelCountModeValue(),
                                            aEngine->NodeMainThread()->ChannelInterpretationValue());
   }
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
+bool
+MediaStreamGraph::IsNonRealtime() const
+{
+  return this != gGraph;
+}
+
 void
 MediaStreamGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   NS_ASSERTION(!graph->mRealtime, "non-realtime only");
 
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -960,16 +960,17 @@ public:
   // We ensure that the graph current time advances in multiples of
   // IdealAudioBlockSize()/IdealAudioRate(). A stream that never blocks
   // and has a track with the ideal audio rate will produce audio in
   // multiples of the block size.
 
   // Main thread only
   static MediaStreamGraph* GetInstance();
   static MediaStreamGraph* CreateNonRealtimeInstance();
+  // Idempotent
   static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
 
   // Control API.
   /**
    * Create a stream that a media decoder (or some other source of
    * media data, such as a camera) can write to.
    */
   SourceMediaStream* CreateSourceStream(DOMMediaStream* aWrapper);
@@ -1007,16 +1008,18 @@ public:
                                      TrackRate aSampleRate = 0);
 
   /**
    * Returns the number of graph updates sent. This can be used to track
    * whether a given update has been processed by the graph thread and reflected
    * in main-thread stream state.
    */
   int64_t GetCurrentGraphUpdateIndex() { return mGraphUpdatesSent; }
+
+  bool IsNonRealtime() const;
   /**
    * Start processing non-realtime for a specific number of ticks.
    */
   void StartNonRealtimeProcessing(uint32_t aTicksToProcess);
 
   /**
    * Media graph thread only.
    * Dispatches a runnable that will run on the main thread after all
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -78,17 +78,17 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioContext,
                                            nsDOMEventTargetHelper)
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
-  void Shutdown();
+  void Shutdown(); // idempotent
   void Suspend();
   void Resume();
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   using nsDOMEventTargetHelper::DispatchTrustedEvent;
 
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -230,16 +230,29 @@ AudioDestinationNode::AudioDestinationNo
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
 
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
 }
 
+void
+AudioDestinationNode::DestroyMediaStream()
+{
+  if (!mStream)
+    return;
+
+  MediaStreamGraph* graph = mStream->Graph();
+  if (graph->IsNonRealtime()) {
+    MediaStreamGraph::DestroyNonRealtimeInstance(graph);
+  }
+  AudioNode::DestroyMediaStream();
+}
+
 uint32_t
 AudioDestinationNode::MaxChannelCount() const
 {
   return Context()->MaxChannelCount();
 }
 
 void
 AudioDestinationNode::SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -20,16 +20,18 @@ public:
   // This node type knows what MediaStreamGraph to use based on
   // whether it's in offline mode.
   AudioDestinationNode(AudioContext* aContext,
                        bool aIsOffline,
                        uint32_t aNumberOfChannels = 0,
                        uint32_t aLength = 0,
                        float aSampleRate = 0.0f);
 
+  virtual void DestroyMediaStream() MOZ_OVERRIDE;
+
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
   {
     return 0;