1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342 |
- /* colibri.js -- a COLIBRI focus
- * The colibri spec has been submitted to the XMPP Standards Foundation
- * for publications as a XMPP extensions:
- * http://xmpp.org/extensions/inbox/colibri.html
- *
- * colibri.js is a participating focus, i.e. the focus participates
- * in the conference. The conference itself can be ad-hoc, through a
- * MUC, through PubSub, etc.
- *
- * colibri.js relies heavily on the strophe.jingle library available
- * from https://github.com/ESTOS/strophe.jingle
- * and interoperates with the Jitsi videobridge available from
- * https://jitsi.org/Projects/JitsiVideobridge
- */
- /*
- Copyright (c) 2013 ESTOS GmbH
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- /* jshint -W117 */
-
- ColibriFocus.prototype = Object.create(SessionBase.prototype);
- function ColibriFocus(connection, bridgejid) {
-
- SessionBase.call(this, connection, Math.random().toString(36).substr(2, 12));
-
- this.bridgejid = bridgejid;
- this.peers = [];
- this.confid = null;
-
- /**
- * Local XMPP resource used to join the multi user chat.
- * @type {*}
- */
- this.myMucResource = Strophe.getResourceFromJid(connection.emuc.myroomjid);
-
- /**
- * Default channel expire value in seconds.
- * @type {number}
- */
- this.channelExpire
- = ('number' === typeof(config.channelExpire))
- ? config.channelExpire
- : 15;
- /**
- * Default channel last-n value.
- * @type {number}
- */
- this.channelLastN
- = ('number' === typeof(config.channelLastN)) ? config.channelLastN : -1;
-
- // media types of the conference
- if (config.openSctp)
- this.media = ['audio', 'video', 'data'];
- else
- this.media = ['audio', 'video'];
-
- this.connection.jingle.sessions[this.sid] = this;
- this.mychannel = [];
- this.channels = [];
- this.remotessrc = {};
-
- // container for candidates from the focus
- // gathered before confid is known
- this.drip_container = [];
-
- // silly wait flag
- this.wait = true;
-
- this.recordingEnabled = false;
-
- // stores information about the endpoints (i.e. display names) to
- // be sent to the videobridge.
- this.endpointsInfo = null;
- }
-
- // creates a conferences with an initial set of peers
- ColibriFocus.prototype.makeConference = function (peers) {
- var self = this;
- if (this.confid !== null) {
- console.error('makeConference called twice? Ignoring...');
- // FIXME: just invite peers?
- return;
- }
- this.confid = 0; // !null
- this.peers = [];
- peers.forEach(function (peer) {
- self.peers.push(peer);
- self.channels.push([]);
- });
-
- this.peerconnection
- = new TraceablePeerConnection(
- this.connection.jingle.ice_config,
- this.connection.jingle.pc_constraints );
-
- if(this.connection.jingle.localAudio) {
- this.peerconnection.addStream(this.connection.jingle.localAudio);
- }
- if(this.connection.jingle.localVideo) {
- this.peerconnection.addStream(this.connection.jingle.localVideo);
- }
- this.peerconnection.oniceconnectionstatechange = function (event) {
- console.warn('ice connection state changed to', self.peerconnection.iceConnectionState);
- /*
- if (self.peerconnection.signalingState == 'stable' && self.peerconnection.iceConnectionState == 'connected') {
- console.log('adding new remote SSRCs from iceconnectionstatechange');
- window.setTimeout(function() { self.modifySources(); }, 1000);
- }
- */
- };
- this.peerconnection.onsignalingstatechange = function (event) {
- console.warn(self.peerconnection.signalingState);
- /*
- if (self.peerconnection.signalingState == 'stable' && self.peerconnection.iceConnectionState == 'connected') {
- console.log('adding new remote SSRCs from signalingstatechange');
- window.setTimeout(function() { self.modifySources(); }, 1000);
- }
- */
- };
- this.peerconnection.onaddstream = function (event) {
- // search the jid associated with this stream
- Object.keys(self.remotessrc).forEach(function (jid) {
- if (self.remotessrc[jid].join('\r\n').indexOf('mslabel:' + event.stream.id) != -1) {
- event.peerjid = jid;
- }
- });
- $(document).trigger('remotestreamadded.jingle', [event, self.sid]);
- };
- this.peerconnection.onicecandidate = function (event) {
- //console.log('focus onicecandidate', self.confid, new Date().getTime(), event.candidate);
- if (!event.candidate) {
- console.log('end of candidates');
- return;
- }
- self.sendIceCandidate(event.candidate);
- };
- this._makeConference();
- /*
- this.peerconnection.createOffer(
- function (offer) {
- self.peerconnection.setLocalDescription(
- offer,
- function () {
- // success
- $(document).trigger('setLocalDescription.jingle', [self.sid]);
- // FIXME: could call _makeConference here and trickle candidates later
- self._makeConference();
- },
- function (error) {
- console.log('setLocalDescription failed', error);
- }
- );
- },
- function (error) {
- console.warn(error);
- }
- );
- */
- };
-
- // Sends a COLIBRI message which enables or disables (according to 'state') the
- // recording on the bridge. Waits for the result IQ and calls 'callback' with
- // the new recording state, according to the IQ.
- ColibriFocus.prototype.setRecording = function(state, token, callback) {
- var self = this;
- var elem = $iq({to: this.bridgejid, type: 'set'});
- elem.c('conference', {
- xmlns: 'http://jitsi.org/protocol/colibri',
- id: this.confid
- });
- elem.c('recording', {state: state, token: token});
- elem.up();
-
- this.connection.sendIQ(elem,
- function (result) {
- console.log('Set recording "', state, '". Result:', result);
- var recordingElem = $(result).find('>conference>recording');
- var newState = ('true' === recordingElem.attr('state'));
-
- self.recordingEnabled = newState;
- callback(newState);
- },
- function (error) {
- console.warn(error);
- }
- );
- };
-
- /*
- * Updates the display name for an endpoint with a specific jid.
- * jid: the jid associated with the endpoint.
- * displayName: the new display name for the endpoint.
- */
- ColibriFocus.prototype.setEndpointDisplayName = function(jid, displayName) {
- var endpointId = jid.substr(1 + jid.lastIndexOf('/'));
- var update = false;
-
- if (this.endpointsInfo === null) {
- this.endpointsInfo = {};
- }
-
- var endpointInfo = this.endpointsInfo[endpointId];
- if ('undefined' === typeof endpointInfo) {
- endpointInfo = this.endpointsInfo[endpointId] = {};
- }
-
- if (endpointInfo['displayname'] !== displayName) {
- endpointInfo['displayname'] = displayName;
- update = true;
- }
-
- if (update) {
- this.updateEndpoints();
- }
- };
-
- /*
- * Sends a colibri message to the bridge that contains the
- * current endpoints and their display names.
- */
- ColibriFocus.prototype.updateEndpoints = function() {
- if (this.confid === null
- || this.endpointsInfo === null) {
- return;
- }
-
- if (this.confid === 0) {
- // the colibri conference is currently initiating
- var self = this;
- window.setTimeout(function() { self.updateEndpoints()}, 1000);
- return;
- }
-
- var elem = $iq({to: this.bridgejid, type: 'set'});
- elem.c('conference', {
- xmlns: 'http://jitsi.org/protocol/colibri',
- id: this.confid
- });
-
- for (var id in this.endpointsInfo) {
- elem.c('endpoint');
- elem.attrs({ id: id,
- displayname: this.endpointsInfo[id]['displayname']
- });
- elem.up();
- }
-
- //elem.up(); //conference
-
- this.connection.sendIQ(
- elem,
- function (result) {},
- function (error) { console.warn(error); }
- );
- };
-
- ColibriFocus.prototype._makeConference = function () {
- var self = this;
- var elem = $iq({ to: this.bridgejid, type: 'get' });
- elem.c('conference', { xmlns: 'http://jitsi.org/protocol/colibri' });
-
- this.media.forEach(function (name) {
- var elemName;
- var elemAttrs = { initiator: 'true', expire: self.channelExpire };
-
- if ('data' === name)
- {
- elemName = 'sctpconnection';
- elemAttrs['port'] = 5000;
- }
- else
- {
- elemName = 'channel';
- if (('video' === name) && (self.channelLastN >= 0))
- elemAttrs['last-n'] = self.channelLastN;
- }
-
- elem.c('content', { name: name });
-
- elem.c(elemName, elemAttrs);
- elem.attrs({ endpoint: self.myMucResource });
- elem.up();// end of channel/sctpconnection
-
- for (var j = 0; j < self.peers.length; j++) {
- var peer = self.peers[j];
-
- elem.c(elemName, elemAttrs);
- elem.attrs({ endpoint: peer.substr(1 + peer.lastIndexOf('/')) });
- if ('channel' === elemName && config.useRtcpMux) {
- elem.c('rtcp-mux').up();
- }
- elem.up(); // end of channel/sctpconnection
- }
- elem.up(); // end of content
- });
-
- if (this.endpointsInfo !== null) {
- for (var id in this.endpointsInfo) {
- elem.c('endpoint');
- elem.attrs({ id: id,
- displayname: this.endpointsInfo[id]['displayname']
- });
- elem.up();
- }
- }
-
- /*
- var localSDP = new SDP(this.peerconnection.localDescription.sdp);
- localSDP.media.forEach(function (media, channel) {
- var name = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
- elem.c('content', {name: name});
- elem.c('channel', {initiator: 'false', expire: self.channelExpire});
-
- // FIXME: should reuse code from .toJingle
- var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
- for (var j = 0; j < mline.fmt.length; j++) {
- var rtpmap = SDPUtil.find_line(media, 'a=rtpmap:' + mline.fmt[j]);
- elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
- elem.up();
- }
-
- localSDP.TransportToJingle(channel, elem);
-
- elem.up(); // end of channel
- for (j = 0; j < self.peers.length; j++) {
- elem.c('channel', {initiator: 'true', expire: self.channelExpire }).up();
- }
- elem.up(); // end of content
- });
- */
-
- this.connection.sendIQ(elem,
- function (result) {
- self.createdConference(result);
- },
- function (error) {
- console.warn(error);
- }
- );
- };
-
- // callback when a conference was created
- ColibriFocus.prototype.createdConference = function (result) {
- console.log('created a conference on the bridge');
- var self = this;
- var tmp;
-
- this.confid = $(result).find('>conference').attr('id');
- var remotecontents = $(result).find('>conference>content').get();
- var numparticipants = 0;
- for (var i = 0; i < remotecontents.length; i++)
- {
- var contentName = $(remotecontents[i]).attr('name');
- var channelName
- = contentName !== 'data' ? '>channel' : '>sctpconnection';
-
- tmp = $(remotecontents[i]).find(channelName).get();
- this.mychannel.push($(tmp.shift()));
- numparticipants = tmp.length;
- for (j = 0; j < tmp.length; j++) {
- if (this.channels[j] === undefined) {
- this.channels[j] = [];
- }
- this.channels[j].push(tmp[j]);
- }
- }
-
- console.log('remote channels', this.channels);
-
- // Notify that the focus has created the conference on the bridge
- $(document).trigger('conferenceCreated.jingle', [self]);
-
- var bridgeSDP = new SDP(
- 'v=0\r\n' +
- 'o=- 5151055458874951233 2 IN IP4 127.0.0.1\r\n' +
- 's=-\r\n' +
- 't=0 0\r\n' +
- /* Audio */
- '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' +
- 'a=mid:audio\r\n' +
- 'a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n' +
- 'a=sendrecv\r\n' +
- 'a=rtpmap:111 opus/48000/2\r\n' +
- 'a=fmtp:111 minptime=10\r\n' +
- 'a=rtpmap:103 ISAC/16000\r\n' +
- 'a=rtpmap:104 ISAC/32000\r\n' +
- 'a=rtpmap:0 PCMU/8000\r\n' +
- 'a=rtpmap:8 PCMA/8000\r\n' +
- 'a=rtpmap:106 CN/32000\r\n' +
- 'a=rtpmap:105 CN/16000\r\n' +
- 'a=rtpmap:13 CN/8000\r\n' +
- 'a=rtpmap:126 telephone-event/8000\r\n' +
- 'a=maxptime:60\r\n' +
- (config.useRtcpMux ? 'a=rtcp-mux\r\n' : '') +
- /* Video */
- 'm=video 1 RTP/SAVPF 100 116 117\r\n' +
- 'c=IN IP4 0.0.0.0\r\n' +
- 'a=rtcp:1 IN IP4 0.0.0.0\r\n' +
- 'a=mid:video\r\n' +
- 'a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\n' +
- 'a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\n' +
- 'a=sendrecv\r\n' +
- 'a=rtpmap:100 VP8/90000\r\n' +
- 'a=rtcp-fb:100 ccm fir\r\n' +
- 'a=rtcp-fb:100 nack\r\n' +
- 'a=rtcp-fb:100 goog-remb\r\n' +
- 'a=rtpmap:116 red/90000\r\n' +
- 'a=rtpmap:117 ulpfec/90000\r\n' +
- (config.useRtcpMux ? 'a=rtcp-mux\r\n' : '') +
- /* Data SCTP */
- (config.openSctp ?
- 'm=application 1 DTLS/SCTP 5000\r\n' +
- 'c=IN IP4 0.0.0.0\r\n' +
- 'a=sctpmap:5000 webrtc-datachannel\r\n' +
- 'a=mid:data\r\n'
- : '')
- );
-
- bridgeSDP.media.length = this.mychannel.length;
- var channel;
- /*
- for (channel = 0; channel < bridgeSDP.media.length; channel++) {
- bridgeSDP.media[channel] = '';
- // unchanged lines
- bridgeSDP.media[channel] += SDPUtil.find_line(localSDP.media[channel], 'm=') + '\r\n';
- bridgeSDP.media[channel] += SDPUtil.find_line(localSDP.media[channel], 'c=') + '\r\n';
- if (SDPUtil.find_line(localSDP.media[channel], 'a=rtcp:')) {
- bridgeSDP.media[channel] += SDPUtil.find_line(localSDP.media[channel], 'a=rtcp:') + '\r\n';
- }
- if (SDPUtil.find_line(localSDP.media[channel], 'a=mid:')) {
- bridgeSDP.media[channel] += SDPUtil.find_line(localSDP.media[channel], 'a=mid:') + '\r\n';
- }
- if (SDPUtil.find_line(localSDP.media[channel], 'a=sendrecv')) {
- bridgeSDP.media[channel] += 'a=sendrecv\r\n';
- }
- if (SDPUtil.find_line(localSDP.media[channel], 'a=extmap:')) {
- bridgeSDP.media[channel] += SDPUtil.find_lines(localSDP.media[channel], 'a=extmap:').join('\r\n') + '\r\n';
- }
-
- // FIXME: should look at m-line and group the ids together
- if (SDPUtil.find_line(localSDP.media[channel], 'a=rtpmap:')) {
- bridgeSDP.media[channel] += SDPUtil.find_lines(localSDP.media[channel], 'a=rtpmap:').join('\r\n') + '\r\n';
- }
- if (SDPUtil.find_line(localSDP.media[channel], 'a=fmtp:')) {
- bridgeSDP.media[channel] += SDPUtil.find_lines(localSDP.media[channel], 'a=fmtp:').join('\r\n') + '\r\n';
- }
- if (SDPUtil.find_line(localSDP.media[channel], 'a=rtcp-fb:')) {
- bridgeSDP.media[channel] += SDPUtil.find_lines(localSDP.media[channel], 'a=rtcp-fb:').join('\r\n') + '\r\n';
- }
- // FIXME: changed lines -- a=sendrecv direction, a=setup direction
- }
- */
- for (channel = 0; channel < bridgeSDP.media.length; channel++) {
- // get the mixed ssrc
- tmp = $(this.mychannel[channel]).find('>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
- // FIXME: check rtp-level-relay-type
-
- var isData = bridgeSDP.media[channel].indexOf('application') !== -1;
- if (!isData && tmp.length)
- {
- bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'cname:mixed' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
- }
- else if (!isData)
- {
- // make chrome happy... '3735928559' == 0xDEADBEEF
- // FIXME: this currently appears as two streams, should be one
- bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'msid:mixedmslabel mixedlabelv0' + '\r\n';
- bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'mslabel:mixedmslabel' + '\r\n';
- }
-
- // FIXME: should take code from .fromJingle
- 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';
- tmp.find('>candidate').each(function () {
- bridgeSDP.media[channel] += SDPUtil.candidateFromJingle(this);
- });
- tmp = tmp.find('>fingerprint');
- if (tmp.length) {
- bridgeSDP.media[channel] += 'a=fingerprint:' + tmp.attr('hash') + ' ' + tmp.text() + '\r\n';
- bridgeSDP.media[channel] += 'a=setup:actpass\r\n'; // offer so always actpass
- }
- }
- }
- bridgeSDP.raw = bridgeSDP.session + bridgeSDP.media.join('');
-
- this.peerconnection.setRemoteDescription(
- new RTCSessionDescription({type: 'offer', sdp: bridgeSDP.raw}),
- function () {
- console.log('setRemoteDescription success');
- self.peerconnection.createAnswer(
- function (answer) {
- self.peerconnection.setLocalDescription(answer,
- function () {
- console.log('setLocalDescription succeded.');
- // make sure our presence is updated
- $(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);
- 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
- });
-
- // 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,
- 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 setLocalDescription succeded",
- error, elem);
- }
- );
-
- // now initiate sessions
- for (var i = 0; i < numparticipants; i++) {
- self.initiate(self.peers[i], true);
- }
-
- // Notify we've created the conference
- $(document).trigger(
- 'conferenceCreated.jingle', self);
- },
- function (error) {
- console.warn('setLocalDescription failed.', error);
- }
- );
- },
- function (error) {
- console.warn('createAnswer failed.', error);
- }
- );
- /*
- for (var i = 0; i < numparticipants; i++) {
- self.initiate(self.peers[i], true);
- }
- */
- },
- function (error) {
- console.log('setRemoteDescription failed.', error);
- }
- );
-
- };
-
- // send a session-initiate to a new participant
- ColibriFocus.prototype.initiate = function (peer, isInitiator) {
- var participant = this.peers.indexOf(peer);
- console.log('tell', peer, participant);
- var sdp;
- if (this.peerconnection !== null && this.peerconnection.signalingState == 'stable') {
- sdp = new SDP(this.peerconnection.remoteDescription.sdp);
- var localSDP = new SDP(this.peerconnection.localDescription.sdp);
- // throw away stuff we don't want
- // not needed with static offer
- 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){
- sdp.removeMediaLines(i, 'a=rtcp-mux');
- }
- sdp.removeMediaLines(i, 'a=ssrc:');
- sdp.removeMediaLines(i, 'a=crypto:');
- sdp.removeMediaLines(i, 'a=candidate:');
- sdp.removeMediaLines(i, 'a=ice-options:google-ice');
- sdp.removeMediaLines(i, 'a=ice-ufrag:');
- sdp.removeMediaLines(i, 'a=ice-pwd:');
- sdp.removeMediaLines(i, 'a=fingerprint:');
- sdp.removeMediaLines(i, 'a=setup:');
-
- if (1) { //i > 0) { // not for audio FIXME: does not work as intended
- // re-add all remote a=ssrcs
- for (var jid in this.remotessrc) {
- if (jid == peer || !this.remotessrc[jid][i])
- continue;
- sdp.media[i] += this.remotessrc[jid][i];
- }
- // and local a=ssrc lines
- sdp.media[i] += SDPUtil.find_lines(localSDP.media[i], 'a=ssrc').join('\r\n') + '\r\n';
- }
- }
- sdp.raw = sdp.session + sdp.media.join('');
- } else {
- console.error('can not initiate a new session without a stable peerconnection');
- return;
- }
-
- // add stuff we got from the bridge
- for (var j = 0; j < sdp.media.length; j++) {
- var chan = $(this.channels[participant][j]);
- console.log('channel id', chan.attr('id'));
-
- tmp = chan.find('>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
- if (tmp.length) {
- sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'cname:mixed' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'label:mixedlabela0' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'msid:mixedmslabel mixedlabela0' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + tmp.attr('ssrc') + ' ' + 'mslabel:mixedmslabel' + '\r\n';
- }
- // No SSRCs for 'data', comes when j == 2
- else if (j < 2)
- {
- // make chrome happy... '3735928559' == 0xDEADBEEF
- sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'msid:mixedmslabel mixedlabelv0' + '\r\n';
- sdp.media[j] += 'a=ssrc:' + '3735928559' + ' ' + 'mslabel:mixedmslabel' + '\r\n';
- }
-
- 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';
- if (tmp.attr('pwd'))
- sdp.media[j] += 'a=ice-pwd:' + tmp.attr('pwd') + '\r\n';
- // and the candidates...
- tmp.find('>candidate').each(function () {
- sdp.media[j] += SDPUtil.candidateFromJingle(this);
- });
- tmp = tmp.find('>fingerprint');
- if (tmp.length) {
- sdp.media[j] += 'a=fingerprint:' + tmp.attr('hash') + ' ' + tmp.text() + '\r\n';
- /*
- if (tmp.attr('direction')) {
- sdp.media[j] += 'a=setup:' + tmp.attr('direction') + '\r\n';
- }
- */
- sdp.media[j] += 'a=setup:actpass\r\n';
- }
- }
- }
- // make a new colibri session and configure it
- // FIXME: is it correct to use this.connection.jid when used in a MUC?
- var sess = new ColibriSession(this.connection.jid,
- Math.random().toString(36).substr(2, 12), // random string
- this.connection);
- sess.initiate(peer);
- sess.colibri = this;
- // We do not announce our audio per conference peer, so only video is set here
- sess.localVideo = this.connection.jingle.localVideo;
- sess.media_constraints = this.connection.jingle.media_constraints;
- sess.pc_constraints = this.connection.jingle.pc_constraints;
- sess.ice_config = this.connection.jingle.ice_config;
-
- this.connection.jingle.sessions[sess.sid] = sess;
- this.connection.jingle.jid2session[sess.peerjid] = sess;
-
- // send a session-initiate
- var init = $iq({to: peer, type: 'set'})
- .c('jingle',
- {xmlns: 'urn:xmpp:jingle:1',
- action: 'session-initiate',
- initiator: sess.me,
- sid: sess.sid
- }
- );
- sdp.toJingle(init, 'initiator');
- this.connection.sendIQ(init,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.log('got error');
- }
- );
- };
-
- // pull in a new participant into the conference
- ColibriFocus.prototype.addNewParticipant = function (peer) {
- var self = this;
- if (this.confid === 0 || !this.peerconnection.localDescription)
- {
- // bad state
- if (this.confid === 0)
- {
- console.error('confid does not exist yet, postponing', peer);
- }
- else
- {
- console.error('local description not ready yet, postponing', peer);
- }
- window.setTimeout(function () { self.addNewParticipant(peer); }, 250);
- return;
- }
- var index = this.channels.length;
- this.channels.push([]);
- this.peers.push(peer);
-
- var elem = $iq({to: this.bridgejid, type: 'get'});
- elem.c(
- 'conference',
- { xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid });
- var localSDP = new SDP(this.peerconnection.localDescription.sdp);
- localSDP.media.forEach(function (media, channel) {
- var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
- var elemName;
- var elemAttrs
- = {
- initiator: 'true',
- expire: self.channelExpire,
- endpoint: peer.substr(1 + peer.lastIndexOf('/'))
- };
-
- if ('data' == name)
- {
- elemName = 'sctpconnection';
- elemAttrs['port'] = 5000;
- }
- else
- {
- elemName = 'channel';
- if (('video' === name) && (self.channelLastN >= 0))
- elemAttrs['last-n'] = self.channelLastN;
- }
-
- elem.c('content', { name: name });
- elem.c(elemName, elemAttrs);
- if ('channel' === elemName && config.useRtcpMux) {
- elem.c('rtcp-mux').up();
- }
- elem.up(); // end of channel/sctpconnection
- elem.up(); // end of content
- });
-
- this.connection.sendIQ(elem,
- function (result) {
- var contents = $(result).find('>conference>content').get();
- for (var i = 0; i < contents.length; i++) {
- var channelXml = $(contents[i]).find('>channel');
- if (channelXml.length)
- {
- tmp = channelXml.get();
- }
- else
- {
- tmp = $(contents[i]).find('>sctpconnection').get();
- }
- self.channels[index][i] = tmp[0];
- }
- self.initiate(peer, true);
- },
- function (error) {
- console.warn(error);
- }
- );
- };
-
- // update the channel description (payload-types + dtls fp) for a participant
- ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
- console.log('change allocation for', this.confid);
- var self = this;
- var change = $iq({to: this.bridgejid, type: 'set'});
- change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
- for (channel = 0; channel < this.channels[participant].length; channel++)
- {
- if (!remoteSDP.media[channel])
- continue;
-
- var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:'));
- change.c('content', {name: name});
- if (name !== 'data')
- {
- change.c('channel', {
- id: $(this.channels[participant][channel]).attr('id'),
- endpoint: $(this.channels[participant][channel]).attr('endpoint'),
- expire: self.channelExpire
- });
- if (config.useRtcpMux) {
- change.c('rtcp-mux').up();
- }
-
- var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
- rtpmap.forEach(function (val) {
- // TODO: too much copy-paste
- var rtpmap = SDPUtil.parse_rtpmap(val);
- change.c('payload-type', rtpmap);
- //
- // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
- /*
- if (SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id)) {
- tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id));
- for (var k = 0; k < tmp.length; k++) {
- change.c('parameter', tmp[k]).up();
- }
- }
- */
- change.up();
- });
- }
- else
- {
- var sctpmap = SDPUtil.find_line(remoteSDP.media[channel], 'a=sctpmap:');
- change.c('sctpconnection', {
- endpoint: $(this.channels[participant][channel]).attr('endpoint'),
- expire: self.channelExpire,
- port: SDPUtil.parse_sctpmap(sctpmap)[0]
- });
- }
- // now add transport
- remoteSDP.TransportToJingle(channel, change);
-
- change.up(); // end of channel/sctpconnection
- change.up(); // end of content
- }
- this.connection.sendIQ(change,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.log('got error');
- }
- );
- };
-
- // tell everyone about a new participants a=ssrc lines (isadd is true)
- // or a leaving participants a=ssrc lines
- ColibriFocus.prototype.sendSSRCUpdate = function (sdpMediaSsrcs, fromJid, isadd) {
- var self = this;
- this.peers.forEach(function (peerjid) {
- if (peerjid == fromJid) return;
- console.log('tell', peerjid, 'about ' + (isadd ? 'new' : 'removed') + ' ssrcs from', fromJid);
- if (!self.remotessrc[peerjid]) {
- // FIXME: this should only send to participants that are stable, i.e. who have sent a session-accept
- // possibly, this.remoteSSRC[session.peerjid] does not exist yet
- console.warn('do we really want to bother', peerjid, 'with updates yet?');
- }
- var peersess = self.connection.jingle.jid2session[peerjid];
- if(!peersess){
- console.warn('no session with peer: '+peerjid+' yet...');
- return;
- }
-
- self.sendSSRCUpdateIq(sdpMediaSsrcs, peersess.sid, peersess.initiator, peerjid, isadd);
- });
- };
-
- /**
- * Overrides SessionBase.addSource.
- *
- * @param elem proprietary 'add source' Jingle request(XML node).
- * @param fromJid JID of the participant to whom new ssrcs belong.
- */
- ColibriFocus.prototype.addSource = function (elem, fromJid) {
-
- var self = this;
- // FIXME: dirty waiting
- if (!this.peerconnection.localDescription)
- {
- console.warn("addSource - localDescription not ready yet")
- setTimeout(function() { self.addSource(elem, fromJid); }, 200);
- return;
- }
-
- this.peerconnection.addSource(elem);
-
- var peerSsrc = this.remotessrc[fromJid];
- //console.log("On ADD", self.addssrc, peerSsrc);
- this.peerconnection.addssrc.forEach(function(val, idx){
- if(!peerSsrc[idx]){
- // add ssrc
- peerSsrc[idx] = val;
- } else {
- if(peerSsrc[idx].indexOf(val) == -1){
- peerSsrc[idx] = peerSsrc[idx]+val;
- }
- }
- });
-
- var oldRemoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);
- this.modifySources(function(){
- // Notify other participants about added ssrc
- var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
- var newSSRCs = oldRemoteSdp.getNewMedia(remoteSDP);
- self.sendSSRCUpdate(newSSRCs, fromJid, true);
- });
- };
-
- /**
- * Overrides SessionBase.removeSource.
- *
- * @param elem proprietary 'remove source' Jingle request(XML node).
- * @param fromJid JID of the participant to whom removed ssrcs belong.
- */
- ColibriFocus.prototype.removeSource = function (elem, fromJid) {
-
- var self = this;
- // FIXME: dirty waiting
- if (!self.peerconnection.localDescription)
- {
- console.warn("removeSource - localDescription not ready yet");
- setTimeout(function() { self.removeSource(elem, fromJid); }, 200);
- return;
- }
-
- this.peerconnection.removeSource(elem);
-
- var peerSsrc = this.remotessrc[fromJid];
- //console.log("On REMOVE", self.removessrc, peerSsrc);
- this.peerconnection.removessrc.forEach(function(val, idx){
- if(peerSsrc[idx]){
- // Remove ssrc
- peerSsrc[idx] = peerSsrc[idx].replace(val, '');
- }
- });
-
- var oldSDP = new SDP(self.peerconnection.remoteDescription.sdp);
- this.modifySources(function(){
- // Notify other participants about removed ssrc
- var remoteSDP = new SDP(self.peerconnection.remoteDescription.sdp);
- var removedSSRCs = remoteSDP.getNewMedia(oldSDP);
- self.sendSSRCUpdate(removedSSRCs, fromJid, false);
- });
- };
-
- ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype) {
- var participant = this.peers.indexOf(session.peerjid);
- console.log('Colibri.setRemoteDescription from', session.peerjid, participant);
- var remoteSDP = new SDP('');
- var channel;
- remoteSDP.fromJingle(elem);
-
- // ACT 1: change allocation on bridge
- this.updateChannel(remoteSDP, participant);
-
- // ACT 2: tell anyone else about the new SSRCs
- this.sendSSRCUpdate(remoteSDP.getMediaSsrcMap(), session.peerjid, true);
-
- // ACT 3: note the SSRCs
- this.remotessrc[session.peerjid] = [];
- for (channel = 0; channel < this.channels[participant].length; channel++) {
- //if (channel == 0) continue; FIXME: does not work as intended
- if (!remoteSDP.media[channel])
- continue;
-
- if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length)
- {
- this.remotessrc[session.peerjid][channel] =
- SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:')
- .join('\r\n') + '\r\n';
- }
- }
-
- // ACT 4: add new a=ssrc lines to local remotedescription
- for (channel = 0; channel < this.channels[participant].length; channel++) {
- //if (channel == 0) continue; FIXME: does not work as intended
- if (!remoteSDP.media[channel])
- continue;
-
- if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) {
- this.peerconnection.enqueueAddSsrc(
- channel,
- SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').join('\r\n') + '\r\n'
- );
- }
- }
- this.modifySources();
- };
-
- // relay ice candidates to bridge using trickle
- ColibriFocus.prototype.addIceCandidate = function (session, elem) {
- var self = this;
- var participant = this.peers.indexOf(session.peerjid);
- //console.log('change transport allocation for', this.confid, session.peerjid, participant);
- var change = $iq({to: this.bridgejid, type: 'set'});
- change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
- $(elem).each(function () {
- var name = $(this).attr('name');
-
- var channel = name == 'audio' ? 0 : 1; // FIXME: search mlineindex in localdesc
- if (name != 'audio' && name != 'video')
- channel = 2; // name == 'data'
-
- change.c('content', {name: name});
- if (name !== 'data')
- {
- change.c('channel', {
- id: $(self.channels[participant][channel]).attr('id'),
- endpoint: $(self.channels[participant][channel]).attr('endpoint'),
- expire: self.channelExpire
- });
-
- if (config.useRtcpMux) {
- change.c('rtcp-mux').up();
- }
- }
- else
- {
- change.c('sctpconnection', {
- endpoint: $(self.channels[participant][channel]).attr('endpoint'),
- expire: self.channelExpire
- });
- }
- $(this).find('>transport').each(function () {
- change.c('transport', {
- ufrag: $(this).attr('ufrag'),
- pwd: $(this).attr('pwd'),
- xmlns: $(this).attr('xmlns')
- });
-
- $(this).find('>candidate').each(function () {
- /* not yet
- if (this.getAttribute('protocol') == 'tcp' && this.getAttribute('port') == 0) {
- // chrome generates TCP candidates with port 0
- return;
- }
- */
- var line = SDPUtil.candidateFromJingle(this);
- change.c('candidate', SDPUtil.candidateToJingle(line)).up();
- });
- change.up(); // end of transport
- });
- change.up(); // end of channel/sctpconnection
- change.up(); // end of content
- });
- // FIXME: need to check if there is at least one candidate when filtering TCP ones
- this.connection.sendIQ(change,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.error('got error', err);
- }
- );
- };
-
- // send our own candidate to the bridge
- ColibriFocus.prototype.sendIceCandidate = function (candidate) {
- var self = this;
- //console.log('candidate', candidate);
- if (!candidate) {
- console.log('end of candidates');
- return;
- }
- if (this.drip_container.length === 0) {
- // start 20ms callout
- window.setTimeout(
- function () {
- if (self.drip_container.length === 0) return;
- self.sendIceCandidates(self.drip_container);
- self.drip_container = [];
- },
- 20);
- }
- this.drip_container.push(candidate);
- };
-
- // sort and send multiple candidates
- ColibriFocus.prototype.sendIceCandidates = function (candidates) {
- var self = this;
- var mycands = $iq({to: this.bridgejid, type: 'set'});
- mycands.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
- // FIXME: multi-candidate logic is taken from strophe.jingle, should be refactored there
- var localSDP = new SDP(this.peerconnection.localDescription.sdp);
- for (var mid = 0; mid < localSDP.media.length; mid++)
- {
- var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
- if (cands.length > 0)
- {
- var name = cands[0].sdpMid;
- mycands.c('content', {name: name });
- if (name !== 'data')
- {
- mycands.c('channel', {
- id: $(this.mychannel[cands[0].sdpMLineIndex]).attr('id'),
- endpoint: $(this.mychannel[cands[0].sdpMLineIndex]).attr('endpoint'),
- expire: self.channelExpire
- });
- if (config.useRtcpMux) {
- mycands.c('rtcp-mux').up();
- }
-
- }
- else
- {
- mycands.c('sctpconnection', {
- endpoint: $(this.mychannel[cands[0].sdpMLineIndex]).attr('endpoint'),
- port: $(this.mychannel[cands[0].sdpMLineIndex]).attr('port'),
- expire: self.channelExpire
- });
- }
- mycands.c('transport', {xmlns: 'urn:xmpp:jingle:transports:ice-udp:1'});
- for (var i = 0; i < cands.length; i++) {
- mycands.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
- }
- mycands.up(); // transport
- mycands.up(); // channel / sctpconnection
- mycands.up(); // content
- }
- }
- console.log('send cands', candidates);
- this.connection.sendIQ(mycands,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.error('got error', err);
- }
- );
- };
-
- ColibriFocus.prototype.terminate = function (session, reason) {
- console.log('remote session terminated from', session.peerjid);
- var participant = this.peers.indexOf(session.peerjid);
- if (!this.remotessrc[session.peerjid] || participant == -1) {
- return;
- }
- var ssrcs = this.remotessrc[session.peerjid];
- for (var i = 0; i < ssrcs.length; i++) {
- this.peerconnection.enqueueRemoveSsrc(i, ssrcs[i]);
- }
- // remove from this.peers
- this.peers.splice(participant, 1);
- // expire channel on bridge
- var change = $iq({to: this.bridgejid, type: 'set'});
- change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
- for (var channel = 0; channel < this.channels[participant].length; channel++) {
- var name = channel === 0 ? 'audio' : 'video';
- if (channel == 2)
- name = 'data';
- change.c('content', {name: name});
- if (name !== 'data')
- {
- change.c('channel', {
- id: $(this.channels[participant][channel]).attr('id'),
- endpoint: $(this.channels[participant][channel]).attr('endpoint'),
- expire: '0'
- });
- }
- else
- {
- change.c('sctpconnection', {
- endpoint: $(this.channels[participant][channel]).attr('endpoint'),
- expire: '0'
- });
- }
- change.up(); // end of channel/sctpconnection
- change.up(); // end of content
- }
- this.connection.sendIQ(change,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.error('got error', err);
- }
- );
- // and remove from channels
- this.channels.splice(participant, 1);
-
- // tell everyone about the ssrcs to be removed
- var sdp = new SDP('');
- var localSDP = new SDP(this.peerconnection.localDescription.sdp);
- var contents = SDPUtil.find_lines(localSDP.raw, 'a=mid:').map(SDPUtil.parse_mid);
- for (var j = 0; j < ssrcs.length; j++) {
- sdp.media[j] = 'a=mid:' + contents[j] + '\r\n';
- sdp.media[j] += ssrcs[j];
- this.peerconnection.enqueueRemoveSsrc(j, ssrcs[j]);
- }
- this.sendSSRCUpdate(sdp.getMediaSsrcMap(), session.peerjid, false);
-
- delete this.remotessrc[session.peerjid];
- this.modifySources();
- };
-
- ColibriFocus.prototype.sendTerminate = function (session, reason, text) {
- var term = $iq({to: session.peerjid, type: 'set'})
- .c('jingle',
- {xmlns: 'urn:xmpp:jingle:1',
- action: 'session-terminate',
- initiator: session.me,
- sid: session.sid})
- .c('reason')
- .c(reason || 'success');
-
- if (text) {
- term.up().c('text').t(text);
- }
-
- this.connection.sendIQ(term,
- function () {
- if (!session)
- return;
-
- if (session.peerconnection) {
- session.peerconnection.close();
- session.peerconnection = null;
- }
-
- session.terminate();
- var ack = {};
- ack.source = 'terminate';
- $(document).trigger('ack.jingle', [session.sid, ack]);
- },
- function (stanza) {
- var error = ($(stanza).find('error').length) ? {
- code: $(stanza).find('error').attr('code'),
- reason: $(stanza).find('error :first')[0].tagName,
- }:{};
- $(document).trigger('ack.jingle', [self.sid, error]);
- },
- 10000);
- if (this.statsinterval !== null) {
- window.clearInterval(this.statsinterval);
- this.statsinterval = null;
- }
- };
-
- ColibriFocus.prototype.setRTCPTerminationStrategy = function (strategyFQN) {
- var self = this;
- var strategyIQ = $iq({to: this.bridgejid, type: 'set'});
- strategyIQ.c('conference', {
- xmlns: 'http://jitsi.org/protocol/colibri',
- id: this.confid,
- });
-
- strategyIQ.c('rtcp-termination-strategy', {name: strategyFQN });
-
- strategyIQ.c('content', {name: "video"});
- strategyIQ.up(); // end of content
-
- console.log('setting RTCP termination strategy', strategyFQN);
- this.connection.sendIQ(strategyIQ,
- function (res) {
- console.log('got result');
- },
- function (err) {
- console.error('got error', err);
- }
- );
- };
-
- /**
- * Sets the default value of the channel last-n attribute in this conference and
- * updates/patches the existing channels.
- */
- ColibriFocus.prototype.setChannelLastN = function (channelLastN) {
- if (('number' === typeof(channelLastN))
- && (this.channelLastN !== channelLastN))
- {
- this.channelLastN = channelLastN;
-
- // Update/patch the existing channels.
- var patch = $iq({ to: this.bridgejid, type: 'set' });
-
- patch.c(
- 'conference',
- { xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid });
- patch.c('content', { name: 'video' });
- patch.c(
- 'channel',
- {
- id: $(this.mychannel[1 /* video */]).attr('id'),
- 'last-n': this.channelLastN
- });
- patch.up(); // end of channel
- for (var p = 0; p < this.channels.length; p++)
- {
- patch.c(
- 'channel',
- {
- id: $(this.channels[p][1 /* video */]).attr('id'),
- 'last-n': this.channelLastN
- });
- patch.up(); // end of channel
- }
- this.connection.sendIQ(
- patch,
- function (res) {
- console.info('Set channel last-n succeeded:', res);
- },
- function (err) {
- console.error('Set channel last-n failed:', err);
- });
- }
- };
|