Bug 866514. Part 1: Add DOMMediaStream::OnTracksAvailableCallback. r=jesup
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 03 May 2013 17:02:55 +1200
changeset 142178 67d21aabfb75ac4cc9706acf3f565fc60ae73521
parent 142177 243a19f75873fdafe725e6cf45c11027eca8e082
child 142179 531c57d87cf4fae9ff62116cb8ff631a1b4cf7ad
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs866514
milestone23.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 866514. Part 1: Add DOMMediaStream::OnTracksAvailableCallback. r=jesup
content/media/DOMMediaStream.cpp
content/media/DOMMediaStream.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
--- a/content/media/DOMMediaStream.cpp
+++ b/content/media/DOMMediaStream.cpp
@@ -108,17 +108,18 @@ public:
   }
 
 private:
   // These fields may only be accessed on the main thread
   DOMMediaStream* mStream;
 };
 
 DOMMediaStream::DOMMediaStream()
-  : mStream(nullptr), mHintContents(0)
+  : mStream(nullptr), mHintContents(0), mTrackTypesAvailable(0),
+    mNotifiedOfMediaStreamGraphShutdown(false)
 {
   SetIsDOMBinding();
 }
 
 DOMMediaStream::~DOMMediaStream()
 {
   Destroy();
 }
@@ -172,26 +173,26 @@ DOMMediaStream::GetVideoTracks(nsTArray<
 
 bool
 DOMMediaStream::IsFinished()
 {
   return !mStream || mStream->IsFinished();
 }
 
 void
-DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
 {
   mWindow = aWindow;
   SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   InitStreamCommon(gm->CreateSourceStream(this));
 }
 
 void
-DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
 {
   mWindow = aWindow;
   SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   InitStreamCommon(gm->CreateTrackUnionStream(this));
 }
 
 void
@@ -200,25 +201,25 @@ DOMMediaStream::InitStreamCommon(MediaSt
   mStream = aStream;
 
   // Setup track listener
   mListener = new StreamListener(this);
   aStream->AddListener(mListener);
 }
 
 already_AddRefed<DOMMediaStream>
-DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
 {
   nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
   stream->InitSourceStream(aWindow, aHintContents);
   return stream.forget();
 }
 
 already_AddRefed<DOMMediaStream>
-DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
 {
   nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
   stream->InitTrackUnionStream(aWindow, aHintContents);
   return stream.forget();
 }
 
 bool
 DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
@@ -228,26 +229,30 @@ DOMMediaStream::CombineWithPrincipal(nsI
 
 MediaStreamTrack*
 DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
 {
   MediaStreamTrack* track;
   switch (aType) {
   case MediaSegment::AUDIO:
     track = new AudioStreamTrack(this, aTrackID);
+    mTrackTypesAvailable |= HINT_CONTENTS_AUDIO;
     break;
   case MediaSegment::VIDEO:
     track = new VideoStreamTrack(this, aTrackID);
+    mTrackTypesAvailable |= HINT_CONTENTS_VIDEO;
     break;
   default:
     MOZ_NOT_REACHED("Unhandled track type");
     return nullptr;
   }
+  mTracks.AppendElement(track);
 
-  mTracks.AppendElement(track);
+  CheckTracksAvailable();
+
   return track;
 }
 
 MediaStreamTrack*
 DOMMediaStream::GetDOMTrackFor(TrackID aTrackID)
 {
   for (uint32_t i = 0; i < mTracks.Length(); ++i) {
     MediaStreamTrack* t = mTracks[i];
@@ -255,16 +260,54 @@ DOMMediaStream::GetDOMTrackFor(TrackID a
     // a different DOMMediaStream. Ignore those.
     if (t->GetTrackID() == aTrackID && t->GetStream() == this) {
       return t;
     }
   }
   return nullptr;
 }
 
+void
+DOMMediaStream::NotifyMediaStreamGraphShutdown()
+{
+  // No more tracks will ever be added, so just clear these callbacks now
+  // to prevent leaks.
+  mNotifiedOfMediaStreamGraphShutdown = true;
+  mRunOnTracksAvailable.Clear();
+}
+
+void
+DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
+{
+  if (mNotifiedOfMediaStreamGraphShutdown) {
+    // No more tracks will ever be added, so just delete the callback now.
+    delete aRunnable;
+    return;
+  }
+  mRunOnTracksAvailable.AppendElement(aRunnable);
+  CheckTracksAvailable();
+}
+
+void
+DOMMediaStream::CheckTracksAvailable()
+{
+  nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
+  callbacks.SwapElements(mRunOnTracksAvailable);
+
+  for (uint32_t i = 0; i < callbacks.Length(); ++i) {
+    OnTracksAvailableCallback* cb = callbacks[i];
+    if (~mTrackTypesAvailable & cb->GetExpectedTracks()) {
+      // Some expected tracks not available yet. Try this callback again later.
+      *mRunOnTracksAvailable.AppendElement() = callbacks[i].forget();
+      continue;
+    }
+    cb->NotifyTracksAvailable(this);
+  }
+}
+
 DOMLocalMediaStream::~DOMLocalMediaStream()
 {
   if (mStream) {
     // Make sure Listeners of this stream know it's going away
     Stop();
   }
 }
 
@@ -278,22 +321,24 @@ void
 DOMLocalMediaStream::Stop()
 {
   if (mStream && mStream->AsSourceStream()) {
     mStream->AsSourceStream()->EndAllTrackAndFinish();
   }
 }
 
 already_AddRefed<DOMLocalMediaStream>
-DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
+                                        TrackTypeHints aHintContents)
 {
   nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
   stream->InitSourceStream(aWindow, aHintContents);
   return stream.forget();
 }
 
 already_AddRefed<DOMLocalMediaStream>
-DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
+DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
+                                            TrackTypeHints aHintContents)
 {
   nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
   stream->InitTrackUnionStream(aWindow, aHintContents);
   return stream.forget();
 }
--- a/content/media/DOMMediaStream.h
+++ b/content/media/DOMMediaStream.h
@@ -7,16 +7,17 @@
 #define NSDOMMEDIASTREAM_H_
 
 #include "nsIDOMMediaStream.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 #include "nsIDOMWindow.h"
 #include "StreamBuffer.h"
+#include "nsIRunnable.h"
 
 class nsXPCClassInfo;
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
 // currentTime getter.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
@@ -44,16 +45,18 @@ class DOMMediaStream : public nsIDOMMedi
                        public nsWrapperCache
 {
   friend class DOMLocalMediaStream;
   typedef dom::MediaStreamTrack MediaStreamTrack;
   typedef dom::AudioStreamTrack AudioStreamTrack;
   typedef dom::VideoStreamTrack VideoStreamTrack;
 
 public:
+  typedef uint8_t TrackTypeHints;
+
   DOMMediaStream();
   virtual ~DOMMediaStream();
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMMediaStream)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   nsIDOMWindow* GetParentObject() const
   {
@@ -79,45 +82,75 @@ public:
    * Indicate that data will be contributed to this stream from origin aPrincipal.
    * If aPrincipal is null, this is ignored. Otherwise, from now on the contents
    * of this stream can only be accessed by principals that subsume aPrincipal.
    * Returns true if the stream's principal changed.
    */
   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();
+
+  // 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; }
+
+  /**
    * Create an nsDOMMediaStream whose underlying stream is a SourceMediaStream.
    */
   static already_AddRefed<DOMMediaStream>
-  CreateSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
-
-  // Hints to tell the SDP generator about whether this
-  // MediaStream probably has audio and/or video
-  enum {
-    HINT_CONTENTS_AUDIO = 0x00000001U,
-    HINT_CONTENTS_VIDEO = 0x00000002U
-  };
-  uint32_t GetHintContents() const { return mHintContents; }
-  void SetHintContents(uint32_t aHintContents) { mHintContents = aHintContents; }
+  CreateSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
 
   /**
    * Create an nsDOMMediaStream whose underlying stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMMediaStream>
-  CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents = 0);
+  CreateTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents = 0);
 
-  // Notifications from StreamListener
+  // Notifications from StreamListener.
+  // CreateDOMTrack should only be called when it's safe to run script.
   MediaStreamTrack* CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType);
   MediaStreamTrack* GetDOMTrackFor(TrackID aTrackID);
 
+  class OnTracksAvailableCallback {
+  public:
+    OnTracksAvailableCallback(uint8_t aExpectedTracks = 0)
+      : mExpectedTracks(aExpectedTracks) {}
+    virtual ~OnTracksAvailableCallback() {}
+    virtual void NotifyTracksAvailable(DOMMediaStream* aStream) = 0;
+    TrackTypeHints GetExpectedTracks() { return mExpectedTracks; }
+    void SetExpectedTracks(TrackTypeHints aExpectedTracks) { mExpectedTracks = aExpectedTracks; }
+  private:
+    TrackTypeHints mExpectedTracks;
+  };
+
+  // When one track of the appropriate type has been added for each bit set
+  // in aCallback->GetExpectedTracks(), run aCallback->NotifyTracksAvailable.
+  // 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);
+
 protected:
   void Destroy();
-  void InitSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
-  void InitTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
+  void InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
+  void InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
   void InitStreamCommon(MediaStream* aStream);
+  void CheckTracksAvailable();
 
   class StreamListener;
   friend class StreamListener;
 
   // We need this to track our parent object.
   nsCOMPtr<nsIDOMWindow> mWindow;
 
   // MediaStream is owned by the graph, but we tell it when to die, and it won't
@@ -125,19 +158,23 @@ protected:
   MediaStream* mStream;
   // Principal identifying who may access the contents of this stream.
   // 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;
 
-  // tells the SDP generator about whether this
-  // MediaStream probably has audio and/or video
-  uint32_t mHintContents;
+  nsTArray<nsAutoPtr<OnTracksAvailableCallback> > mRunOnTracksAvailable;
+
+  // 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,
                             public nsIDOMLocalMediaStream
 {
 public:
   DOMLocalMediaStream() {}
   virtual ~DOMLocalMediaStream();
@@ -148,20 +185,20 @@ public:
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual void Stop();
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a SourceMediaStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
-  CreateSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
+  CreateSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
 
   /**
    * Create an nsDOMLocalMediaStream whose underlying stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMLocalMediaStream>
-  CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents = 0);
+  CreateTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents = 0);
 };
 
 }
 
 #endif /* NSDOMMEDIASTREAM_H_ */
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -17,16 +17,17 @@
 #include "VideoUtils.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelCommon.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include <algorithm>
+#include "DOMMediaStream.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 #ifdef PR_LOGGING
 PRLogModuleInfo* gMediaStreamGraphLog;
@@ -1132,23 +1133,31 @@ private:
 
 class MediaStreamGraphShutDownRunnable : public nsRunnable {
 public:
   MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph) : mGraph(aGraph) {}
   NS_IMETHOD Run()
   {
     NS_ASSERTION(mGraph->mDetectedNotRunning,
                  "We should know the graph thread control loop isn't running!");
+
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
       // mGraph is no longer needed, so delete it. If the graph is not empty
       // then we must be in a forced shutdown and some later AppendMessage will
       // detect that the manager has been emptied, and delete it.
       delete mGraph;
     } else {
+      for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) {
+        DOMMediaStream* s = mGraph->mStreams[i]->GetWrapper();
+        if (s) {
+          s->NotifyMediaStreamGraphShutdown();
+        }
+      }
+
       NS_ASSERTION(mGraph->mForceShutDown, "Not in forced shutdown?");
       mGraph->mLifecycleState =
         MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
     }
     return NS_OK;
   }
 private:
   MediaStreamGraphImpl* mGraph;
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -422,16 +422,22 @@ public:
    * will not be blocked after mStateComputedTime.
    */
   GraphTime StreamTimeToGraphTime(StreamTime aTime);
   bool IsFinishedOnGraphThread() { return mFinished; }
   void FinishOnGraphThread();
 
   bool HasCurrentData() { return mHasCurrentData; }
 
+  DOMMediaStream* GetWrapper()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only use DOMMediaStream on main thread");
+    return mWrapper;
+  }
+
 protected:
   virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime)
   {
     mBufferStartTime += aBlockedTime;
     mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime);
     mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime);
     mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime);