diff --git a/libs/strophe/strophe.jingle.sdp.js b/libs/strophe/strophe.jingle.sdp.js index e9e9ff61b2..4a6b7ed349 100644 --- a/libs/strophe/strophe.jingle.sdp.js +++ b/libs/strophe/strophe.jingle.sdp.js @@ -17,27 +17,37 @@ function SDP(sdp) { SDP.prototype.getMediaSsrcMap = function() { var self = this; var media_ssrcs = {}; - for (channelNum = 0; channelNum < self.media.length; channelNum++) { - modified = true; - tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc:'); - var type = SDPUtil.parse_mid(SDPUtil.find_line(self.media[channelNum], 'a=mid:')); - var channel = new MediaChannel(channelNum, type); - media_ssrcs[channelNum] = channel; + var tmp; + for (var mediaindex = 0; mediaindex < self.media.length; mediaindex++) { + tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc:'); + var mid = SDPUtil.parse_mid(SDPUtil.find_line(self.media[mediaindex], 'a=mid:')); + var media = { + mediaindex: mediaindex, + mid: mid, + ssrcs: {}, + ssrcGroups: [] + }; + media_ssrcs[mediaindex] = media; tmp.forEach(function (line) { var linessrc = line.substring(7).split(' ')[0]; // allocate new ChannelSsrc - if(!channel.ssrcs[linessrc]) { - channel.ssrcs[linessrc] = new ChannelSsrc(linessrc, type); + if(!media.ssrcs[linessrc]) { + media.ssrcs[linessrc] = { + ssrc: linessrc, + lines: [] + }; } - channel.ssrcs[linessrc].lines.push(line); + media.ssrcs[linessrc].lines.push(line); }); - tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc-group:'); + tmp = SDPUtil.find_lines(self.media[mediaindex], 'a=ssrc-group:'); tmp.forEach(function(line){ var semantics = line.substr(0, idx).substr(13); var ssrcs = line.substr(14 + semantics.length).split(' '); if (ssrcs.length != 0) { - var ssrcGroup = new ChannelSsrcGroup(semantics, ssrcs); - channel.ssrcGroups.push(ssrcGroup); + media.ssrcGroups.push({ + semantics: semantics, + ssrcs: ssrcs + }); } }); } @@ -49,23 +59,28 @@ SDP.prototype.getMediaSsrcMap = function() { * @returns {boolean} true if this SDP contains given SSRC. */ SDP.prototype.containsSSRC = function(ssrc) { - var channels = this.getMediaSsrcMap(); + var medias = this.getMediaSsrcMap(); var contains = false; - Object.keys(channels).forEach(function(chNumber){ - var channel = channels[chNumber]; + Object.keys(medias).forEach(function(mediaindex){ + var media = medias[mediaindex]; //console.log("Check", channel, ssrc); - if(Object.keys(channel.ssrcs).indexOf(ssrc) != -1){ + if(Object.keys(media.ssrcs).indexOf(ssrc) != -1){ contains = true; } }); return contains; }; +function SDPDiffer(mySDP, otherSDP) { + this.mySDP = new SDP(mySDP); + this.otherSDP = new SDP(otherSDP); +} + /** * Returns map of MediaChannel that contains only media not contained in otherSdp. Mapped by channel idx. * @param otherSdp the other SDP to check ssrc with. */ -SDP.prototype.getNewMedia = function(otherSdp) { +SDPDiffer.prototype.getNewMedia = function() { // this could be useful in Array.prototype. function arrayEquals(array) { @@ -92,35 +107,40 @@ SDP.prototype.getNewMedia = function(otherSdp) { return true; } - var myMedia = this.getMediaSsrcMap(); - var othersMedia = otherSdp.getMediaSsrcMap(); + var myMedias = this.mySDP.getMediaSsrcMap(); + var othersMedias = this.otherSDP.getMediaSsrcMap(); var newMedia = {}; - Object.keys(othersMedia).forEach(function(channelNum) { - var myChannel = myMedia[channelNum]; - var othersChannel = othersMedia[channelNum]; - if(!myChannel && othersChannel) { + Object.keys(othersMedias).forEach(function(othersMediaIdx) { + var myMedia = myMedias[othersMediaIdx]; + var othersMedia = othersMedias[othersMediaIdx]; + if(!myMedia && othersMedia) { // Add whole channel - newMedia[channelNum] = othersChannel; + newMedia[othersMediaIdx] = othersMedia; return; } // Look for new ssrcs accross the channel - Object.keys(othersChannel.ssrcs).forEach(function(ssrc) { - if(Object.keys(myChannel.ssrcs).indexOf(ssrc) === -1) { + Object.keys(othersMedia.ssrcs).forEach(function(ssrc) { + if(Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) { // Allocate channel if we've found ssrc that doesn't exist in our channel - if(!newMedia[channelNum]){ - newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType); + if(!newMedia[othersMediaIdx]){ + newMedia[othersMediaIdx] = { + mediaindex: othersMedia.mediaindex, + mid: othersMedia.mid, + ssrcs: {}, + ssrcGroups: [] + }; } - newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc]; + newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc]; } }); // Look for new ssrc groups across the channels - othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){ + othersMedia.ssrcGroups.forEach(function(otherSsrcGroup){ // try to match the other ssrc-group with an ssrc-group of ours var matched = false; - for (var i = 0; i < myChannel.ssrcGroups.length; i++) { - var mySsrcGroup = myChannel.ssrcGroups[i]; + for (var i = 0; i < myMedia.ssrcGroups.length; i++) { + var mySsrcGroup = myMedia.ssrcGroups[i]; if (otherSsrcGroup.semantics == mySsrcGroup.semantics && arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) { @@ -133,16 +153,88 @@ SDP.prototype.getNewMedia = function(otherSdp) { // Allocate channel if we've found an ssrc-group that doesn't // exist in our channel - if(!newMedia[channelNum]){ - newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType); + if(!newMedia[othersMediaIdx]){ + newMedia[othersMediaIdx] = { + mediaindex: othersMedia.mediaindex, + mid: othersMedia.mid, + ssrcs: {}, + ssrcGroups: [] + }; } - newMedia[channelNum].ssrcGroups.push(otherSsrcGroup); + newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup); } }); }); return newMedia; }; +/** + * Sends SSRC update IQ. + * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove. + * @param sid session identifier that will be put into the IQ. + * @param initiator initiator identifier. + * @param toJid destination Jid + * @param isAdd indicates if this is remove or add operation. + */ +SDPDiffer.prototype.toJingle = function(modify) { + var sdpMediaSsrcs = this.getNewMedia(); + var self = this; + + // FIXME: only announce video ssrcs since we mix audio and dont need + // the audio ssrcs therefore + var modified = false; + Object.keys(sdpMediaSsrcs).forEach(function(mediaindex){ + modified = true; + var media = sdpMediaSsrcs[mediaindex]; + modify.c('content', {name: media.mid}); + + modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: media.mid}); + // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly + // generate sources from lines + Object.keys(media.ssrcs).forEach(function(ssrcNum) { + var mediaSsrc = media.ssrcs[ssrcNum]; + modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); + modify.attrs({ssrc: mediaSsrc.ssrc}); + // iterate over ssrc lines + mediaSsrc.lines.forEach(function (line) { + var idx = line.indexOf(' '); + var kv = line.substr(idx + 1); + modify.c('parameter'); + if (kv.indexOf(':') == -1) { + modify.attrs({ name: kv }); + } else { + modify.attrs({ name: kv.split(':', 2)[0] }); + modify.attrs({ value: kv.split(':', 2)[1] }); + } + modify.up(); // end of parameter + }); + modify.up(); // end of source + }); + + // generate source groups from lines + media.ssrcGroups.forEach(function(ssrcGroup) { + if (ssrcGroup.ssrcs.length != 0) { + + modify.c('ssrc-group', { + semantics: ssrcGroup.semantics, + xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' + }); + + ssrcGroup.ssrcs.forEach(function (ssrc) { + modify.c('source', { ssrc: ssrc }) + .up(); // end of source + }); + modify.up(); // end of ssrc-group + } + }); + + modify.up(); // end of description + modify.up(); // end of content + }); + + return modified; +}; + // remove iSAC and CN from SDP SDP.prototype.mangle = function () { var i, j, mline, lines, rtpmap, newdesc; @@ -683,62 +775,6 @@ SDP.prototype.jingle2media = function (content) { return media; }; -/** - * Contains utility classes used in SDP class. - * - */ - -/** - * Class holds a=ssrc lines and media type a=mid - * @param ssrc synchronization source identifier number(a=ssrc lines from SDP) - * @param type media type eg. "audio" or "video"(a=mid frm SDP) - * @constructor - */ -function ChannelSsrc(ssrc, type) { - this.ssrc = ssrc; - this.type = type; - this.lines = []; -} - -/** - * Class holds a=ssrc-group: lines - * @param semantics - * @param ssrcs - * @constructor - */ -function ChannelSsrcGroup(semantics, ssrcs, line) { - this.semantics = semantics; - this.ssrcs = ssrcs; -} - -/** - * Helper class represents media channel. Is a container for ChannelSsrc, holds channel idx and media type. - * @param channelNumber channel idx in SDP media array. - * @param mediaType media type(a=mid) - * @constructor - */ -function MediaChannel(channelNumber, mediaType) { - /** - * SDP channel number - * @type {*} - */ - this.chNumber = channelNumber; - /** - * Channel media type(a=mid) - * @type {*} - */ - this.mediaType = mediaType; - /** - * The maps of ssrc numbers to ChannelSsrc objects. - */ - this.ssrcs = {}; - - /** - * The array of ChannelSsrcGroup objects. - * @type {Array} - */ - this.ssrcGroups = []; -} SDPUtil = { iceparams: function (mediadesc, sessiondesc) { diff --git a/libs/strophe/strophe.jingle.session.js b/libs/strophe/strophe.jingle.session.js index 4c7513bb05..31eac54a74 100644 --- a/libs/strophe/strophe.jingle.session.js +++ b/libs/strophe/strophe.jingle.session.js @@ -968,121 +968,57 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success */ JingleSession.prototype.notifyMySSRCUpdate = function (old_sdp, new_sdp) { - var old_media = old_sdp.getMediaSsrcMap(); - var new_media = new_sdp.getMediaSsrcMap(); - //console.log("old/new medias: ", old_media, new_media); - - var toAdd = old_sdp.getNewMedia(new_sdp); - var toRemove = new_sdp.getNewMedia(old_sdp); - //console.log("to add", toAdd); - //console.log("to remove", toRemove); - if(Object.keys(toRemove).length > 0){ - this.sendSSRCUpdate(toRemove, null, false); - } - if(Object.keys(toAdd).length > 0){ - this.sendSSRCUpdate(toAdd, null, true); - } -}; - -/** - * Empty method that does nothing by default. It should send SSRC update IQs to session participants. - * @param sdpMediaSsrcs array of - * @param fromJid - * @param isAdd - */ -JingleSession.prototype.sendSSRCUpdate = function(sdpMediaSsrcs, fromJid, isAdd) { - var self = this; - console.log('tell', self.peerjid, 'about ' + (isadd ? 'new' : 'removed') + ' ssrcs from' + self.me); - if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')){ console.log("Too early to send updates"); return; } - this.sendSSRCUpdateIq(sdpMediaSsrcs, self.sid, self.initiator, self.peerjid, isadd); -} - -/** - * Sends SSRC update IQ. - * @param sdpMediaSsrcs SSRCs map obtained from SDP.getNewMedia. Cntains SSRCs to add/remove. - * @param sid session identifier that will be put into the IQ. - * @param initiator initiator identifier. - * @param toJid destination Jid - * @param isAdd indicates if this is remove or add operation. - */ -JingleSession.prototype.sendSSRCUpdateIq = function(sdpMediaSsrcs, sid, initiator, toJid, isAdd) { - - var self = this; - var modify = $iq({to: toJid, type: 'set'}) + // send source-add IQ. + var sdpDiffer = new SDPDiffer(old_sdp, new_sdp); + var add = $iq({to: self.peerjid, type: 'set'}) .c('jingle', { xmlns: 'urn:xmpp:jingle:1', - action: isAdd ? 'source-add' : 'source-remove', - initiator: initiator, - sid: sid + action: 'source-add', + initiator: self.initiator, + sid: self.sid } ); - // FIXME: only announce video ssrcs since we mix audio and dont need - // the audio ssrcs therefore - var modified = false; - Object.keys(sdpMediaSsrcs).forEach(function(channelNum){ - modified = true; - var channel = sdpMediaSsrcs[channelNum]; - modify.c('content', {name: channel.mediaType}); - - modify.c('description', {xmlns:'urn:xmpp:jingle:apps:rtp:1', media: channel.mediaType}); - // FIXME: not completly sure this operates on blocks and / or handles different ssrcs correctly - // generate sources from lines - Object.keys(channel.ssrcs).forEach(function(ssrcNum) { - var mediaSsrc = channel.ssrcs[ssrcNum]; - modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' }); - modify.attrs({ssrc: mediaSsrc.ssrc}); - // iterate over ssrc lines - mediaSsrc.lines.forEach(function (line) { - var idx = line.indexOf(' '); - var kv = line.substr(idx + 1); - modify.c('parameter'); - if (kv.indexOf(':') == -1) { - modify.attrs({ name: kv }); - } else { - modify.attrs({ name: kv.split(':', 2)[0] }); - modify.attrs({ value: kv.split(':', 2)[1] }); - } - modify.up(); // end of parameter - }); - modify.up(); // end of source - }); - - // generate source groups from lines - channel.ssrcGroups.forEach(function(ssrcGroup) { - if (ssrcGroup.ssrcs.length != 0) { - - modify.c('ssrc-group', { - semantics: ssrcGroup.semantics, - xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' - }); - - ssrcGroup.ssrcs.forEach(function (ssrc) { - modify.c('source', { ssrc: ssrc }) - .up(); // end of source - }); - modify.up(); // end of ssrc-group + var added = sdpDiffer.toJingle(add); + if (added) { + this.connection.sendIQ(add, + function (res) { + console.info('got add result', res); + }, + function (err) { + console.error('got add error', err); } - }); + ); + } else { + console.log('addition not necessary'); + } - modify.up(); // end of description - modify.up(); // end of content - }); - if (modified) { - self.connection.sendIQ(modify, + // send source-remove IQ. + sdpDiffer = new SDPDiffer(new_sdp, old_sdp); + var remove = $iq({to: self.peerjid, type: 'set'}) + .c('jingle', { + xmlns: 'urn:xmpp:jingle:1', + action: 'source-remove', + initiator: self.initiator, + sid: self.sid + } + ); + var removed = sdpDiffer.toJingle(remove); + if (removed) { + this.connection.sendIQ(remove, function (res) { - console.info('got modify result', res); + console.info('got remove result', res); }, function (err) { - console.error('got modify error', err); + console.error('got remove error', err); } ); } else { - console.log('modification not necessary'); + console.log('removal not necessary'); } };