Bug 1032835 - addTrack/removeTrack on-top of existing implementation. r=smaug, r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2014-08-13 21:40:54 -04:00
parent 39849d0595
commit 185c7d4bdd
15 changed files with 317 additions and 59 deletions

View File

@ -196,6 +196,18 @@ DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks)
}
}
void
DOMMediaStream::GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks)
{
aTracks.AppendElements(mTracks);
}
bool
DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const
{
return mTracks.Contains(&aTrack);
}
bool
DOMMediaStream::IsFinished()
{

View File

@ -81,6 +81,8 @@ public:
void GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks);
void GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks);
void GetTracks(nsTArray<nsRefPtr<MediaStreamTrack> >& aTracks);
bool HasTrack(const MediaStreamTrack& aTrack) const;
MediaStream* GetStream() const { return mStream; }

View File

@ -210,12 +210,6 @@ RTCSessionDescription.prototype = {
};
function RTCStatsReport(win, dict) {
function appendStats(stats, report) {
stats.forEach(function(stat) {
report[stat.id] = stat;
});
}
this._win = win;
this._pcid = dict.pcid;
this._report = convertToRTCStatsReport(dict);
@ -287,6 +281,8 @@ RTCIdentityAssertion.prototype = {
function RTCPeerConnection() {
this._queue = [];
this._senders = [];
this._receivers = [];
this._pc = null;
this._observer = null;
@ -341,6 +337,7 @@ RTCPeerConnection.prototype = {
}
this.makeGetterSetterEH("onaddstream");
this.makeGetterSetterEH("onaddtrack");
this.makeGetterSetterEH("onicecandidate");
this.makeGetterSetterEH("onnegotiationneeded");
this.makeGetterSetterEH("onsignalingstatechange");
@ -805,6 +802,26 @@ RTCPeerConnection.prototype = {
throw new this._win.DOMError("", "getStreamById not yet implemented");
},
addTrack: function(track, stream) {
if (stream.currentTime === undefined) {
throw new this._win.DOMError("", "invalid stream.");
}
if (stream.getTracks().indexOf(track) == -1) {
throw new this._win.DOMError("", "track is not in stream.");
}
this._checkClosed();
this._impl.addTrack(track, stream);
let sender = this._win.RTCRtpSender._create(this._win,
new RTCRtpSender(this, track));
this._senders.push({ sender: sender, stream: stream });
return sender;
},
removeTrack: function(sender) {
// Bug 844295: Not implementing this functionality.
throw new this._win.DOMError("", "removeTrack not yet implemented");
},
close: function() {
if (this._closed) {
return;
@ -830,6 +847,36 @@ RTCPeerConnection.prototype = {
return this._impl.getRemoteStreams();
},
getSenders: function() {
this._checkClosed();
let streams = this._impl.getLocalStreams();
let senders = [];
// prune senders in case any streams have disappeared down below
for (let i = this._senders.length - 1; i >= 0; i--) {
if (streams.indexOf(this._senders[i].stream) != -1) {
senders.push(this._senders[i].sender);
} else {
this._senders.splice(i,1);
}
}
return senders;
},
getReceivers: function() {
this._checkClosed();
let streams = this._impl.getRemoteStreams();
let receivers = [];
// prune receivers in case any streams have disappeared down below
for (let i = this._receivers.length - 1; i >= 0; i--) {
if (streams.indexOf(this._receivers[i].stream) != -1) {
receivers.push(this._receivers[i].receiver);
} else {
this._receivers.splice(i,1);
}
}
return receivers;
},
get localDescription() {
this._checkClosed();
let sdp = this._impl.localDescription;
@ -1248,6 +1295,17 @@ PeerConnectionObserver.prototype = {
{ stream: stream }));
},
onAddTrack: function(track) {
let ev = new this._dompc._win.MediaStreamTrackEvent("addtrack",
{ track: track });
this._dompc.dispatchEvent(ev);
},
onRemoveTrack: function(track, type) {
this.dispatchEvent(new this._dompc._win.MediaStreamTrackEvent("removetrack",
{ track: track }));
},
foundIceCandidate: function(cand) {
this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
{ candidate: cand } ));
@ -1279,34 +1337,26 @@ RTCPeerConnectionStatic.prototype = {
},
};
function RTCRtpSender() {}
function RTCRtpSender(pc, track) {
this.pc = pc;
this.track = track;
}
RTCRtpSender.prototype = {
classDescription: "RTCRtpSender",
classID: PC_SENDER_CID,
contractID: PC_SENDER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer]),
init: function(win) { this._win = win; },
__init: function(track) {
this.track = track;
}
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
};
function RTCRtpReceiver() {}
function RTCRtpReceiver(pc, track) {
this.pc = pc;
this.track = track;
}
RTCRtpReceiver.prototype = {
classDescription: "RTCRtpReceiver",
classID: PC_RECEIVER_CID,
contractID: PC_RECEIVER_CONTRACT,
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIDOMGlobalPropertyInitializer]),
init: function(win) { this._win = win; },
__init: function(track) {
this.track = track;
}
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(

View File

@ -1543,7 +1543,9 @@ PeerConnectionWrapper.prototype = {
this.streams.push(stream);
if (side === 'local') {
this._pc.addStream(stream);
stream.getTracks().forEach(function(track) {
this._pc.addTrack(track, stream);
}.bind(this));
}
var element = createMediaElement(type, this.label + '_' + side);

View File

@ -23,8 +23,9 @@ dictionary MediaStreamConstraints {
interface MediaStream {
// readonly attribute DOMString id;
sequence<AudioStreamTrack> getAudioTracks ();
sequence<VideoStreamTrack> getVideoTracks ();
sequence<AudioStreamTrack> getAudioTracks();
sequence<VideoStreamTrack> getVideoTracks();
sequence<MediaStreamTrack> getTracks();
// MediaStreamTrack getTrackById (DOMString trackId);
// void addTrack (MediaStreamTrack track);
// void removeTrack (MediaStreamTrack track);

View File

@ -38,7 +38,11 @@ interface PeerConnectionImpl {
[Throws]
void getStats(MediaStreamTrack? selector);
/* Adds the stream created by GetUserMedia */
/* Adds the tracks created by GetUserMedia */
[Throws]
void addTrack(MediaStreamTrack track, MediaStream... streams);
[Throws]
void removeTrack(MediaStreamTrack track);
[Throws]
void addStream(MediaStream stream);
[Throws]

View File

@ -34,9 +34,9 @@ interface PeerConnectionObserver
/* Notification of one of several types of state changed */
void onStateChange(PCObserverStateType state);
/* Changes to MediaStreams */
/* Changes to MediaStreamTracks */
void onAddStream(MediaStream stream);
void onRemoveStream();
void onAddTrack();
void onAddTrack(MediaStreamTrack track);
void onRemoveTrack();
};

View File

@ -107,10 +107,12 @@ interface mozRTCPeerConnection : EventTarget {
void removeStream (MediaStream stream);
// replaces addStream; fails if already added
// because a track can be part of multiple streams, the id parameter
// indicates which particular stream should be referenced in signaling
// because a track can be part of multiple streams, stream parameters
// indicate which particular streams should be referenced in signaling
RTCRtpSender addTrack(MediaStreamTrack track, DOMString streamId);
RTCRtpSender addTrack(MediaStreamTrack track,
MediaStream stream,
MediaStream... moreStreams);
void removeTrack(RTCRtpSender sender);
sequence<RTCRtpSender> getSenders();

View File

@ -8,8 +8,7 @@
*/
[Pref="media.peerconnection.enabled",
JSImplementation="@mozilla.org/dom/rtpreceiver;1",
Constructor (MediaStreamTrack track)]
JSImplementation="@mozilla.org/dom/rtpreceiver;1"]
interface RTCRtpReceiver {
readonly attribute MediaStreamTrack track;
};

View File

@ -8,8 +8,7 @@
*/
[Pref="media.peerconnection.enabled",
JSImplementation="@mozilla.org/dom/rtpsender;1",
Constructor (MediaStreamTrack track)]
JSImplementation="@mozilla.org/dom/rtpsender;1"]
interface RTCRtpSender {
readonly attribute MediaStreamTrack track;
};

View File

@ -2535,6 +2535,10 @@ static int vcmTxStartICE_m(cc_mcapid_t mcap_id,
ENSURE_PC(pc, VCM_ERROR);
nsRefPtr<sipcc::LocalSourceStreamInfo> stream =
pc.impl()->media()->GetLocalStream(pc_stream_id);
if (!stream) {
CSFLogError(logTag, "Stream not found");
return VCM_ERROR;
}
// Create the transport flows
mozilla::RefPtr<TransportFlow> rtp_flow =

View File

@ -285,6 +285,17 @@ public:
CSFLogInfo(logTag, "Returning success for OnAddStream()");
// We are running on main thread here so we shouldn't have a race
// on this callback
nsTArray<nsRefPtr<MediaStreamTrack>> tracks;
aStream->GetTracks(tracks);
for (uint32_t i = 0; i < tracks.Length(); i++) {
JSErrorResult rv;
mObserver->OnAddTrack(*tracks[i], rv);
if (rv.Failed()) {
CSFLogError(logTag, ": OnAddTrack(%d) failed! Error: %d", i,
rv.ErrorCode());
}
}
JSErrorResult rv;
mObserver->OnAddStream(*aStream, rv);
if (rv.Failed()) {
@ -1465,7 +1476,7 @@ PeerConnectionImpl::AddStream(DOMMediaStream &aMediaStream)
}
uint32_t stream_id;
nsresult res = mMedia->AddStream(&aMediaStream, &stream_id);
nsresult res = mMedia->AddStream(&aMediaStream, hints, &stream_id);
if (NS_FAILED(res)) {
return res;
}
@ -1490,15 +1501,141 @@ NS_IMETHODIMP
PeerConnectionImpl::RemoveStream(DOMMediaStream& aMediaStream) {
PC_AUTO_ENTER_API_CALL(true);
uint32_t hints = aMediaStream.GetHintContents();
uint32_t stream_id;
nsresult res = mMedia->RemoveStream(&aMediaStream, &stream_id);
nsresult res = mMedia->RemoveStream(&aMediaStream, hints, &stream_id);
if (NS_FAILED(res))
return res;
aMediaStream.RemovePrincipalChangeObserver(this);
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
mInternal->mCall->removeStream(stream_id, 0, AUDIO);
MOZ_ASSERT(mNumAudioStreams > 0);
mNumAudioStreams--;
}
if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
mInternal->mCall->removeStream(stream_id, 1, VIDEO);
MOZ_ASSERT(mNumVideoStreams > 0);
mNumVideoStreams--;
}
return NS_OK;
}
nsresult
PeerConnectionImpl::AddTrack(MediaStreamTrack& aTrack,
const Sequence<OwningNonNull<DOMMediaStream>>& aStreams)
{
PC_AUTO_ENTER_API_CALL(true);
if (!aStreams.Length()) {
CSFLogError(logTag, "%s: At least one stream arg required", __FUNCTION__);
return NS_ERROR_FAILURE;
}
DOMMediaStream& aMediaStream = aStreams[0];
#ifdef MOZILLA_INTERNAL_API
if (!aMediaStream.HasTrack(aTrack)) {
CSFLogError(logTag, "%s: Track is not in stream", __FUNCTION__);
return NS_ERROR_FAILURE;
}
uint32_t hints = aMediaStream.GetHintContents() &
((aTrack.AsAudioStreamTrack()? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
(aTrack.AsVideoStreamTrack()? DOMMediaStream::HINT_CONTENTS_VIDEO : 0));
#else
uint32_t hints = aMediaStream.GetHintContents();
#endif
// XXX Remove this check once addStream has an error callback
// available and/or we have plumbing to handle multiple
// local audio streams.
if ((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) &&
mNumAudioStreams > 0) {
CSFLogError(logTag, "%s: Only one local audio stream is supported for now",
__FUNCTION__);
return NS_ERROR_FAILURE;
}
// XXX Remove this check once addStream has an error callback
// available and/or we have plumbing to handle multiple
// local video streams.
if ((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) &&
mNumVideoStreams > 0) {
CSFLogError(logTag, "%s: Only one local video stream is supported for now",
__FUNCTION__);
return NS_ERROR_FAILURE;
}
uint32_t num = mMedia->LocalStreamsLength();
uint32_t stream_id;
nsresult res = mMedia->AddStream(&aMediaStream, hints, &stream_id);
if (NS_FAILED(res)) {
return res;
}
if (num != mMedia->LocalStreamsLength()) {
aMediaStream.AddPrincipalChangeObserver(this);
}
// TODO(ekr@rtfm.com): these integers should be the track IDs
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
mInternal->mCall->addStream(stream_id, 0, AUDIO);
mNumAudioStreams++;
}
if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
mInternal->mCall->addStream(stream_id, 1, VIDEO);
mNumVideoStreams++;
}
return NS_OK;
}
NS_IMETHODIMP
PeerConnectionImpl::RemoveTrack(MediaStreamTrack& aTrack) {
PC_AUTO_ENTER_API_CALL(true);
DOMMediaStream *stream = nullptr;
#ifdef MOZILLA_INTERNAL_API
for (uint32_t i = 0; i < mMedia->LocalStreamsLength(); ++i) {
auto* candidate = mMedia->GetLocalStream(i)->GetMediaStream();
if (candidate->HasTrack(aTrack)) {
stream = candidate;
break;
}
}
#endif
if (!stream) {
CSFLogError(logTag, "%s: Track not found", __FUNCTION__);
return NS_OK;
}
DOMMediaStream& aMediaStream = *stream;
#ifdef MOZILLA_INTERNAL_API
uint32_t hints = aMediaStream.GetHintContents() &
((aTrack.AsAudioStreamTrack()? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
(aTrack.AsVideoStreamTrack()? DOMMediaStream::HINT_CONTENTS_VIDEO : 0));
#else
uint32_t hints = aMediaStream.GetHintContents();
#endif
uint32_t num = mMedia->LocalStreamsLength();
uint32_t stream_id;
nsresult res = mMedia->RemoveStream(&aMediaStream, hints, &stream_id);
if (NS_FAILED(res)) {
return res;
}
if (num != mMedia->LocalStreamsLength()) {
aMediaStream.RemovePrincipalChangeObserver(this);
}
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
mInternal->mCall->removeStream(stream_id, 0, AUDIO);

View File

@ -375,6 +375,18 @@ public:
rv = RemoveStream(aMediaStream);
}
NS_IMETHODIMP_TO_ERRORRESULT(AddTrack, ErrorResult &rv,
mozilla::dom::MediaStreamTrack& aTrack,
const mozilla::dom::Sequence<mozilla::dom::OwningNonNull<DOMMediaStream>>& aStreams)
{
rv = AddTrack(aTrack, aStreams);
}
NS_IMETHODIMP_TO_ERRORRESULT(RemoveTrack, ErrorResult &rv,
mozilla::dom::MediaStreamTrack& aTrack)
{
rv = RemoveTrack(aTrack);
}
nsresult GetPeerIdentity(nsAString& peerIdentity)
{

View File

@ -43,6 +43,12 @@ LocalSourceStreamInfo::ExpectAudio(const mozilla::TrackID aID)
mAudioTracks.AppendElement(aID);
}
void
LocalSourceStreamInfo::RemoveAudio(const mozilla::TrackID aID)
{
mAudioTracks.RemoveElement(aID);
}
// If the ExpectVideo hint is on we will add a track at the default first
// video track ID (1).
void
@ -51,6 +57,12 @@ LocalSourceStreamInfo::ExpectVideo(const mozilla::TrackID aID)
mVideoTracks.AppendElement(aID);
}
void
LocalSourceStreamInfo::RemoveVideo(const mozilla::TrackID aID)
{
mVideoTracks.RemoveElement(aID);
}
unsigned
LocalSourceStreamInfo::AudioTrackCount()
{
@ -234,7 +246,9 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
}
nsresult
PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id)
PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream,
uint32_t hints,
uint32_t *stream_id)
{
ASSERT_ON_THREAD(mMainThread);
@ -245,11 +259,9 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream
DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
CSFLogDebug(logTag, "%s: MediaStream: %p",
__FUNCTION__, aMediaStream);
CSFLogDebug(logTag, "%s: MediaStream: %p", __FUNCTION__, aMediaStream);
// Adding tracks here based on nsDOMMediaStream expectation settings
uint32_t hints = stream->GetHintContents();
#ifdef MOZILLA_INTERNAL_API
if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) {
hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO);
@ -262,23 +274,30 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream
return NS_OK;
}
// Now see if we already have a stream of this type, since we only
// allow one of each.
// Now see if we already have this stream or another stream with
// tracks of the same type, since we only allow one track of each type.
// TODO(ekr@rtfm.com): remove this when multiple of each stream
// is allowed
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u];
nsRefPtr<LocalSourceStreamInfo> localSourceStream = nullptr;
if (localSourceStream->GetMediaStream()->GetHintContents() & hints) {
for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) {
auto& lss = mLocalSourceStreams[u];
if (((hints & DOMMediaStream::HINT_CONTENTS_AUDIO) && lss->AudioTrackCount()) ||
((hints & DOMMediaStream::HINT_CONTENTS_VIDEO) && lss->VideoTrackCount())) {
CSFLogError(logTag, "Only one stream of any given type allowed");
return NS_ERROR_FAILURE;
}
if (stream == lss->GetMediaStream()) {
localSourceStream = lss;
*stream_id = u;
break;
}
}
if (!localSourceStream) {
localSourceStream = new LocalSourceStreamInfo(stream, this);
mLocalSourceStreams.AppendElement(localSourceStream);
*stream_id = mLocalSourceStreams.Length() - 1;
}
// OK, we're good to add
nsRefPtr<LocalSourceStreamInfo> localSourceStream =
new LocalSourceStreamInfo(stream, this);
*stream_id = mLocalSourceStreams.Length();
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
localSourceStream->ExpectAudio(TRACK_AUDIO);
@ -287,14 +306,13 @@ PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream
if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
localSourceStream->ExpectVideo(TRACK_VIDEO);
}
mLocalSourceStreams.AppendElement(localSourceStream);
return NS_OK;
}
nsresult
PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id)
PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream,
uint32_t hints,
uint32_t *stream_id)
{
MOZ_ASSERT(aMediaStream);
ASSERT_ON_THREAD(mMainThread);
@ -308,6 +326,17 @@ PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *str
nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u];
if (localSourceStream->GetMediaStream() == stream) {
*stream_id = u;
if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
localSourceStream->RemoveAudio(TRACK_AUDIO);
}
if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
localSourceStream->RemoveAudio(TRACK_VIDEO);
}
if (!(localSourceStream->AudioTrackCount() +
localSourceStream->VideoTrackCount())) {
mLocalSourceStreams.RemoveElementAt(u);
}
return NS_OK;
}
}

View File

@ -226,6 +226,8 @@ public:
void ExpectAudio(const mozilla::TrackID);
void ExpectVideo(const mozilla::TrackID);
void RemoveAudio(const mozilla::TrackID);
void RemoveVideo(const mozilla::TrackID);
unsigned AudioTrackCount();
unsigned VideoTrackCount();
void DetachTransport_s();
@ -301,10 +303,13 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
}
// Add a stream (main thread only)
nsresult AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id);
nsresult AddStream(nsIDOMMediaStream* aMediaStream, uint32_t hints,
uint32_t *stream_id);
// Remove a stream (main thread only)
nsresult RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id);
nsresult RemoveStream(nsIDOMMediaStream* aMediaStream,
uint32_t hints,
uint32_t *stream_id);
// Get a specific local stream
uint32_t LocalStreamsLength()