diff --git a/dom/media/AudioStreamTrack.h b/dom/media/AudioStreamTrack.h index 278e14fc48ce..aa1e2fd8854b 100644 --- a/dom/media/AudioStreamTrack.h +++ b/dom/media/AudioStreamTrack.h @@ -27,6 +27,17 @@ public: // WebIDL void GetKind(nsAString& aKind) override { aKind.AssignLiteral("audio"); } + +protected: + already_AddRefed CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) override + { + return do_AddRef(new AudioStreamTrack(aOwningStream, + aTrackID, + mInputTrackID, + mLabel, + mSource)); + } }; } // namespace dom diff --git a/dom/media/DOMMediaStream.cpp b/dom/media/DOMMediaStream.cpp index 4ddcca79e63d..09b22f7e8fb6 100644 --- a/dom/media/DOMMediaStream.cpp +++ b/dom/media/DOMMediaStream.cpp @@ -863,18 +863,51 @@ DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID)); - RefPtr ownedTrackPort = - new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL); - mOwnedTracks.AppendElement(ownedTrackPort.forget()); + mOwnedTracks.AppendElement( + new TrackPort(mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL)); - RefPtr 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 +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 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 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 @@ -907,7 +940,11 @@ DOMMediaStream::FindOwnedTrackPort(const MediaStreamTrack& aTrack) const 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& info : mTracks) { if (info->GetInputPort() == mPlaybackPort && diff --git a/dom/media/DOMMediaStream.h b/dom/media/DOMMediaStream.h index 83a490b7215b..bd0179b0a31a 100644 --- a/dom/media/DOMMediaStream.h +++ b/dom/media/DOMMediaStream.h @@ -518,6 +518,14 @@ public: 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 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. diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index ce62525c9c04..2060bc6aa13c 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -200,6 +200,22 @@ MediaStreamTrack::RemovePrincipalChangeObserver( return mPrincipalChangeObservers.RemoveElement(aObserver); } +already_AddRefed +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 newStream = + new DOMMediaStream(mOwningStream->GetParentObject(), nullptr); + + MediaStreamGraph* graph = Graph(); + newStream->InitOwnedStreamCommon(graph); + newStream->InitPlaybackStreamCommon(graph); + + return newStream->CreateClonedDOMTrack(*this, mTrackID); +} + DOMMediaStream* MediaStreamTrack::GetInputDOMStream() { diff --git a/dom/media/MediaStreamTrack.h b/dom/media/MediaStreamTrack.h index 3bfdd47c59b4..ab4491a4407c 100644 --- a/dom/media/MediaStreamTrack.h +++ b/dom/media/MediaStreamTrack.h @@ -215,6 +215,7 @@ public: void Stop(); already_AddRefed ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv); + already_AddRefed Clone(); bool Ended() const { return mEnded; } // Notifications from the MediaStreamGraph @@ -282,6 +283,14 @@ protected: // 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 CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) = 0; + nsTArray*> mPrincipalChangeObservers; RefPtr mOwningStream; diff --git a/dom/media/VideoStreamTrack.h b/dom/media/VideoStreamTrack.h index cd40e6fb21eb..1f2b9e793403 100644 --- a/dom/media/VideoStreamTrack.h +++ b/dom/media/VideoStreamTrack.h @@ -27,6 +27,17 @@ public: // WebIDL void GetKind(nsAString& aKind) override { aKind.AssignLiteral("video"); } + +protected: + already_AddRefed CloneInternal(DOMMediaStream* aOwningStream, + TrackID aTrackID) override + { + return do_AddRef(new VideoStreamTrack(aOwningStream, + aTrackID, + mInputTrackID, + mLabel, + mSource)); + } }; } // namespace dom diff --git a/dom/webidl/MediaStreamTrack.webidl b/dom/webidl/MediaStreamTrack.webidl index bea52288703b..71a335029441 100644 --- a/dom/webidl/MediaStreamTrack.webidl +++ b/dom/webidl/MediaStreamTrack.webidl @@ -76,7 +76,7 @@ interface MediaStreamTrack : EventTarget { // readonly attribute boolean remote; // readonly attribute MediaStreamTrackState readyState; // attribute EventHandler onended; -// MediaStreamTrack clone (); + MediaStreamTrack clone (); void stop (); // MediaTrackCapabilities getCapabilities (); // MediaTrackConstraints getConstraints ();