From 20b69ce5ca74df67f172e2a7ed876379bd36a03a Mon Sep 17 00:00:00 2001 From: Boris Grozev Date: Mon, 18 Aug 2014 11:44:16 +0300 Subject: [PATCH] Adds support for bundle. --- config.js | 1 + libs/colibri/colibri.focus.js | 72 ++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/config.js b/config.js index 293d97a1e..bf086564a 100644 --- a/config.js +++ b/config.js @@ -19,5 +19,6 @@ var config = { //defaultSipNumber: '20669', //Default SIP number used in call dialog // channelLastN: -1, // The default value of the channel attribute last-n. // useRtcpMux: true, +// useBundle: true, enableRecording: false }; diff --git a/libs/colibri/colibri.focus.js b/libs/colibri/colibri.focus.js index cd30c1139..084c98dfa 100644 --- a/libs/colibri/colibri.focus.js +++ b/libs/colibri/colibri.focus.js @@ -72,6 +72,7 @@ function ColibriFocus(connection, bridgejid) { this.media = ['audio', 'video']; this.connection.jingle.sessions[this.sid] = this; + this.bundledTransports = {}; this.mychannel = []; this.channels = []; this.remotessrc = {}; @@ -296,13 +297,20 @@ ColibriFocus.prototype._makeConference = function () { elem.c(elemName, elemAttrs); elem.attrs({ endpoint: self.myMucResource }); + if (config.useBundle) { + elem.attrs({ 'channel-bundle-id': self.myMucResource }); + } elem.up();// end of channel/sctpconnection for (var j = 0; j < self.peers.length; j++) { var peer = self.peers[j]; + var peerEndpoint = peer.substr(1 + peer.lastIndexOf('/')); elem.c(elemName, elemAttrs); - elem.attrs({ endpoint: peer.substr(1 + peer.lastIndexOf('/')) }); + elem.attrs({ endpoint: peerEndpoint }); + if (config.useBundle) { + elem.attrs({ 'channel-bundle-id': peerEndpoint }); + } elem.up(); // end of channel/sctpconnection } elem.up(); // end of content @@ -353,7 +361,7 @@ ColibriFocus.prototype._makeConference = function () { ); }; -// callback when a conference was created +// callback when a colibri conference was created ColibriFocus.prototype.createdConference = function (result) { console.log('created a conference on the bridge'); var self = this; @@ -379,6 +387,14 @@ ColibriFocus.prototype.createdConference = function (result) { } } + // save the 'transport' elements from 'channel-bundle'-s + var channelBundles = $(result).find('>conference>channel-bundle'); + for (var i = 0; i < channelBundles.length; i++) + { + var endpointId = $(channelBundles[i]).attr('id'); + this.bundledTransports[endpointId] = $(channelBundles[i]).find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + } + console.log('remote channels', this.channels); // Notify that the focus has created the conference on the bridge @@ -390,6 +406,11 @@ ColibriFocus.prototype.createdConference = function (result) { 's=-\r\n' + 't=0 0\r\n' + /* Audio */ + (config.useBundle + ? ('a=group:BUNDLE audio video' + + (config.openSctp ? ' data' : '') + + '\r\n') + : '') + 'm=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126\r\n' + 'c=IN IP4 0.0.0.0\r\n' + 'a=rtcp:1 IN IP4 0.0.0.0\r\n' + @@ -490,7 +511,13 @@ ColibriFocus.prototype.createdConference = function (result) { } // FIXME: should take code from .fromJingle - tmp = $(this.mychannel[channel]).find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + var channelBundleId = $(this.mychannel[channel]).attr('channel-bundle-id'); + if (typeof channelBundleId != 'undefined') { + tmp = this.bundledTransports[channelBundleId]; + } else { + tmp = $(this.mychannel[channel]).find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + } + if (tmp.length) { bridgeSDP.media[channel] += 'a=ice-ufrag:' + tmp.attr('ufrag') + '\r\n'; bridgeSDP.media[channel] += 'a=ice-pwd:' + tmp.attr('pwd') + '\r\n'; @@ -514,7 +541,7 @@ ColibriFocus.prototype.createdConference = function (result) { function (answer) { self.peerconnection.setLocalDescription(answer, function () { - console.log('setLocalDescription succeded.'); + console.log('setLocalDescription succeeded.'); // make sure our presence is updated $(document).trigger('setLocalDescription.jingle', [self.sid]); var elem = $iq({to: self.bridgejid, type: 'get'}); @@ -570,7 +597,7 @@ ColibriFocus.prototype.createdConference = function (result) { }, function (error) { console.error( - "ERROR setLocalDescription succeded", + "ERROR sending colibri message", error, elem); } ); @@ -616,7 +643,9 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) { var localSDP = new SDP(this.peerconnection.localDescription.sdp); // throw away stuff we don't want // not needed with static offer - sdp.removeSessionLines('a=group:'); + if (!config.useBundle) { + sdp.removeSessionLines('a=group:'); + } sdp.removeSessionLines('a=msid-semantic:'); // FIXME: not mapped over jingle anyway... for (var i = 0; i < sdp.media.length; i++) { if (!config.useRtcpMux){ @@ -670,7 +699,14 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) { sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'mslabel:mixedmslabel' + '\r\n'; } - tmp = chan.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + // In the case of bundle, we add each candidate to all m= lines/jingle contents, + // just as chrome does + if (config.useBundle){ + tmp = this.bundledTransports[chan.attr('channel-bundle-id')]; + } else { + tmp = chan.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + } + if (tmp.length) { if (tmp.attr('ufrag')) sdp.media[j] += 'a=ice-ufrag:' + tmp.attr('ufrag') + '\r\n'; @@ -757,12 +793,17 @@ ColibriFocus.prototype.addNewParticipant = function (peer) { localSDP.media.forEach(function (media, channel) { var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:')); var elemName; + var endpointId = peer.substr(1 + peer.lastIndexOf('/')); var elemAttrs = { initiator: 'true', expire: self.channelExpire, - endpoint: peer.substr(1 + peer.lastIndexOf('/')) + endpoint: endpointId }; + if (config.useBundle) { + elemAttrs['channel-bundle-id'] = endpointId; + } + if ('data' == name) { @@ -785,7 +826,8 @@ ColibriFocus.prototype.addNewParticipant = function (peer) { this.connection.sendIQ(elem, function (result) { var contents = $(result).find('>conference>content').get(); - for (var i = 0; i < contents.length; i++) { + var i; + for (i = 0; i < contents.length; i++) { var channelXml = $(contents[i]).find('>channel'); if (channelXml.length) { @@ -797,6 +839,12 @@ ColibriFocus.prototype.addNewParticipant = function (peer) { } self.channels[index][i] = tmp[0]; } + var channelBundles = $(result).find('>conference>channel-bundle'); + for (i = 0; i < channelBundles.length; i++) + { + var endpointId = $(channelBundles[i]).attr('id'); + self.bundledTransports[endpointId] = $(channelBundles[i]).find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]'); + } self.initiate(peer, true); }, function (error) { @@ -1023,6 +1071,12 @@ ColibriFocus.prototype.addIceCandidate = function (session, elem) { $(elem).each(function () { var name = $(this).attr('name'); + // If we are using bundle, audio/video/data channel will have the same candidates, so only send them for + // the audio channel. + if (config.useBundle && name !== 'audio') { + return; + } + var channel = name == 'audio' ? 0 : 1; // FIXME: search mlineindex in localdesc if (name != 'audio' && name != 'video') channel = 2; // name == 'data'