Skip to content

Commit

Permalink
ref(SDPDiffer) Convert to ES6 class.
Browse files Browse the repository at this point in the history
Make it work directly with unified plan SDP that has multiple m-lines and add more unit tests.
  • Loading branch information
jallamsetty1 committed Sep 25, 2024
1 parent 4be1919 commit baa79f4
Show file tree
Hide file tree
Showing 4 changed files with 340 additions and 236 deletions.
12 changes: 7 additions & 5 deletions modules/sdp/SDP.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export default class SDP {
containsSSRC(ssrc) {
const souceMap = this.getMediaSsrcMap();

return Object.values(souceMap).some(media => media.ssrcs[ssrc]);
return Array.from(Object.values(souceMap)).some(media => media.ssrcs[ssrc]);
}

/**
Expand Down Expand Up @@ -141,19 +141,19 @@ export default class SDP {
* @returns {*}
*/
getMediaSsrcMap() {
const mediaSSRCs = {};
const sourceInfo = new Map();

this.media.forEach((mediaItem, mediaindex) => {
const mid = SDPUtil.parseMID(SDPUtil.findLine(mediaItem, 'a=mid:'));
const mline = SDPUtil.parseMLine(mediaItem.split('\r\n')[0]);
const media = {
mediaindex,
mediaType: mline.media,
mid,
ssrcs: {},
ssrcGroups: []
};

mediaSSRCs[mediaindex] = media;

SDPUtil.findLines(mediaItem, 'a=ssrc:').forEach(line => {
const linessrc = line.substring(7).split(' ')[0];

Expand All @@ -179,9 +179,11 @@ export default class SDP {
});
}
});

sourceInfo.set(mediaindex, media);
});

return mediaSSRCs;
return sourceInfo;
}

/**
Expand Down
280 changes: 86 additions & 194 deletions modules/sdp/SDPDiffer.js
Original file line number Diff line number Diff line change
@@ -1,220 +1,112 @@
import { isEqual } from 'lodash-es';

import { XEP } from '../../service/xmpp/XMPPExtensioProtocols';

import SDPUtil from './SDPUtil';

// this could be useful in Array.prototype.
/**
*
* @param array1
* @param array2
* A class that provides methods for comparing the source information present in two different SDPs so that the delta
* can be signaled to Jicofo via 'source-remove' or 'source-add'.
*/
function arrayEquals(array1, array2) {
// if the other array is a falsy value, return
if (!array2) {
return false;
export class SDPDiffer {
/**
* Constructor.
*
* @param {SDP} mySdp - the new SDP.
* @param {SDP} othersSdp - the old SDP.
*/
constructor(mySdp, othersSdp) {
this.mySdp = mySdp;
this.othersSdp = othersSdp;
}

// compare lengths - can save a lot of time
if (array1.length !== array2.length) {
return false;
}

for (let i = 0, l = array1.length; i < l; i++) {
// Check if we have nested arrays
if (array1[i] instanceof Array && array2[i] instanceof Array) {
// recurse into the nested arrays
if (!array1[i].equals(array2[i])) {
return false;
/**
* Returns a map of the sources that are present in 'othersSdp' but not in 'mySdp'.
*
* @returns {*}
*/
getNewMedia() {
const mySources = this.mySdp.getMediaSsrcMap();
const othersSources = this.othersSdp.getMediaSsrcMap();
const diff = {};

for (const [ index, othersSource ] of othersSources.entries()) {
const mySource = mySources.get(index);

if (!mySource || !isEqual(mySource, othersSource)) {
diff[index] = othersSource;
}
} else if (array1[i] !== array2[i]) {
// Warning - two different object instances will never be
// equal: {x:20} != {x:20}
return false;
}
}

return true;
}

/**
*
* @param mySDP
* @param otherSDP
*/
export default function SDPDiffer(mySDP, otherSDP) {
this.mySDP = mySDP;
this.otherSDP = otherSDP;
if (!mySDP) {
throw new Error('"mySDP" is undefined!');
} else if (!otherSDP) {
throw new Error('"otherSDP" is undefined!');
return diff;
}
}

/**
* Returns map of MediaChannel that contains media contained in
* 'mySDP', but not contained in 'otherSdp'. Mapped by channel idx.
*/
SDPDiffer.prototype.getNewMedia = function() {

const myMedias = this.mySDP.getMediaSsrcMap();
const othersMedias = this.otherSDP.getMediaSsrcMap();
const newMedia = {};

Object.keys(othersMedias).forEach(othersMediaIdx => {
const myMedia = myMedias[othersMediaIdx];
const othersMedia = othersMedias[othersMediaIdx];
/**
* Adds the diff source info to the provided IQ stanza.
*
* @param {*} modify - Stanza IQ.
* @returns {boolean}
*/
toJingle(modify) {
let modified = false;
const diffSourceInfo = this.getNewMedia();

for (const media of Object.values(diffSourceInfo)) {
modified = true;
modify.c('content', { name: media.mid });

modify.c('description', {
xmlns: XEP.RTP_MEDIA,
media: media.mid
});

if (!myMedia && othersMedia) {
// Add whole channel
newMedia[othersMediaIdx] = othersMedia;
Object.keys(media.ssrcs).forEach(ssrcNum => {
const mediaSsrc = media.ssrcs[ssrcNum];
const ssrcLines = mediaSsrc.lines;
const sourceName = SDPUtil.parseSourceNameLine(ssrcLines);
const videoType = SDPUtil.parseVideoTypeLine(ssrcLines);

modify.c('source', { xmlns: XEP.SOURCE_ATTRIBUTES });
modify.attrs({
name: sourceName,
videoType,
ssrc: mediaSsrc.ssrc
});

return;
}
// Only MSID attribute is sent
const msid = SDPUtil.parseMSIDAttribute(ssrcLines);

// Look for new ssrcs across the channel
Object.keys(othersMedia.ssrcs).forEach(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: []
};
if (msid) {
modify.c('parameter');
modify.attrs({ name: 'msid' });
modify.attrs({ value: msid });
modify.up();
}
newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];
} else if (othersMedia.ssrcs[ssrc].lines
&& myMedia.ssrcs[ssrc].lines) {
// we want to detect just changes in adding/removing msid
const myContainMsid = myMedia.ssrcs[ssrc].lines.find(
line => line.indexOf('msid') !== -1) !== undefined;
const newContainMsid = othersMedia.ssrcs[ssrc].lines.find(
line => line.indexOf('msid') !== -1) !== undefined;

if (myContainMsid !== newContainMsid) {
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(otherSsrcGroup => {

// try to match the other ssrc-group with an ssrc-group of ours
let matched = false;

for (let i = 0; i < myMedia.ssrcGroups.length; i++) {
const mySsrcGroup = myMedia.ssrcGroups[i];
modify.up(); // end of source
});

if (otherSsrcGroup.semantics === mySsrcGroup.semantics
&& arrayEquals(otherSsrcGroup.ssrcs, mySsrcGroup.ssrcs)) {
// generate source groups from lines
media.ssrcGroups.forEach(ssrcGroup => {
if (ssrcGroup.ssrcs.length) {

matched = true;
break;
}
}
modify.c('ssrc-group', {
semantics: ssrcGroup.semantics,
xmlns: XEP.SOURCE_ATTRIBUTES
});

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: []
};
ssrcGroup.ssrcs.forEach(ssrc => {
modify.c('source', { ssrc })
.up(); // end of source
});
modify.up(); // end of ssrc-group
}
newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);
}
});
});

return newMedia;
};

/**
* TODO: document!
*/
SDPDiffer.prototype.toJingle = function(modify) {
const sdpMediaSsrcs = this.getNewMedia();

let modified = false;

Object.keys(sdpMediaSsrcs).forEach(mediaindex => {
modified = true;
const media = sdpMediaSsrcs[mediaindex];

modify.c('content', { name: media.mid });

modify.c('description', {
xmlns: XEP.RTP_MEDIA,
media: media.mid
});

// FIXME: not completely sure this operates on blocks and / or handles
// different ssrcs correctly
// generate sources from lines
Object.keys(media.ssrcs).forEach(ssrcNum => {
const mediaSsrc = media.ssrcs[ssrcNum];
const ssrcLines = mediaSsrc.lines;
const sourceName = SDPUtil.parseSourceNameLine(ssrcLines);
const videoType = SDPUtil.parseVideoTypeLine(ssrcLines);

modify.c('source', { xmlns: XEP.SOURCE_ATTRIBUTES });
modify.attrs({
name: sourceName,
videoType,
ssrc: mediaSsrc.ssrc
});

// Only MSID attribute is sent
const msid = SDPUtil.parseMSIDAttribute(ssrcLines);

if (msid) {
modify.c('parameter');
modify.attrs({ name: 'msid' });
modify.attrs({ value: msid });
modify.up();
}

modify.up(); // end of source
});

// generate source groups from lines
media.ssrcGroups.forEach(ssrcGroup => {
if (ssrcGroup.ssrcs.length) {

modify.c('ssrc-group', {
semantics: ssrcGroup.semantics,
xmlns: XEP.SOURCE_ATTRIBUTES
});

ssrcGroup.ssrcs.forEach(ssrc => {
modify.c('source', { ssrc })
.up(); // end of source
});
modify.up(); // end of ssrc-group
}
});

modify.up(); // end of description
modify.up(); // end of content
});
modify.up(); // end of description
modify.up(); // end of content
}

return modified;
};
return modified;
}
}
Loading

0 comments on commit baa79f4

Please sign in to comment.