Bug 834835. Part 2: Make DOMMediaStream maintain a list of MediaStreamTrack objects. r=jesup
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 17 Apr 2013 17:18:24 +1200
changeset 129026 7f2943d34a13d1cb897d4d6379f188baf7e85a2d
parent 129025 6efeef98993c6d1117c318996d8ae7c24a5f4984
child 129027 025b03b9e8d970afc41a4e4a1d01199ad95b02e9
push id26629
push userrocallahan@mozilla.com
push dateWed, 17 Apr 2013 05:56:27 +0000
treeherdermozilla-inbound@f9236671098e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs834835
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 834835. Part 2: Make DOMMediaStream maintain a list of MediaStreamTrack objects. r=jesup We use a MediaStreamListener to watch the MediaStream and maintain the list of MediaStreamTrack objects.
content/media/DOMMediaStream.cpp
content/media/DOMMediaStream.h
--- a/content/media/DOMMediaStream.cpp
+++ b/content/media/DOMMediaStream.cpp
@@ -4,34 +4,119 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMMediaStream.h"
 #include "nsDOMClassInfoID.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/LocalMediaStreamBinding.h"
 #include "MediaStreamGraph.h"
+#include "AudioStreamTrack.h"
+#include "VideoStreamTrack.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream)
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(DOMMediaStream, mWindow)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(DOMMediaStream, mWindow, mTracks)
 
 NS_IMPL_ISUPPORTS_INHERITED1(DOMLocalMediaStream, DOMMediaStream,
                              nsIDOMLocalMediaStream)
 
+class DOMMediaStream::StreamListener : public MediaStreamListener {
+public:
+  StreamListener(DOMMediaStream* aStream)
+    : mStream(aStream)
+  {}
+
+  // Main thread only
+  void Forget() { mStream = nullptr; }
+  DOMMediaStream* GetStream() { return mStream; }
+
+  class TrackChange : public nsRunnable {
+  public:
+    TrackChange(StreamListener* aListener,
+                TrackID aID, TrackTicks aTrackOffset,
+                uint32_t aEvents, MediaSegment::Type aType)
+      : mListener(aListener), mID(aID), mEvents(aEvents), mType(aType)
+    {
+    }
+
+    NS_IMETHOD Run()
+    {
+      NS_ASSERTION(NS_IsMainThread(), "main thread only");
+
+      DOMMediaStream* stream = mListener->GetStream();
+      if (!stream) {
+        return NS_OK;
+      }
+
+      nsRefPtr<MediaStreamTrack> track;
+      if (mEvents & MediaStreamListener::TRACK_EVENT_CREATED) {
+        track = stream->CreateDOMTrack(mID, mType);
+      } else {
+        track = stream->GetDOMTrackFor(mID);
+      }
+      if (mEvents & MediaStreamListener::TRACK_EVENT_ENDED) {
+        track->NotifyEnded();
+      }
+      return NS_OK;
+    }
+
+    StreamTime mEndTime;
+    nsRefPtr<StreamListener> mListener;
+    TrackID mID;
+    uint32_t mEvents;
+    MediaSegment::Type mType;
+  };
+
+  /**
+   * Notify that changes to one of the stream tracks have been queued.
+   * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
+   * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
+   * at aTrackOffset (relative to the start of the stream).
+   * aQueuedMedia can be null if there is no output.
+   */
+  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                        TrackRate aTrackRate,
+                                        TrackTicks aTrackOffset,
+                                        uint32_t aTrackEvents,
+                                        const MediaSegment& aQueuedMedia)
+  {
+    if (aTrackEvents & (TRACK_EVENT_CREATED | TRACK_EVENT_ENDED)) {
+      nsRefPtr<TrackChange> runnable =
+        new TrackChange(this, aID, aTrackOffset, aTrackEvents,
+                        aQueuedMedia.GetType());
+      NS_DispatchToMainThread(runnable);
+    }
+  }
+
+private:
+  // These fields may only be accessed on the main thread
+  DOMMediaStream* mStream;
+};
+
+DOMMediaStream::DOMMediaStream()
+  : mStream(nullptr), mHintContents(0)
+{
+  SetIsDOMBinding();
+}
+
 DOMMediaStream::~DOMMediaStream()
 {
+  if (mListener) {
+    mListener->Forget();
+  }
   if (mStream) {
     mStream->Destroy();
   }
 }
 
 JSObject*
 DOMMediaStream::WrapObject(JSContext* aCx, JSObject* aScope)
 {
@@ -51,26 +136,36 @@ DOMMediaStream::IsFinished()
 }
 
 void
 DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
 {
   mWindow = aWindow;
   SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
-  mStream = gm->CreateSourceStream(this);
+  InitStreamCommon(gm->CreateSourceStream(this));
 }
 
 void
 DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
 {
   mWindow = aWindow;
   SetHintContents(aHintContents);
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
-  mStream = gm->CreateTrackUnionStream(this);
+  InitStreamCommon(gm->CreateTrackUnionStream(this));
+}
+
+void
+DOMMediaStream::InitStreamCommon(MediaStream* aStream)
+{
+  mStream = aStream;
+
+  // Setup track listener
+  mListener = new StreamListener(this);
+  aStream->AddListener(mListener);
 }
 
 already_AddRefed<DOMMediaStream>
 DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents)
 {
   nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
   stream->InitSourceStream(aWindow, aHintContents);
   return stream.forget();
@@ -85,16 +180,50 @@ DOMMediaStream::CreateTrackUnionStream(n
 }
 
 bool
 DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
 {
   return nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
 }
 
+MediaStreamTrack*
+DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
+{
+  MediaStreamTrack* track;
+  switch (aType) {
+  case MediaSegment::AUDIO:
+    track = new AudioStreamTrack(this, aTrackID);
+    break;
+  case MediaSegment::VIDEO:
+    track = new VideoStreamTrack(this, aTrackID);
+    break;
+  default:
+    MOZ_NOT_REACHED("Unhandled track type");
+    return nullptr;
+  }
+
+  mTracks.AppendElement(track);
+  return track;
+}
+
+MediaStreamTrack*
+DOMMediaStream::GetDOMTrackFor(TrackID aTrackID)
+{
+  for (uint32_t i = 0; i < mTracks.Length(); ++i) {
+    MediaStreamTrack* t = mTracks[i];
+    // We may add streams to our track list that are actually owned by
+    // a different DOMMediaStream. Ignore those.
+    if (t->GetTrackID() == aTrackID && t->GetStream() == this) {
+      return t;
+    }
+  }
+  return nullptr;
+}
+
 DOMLocalMediaStream::~DOMLocalMediaStream()
 {
   if (mStream) {
     // Make sure Listeners of this stream know it's going away
     Stop();
   }
 }
 
--- a/content/media/DOMMediaStream.h
+++ b/content/media/DOMMediaStream.h
@@ -6,16 +6,17 @@
 #ifndef NSDOMMEDIASTREAM_H_
 #define NSDOMMEDIASTREAM_H_
 
 #include "nsIDOMMediaStream.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 #include "nsIDOMWindow.h"
+#include "StreamBuffer.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
@@ -25,29 +26,31 @@ class nsXPCClassInfo;
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 namespace mozilla {
 
 class MediaStream;
 
+namespace dom {
+class MediaStreamTrack;
+}
+
 /**
  * DOM wrapper for MediaStreams.
  */
 class DOMMediaStream : public nsIDOMMediaStream,
                        public nsWrapperCache
 {
   friend class DOMLocalMediaStream;
+  typedef dom::MediaStreamTrack MediaStreamTrack;
 
 public:
-  DOMMediaStream() : mStream(nullptr), mHintContents(0)
-  {
-    SetIsDOMBinding();
-  }
+  DOMMediaStream();
   virtual ~DOMMediaStream();
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMMediaStream)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   nsIDOMWindow* GetParentObject() const
   {
     return mWindow;
@@ -87,30 +90,41 @@ public:
   void SetHintContents(uint32_t aHintContents) { mHintContents = aHintContents; }
 
   /**
    * Create an nsDOMMediaStream whose underlying stream is a TrackUnionStream.
    */
   static already_AddRefed<DOMMediaStream>
   CreateTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents = 0);
 
+  // Notifications from StreamListener
+  MediaStreamTrack* CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType);
+  MediaStreamTrack* GetDOMTrackFor(TrackID aTrackID);
+
 protected:
   void InitSourceStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
   void InitTrackUnionStream(nsIDOMWindow* aWindow, uint32_t aHintContents);
+  void InitStreamCommon(MediaStream* aStream);
+
+  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
   // die until we let it.
   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;
 };
 
 class DOMLocalMediaStream : public DOMMediaStream,
                             public nsIDOMLocalMediaStream
 {