180 lines
6.4 KiB
JavaScript
180 lines
6.4 KiB
JavaScript
|
|
var SDPUtil = require("./SDPUtil");
|
|
|
|
function SDPDiffer(mySDP, otherSDP) {
|
|
this.mySDP = mySDP;
|
|
this.otherSDP = otherSDP;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
SDPDiffer.prototype.getNewMedia = function() {
|
|
|
|
// this could be useful in Array.prototype.
|
|
function arrayEquals(array) {
|
|
// if the other array is a falsy value, return
|
|
if (!array)
|
|
return false;
|
|
|
|
// compare lengths - can save a lot of time
|
|
if (this.length != array.length)
|
|
return false;
|
|
|
|
for (var i = 0, l=this.length; i < l; i++) {
|
|
// Check if we have nested arrays
|
|
if (this[i] instanceof Array && array[i] instanceof Array) {
|
|
// recurse into the nested arrays
|
|
if (!this[i].equals(array[i]))
|
|
return false;
|
|
}
|
|
else if (this[i] != array[i]) {
|
|
// Warning - two different object instances will never be equal: {x:20} != {x:20}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
var myMedias = this.mySDP.getMediaSsrcMap();
|
|
var othersMedias = this.otherSDP.getMediaSsrcMap();
|
|
var newMedia = {};
|
|
Object.keys(othersMedias).forEach(function(othersMediaIdx) {
|
|
var myMedia = myMedias[othersMediaIdx];
|
|
var othersMedia = othersMedias[othersMediaIdx];
|
|
if(!myMedia && othersMedia) {
|
|
// Add whole channel
|
|
newMedia[othersMediaIdx] = othersMedia;
|
|
return;
|
|
}
|
|
// Look for new ssrcs accross the channel
|
|
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[othersMediaIdx]){
|
|
newMedia[othersMediaIdx] = {
|
|
mediaindex: othersMedia.mediaindex,
|
|
mid: othersMedia.mid,
|
|
ssrcs: {},
|
|
ssrcGroups: []
|
|
};
|
|
}
|
|
newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
|
|
}
|
|
});
|
|
|
|
// Look for new ssrc groups across the channels
|
|
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 < myMedia.ssrcGroups.length; i++) {
|
|
var mySsrcGroup = myMedia.ssrcGroups[i];
|
|
if (otherSsrcGroup.semantics == mySsrcGroup.semantics
|
|
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
|
|
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!matched) {
|
|
// Allocate channel if we've found an ssrc-group that doesn't
|
|
// exist in our channel
|
|
|
|
if(!newMedia[othersMediaIdx]){
|
|
newMedia[othersMediaIdx] = {
|
|
mediaindex: othersMedia.mediaindex,
|
|
mid: othersMedia.mid,
|
|
ssrcs: {},
|
|
ssrcGroups: []
|
|
};
|
|
}
|
|
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, videoType) {
|
|
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 {
|
|
var nv = kv.split(':', 2);
|
|
var name = nv[0];
|
|
var value = SDPUtil.filter_special_chars(nv[1]);
|
|
modify.attrs({ name: name });
|
|
modify.attrs({ value: value });
|
|
}
|
|
modify.up(); // end of parameter
|
|
});
|
|
// indicate video type
|
|
if (videoType && media.mid == 'video') {
|
|
modify.c('ssrc-info',
|
|
{
|
|
xmlns: 'http://jitsi.org/jitmeet',
|
|
'video-type': videoType
|
|
})
|
|
.up();
|
|
}
|
|
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;
|
|
};
|
|
|
|
module.exports = SDPDiffer; |