Permalink
Browse files

feat: add SdpTransformUtil

  • Loading branch information...
paweldomas committed Jan 31, 2017
1 parent cdfdef7 commit 452697121a6233495c1bd5234bf308950571d400
Showing with 517 additions and 181 deletions.
  1. +50 −110 modules/xmpp/RtxModifier.js
  2. +28 −71 modules/xmpp/SdpConsistency.js
  3. +439 −0 modules/xmpp/SdpTransformUtil.js
View
@@ -1,82 +1,31 @@
import { getLogger } from "jitsi-meet-logger";
const logger = getLogger(__filename);
import * as transform from 'sdp-transform';
import { SdpTransformWrap, parseSecondarySSRC } from './SdpTransformUtil';
import * as SDPUtil from "./SDPUtil";
/**
* Begin helper functions
*/
/**
* Given a videoMLine, returns a list of the video
* ssrcs (those used to actually send video, not
* any associated secondary streams)
* @param {object} videoMLine media line object from transform.parse
* @returns {list<string>} list of primary video ssrcs
*/
function getPrimaryVideoSsrcs (videoMLine) {
let videoSsrcs = videoMLine.ssrcs
.map(ssrcInfo => ssrcInfo.id)
.filter((ssrc, index, array) => array.indexOf(ssrc) === index);
if (videoMLine.ssrcGroups) {
videoMLine.ssrcGroups.forEach((ssrcGroupInfo) => {
// Right now, FID groups are the only ones we parse to
// disqualify streams. If/when others arise we'll
// need to add support for them here
if (ssrcGroupInfo.semantics === "FID") {
// secondary FID streams should be filtered out
let secondarySsrc = ssrcGroupInfo.ssrcs.split(" ")[1];
videoSsrcs.splice(
videoSsrcs.indexOf(parseInt(secondarySsrc)), 1);
}
});
}
return videoSsrcs;
}
/**
* Given a video mline (as parsed from transform.parse),
* and a primary ssrc, return the corresponding rtx ssrc
* (if there is one) for that video ssrc
* @param {object} videoMLine the video MLine from which to extract the
* rtx video ssrc
* @param {number} primarySsrc the video ssrc for which to find the
* corresponding rtx ssrc
* @returns {number} the rtx ssrc (or undefined if there isn't one)
*/
function getRtxSsrc (videoMLine, primarySsrc) {
if (videoMLine.ssrcGroups) {
let fidGroup = videoMLine.ssrcGroups.find(group => {
if (group.semantics === "FID") {
let groupPrimarySsrc = SDPUtil.parseGroupSsrcs(group)[0];
return groupPrimarySsrc === primarySsrc;
}
});
if (fidGroup) {
return SDPUtil.parseGroupSsrcs(fidGroup)[1];
}
}
}
/**
* Updates or inserts the appropriate rtx information for primarySsrc with
* the given rtxSsrc. If no rtx ssrc for primarySsrc currently exists, it will
* add the appropriate ssrc and ssrc group lines. If primarySsrc already has
* an rtx ssrc, the appropriate ssrc and group lines will be updated
* @param {object} videoMLine video mline object that will be updated (in place)
* @param {object} primarySsrcInfo the info (ssrc, msid & cname) for the
* @param {SdpTransformWrap} sdpTransformer the transformer instance which will
* be used to modify video media description
* @param {object} primarySsrcInfo the info (ssrc, msid & cname) for the
* primary ssrc
* @param {number} rtxSsrc the rtx ssrc to associate with the primary ssrc
*/
function updateAssociatedRtxStream (videoMLine, primarySsrcInfo, rtxSsrc) {
logger.debug("Updating mline to associate " + rtxSsrc +
function updateAssociatedRtxStream (sdpTransformer, primarySsrcInfo, rtxSsrc) {
logger.debug("Updating mline to associate " + rtxSsrc +
" rtx ssrc with primary stream ", primarySsrcInfo.id);
let primarySsrc = primarySsrcInfo.id;
let primarySsrcMsid = primarySsrcInfo.msid;
let primarySsrcCname = primarySsrcInfo.cname;
let previousAssociatedRtxStream =
getRtxSsrc (videoMLine, primarySsrc);
let previousAssociatedRtxStream
= sdpTransformer.getRtxSSRC(primarySsrc);
if (previousAssociatedRtxStream === rtxSsrc) {
logger.debug(rtxSsrc + " was already associated with " +
primarySsrc);
@@ -87,30 +36,23 @@ function updateAssociatedRtxStream (videoMLine, primarySsrcInfo, rtxSsrc) {
previousAssociatedRtxStream + ", removing all references to it");
// Stream already had an rtx ssrc that is different than the one given,
// remove all trace of the old one
videoMLine.ssrcs = videoMLine.ssrcs
.filter(ssrcInfo => ssrcInfo.id !== previousAssociatedRtxStream);
logger.debug("groups before filtering for " +
sdpTransformer.removeSSRC(previousAssociatedRtxStream);
logger.debug("groups before filtering for " +
previousAssociatedRtxStream);
logger.debug(JSON.stringify(videoMLine.ssrcGroups));
videoMLine.ssrcGroups = videoMLine.ssrcGroups
.filter(groupInfo => {
return groupInfo
.ssrcs
.indexOf(previousAssociatedRtxStream + "") === -1;
});
logger.debug(sdpTransformer.dumpSSRCGroups());
sdpTransformer.removeGroupsWithSSRC(previousAssociatedRtxStream);
}
videoMLine.ssrcs.push({
sdpTransformer.addSSRCAttribute({
id: rtxSsrc,
attribute: "cname",
value: primarySsrcCname
});
videoMLine.ssrcs.push({
sdpTransformer.addSSRCAttribute({
id: rtxSsrc,
attribute: "msid",
value: primarySsrcMsid
});
videoMLine.ssrcGroups = videoMLine.ssrcGroups || [];
videoMLine.ssrcGroups.push({
sdpTransformer.addSSRCGroup({
semantics: "FID",
ssrcs: primarySsrc + " " + rtxSsrc
});
@@ -163,62 +105,64 @@ export default class RtxModifier {
* @param {string} sdpStr sdp in raw string format
*/
modifyRtxSsrcs (sdpStr) {
let parsedSdp = transform.parse(sdpStr);
let videoMLine =
parsedSdp.media.find(mLine => mLine.type === "video");
if (videoMLine.direction === "inactive" ||
videoMLine.direction === "recvonly") {
const sdpTransformer = new SdpTransformWrap(sdpStr);
if (!sdpTransformer.selectMedia("video")) {
logger.error("No 'video' media found in the sdp: " + sdpStr);
return sdpStr;
}
const direction = sdpTransformer.mediaDirection;
if (direction === "inactive" || direction === "recvonly") {
logger.debug("RtxModifier doing nothing, video " +
"m line is inactive or recvonly");
return sdpStr;
}
if (!videoMLine.ssrcs) {
if (sdpTransformer.getSSRCCount() < 1) {
logger.debug("RtxModifier doing nothing, no video ssrcs present");
return sdpStr;
}
logger.debug("Current ssrc mapping: ", this.correspondingRtxSsrcs);
let primaryVideoSsrcs = getPrimaryVideoSsrcs(videoMLine);
let primaryVideoSsrcs = sdpTransformer.getPrimaryVideoSSRCs();
logger.debug("Parsed primary video ssrcs ", primaryVideoSsrcs, " " +
"making sure all have rtx streams");
primaryVideoSsrcs.forEach(ssrc => {
let msid = SDPUtil.getSsrcAttribute(videoMLine, ssrc, "msid");
let cname = SDPUtil.getSsrcAttribute(videoMLine, ssrc, "cname");
let msid = sdpTransformer.getSSRCAttrValue(ssrc, "msid");
let cname = sdpTransformer.getSSRCAttrValue(ssrc, "cname");
let correspondingRtxSsrc = this.correspondingRtxSsrcs.get(ssrc);
if (correspondingRtxSsrc) {
logger.debug("Already have an associated rtx ssrc for " +
" video ssrc " + ssrc + ": " +
" video ssrc " + ssrc + ": " +
correspondingRtxSsrc);
} else {
logger.debug("No previously associated rtx ssrc for " +
" video ssrc " + ssrc);
// If there's one in the sdp already for it, we'll just set
// that as the corresponding one
let previousAssociatedRtxStream =
getRtxSsrc (videoMLine, ssrc);
let previousAssociatedRtxStream
= sdpTransformer.getRtxSSRC(ssrc);
if (previousAssociatedRtxStream) {
logger.debug("Rtx stream " + previousAssociatedRtxStream +
logger.debug("Rtx stream " + previousAssociatedRtxStream +
" already existed in the sdp as an rtx stream for " +
ssrc);
correspondingRtxSsrc = previousAssociatedRtxStream;
} else {
correspondingRtxSsrc = SDPUtil.generateSsrc();
logger.debug("Generated rtx ssrc " + correspondingRtxSsrc +
logger.debug("Generated rtx ssrc " + correspondingRtxSsrc +
" for ssrc " + ssrc);
}
logger.debug("Caching rtx ssrc " + correspondingRtxSsrc +
logger.debug("Caching rtx ssrc " + correspondingRtxSsrc +
" for video ssrc " + ssrc);
this.correspondingRtxSsrcs.set(ssrc, correspondingRtxSsrc);
}
updateAssociatedRtxStream(
videoMLine,
sdpTransformer,
{
id: ssrc,
cname: cname,
msid: msid
},
correspondingRtxSsrc);
});
return transform.write(parsedSdp);
return sdpTransformer.toRawSDP();
}
/**
@@ -227,39 +171,35 @@ export default class RtxModifier {
* @returns {string} sdp string with all rtx streams stripped
*/
stripRtx (sdpStr) {
const parsedSdp = transform.parse(sdpStr);
const videoMLine =
parsedSdp.media.find(mLine => mLine.type === "video");
if (videoMLine.direction === "inactive" ||
videoMLine.direction === "recvonly") {
const sdpTransformer = new SdpTransformWrap(sdpStr);
if (!sdpTransformer.selectMedia("video")) {
logger.error("No 'video' media found in the sdp: " + sdpStr);
return sdpStr;
}
const direction = sdpTransformer.mediaDirection;
if (direction === "inactive" || direction === "recvonly") {
logger.debug("RtxModifier doing nothing, video " +
"m line is inactive or recvonly");
return sdpStr;
}
if (!videoMLine.ssrcs) {
if (sdpTransformer.getSSRCCount() < 1) {
logger.debug("RtxModifier doing nothing, no video ssrcs present");
return sdpStr;
}
if (!videoMLine.ssrcGroups) {
logger.debug("RtxModifier doing nothing, " +
if (!sdpTransformer.containsAnySSRCGroups()) {
logger.debug("RtxModifier doing nothing, " +
"no video ssrcGroups present");
return sdpStr;
}
const fidGroups = videoMLine.ssrcGroups
.filter(group => group.semantics === "FID");
const fidGroups = sdpTransformer.findGroups("FID");
// Remove the fid groups from the mline
videoMLine.ssrcGroups = videoMLine.ssrcGroups
.filter(group => group.semantics !== "FID");
sdpTransformer.removeGroupsBySemantics("FID");
// Get the rtx ssrcs and remove them from the mline
const ssrcsToRemove = [];
fidGroups.forEach(fidGroup => {
const groupSsrcs = SDPUtil.parseGroupSsrcs(fidGroup);
const rtxSsrc = groupSsrcs[1];
ssrcsToRemove.push(rtxSsrc);
const rtxSsrc = parseSecondarySSRC(fidGroup);
sdpTransformer.removeSSRC(rtxSsrc);
});
videoMLine.ssrcs = videoMLine.ssrcs
.filter(line => ssrcsToRemove.indexOf(line.id) === -1);
return transform.write(parsedSdp);
return sdpTransformer.toRawSDP();
}
}
Oops, something went wrong.

0 comments on commit 4526971

Please sign in to comment.