Fixes desktop sharing when used with simulcast.
This commit is contained in:
parent
ee1c221e6d
commit
a0092b78ca
|
@ -11,8 +11,8 @@
|
||||||
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
<meta itemprop="image" content="/images/jitsilogo.png"/>
|
||||||
<script src="libs/jquery-2.1.1.min.js"></script>
|
<script src="libs/jquery-2.1.1.min.js"></script>
|
||||||
<script src="config.js?v=5"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
<script src="config.js?v=5"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
|
||||||
<script src="simulcast.js?v=6"></script><!-- simulcast handling -->
|
<script src="simulcast.js?v=7"></script><!-- simulcast handling -->
|
||||||
<script src="libs/strophe/strophe.jingle.adapter.js?v=2"></script><!-- strophe.jingle bundles -->
|
<script src="libs/strophe/strophe.jingle.adapter.js?v=3"></script><!-- strophe.jingle bundles -->
|
||||||
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
<script src="libs/strophe/strophe.min.js?v=1"></script>
|
||||||
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
<script src="libs/strophe/strophe.disco.min.js?v=1"></script>
|
||||||
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
<script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<script src="libs/strophe/strophe.jingle.sessionbase.js?v=1"></script>
|
<script src="libs/strophe/strophe.jingle.sessionbase.js?v=1"></script>
|
||||||
<script src="libs/strophe/strophe.jingle.session.js?v=2"></script>
|
<script src="libs/strophe/strophe.jingle.session.js?v=2"></script>
|
||||||
<script src="libs/strophe/strophe.util.js"></script>
|
<script src="libs/strophe/strophe.util.js"></script>
|
||||||
<script src="libs/colibri/colibri.focus.js?v=11"></script><!-- colibri focus implementation -->
|
<script src="libs/colibri/colibri.focus.js?v=12"></script><!-- colibri focus implementation -->
|
||||||
<script src="libs/colibri/colibri.session.js?v=1"></script>
|
<script src="libs/colibri/colibri.session.js?v=1"></script>
|
||||||
<script src="libs/jquery-ui.js"></script>
|
<script src="libs/jquery-ui.js"></script>
|
||||||
<script src="libs/rayo.js?v=1"></script>
|
<script src="libs/rayo.js?v=1"></script>
|
||||||
|
|
|
@ -551,82 +551,8 @@ ColibriFocus.prototype.createdConference = function (result) {
|
||||||
console.log('setLocalDescription succeeded.');
|
console.log('setLocalDescription succeeded.');
|
||||||
// make sure our presence is updated
|
// make sure our presence is updated
|
||||||
$(document).trigger('setLocalDescription.jingle', [self.sid]);
|
$(document).trigger('setLocalDescription.jingle', [self.sid]);
|
||||||
var elem = $iq({to: self.bridgejid, type: 'get'});
|
|
||||||
elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: self.confid});
|
|
||||||
var localSDP = new SDP(self.peerconnection.localDescription.sdp);
|
var localSDP = new SDP(self.peerconnection.localDescription.sdp);
|
||||||
localSDP.media.forEach(function (media, channel) {
|
self.updateLocalChannel(localSDP);
|
||||||
var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
|
|
||||||
elem.c('content', {name: name});
|
|
||||||
var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
|
|
||||||
if (name !== 'data')
|
|
||||||
{
|
|
||||||
elem.c('channel', {
|
|
||||||
initiator: 'true',
|
|
||||||
expire: self.channelExpire,
|
|
||||||
id: self.mychannel[channel].attr('id'),
|
|
||||||
endpoint: self.myMucResource
|
|
||||||
});
|
|
||||||
|
|
||||||
// signal (through COLIBRI) to the bridge
|
|
||||||
// the SSRC groups of the participant
|
|
||||||
// that plays the role of the focus
|
|
||||||
var ssrc_group_lines = SDPUtil.find_lines(media, 'a=ssrc-group:');
|
|
||||||
var idx = 0;
|
|
||||||
ssrc_group_lines.forEach(function(line) {
|
|
||||||
idx = line.indexOf(' ');
|
|
||||||
var semantics = line.substr(0, idx).substr(13);
|
|
||||||
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
|
||||||
if (ssrcs.length != 0) {
|
|
||||||
elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
|
||||||
ssrcs.forEach(function(ssrc) {
|
|
||||||
elem.c('source', { ssrc: ssrc })
|
|
||||||
.up();
|
|
||||||
});
|
|
||||||
elem.up();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// FIXME: should reuse code from .toJingle
|
|
||||||
for (var j = 0; j < mline.fmt.length; j++)
|
|
||||||
{
|
|
||||||
var rtpmap = SDPUtil.find_line(media, 'a=rtpmap:' + mline.fmt[j]);
|
|
||||||
if (rtpmap)
|
|
||||||
{
|
|
||||||
elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
|
|
||||||
elem.up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var sctpmap = SDPUtil.find_line(media, 'a=sctpmap:' + mline.fmt[0]);
|
|
||||||
var sctpPort = SDPUtil.parse_sctpmap(sctpmap)[0];
|
|
||||||
elem.c("sctpconnection",
|
|
||||||
{
|
|
||||||
initiator: 'true',
|
|
||||||
expire: self.channelExpire,
|
|
||||||
id: self.mychannel[channel].attr('id'),
|
|
||||||
endpoint: self.myMucResource,
|
|
||||||
port: sctpPort
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
localSDP.TransportToJingle(channel, elem);
|
|
||||||
|
|
||||||
elem.up(); // end of channel
|
|
||||||
elem.up(); // end of content
|
|
||||||
});
|
|
||||||
|
|
||||||
self.connection.sendIQ(elem,
|
|
||||||
function (result) {
|
|
||||||
// ...
|
|
||||||
},
|
|
||||||
function (error) {
|
|
||||||
console.error(
|
|
||||||
"ERROR sending colibri message",
|
|
||||||
error, elem);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// now initiate sessions
|
// now initiate sessions
|
||||||
for (var i = 0; i < numparticipants; i++) {
|
for (var i = 0; i < numparticipants; i++) {
|
||||||
|
@ -659,6 +585,96 @@ ColibriFocus.prototype.createdConference = function (result) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ColibriFocus.prototype.updateLocalChannel = function(localSDP, parts) {
|
||||||
|
var self = this;
|
||||||
|
var elem = $iq({to: self.bridgejid, type: 'get'});
|
||||||
|
elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: self.confid});
|
||||||
|
localSDP.media.forEach(function (media, channel) {
|
||||||
|
var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
|
||||||
|
elem.c('content', {name: name});
|
||||||
|
var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
|
||||||
|
if (name !== 'data') {
|
||||||
|
elem.c('channel', {
|
||||||
|
initiator: 'true',
|
||||||
|
expire: self.channelExpire,
|
||||||
|
id: self.mychannel[channel].attr('id'),
|
||||||
|
endpoint: self.myMucResource
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('sources') !== -1) {
|
||||||
|
// signal (through COLIBRI) to the bridge
|
||||||
|
// the SSRC groups of the participant
|
||||||
|
// that plays the role of the focus
|
||||||
|
var ssrc_group_lines = SDPUtil.find_lines(media, 'a=ssrc-group:');
|
||||||
|
var idx = 0;
|
||||||
|
var hasSIM = false;
|
||||||
|
ssrc_group_lines.forEach(function (line) {
|
||||||
|
idx = line.indexOf(' ');
|
||||||
|
var semantics = line.substr(0, idx).substr(13);
|
||||||
|
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
||||||
|
if (ssrcs.length != 0) {
|
||||||
|
elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
||||||
|
ssrcs.forEach(function (ssrc) {
|
||||||
|
elem.c('source', { ssrc: ssrc })
|
||||||
|
.up();
|
||||||
|
});
|
||||||
|
elem.up();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasSIM && name == 'video') {
|
||||||
|
// disable simulcast with an empty ssrc-group element.
|
||||||
|
elem.c('ssrc-group', { semantics: 'SIM', xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
||||||
|
elem.up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('payload-type') !== -1) {
|
||||||
|
// FIXME: should reuse code from .toJingle
|
||||||
|
for (var j = 0; j < mline.fmt.length; j++) {
|
||||||
|
var rtpmap = SDPUtil.find_line(media, 'a=rtpmap:' + mline.fmt[j]);
|
||||||
|
if (rtpmap) {
|
||||||
|
elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
|
||||||
|
elem.up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sctpmap = SDPUtil.find_line(media, 'a=sctpmap:' + mline.fmt[0]);
|
||||||
|
var sctpPort = SDPUtil.parse_sctpmap(sctpmap)[0];
|
||||||
|
elem.c("sctpconnection",
|
||||||
|
{
|
||||||
|
initiator: 'true',
|
||||||
|
expire: self.channelExpire,
|
||||||
|
id: self.mychannel[channel].attr('id'),
|
||||||
|
endpoint: self.myMucResource,
|
||||||
|
port: sctpPort
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('transport') !== -1) {
|
||||||
|
localSDP.TransportToJingle(channel, elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
elem.up(); // end of channel
|
||||||
|
elem.up(); // end of content
|
||||||
|
});
|
||||||
|
|
||||||
|
self.connection.sendIQ(elem,
|
||||||
|
function (result) {
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
console.error(
|
||||||
|
"ERROR sending colibri message",
|
||||||
|
error, elem);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// send a session-initiate to a new participant
|
// send a session-initiate to a new participant
|
||||||
ColibriFocus.prototype.initiate = function (peer, isInitiator) {
|
ColibriFocus.prototype.initiate = function (peer, isInitiator) {
|
||||||
var participant = this.peers.indexOf(peer);
|
var participant = this.peers.indexOf(peer);
|
||||||
|
@ -894,33 +910,36 @@ ColibriFocus.prototype.addNewParticipant = function (peer) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the channel description (payload-types + dtls fp) for a participant
|
// update the channel description (payload-types + dtls fp) for a participant
|
||||||
ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
|
ColibriFocus.prototype.updateRemoteChannel = function (remoteSDP, participant, parts) {
|
||||||
console.log('change allocation for', this.confid);
|
console.log('change allocation for', this.confid);
|
||||||
var self = this;
|
var self = this;
|
||||||
var change = $iq({to: this.bridgejid, type: 'set'});
|
var change = $iq({to: this.bridgejid, type: 'set'});
|
||||||
change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
|
change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
|
||||||
for (channel = 0; channel < this.channels[participant].length; channel++)
|
for (channel = 0; channel < this.channels[participant].length; channel++) {
|
||||||
{
|
|
||||||
if (!remoteSDP.media[channel])
|
if (!remoteSDP.media[channel])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:'));
|
var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:'));
|
||||||
change.c('content', {name: name});
|
change.c('content', {name: name});
|
||||||
if (name !== 'data')
|
if (name !== 'data') {
|
||||||
{
|
|
||||||
change.c('channel', {
|
change.c('channel', {
|
||||||
id: $(this.channels[participant][channel]).attr('id'),
|
id: $(this.channels[participant][channel]).attr('id'),
|
||||||
endpoint: $(this.channels[participant][channel]).attr('endpoint'),
|
endpoint: $(this.channels[participant][channel]).attr('endpoint'),
|
||||||
expire: self.channelExpire
|
expire: self.channelExpire
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('sources') !== -1) {
|
||||||
// signal (throught COLIBRI) to the bridge the SSRC groups of this
|
// signal (throught COLIBRI) to the bridge the SSRC groups of this
|
||||||
// participant
|
// participant
|
||||||
var ssrc_group_lines = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:');
|
var ssrc_group_lines = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:');
|
||||||
var idx = 0;
|
var idx = 0;
|
||||||
|
var hasSIM = false;
|
||||||
ssrc_group_lines.forEach(function (line) {
|
ssrc_group_lines.forEach(function (line) {
|
||||||
idx = line.indexOf(' ');
|
idx = line.indexOf(' ');
|
||||||
var semantics = line.substr(0, idx).substr(13);
|
var semantics = line.substr(0, idx).substr(13);
|
||||||
|
if (semantics == 'SIM') {
|
||||||
|
hasSIM = true;
|
||||||
|
}
|
||||||
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
var ssrcs = line.substr(14 + semantics.length).split(' ');
|
||||||
if (ssrcs.length != 0) {
|
if (ssrcs.length != 0) {
|
||||||
change.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
change.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
||||||
|
@ -932,6 +951,14 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!hasSIM && name == 'video') {
|
||||||
|
// disable simulcast with an empty ssrc-group element.
|
||||||
|
change.c('ssrc-group', { semantics: 'SIM', xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
|
||||||
|
change.up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('payload-type') !== -1) {
|
||||||
var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
|
var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
|
||||||
rtpmap.forEach(function (val) {
|
rtpmap.forEach(function (val) {
|
||||||
// TODO: too much copy-paste
|
// TODO: too much copy-paste
|
||||||
|
@ -950,6 +977,7 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
|
||||||
change.up();
|
change.up();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var sctpmap = SDPUtil.find_line(remoteSDP.media[channel], 'a=sctpmap:');
|
var sctpmap = SDPUtil.find_line(remoteSDP.media[channel], 'a=sctpmap:');
|
||||||
|
@ -960,8 +988,11 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
|
||||||
port: SDPUtil.parse_sctpmap(sctpmap)[0]
|
port: SDPUtil.parse_sctpmap(sctpmap)[0]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!parts || parts.indexOf('transport') !== -1) {
|
||||||
// now add transport
|
// now add transport
|
||||||
remoteSDP.TransportToJingle(channel, change);
|
remoteSDP.TransportToJingle(channel, change);
|
||||||
|
}
|
||||||
|
|
||||||
change.up(); // end of channel/sctpconnection
|
change.up(); // end of channel/sctpconnection
|
||||||
change.up(); // end of content
|
change.up(); // end of content
|
||||||
|
@ -976,6 +1007,57 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches video streams.
|
||||||
|
* @param new_stream new stream that will be used as video of this session.
|
||||||
|
* @param oldStream old video stream of this session.
|
||||||
|
* @param success_callback callback executed after successful stream switch.
|
||||||
|
*/
|
||||||
|
ColibriFocus.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// Stop the stream to trigger onended event for old stream
|
||||||
|
oldStream.stop();
|
||||||
|
|
||||||
|
// Remember SDP to figure out added/removed SSRCs
|
||||||
|
var oldSdp = null;
|
||||||
|
if(self.peerconnection) {
|
||||||
|
if(self.peerconnection.localDescription) {
|
||||||
|
oldSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||||
|
}
|
||||||
|
self.peerconnection.removeStream(oldStream);
|
||||||
|
self.peerconnection.addStream(new_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.connection.jingle.localVideo = new_stream;
|
||||||
|
|
||||||
|
self.connection.jingle.localStreams = [];
|
||||||
|
self.connection.jingle.localStreams.push(self.connection.jingle.localAudio);
|
||||||
|
self.connection.jingle.localStreams.push(self.connection.jingle.localVideo);
|
||||||
|
|
||||||
|
// Conference is not active
|
||||||
|
if(!oldSdp || !self.peerconnection) {
|
||||||
|
success_callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.peerconnection.switchstreams = true;
|
||||||
|
self.modifySources(function() {
|
||||||
|
console.log('modify sources done');
|
||||||
|
|
||||||
|
var newSdp = new SDP(self.peerconnection.localDescription.sdp);
|
||||||
|
|
||||||
|
// change allocation on bridge
|
||||||
|
self.updateLocalChannel(newSdp, ['sources']);
|
||||||
|
|
||||||
|
console.log("SDPs", oldSdp, newSdp);
|
||||||
|
self.notifyMySSRCUpdate(oldSdp, newSdp);
|
||||||
|
|
||||||
|
success_callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// tell everyone about a new participants a=ssrc lines (isadd is true)
|
// tell everyone about a new participants a=ssrc lines (isadd is true)
|
||||||
// or a leaving participants a=ssrc lines
|
// or a leaving participants a=ssrc lines
|
||||||
ColibriFocus.prototype.sendSSRCUpdate = function (sdpMediaSsrcs, fromJid, isadd) {
|
ColibriFocus.prototype.sendSSRCUpdate = function (sdpMediaSsrcs, fromJid, isadd) {
|
||||||
|
@ -1010,7 +1092,7 @@ ColibriFocus.prototype.addSource = function (elem, fromJid) {
|
||||||
// FIXME: dirty waiting
|
// FIXME: dirty waiting
|
||||||
if (!this.peerconnection.localDescription)
|
if (!this.peerconnection.localDescription)
|
||||||
{
|
{
|
||||||
console.warn("addSource - localDescription not ready yet")
|
console.warn("addSource - localDescription not ready yet");
|
||||||
setTimeout(function() { self.addSource(elem, fromJid); }, 200);
|
setTimeout(function() { self.addSource(elem, fromJid); }, 200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1036,6 +1118,16 @@ ColibriFocus.prototype.addSource = function (elem, fromJid) {
|
||||||
var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
|
var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
|
||||||
var newSSRCs = oldRemoteSdp.getNewMedia(remoteSDP);
|
var newSSRCs = oldRemoteSdp.getNewMedia(remoteSDP);
|
||||||
self.sendSSRCUpdate(newSSRCs, fromJid, true);
|
self.sendSSRCUpdate(newSSRCs, fromJid, true);
|
||||||
|
// change allocation on bridge
|
||||||
|
if (peerSsrc[1] /* video */) {
|
||||||
|
// If the remote peer has changed its video sources, then we need to
|
||||||
|
// update the bridge with this information, in order for the
|
||||||
|
// simulcast manager of the remote peer to update its layers, and
|
||||||
|
// any associated receivers to adjust to the change.
|
||||||
|
var videoSDP = new SDP(['v=0', 'm=audio', 'a=mid:audio', peerSsrc[0]].join('\r\n') + ['m=video', 'a=mid:video', peerSsrc[1]].join('\r\n'));
|
||||||
|
var participant = self.peers.indexOf(fromJid);
|
||||||
|
self.updateRemoteChannel(videoSDP, participant, ['sources']);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1073,6 +1165,16 @@ ColibriFocus.prototype.removeSource = function (elem, fromJid) {
|
||||||
var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
|
var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
|
||||||
var removedSSRCs = remoteSDP.getNewMedia(oldSDP);
|
var removedSSRCs = remoteSDP.getNewMedia(oldSDP);
|
||||||
self.sendSSRCUpdate(removedSSRCs, fromJid, false);
|
self.sendSSRCUpdate(removedSSRCs, fromJid, false);
|
||||||
|
// change allocation on bridge
|
||||||
|
if (peerSsrc[1] /* video */) {
|
||||||
|
// If the remote peer has changed its video sources, then we need to
|
||||||
|
// update the bridge with this information, in order for the
|
||||||
|
// simulcast manager of the remote peer to update its layers, and
|
||||||
|
// any associated receivers to adjust to the change.
|
||||||
|
var videoSDP = new SDP(['v=0', 'm=audio', 'a=mid:audio', peerSsrc[0]].join('\r\n') + ['m=video', 'a=mid:video', peerSsrc[1]].join('\r\n'));
|
||||||
|
var participant = self.peers.indexOf(fromJid);
|
||||||
|
self.updateRemoteChannel(videoSDP, participant, ['sources']);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1084,7 +1186,7 @@ ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype)
|
||||||
remoteSDP.fromJingle(elem);
|
remoteSDP.fromJingle(elem);
|
||||||
|
|
||||||
// ACT 1: change allocation on bridge
|
// ACT 1: change allocation on bridge
|
||||||
this.updateChannel(remoteSDP, participant);
|
this.updateRemoteChannel(remoteSDP, participant);
|
||||||
|
|
||||||
// ACT 2: tell anyone else about the new SSRCs
|
// ACT 2: tell anyone else about the new SSRCs
|
||||||
this.sendSSRCUpdate(remoteSDP.getMediaSsrcMap(), session.peerjid, true);
|
this.sendSSRCUpdate(remoteSDP.getMediaSsrcMap(), session.peerjid, true);
|
||||||
|
|
|
@ -140,11 +140,13 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
|
||||||
|
|
||||||
TraceablePeerConnection.prototype.addStream = function (stream) {
|
TraceablePeerConnection.prototype.addStream = function (stream) {
|
||||||
this.trace('addStream', stream.id);
|
this.trace('addStream', stream.id);
|
||||||
|
simulcast.resetSender();
|
||||||
this.peerconnection.addStream(stream);
|
this.peerconnection.addStream(stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
TraceablePeerConnection.prototype.removeStream = function (stream) {
|
TraceablePeerConnection.prototype.removeStream = function (stream) {
|
||||||
this.trace('removeStream', stream.id);
|
this.trace('removeStream', stream.id);
|
||||||
|
simulcast.resetSender();
|
||||||
this.peerconnection.removeStream(stream);
|
this.peerconnection.removeStream(stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -204,7 +206,7 @@ TraceablePeerConnection.prototype.enqueueAddSsrc = function(channel, ssrcLines)
|
||||||
this.addssrc[channel] = '';
|
this.addssrc[channel] = '';
|
||||||
}
|
}
|
||||||
this.addssrc[channel] += ssrcLines;
|
this.addssrc[channel] += ssrcLines;
|
||||||
}
|
};
|
||||||
|
|
||||||
TraceablePeerConnection.prototype.addSource = function (elem) {
|
TraceablePeerConnection.prototype.addSource = function (elem) {
|
||||||
console.log('addssrc', new Date().getTime());
|
console.log('addssrc', new Date().getTime());
|
||||||
|
@ -260,7 +262,7 @@ TraceablePeerConnection.prototype.enqueueRemoveSsrc = function(channel, ssrcLine
|
||||||
this.removessrc[channel] = '';
|
this.removessrc[channel] = '';
|
||||||
}
|
}
|
||||||
this.removessrc[channel] += ssrcLines;
|
this.removessrc[channel] += ssrcLines;
|
||||||
}
|
};
|
||||||
|
|
||||||
TraceablePeerConnection.prototype.removeSource = function (elem) {
|
TraceablePeerConnection.prototype.removeSource = function (elem) {
|
||||||
console.log('removessrc', new Date().getTime());
|
console.log('removessrc', new Date().getTime());
|
||||||
|
|
|
@ -42,7 +42,7 @@ SDP.prototype.getMediaSsrcMap = function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return media_ssrcs;
|
return media_ssrcs;
|
||||||
}
|
};
|
||||||
/**
|
/**
|
||||||
* Returns <tt>true</tt> if this SDP contains given SSRC.
|
* Returns <tt>true</tt> if this SDP contains given SSRC.
|
||||||
* @param ssrc the ssrc to check.
|
* @param ssrc the ssrc to check.
|
||||||
|
@ -59,7 +59,8 @@ SDP.prototype.containsSSRC = function(ssrc) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return contains;
|
return contains;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
|
* Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
|
||||||
* @param otherSdp the other SDP to check ssrc with.
|
* @param otherSdp the other SDP to check ssrc with.
|
||||||
|
@ -89,7 +90,7 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
var myMedia = this.getMediaSsrcMap();
|
var myMedia = this.getMediaSsrcMap();
|
||||||
var othersMedia = otherSdp.getMediaSsrcMap();
|
var othersMedia = otherSdp.getMediaSsrcMap();
|
||||||
|
@ -111,7 +112,7 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
||||||
}
|
}
|
||||||
newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
|
newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// Look for new ssrc groups across the channels
|
// Look for new ssrc groups across the channels
|
||||||
othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
|
othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
|
||||||
|
@ -120,7 +121,7 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
||||||
var matched = false;
|
var matched = false;
|
||||||
for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
|
for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
|
||||||
var mySsrcGroup = myChannel.ssrcGroups[i];
|
var mySsrcGroup = myChannel.ssrcGroups[i];
|
||||||
if (otherSsrcGroup.semantics == mySsrcGroup
|
if (otherSsrcGroup.semantics == mySsrcGroup.semantics
|
||||||
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
|
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
|
||||||
|
|
||||||
matched = true;
|
matched = true;
|
||||||
|
@ -140,7 +141,8 @@ SDP.prototype.getNewMedia = function(otherSdp) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return newMedia;
|
return newMedia;
|
||||||
}
|
};
|
||||||
|
|
||||||
// remove iSAC and CN from SDP
|
// remove iSAC and CN from SDP
|
||||||
SDP.prototype.mangle = function () {
|
SDP.prototype.mangle = function () {
|
||||||
var i, j, mline, lines, rtpmap, newdesc;
|
var i, j, mline, lines, rtpmap, newdesc;
|
||||||
|
|
73
simulcast.js
73
simulcast.js
|
@ -490,7 +490,6 @@ function SimulcastSender() {
|
||||||
this.logger = new SimulcastLogger('SimulcastSender', 1);
|
this.logger = new SimulcastLogger('SimulcastSender', 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulcastSender.prototype._localVideoSourceCache = '';
|
|
||||||
SimulcastSender.prototype.displayedLocalVideoStream = null;
|
SimulcastSender.prototype.displayedLocalVideoStream = null;
|
||||||
|
|
||||||
SimulcastSender.prototype._generateGuid = (function () {
|
SimulcastSender.prototype._generateGuid = (function () {
|
||||||
|
@ -506,14 +505,6 @@ SimulcastSender.prototype._generateGuid = (function () {
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
||||||
SimulcastSender.prototype._cacheLocalVideoSources = function (lines) {
|
|
||||||
this._localVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
|
|
||||||
};
|
|
||||||
|
|
||||||
SimulcastSender.prototype._restoreLocalVideoSources = function (lines) {
|
|
||||||
this.simulcastUtils._replaceVideoSources(lines, this._localVideoSourceCache);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns a random integer between min (included) and max (excluded)
|
// Returns a random integer between min (included) and max (excluded)
|
||||||
// Using Math.round() gives a non-uniform distribution!
|
// Using Math.round() gives a non-uniform distribution!
|
||||||
SimulcastSender.prototype._generateRandomSSRC = function () {
|
SimulcastSender.prototype._generateRandomSSRC = function () {
|
||||||
|
@ -521,7 +512,37 @@ SimulcastSender.prototype._generateRandomSSRC = function () {
|
||||||
return Math.floor(Math.random() * (max - min)) + min;
|
return Math.floor(Math.random() * (max - min)) + min;
|
||||||
};
|
};
|
||||||
|
|
||||||
SimulcastSender.prototype._appendSimulcastGroup = function (lines) {
|
SimulcastSender.prototype.getLocalVideoStream = function () {
|
||||||
|
return (this.displayedLocalVideoStream != null)
|
||||||
|
? this.displayedLocalVideoStream
|
||||||
|
// in case we have no simulcast at all, i.e. we didn't perform the GUM
|
||||||
|
: connection.jingle.localVideo;
|
||||||
|
};
|
||||||
|
|
||||||
|
function NativeSimulcastSender() {
|
||||||
|
SimulcastSender.call(this); // call the super constructor.
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype._localExplosionMap = {};
|
||||||
|
NativeSimulcastSender.prototype._isUsingScreenStream = false;
|
||||||
|
NativeSimulcastSender.prototype._localVideoSourceCache = '';
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype.reset = function () {
|
||||||
|
this._localExplosionMap = {};
|
||||||
|
this._isUsingScreenStream = isUsingScreenStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {
|
||||||
|
this._localVideoSourceCache = this.simulcastUtils._getVideoSources(lines);
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype._restoreLocalVideoSources = function (lines) {
|
||||||
|
this.simulcastUtils._replaceVideoSources(lines, this._localVideoSourceCache);
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeSimulcastSender.prototype._appendSimulcastGroup = function (lines) {
|
||||||
var videoSources, ssrcGroup, simSSRC, numOfSubs = 2, i, sb, msid;
|
var videoSources, ssrcGroup, simSSRC, numOfSubs = 2, i, sb, msid;
|
||||||
|
|
||||||
this.logger.info('Appending simulcast group...');
|
this.logger.info('Appending simulcast group...');
|
||||||
|
@ -557,7 +578,7 @@ SimulcastSender.prototype._appendSimulcastGroup = function (lines) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Does the actual patching.
|
// Does the actual patching.
|
||||||
SimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
|
NativeSimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
|
||||||
|
|
||||||
this.logger.info('Ensuring simulcast group...');
|
this.logger.info('Ensuring simulcast group...');
|
||||||
|
|
||||||
|
@ -571,21 +592,6 @@ SimulcastSender.prototype._ensureSimulcastGroup = function (lines) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SimulcastSender.prototype.getLocalVideoStream = function () {
|
|
||||||
return (this.displayedLocalVideoStream != null)
|
|
||||||
? this.displayedLocalVideoStream
|
|
||||||
// in case we have no simulcast at all, i.e. we didn't perform the GUM
|
|
||||||
: connection.jingle.localVideo;
|
|
||||||
};
|
|
||||||
|
|
||||||
function NativeSimulcastSender() {
|
|
||||||
SimulcastSender.call(this); // call the super constructor.
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeSimulcastSender.prototype = Object.create(SimulcastSender.prototype);
|
|
||||||
|
|
||||||
NativeSimulcastSender.prototype._localExplosionMap = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a single stream with multiple tracks for local video sources.
|
* Produces a single stream with multiple tracks for local video sources.
|
||||||
*
|
*
|
||||||
|
@ -658,7 +664,7 @@ NativeSimulcastSender.prototype.getUserMedia = function (constraints, success, e
|
||||||
NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
|
NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (desc) {
|
||||||
var sb;
|
var sb;
|
||||||
|
|
||||||
if (!desc || desc == null) {
|
if (!desc || desc == null || this._isUsingScreenStream) {
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,6 +692,10 @@ NativeSimulcastSender.prototype.reverseTransformLocalDescription = function (des
|
||||||
*/
|
*/
|
||||||
NativeSimulcastSender.prototype.transformAnswer = function (desc) {
|
NativeSimulcastSender.prototype.transformAnswer = function (desc) {
|
||||||
|
|
||||||
|
if (!desc || desc == null || this._isUsingScreenStream) {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
var sb = desc.sdp.split('\r\n');
|
var sb = desc.sdp.split('\r\n');
|
||||||
|
|
||||||
// Even if we have enabled native simulcasting previously
|
// Even if we have enabled native simulcasting previously
|
||||||
|
@ -734,7 +744,8 @@ SimulcastReceiver.prototype.transformRemoteDescription = function (desc) {
|
||||||
this._updateRemoteMaps(sb);
|
this._updateRemoteMaps(sb);
|
||||||
this._cacheRemoteVideoSources(sb);
|
this._cacheRemoteVideoSources(sb);
|
||||||
|
|
||||||
// NOTE(gp) this needs to be called after updateRemoteMaps because we need the simulcast group in the _updateRemoteMaps() method.
|
// NOTE(gp) this needs to be called after updateRemoteMaps because we
|
||||||
|
// need the simulcast group in the _updateRemoteMaps() method.
|
||||||
this.simulcastUtils._removeSimulcastGroup(sb);
|
this.simulcastUtils._removeSimulcastGroup(sb);
|
||||||
|
|
||||||
if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
|
if (desc.sdp.indexOf('a=ssrc-group:SIM') !== -1) {
|
||||||
|
@ -1189,6 +1200,12 @@ SimulcastManager.prototype._setLocalVideoStreamEnabled = function(ssrc, enabled)
|
||||||
this.simulcastSender._setLocalVideoStreamEnabled(ssrc, enabled);
|
this.simulcastSender._setLocalVideoStreamEnabled(ssrc, enabled);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SimulcastManager.prototype.resetSender = function() {
|
||||||
|
if (typeof this.simulcastSender.reset === 'function'){
|
||||||
|
this.simulcastSender.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
|
|
Loading…
Reference in New Issue