Bug 1208371 - Implement MediaStreamTrack::Clone(). r?smaug,jib,roc draft
authorAndreas Pehrson <pehrsons@gmail.com>
Fri, 22 Jan 2016 16:27:51 +0800
changeset 342121 915c766d529e027cd134c727b6ef905bf2227521
parent 342120 b77b2be1ea9161fc8773f049b6a0afe072146ae2
child 342122 b660314897043e67ac94d1d3d97fb7ffe23fb8fd
push id13352
push userpehrsons@gmail.com
push dateFri, 18 Mar 2016 13:49:47 +0000
reviewerssmaug, jib, roc
bugs1208371
milestone47.0a1
Bug 1208371 - Implement MediaStreamTrack::Clone(). r?smaug,jib,roc MozReview-Commit-ID: HrL0RFMcG4B
dom/media/AudioStreamTrack.h
dom/media/DOMMediaStream.cpp
dom/media/DOMMediaStream.h
dom/media/MediaStreamTrack.cpp
dom/media/MediaStreamTrack.h
dom/media/VideoStreamTrack.h
dom/webidl/MediaStreamTrack.webidl
--- a/dom/media/AudioStreamTrack.h
+++ b/dom/media/AudioStreamTrack.h
@@ -22,14 +22,25 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioStreamTrack* AsAudioStreamTrack() override { return this; }
 
   const AudioStreamTrack* AsAudioStreamTrack() const override { return this; }
 
   // WebIDL
   void GetKind(nsAString& aKind) override { aKind.AssignLiteral("audio"); }
+
+protected:
+  already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
+                                                   TrackID aTrackID) override
+  {
+    return do_AddRef(new AudioStreamTrack(aOwningStream,
+                                          aTrackID,
+                                          mInputTrackID,
+                                          mLabel,
+                                          mSource));
+  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* AUDIOSTREAMTRACK_H_ */
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -851,28 +851,61 @@ DOMMediaStream::CreateOwnDOMTrack(TrackI
     track = new VideoStreamTrack(this, aTrackID, aTrackID, aLabel, aSource);
     break;
   default:
     MOZ_CRASH("Unhandled track type");
   }
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID));
 
-  RefPtr<TrackPort> ownedTrackPort =
-    new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL);
-  mOwnedTracks.AppendElement(ownedTrackPort.forget());
+  mOwnedTracks.AppendElement(
+    new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL));
 
-  RefPtr<TrackPort> playbackTrackPort =
-    new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL);
-  mTracks.AppendElement(playbackTrackPort.forget());
+  mTracks.AppendElement(
+    new TrackPort(mPlaybackPort, track, TrackPort::InputPortOwnership::EXTERNAL));
 
   NotifyTrackAdded(track);
   return track;
 }
 
+already_AddRefed<MediaStreamTrack>
+DOMMediaStream::CreateClonedDOMTrack(MediaStreamTrack& aTrack,
+                                     TrackID aCloneTrackID)
+{
+  MOZ_RELEASE_ASSERT(mOwnedStream);
+  MOZ_RELEASE_ASSERT(mPlaybackStream);
+  MOZ_RELEASE_ASSERT(IsTrackIDExplicit(aCloneTrackID));
+
+  TrackID inputTrackID = aTrack.mInputTrackID;
+  MediaStream* inputStream = aTrack.GetInputStream();
+
+  RefPtr<MediaStreamTrack> newTrack = aTrack.CloneInternal(this, aCloneTrackID);
+
+  newTrack->mOriginalTrack =
+    aTrack.mOriginalTrack ? aTrack.mOriginalTrack.get() : &aTrack;
+
+  LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p cloned from stream %p track %d",
+                        this, newTrack.get(), inputStream, inputTrackID));
+
+  RefPtr<MediaInputPort> inputPort =
+    mOwnedStream->AllocateInputPort(inputStream, inputTrackID, aCloneTrackID);
+
+  mOwnedTracks.AppendElement(
+    new TrackPort(inputPort, newTrack, TrackPort::InputPortOwnership::OWNED));
+
+  mTracks.AppendElement(
+    new TrackPort(mPlaybackPort, newTrack, TrackPort::InputPortOwnership::EXTERNAL));
+
+  NotifyTrackAdded(newTrack);
+
+  newTrack->SetEnabled(aTrack.Enabled());
+
+  return newTrack.forget();
+}
+
 MediaStreamTrack*
 DOMMediaStream::FindOwnedDOMTrack(MediaStream* aInputStream,
                                   TrackID aInputTrackID) const
 {
   MOZ_RELEASE_ASSERT(mOwnedStream);
 
   for (const RefPtr<TrackPort>& info : mOwnedTracks) {
     if (info->GetInputPort() &&
@@ -895,17 +928,21 @@ DOMMediaStream::FindOwnedTrackPort(const
   }
   return nullptr;
 }
 
 
 MediaStreamTrack*
 DOMMediaStream::FindPlaybackDOMTrack(MediaStream* aInputStream, TrackID aInputTrackID) const
 {
-  MOZ_RELEASE_ASSERT(mPlaybackStream);
+  if (!mPlaybackStream) {
+    // One would think we can assert mPlaybackStream here, but track clones have
+    // a dummy DOMMediaStream that doesn't have a playback stream, so we can't.
+    return nullptr;
+  }
 
   for (const RefPtr<TrackPort>& info : mTracks) {
     if (info->GetInputPort() == mPlaybackPort &&
         aInputStream == mOwnedStream &&
         info->GetTrack()->mInputTrackID == aInputTrackID) {
       // This track is in our owned and playback streams.
       return info->GetTrack();
     }
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -513,16 +513,24 @@ public:
    * are carrying that track.
    *
    * Creates a MediaStreamTrack, adds it to mTracks and returns it.
    */
   MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
                                       const nsString& aLabel,
                                       MediaStreamTrackSource* aSource);
 
+  /**
+   * Creates a MediaStreamTrack cloned from aTrack, adds it to mTracks and
+   * returns it.
+   * aCloneTrackID is the TrackID the new track will get in mOwnedStream.
+   */
+  already_AddRefed<MediaStreamTrack> CreateClonedDOMTrack(MediaStreamTrack& aTrack,
+                                                          TrackID aCloneTrackID);
+
   // When the initial set of tracks has been added, 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.
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -195,16 +195,32 @@ MediaStreamTrack::AddPrincipalChangeObse
 
 bool
 MediaStreamTrack::RemovePrincipalChangeObserver(
   PrincipalChangeObserver<MediaStreamTrack>* aObserver)
 {
   return mPrincipalChangeObservers.RemoveElement(aObserver);
 }
 
+already_AddRefed<MediaStreamTrack>
+MediaStreamTrack::Clone()
+{
+  // MediaStreamTracks are currently governed by streams, so we need a dummy
+  // DOMMediaStream to own our track clone. The dummy will never see any
+  // dynamically created tracks (no input stream) so no need for a SourceGetter.
+  RefPtr<DOMMediaStream> newStream =
+    new DOMMediaStream(mOwningStream->GetParentObject(), nullptr);
+
+  MediaStreamGraph* graph = Graph();
+  newStream->InitOwnedStreamCommon(graph);
+  newStream->InitPlaybackStreamCommon(graph);
+
+  return newStream->CreateClonedDOMTrack(*this, mTrackID);
+}
+
 DOMMediaStream*
 MediaStreamTrack::GetInputDOMStream()
 {
   MediaStreamTrack* originalTrack =
     mOriginalTrack ? mOriginalTrack.get() : this;
   MOZ_RELEASE_ASSERT(originalTrack->mOwningStream);
   return originalTrack->mOwningStream;
 }
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -210,16 +210,17 @@ public:
   virtual void GetKind(nsAString& aKind) = 0;
   void GetId(nsAString& aID) const;
   void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); }
   bool Enabled() { return mEnabled; }
   void SetEnabled(bool aEnabled);
   void Stop();
   already_AddRefed<Promise>
   ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
+  already_AddRefed<MediaStreamTrack> Clone();
 
   bool Ended() const { return mEnded; }
   // Notifications from the MediaStreamGraph
   void NotifyEnded() { mEnded = true; }
 
   /**
    * Get this track's principal.
    */
@@ -277,16 +278,24 @@ protected:
 
   // Returns the owning DOMMediaStream's underlying owned stream.
   ProcessedMediaStream* GetOwnedStream();
 
   // Returns the original DOMMediaStream. If this track is a clone,
   // the original track's owning DOMMediaStream is returned.
   DOMMediaStream* GetInputDOMStream();
 
+  /**
+   * Creates a new MediaStreamTrack with the same type, input track ID and
+   * source as this MediaStreamTrack.
+   * aTrackID is the TrackID the new track will have in its owned stream.
+   */
+  virtual already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
+                                                           TrackID aTrackID) = 0;
+
   nsTArray<PrincipalChangeObserver<MediaStreamTrack>*> mPrincipalChangeObservers;
 
   RefPtr<DOMMediaStream> mOwningStream;
   TrackID mTrackID;
   TrackID mInputTrackID;
   RefPtr<MediaStreamTrackSource> mSource;
   RefPtr<MediaStreamTrack> mOriginalTrack;
   nsString mID;
--- a/dom/media/VideoStreamTrack.h
+++ b/dom/media/VideoStreamTrack.h
@@ -22,14 +22,25 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   VideoStreamTrack* AsVideoStreamTrack() override { return this; }
 
   const VideoStreamTrack* AsVideoStreamTrack() const override { return this; }
 
   // WebIDL
   void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); }
+
+protected:
+  already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
+                                                   TrackID aTrackID) override
+  {
+    return do_AddRef(new VideoStreamTrack(aOwningStream,
+                                          aTrackID,
+                                          mInputTrackID,
+                                          mLabel,
+                                          mSource));
+  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* VIDEOSTREAMTRACK_H_ */
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -71,17 +71,17 @@ interface MediaStreamTrack : EventTarget
                 attribute boolean               enabled;
 //  readonly    attribute boolean               muted;
 //              attribute EventHandler          onmute;
 //              attribute EventHandler          onunmute;
 //  readonly    attribute boolean               _readonly;
 //  readonly    attribute boolean               remote;
 //  readonly    attribute MediaStreamTrackState readyState;
 //                attribute EventHandler          onended;
-//  MediaStreamTrack       clone ();
+    MediaStreamTrack       clone ();
     void                   stop ();
 //  MediaTrackCapabilities getCapabilities ();
 //  MediaTrackConstraints  getConstraints ();
 //  MediaTrackSettings     getSettings ();
 
     [Throws]
     Promise<void>          applyConstraints (optional MediaTrackConstraints constraints);
 //              attribute EventHandler          onoverconstrained;