Bug 856361. Part 6: Make MediaStreamAudioSourceNode keep its DOMMediaStream alive, and make the DOMMediaStream keep the MediaStreamAudioSourceNode alive. r=ehsan
☠☠ backed out by ebd005b9e9d7 ☠ ☠
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 25 Jul 2013 14:07:34 +1200
changeset 140293 3d6f6f06da5ebd710e31ea1a0f5d5b84434c47ab
parent 140292 9e90a81b9e37371a8c209ba1675d3391405a1afe
child 140294 911a11878522579e814c2eade99f922839728667
push id31683
push userrocallahan@mozilla.com
push dateMon, 29 Jul 2013 03:01:50 +0000
treeherdermozilla-inbound@911a11878522 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs856361
milestone25.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 856361. Part 6: Make MediaStreamAudioSourceNode keep its DOMMediaStream alive, and make the DOMMediaStream keep the MediaStreamAudioSourceNode alive. r=ehsan
content/media/DOMMediaStream.cpp
content/media/DOMMediaStream.h
content/media/MediaStreamGraph.cpp
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/MediaStreamAudioSourceNode.cpp
content/media/webaudio/MediaStreamAudioSourceNode.h
--- a/content/media/DOMMediaStream.cpp
+++ b/content/media/DOMMediaStream.cpp
@@ -23,21 +23,23 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMMediaStream)
   tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMMediaStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DOMMediaStream)
 
 NS_IMPL_ISUPPORTS_INHERITED1(DOMLocalMediaStream, DOMMediaStream,
                              nsIDOMLocalMediaStream)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DOMAudioNodeMediaStream, DOMMediaStream,
@@ -280,16 +282,26 @@ DOMMediaStream::GetDOMTrackFor(TrackID a
 
 void
 DOMMediaStream::NotifyMediaStreamGraphShutdown()
 {
   // No more tracks will ever be added, so just clear these callbacks now
   // to prevent leaks.
   mNotifiedOfMediaStreamGraphShutdown = true;
   mRunOnTracksAvailable.Clear();
+
+  mConsumersToKeepAlive.Clear();
+}
+
+void
+DOMMediaStream::NotifyStreamStateChanged()
+{
+  if (IsFinished()) {
+    mConsumersToKeepAlive.Clear();
+  }
 }
 
 void
 DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
 {
   if (mNotifiedOfMediaStreamGraphShutdown) {
     // No more tracks will ever be added, so just delete the callback now.
     delete aRunnable;
--- a/content/media/DOMMediaStream.h
+++ b/content/media/DOMMediaStream.h
@@ -90,16 +90,20 @@ public:
   bool CombineWithPrincipal(nsIPrincipal* aPrincipal);
 
   /**
    * Called when this stream's MediaStreamGraph has been shut down. Normally
    * MSGs are only shut down when all streams have been removed, so this
    * will only be called during a forced shutdown due to application exit.
    */
   void NotifyMediaStreamGraphShutdown();
+  /**
+   * Called when the main-thread state of the MediaStream changed.
+   */
+  void NotifyStreamStateChanged();
 
   // Indicate what track types we eventually expect to add to this stream
   enum {
     HINT_CONTENTS_AUDIO = 1 << 0,
     HINT_CONTENTS_VIDEO = 1 << 1
   };
   TrackTypeHints GetHintContents() const { return mHintContents; }
   void SetHintContents(TrackTypeHints aHintContents) { mHintContents = aHintContents; }
@@ -142,16 +146,27 @@ public:
   // It is allowed to do anything, including run script.
   // aCallback may run immediately during this call if tracks are already
   // available!
   // We only care about track additions, we'll fire the notification even if
   // some of the tracks have been removed.
   // Takes ownership of aCallback.
   void OnTracksAvailable(OnTracksAvailableCallback* aCallback);
 
+  /**
+   * Add an nsISupports object that this stream will keep alive as long as
+   * the stream is not finished.
+   */
+  void AddConsumerToKeepAlive(nsISupports* aConsumer)
+  {
+    if (!IsFinished() && !mNotifiedOfMediaStreamGraphShutdown) {
+      mConsumersToKeepAlive.AppendElement(aConsumer);
+    }
+  }
+
 protected:
   void Destroy();
   void InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
   void InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
   void InitStreamCommon(MediaStream* aStream);
   void CheckTracksAvailable();
 
   class StreamListener;
@@ -170,16 +185,19 @@ protected:
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsAutoTArray<nsRefPtr<MediaStreamTrack>,2> mTracks;
   nsRefPtr<StreamListener> mListener;
 
   nsTArray<nsAutoPtr<OnTracksAvailableCallback> > mRunOnTracksAvailable;
 
+  // Keep these alive until the stream finishes
+  nsTArray<nsCOMPtr<nsISupports> > mConsumersToKeepAlive;
+
   // Indicate what track types we eventually expect to add to this stream
   uint8_t mHintContents;
   // Indicate what track types have been added to this stream
   uint8_t mTrackTypesAvailable;
   bool mNotifiedOfMediaStreamGraphShutdown;
 };
 
 class DOMLocalMediaStream : public DOMMediaStream,
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1215,16 +1215,19 @@ MediaStreamGraphImpl::ApplyStreamUpdate(
   mMonitor.AssertCurrentThreadOwns();
 
   MediaStream* stream = aUpdate->mStream;
   if (!stream)
     return;
   stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
   stream->mMainThreadFinished = aUpdate->mNextMainThreadFinished;
 
+  if (stream->mWrapper) {
+    stream->mWrapper->NotifyStreamStateChanged();
+  }
   for (int32_t i = stream->mMainThreadListeners.Length() - 1; i >= 0; --i) {
     stream->mMainThreadListeners[i]->NotifyMainThreadStateChanged();
   }
 }
 
 void
 MediaStreamGraphImpl::ShutdownThreads()
 {
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -250,17 +250,17 @@ AudioContext::CreateScriptProcessor(uint
 already_AddRefed<AnalyserNode>
 AudioContext::CreateAnalyser()
 {
   nsRefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
   return analyserNode.forget();
 }
 
 already_AddRefed<MediaStreamAudioSourceNode>
-AudioContext::CreateMediaStreamSource(const DOMMediaStream& aMediaStream,
+AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
                                       ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
   nsRefPtr<MediaStreamAudioSourceNode> mediaStreamAudioSourceNode = new MediaStreamAudioSourceNode(this, &aMediaStream);
   return mediaStreamAudioSourceNode.forget();
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -158,17 +158,17 @@ public:
 
   already_AddRefed<GainNode>
   CreateGainNode()
   {
     return CreateGain();
   }
 
   already_AddRefed<MediaStreamAudioSourceNode>
-  CreateMediaStreamSource(const DOMMediaStream& aMediaStream, ErrorResult& aRv);
+  CreateMediaStreamSource(DOMMediaStream& aMediaStream, ErrorResult& aRv);
 
   already_AddRefed<DelayNode>
   CreateDelay(double aMaxDelayTime, ErrorResult& aRv);
 
   already_AddRefed<DelayNode>
   CreateDelayNode(double aMaxDelayTime, ErrorResult& aRv)
   {
     return CreateDelay(aMaxDelayTime, aRv);
--- a/content/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -8,28 +8,48 @@
 #include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeExternalInputStream.h"
 #include "DOMMediaStream.h"
 
 namespace mozilla {
 namespace dom {
 
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode)
+NS_INTERFACE_MAP_END_INHERITING(MediaStreamAudioSourceNode)
+
+NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+
 MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext,
-                                                       const DOMMediaStream* aMediaStream)
+                                                       DOMMediaStream* aMediaStream)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
-              ChannelInterpretation::Speakers)
+              ChannelInterpretation::Speakers),
+    mInputStream(aMediaStream)
 {
   AudioNodeEngine* engine = new AudioNodeEngine(this);
   mStream = aContext->Graph()->CreateAudioNodeExternalInputStream(engine);
   ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get());
   mInputPort = outputStream->AllocateInputPort(aMediaStream->GetStream(),
                                                MediaInputPort::FLAG_BLOCK_INPUT);
+  mInputStream->AddConsumerToKeepAlive(this);
+}
+
+MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
+{
 }
 
 void
 MediaStreamAudioSourceNode::DestroyMediaStream()
 {
   if (mInputPort) {
     mInputPort->Destroy();
     mInputPort = nullptr;
--- a/content/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.h
@@ -5,30 +5,38 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaStreamAudioSourceNode_h_
 #define MediaStreamAudioSourceNode_h_
 
 #include "AudioNode.h"
 
 namespace mozilla {
+
+class DOMMediaStream;
+
 namespace dom {
 
 class MediaStreamAudioSourceNode : public AudioNode
 {
 public:
-  MediaStreamAudioSourceNode(AudioContext* aContext, const DOMMediaStream* aMediaStream);
+  MediaStreamAudioSourceNode(AudioContext* aContext, DOMMediaStream* aMediaStream);
+  // Define consturctor out-of-line so we can forward-declare DOMMediaStream
+  virtual ~MediaStreamAudioSourceNode();
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return 0; }
 
 private:
   nsRefPtr<MediaInputPort> mInputPort;
+  nsRefPtr<DOMMediaStream> mInputStream;
 };
 
 }
 }
 
 #endif
-