Bug 1208371 - Implement DOMMediaStream::Clone() r?smaug,jib,roc draft
authorAndreas Pehrson <pehrsons@gmail.com>
Thu, 05 Nov 2015 15:42:24 +0800
changeset 306658 59035f24025ad781c86e6bf6f74d6dbfbf994d5d
parent 306657 870eef8b92f9b4d2eec97124ba13f651ad7ea9ba
child 306659 481d2dace480c02b375a91e902ece00eef223c2b
push id7183
push userpehrsons@gmail.com
push dateThu, 05 Nov 2015 07:42:40 +0000
reviewerssmaug, jib, roc
bugs1208371
milestone45.0a1
Bug 1208371 - Implement DOMMediaStream::Clone() r?smaug,jib,roc
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/webidl/MediaStream.webidl
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -364,25 +364,25 @@ DOMMediaStream::Destroy()
     mPlaybackPort->Destroy();
     mPlaybackPort = nullptr;
   }
   if (mOwnedPort) {
     mOwnedPort->Destroy();
     mOwnedPort = nullptr;
   }
   if (mPlaybackStream) {
-    mPlaybackStream->Destroy();
+    mPlaybackStream->UnregisterUser();
     mPlaybackStream = nullptr;
   }
   if (mOwnedStream) {
-    mOwnedStream->Destroy();
+    mOwnedStream->UnregisterUser();
     mOwnedStream = nullptr;
   }
   if (mInputStream) {
-    mInputStream->Destroy();
+    mInputStream->UnregisterUser();
     mInputStream = nullptr;
   }
 }
 
 JSObject*
 DOMMediaStream::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
@@ -568,16 +568,94 @@ DOMMediaStream::RemoveTrack(MediaStreamT
   // end.
   toRemove->BlockTrackId(aTrack.mTrackID);
 
   DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
   MOZ_ASSERT(removed);
   LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
 }
 
+already_AddRefed<DOMMediaStream>
+DOMMediaStream::Clone()
+{
+  class ClonedStreamSourceGetter : public MediaStreamTrackSourceGetter
+  {
+  public:
+    ClonedStreamSourceGetter(DOMMediaStream* aStream)
+      : mStream(aStream) {}
+
+    already_AddRefed<MediaStreamTrackSource>
+    GetMediaStreamTrackSource(TrackID aInputTrackID) override
+    {
+      for (const TrackPort* port : mStream->mOwnedTracks) {
+        if (port->GetTrack()->mInputTrackID == aInputTrackID) {
+          return do_AddRef(&port->GetTrack()->GetSource());
+        }
+      }
+
+      return nullptr;
+    }
+
+  protected:
+    virtual ~ClonedStreamSourceGetter() {}
+
+    WeakPtr<DOMMediaStream> mStream;
+  };
+
+  RefPtr<DOMMediaStream> newStream =
+    new DOMMediaStream(GetParentObject(), new ClonedStreamSourceGetter(this));
+
+  LOG(LogLevel::Info, ("DOMMediaStream %p created clone %p",
+                       this, newStream.get()));
+
+  MOZ_RELEASE_ASSERT(mPlaybackStream);
+  MOZ_RELEASE_ASSERT(mPlaybackStream->Graph());
+  MediaStreamGraph* graph = mPlaybackStream->Graph();
+
+  // We initiate the owned and playback streams first, since we need to create
+  // all existing DOM tracks before we add the generic input port from
+  // mInputStream to mOwnedStream (see AllocateInputPort wrt. destination
+  // TrackID as to why).
+  newStream->InitOwnedStreamCommon(graph);
+  newStream->InitPlaybackStreamCommon(graph);
+
+  // We set up track-locked input ports for all existing DOM tracks, so
+  // we need to block those in the generic input port set up at the end.
+  nsTArray<TrackID> tracksToBlock;
+  for (const RefPtr<TrackPort>& info : mOwnedTracks) {
+    tracksToBlock.AppendElement(info->GetTrack()->mTrackID);
+  }
+
+  // Set up existing DOM tracks.
+  TrackID allocatedTrackID = 1;
+  for (const RefPtr<TrackPort>& info : mTracks) {
+    MediaStreamTrack& track = *info->GetTrack();
+
+    LOG(LogLevel::Debug, ("DOMMediaStream %p forwarding external track %p to clone %p",
+                          this, &track, newStream.get()));
+    RefPtr<MediaStreamTrack> trackClone =
+      newStream->CreateClonedDOMTrack(track, allocatedTrackID++);
+  }
+
+  // Set up an input port from our input stream to the new DOM stream's owned
+  // stream, to allow for dynamically added tracks at the source to appear in
+  // the clone. The clone may treat mInputStream as its own mInputStream but
+  // ownership remains with us.
+  newStream->mInputStream = mInputStream;
+  if (mInputStream) {
+    newStream->mInputStream->RegisterUser();
+    newStream->mOwnedPort =
+      newStream->mOwnedStream->AllocateInputPort(mInputStream,
+                                                 TRACK_ANY, TRACK_ANY, 0, 0,
+                                                 &tracksToBlock);
+  }
+
+  return newStream.forget();
+}
+
 bool
 DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const
 {
   return !!FindPlaybackTrackPort(aTrack);
 }
 
 bool
 DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const
@@ -626,41 +704,44 @@ DOMMediaStream::InitAudioCaptureStream(M
 
 void
 DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
                                       MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(!mOwnedStream, "Input stream must be initialized before owned stream");
 
   mInputStream = aStream;
+  mInputStream->RegisterUser();
 }
 
 void
 DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(!mPlaybackStream, "Owned stream must be initialized before playback stream");
 
   // We pass null as the wrapper since it is only used to signal finished
   // streams. This is only needed for the playback stream.
   mOwnedStream = aGraph->CreateTrackUnionStream(nullptr);
   mOwnedStream->SetAutofinish(true);
+  mOwnedStream->RegisterUser();
   if (mInputStream) {
     mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
   }
 
   // Setup track listeners
   mOwnedListener = new OwnedStreamListener(this);
   mOwnedStream->AddListener(mOwnedListener);
 }
 
 void
 DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph)
 {
   mPlaybackStream = aGraph->CreateTrackUnionStream(this);
   mPlaybackStream->SetAutofinish(true);
+  mPlaybackStream->RegisterUser();
   if (mOwnedStream) {
     mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
   }
 
   mPlaybackListener = new PlaybackStreamListener(this);
   mPlaybackStream->AddListener(mPlaybackListener);
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, mOwnedStream=%p, mPlaybackStream=%p",
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -358,16 +358,17 @@ public:
 
   void GetId(nsAString& aID) const;
 
   void GetAudioTracks(nsTArray<RefPtr<AudioStreamTrack> >& aTracks) const;
   void GetVideoTracks(nsTArray<RefPtr<VideoStreamTrack> >& aTracks) const;
   void GetTracks(nsTArray<RefPtr<MediaStreamTrack> >& aTracks) const;
   void AddTrack(MediaStreamTrack& aTrack);
   void RemoveTrack(MediaStreamTrack& aTrack);
+  already_AddRefed<DOMMediaStream> Clone();
 
   // NON-WebIDL
 
   /**
    * Returns true if this DOMMediaStream has aTrack in its mPlaybackStream.
    */
   bool HasTrack(const MediaStreamTrack& aTrack) const;
 
--- a/dom/webidl/MediaStream.webidl
+++ b/dom/webidl/MediaStream.webidl
@@ -34,16 +34,16 @@ dictionary MediaStreamConstraints {
 interface MediaStream : EventTarget {
     readonly    attribute DOMString    id;
     sequence<AudioStreamTrack> getAudioTracks ();
     sequence<VideoStreamTrack> getVideoTracks ();
     sequence<MediaStreamTrack> getTracks ();
     // MediaStreamTrack?          getTrackById (DOMString trackId);
     void                       addTrack (MediaStreamTrack track);
     void                       removeTrack (MediaStreamTrack track);
-    // MediaStream                clone ();
+    MediaStream                clone ();
     // readonly    attribute boolean      active;
     //             attribute EventHandler onactive;
     //             attribute EventHandler oninactive;
     //             attribute EventHandler onaddtrack;
     //             attribute EventHandler onremovetrack;
     readonly attribute double currentTime;
 };