twilio-video
Version:
Twilio Video JavaScript Library
262 lines • 10.3 kB
JavaScript
;
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var _a = require('../'), difference = _a.difference, flatMap = _a.flatMap;
/**
* Create a random {@link SSRC}.
* @returns {SSRC}
*/
function createSSRC() {
var ssrcMax = 0xffffffff;
return String(Math.floor(Math.random() * ssrcMax));
}
/**
* @property {string} cName
* @property {boolean} isSimulcastEnabled
* @property {Map<RtxSSRC, PrimarySSRC>} rtxPairs
* @property {Set<PrimarySSRC>} primarySSRCs
* @property {string} streamId
* @property {Track.ID} trackId
*/
var TrackAttributes = /** @class */ (function () {
/**
* Construct a {@link MediaStreamTrack} attribute store.
* @param {Track.ID} trackId - The MediaStreamTrack ID
* @param {MediaStreamID} streamId - The MediaStream ID
* @param {string} cName - The MediaStream cname
*/
function TrackAttributes(trackId, streamId, cName) {
Object.defineProperties(this, {
cName: {
enumerable: true,
value: cName
},
isSimulcastEnabled: {
enumerable: true,
value: false,
writable: true
},
primarySSRCs: {
enumerable: true,
value: new Set()
},
rtxPairs: {
enumerable: true,
value: new Map()
},
streamId: {
enumerable: true,
value: streamId
},
trackId: {
enumerable: true,
value: trackId
}
});
}
/**
* Add {@link SimSSRC}s to the {@link TrackAttributes}.
* @returns {void}
*/
TrackAttributes.prototype.addSimulcastSSRCs = function () {
if (this.isSimulcastEnabled) {
return;
}
var simulcastSSRCs = [createSSRC(), createSSRC()];
simulcastSSRCs.forEach(function (ssrc) {
this.primarySSRCs.add(ssrc);
}, this);
if (this.rtxPairs.size) {
simulcastSSRCs.forEach(function (ssrc) {
this.rtxPairs.set(createSSRC(), ssrc);
}, this);
}
};
/**
* Add the given {@link PrimarySSRC} or {@link RtxSSRC} to the {@link TrackAttributes}
* and update the "isSimulcastEnabled" flag if it is also a {@link SimSSRC}.
* @param {SSRC} ssrc - The {@link SSRC} to be added
* @param {?PrimarySSRC} primarySSRC - The {@link PrimarySSRC}; if the given
* {@link SSRC} itself is the {@link PrimarySSRC}, then this is set to null
* @param {boolean} isSimSSRC - true if the given {@link SSRC} is a
* {@link SimSSRC}; false otherwise
* @returns {void}
*/
TrackAttributes.prototype.addSSRC = function (ssrc, primarySSRC, isSimSSRC) {
if (primarySSRC) {
this.rtxPairs.set(ssrc, primarySSRC);
}
else {
this.primarySSRCs.add(ssrc);
}
this.isSimulcastEnabled = this.isSimulcastEnabled || isSimSSRC;
};
/**
* Construct the SDP lines for the {@link TrackAttributes}.
* @param {boolean} [excludeRtx=false]
* @returns {Array<string>} Array of SDP lines
*/
TrackAttributes.prototype.toSdpLines = function (excludeRtx) {
var _this = this;
var rtxPairs = excludeRtx
? []
: Array.from(this.rtxPairs.entries()).map(function (rtxPair) { return rtxPair.reverse(); });
var simSSRCs = Array.from(this.primarySSRCs.values());
var ssrcs = rtxPairs.length ? flatMap(rtxPairs) : simSSRCs;
var attrLines = flatMap(ssrcs, function (ssrc) { return [
"a=ssrc:".concat(ssrc, " cname:").concat(_this.cName),
"a=ssrc:".concat(ssrc, " msid:").concat(_this.streamId, " ").concat(_this.trackId)
]; });
var rtxPairLines = rtxPairs.map(function (rtxPair) { return "a=ssrc-group:FID ".concat(rtxPair.join(' ')); });
var simGroupLines = [
"a=ssrc-group:SIM ".concat(simSSRCs.join(' '))
];
return rtxPairLines.concat(attrLines).concat(simGroupLines);
};
return TrackAttributes;
}());
/**
* Get the matches for a given RegEx pattern.
* @param {string} section - SDP media section
* @param {string} pattern - RegEx pattern
* @returns {Array<Array<string>>} - Array of pattern matches
*/
function getMatches(section, pattern) {
var matches = section.match(new RegExp(pattern, 'gm')) || [];
return matches.map(function (match) {
var matches = match.match(new RegExp(pattern)) || [];
return matches.slice(1);
});
}
/**
* Get the {@link SimSSRC}s that belong to a simulcast group.
* @param {string} section - SDP media section
* @returns {Set<SimSSRC>} Set of simulcast {@link SSRC}s
*/
function getSimulcastSSRCs(section) {
var simGroupPattern = '^a=ssrc-group:SIM ([0-9]+) ([0-9]+) ([0-9]+)$';
return new Set(flatMap(getMatches(section, simGroupPattern)));
}
/**
* Get the value of the given attribute for an SSRC.
* @param {string} section - SDP media section
* @param {SSRC} ssrc - {@link SSRC} whose attribute's value is to be determinded
* @param {string} attribute - {@link SSRC} attribute name
* @param {string} - {@link SSRC} attribute value
*/
function getSSRCAttribute(section, ssrc, attribute) {
var pattern = "a=ssrc:".concat(ssrc, " ").concat(attribute, ":(.+)");
return section.match(new RegExp(pattern))[1];
}
/**
* Create a Map of {@link PrimarySSRC}s and their {@link RtxSSRC}s.
* @param {string} section - SDP media section
* @returns {Map<RtxSSRC, PrimarySSRC>} - Map of {@link RtxSSRC}s and their
* corresponding {@link PrimarySSRC}s
*/
function getSSRCRtxPairs(section) {
var rtxPairPattern = '^a=ssrc-group:FID ([0-9]+) ([0-9]+)$';
return new Map(getMatches(section, rtxPairPattern).map(function (pair) { return pair.reverse(); }));
}
/**
* Create SSRC attribute tuples.
* @param {string} section
* @returns {Array<[SSRC, MediaStreamID, Track.ID]>}
*/
function createSSRCAttributeTuples(section) {
var _a = __read(flatMap(getMatches(section, '^a=msid:(.+) (.+)$')), 2), streamId = _a[0], trackId = _a[1];
var ssrcs = flatMap(getMatches(section, '^a=ssrc:(.+) cname:.+$'));
return ssrcs.map(function (ssrc) { return [ssrc, streamId, trackId]; });
}
/**
* Create a Map of MediaStreamTrack IDs and their {@link TrackAttributes}.
* @param {string} section - SDP media section
* @returns {Map<Track.ID, TrackAttributes>}
*/
function createTrackIdsToAttributes(section) {
var simSSRCs = getSimulcastSSRCs(section);
var rtxPairs = getSSRCRtxPairs(section);
var ssrcAttrTuples = createSSRCAttributeTuples(section);
return ssrcAttrTuples.reduce(function (trackIdsToSSRCs, tuple) {
var ssrc = tuple[0];
var streamId = tuple[1];
var trackId = tuple[2];
var trackAttributes = trackIdsToSSRCs.get(trackId) || new TrackAttributes(trackId, streamId, getSSRCAttribute(section, ssrc, 'cname'));
var primarySSRC = rtxPairs.get(ssrc) || null;
trackAttributes.addSSRC(ssrc, primarySSRC, simSSRCs.has(ssrc));
return trackIdsToSSRCs.set(trackId, trackAttributes);
}, new Map());
}
/**
* Apply simulcast settings to the given SDP media section.
* @param {string} section - SDP media section
* @param {Map<Track.ID, TrackAttributes>} trackIdsToAttributes - Existing
* map which will be updated for new MediaStreamTrack IDs
* @returns {string} - The transformed SDP media section
*/
function setSimulcastInMediaSection(section, trackIdsToAttributes) {
var newTrackIdsToAttributes = createTrackIdsToAttributes(section);
var newTrackIds = Array.from(newTrackIdsToAttributes.keys());
var trackIds = Array.from(trackIdsToAttributes.keys());
var trackIdsToAdd = difference(newTrackIds, trackIds);
var trackIdsToIgnore = difference(trackIds, newTrackIds);
// Update "trackIdsToAttributes" with TrackAttributes for new
// MediaStreamTrack IDs.
var trackAttributesToAdd = flatMap(trackIdsToAdd, function (trackId) { return newTrackIdsToAttributes.get(trackId); });
trackAttributesToAdd.forEach(function (trackAttributes) {
trackAttributes.addSimulcastSSRCs();
trackIdsToAttributes.set(trackAttributes.trackId, trackAttributes);
});
// Get the SDP lines of the relevant MediaStreamTrack IDs from
// "trackIdsToAttributes".
trackIds = Array.from(trackIdsToAttributes.keys());
var relevantTrackIds = difference(trackIds, trackIdsToIgnore);
var relevantTrackAttributes = flatMap(relevantTrackIds, function (trackId) { return trackIdsToAttributes.get(trackId); });
var excludeRtx = !section.match(/a=rtpmap:[0-9]+ rtx/);
var relevantSdpLines = flatMap(relevantTrackAttributes, function (trackAttributes) { return trackAttributes.toSdpLines(excludeRtx); });
// Add the simulcast SSRC SDP lines to the media section. The Set ensures
// that the duplicates of the SSRC SDP lines that are in both "section" and
// "relevantSdpLines" are removed.
var sectionLines = flatMap(new Set(section.split('\r\n').concat(relevantSdpLines)));
var xGoogleFlagConference = 'a=x-google-flag:conference';
if (!section.match(xGoogleFlagConference)) {
sectionLines.push(xGoogleFlagConference);
}
return sectionLines.join('\r\n');
}
/**
* String representing a MediaStream ID.
* @typedef {string} MediaStreamID
*/
/**
* String representing the SSRC of a MediaStreamTrack.
* @typedef {string} SSRC
*/
/**
* Primary SSRC.
* @typedef {SSRC} PrimarySSRC
*/
/**
* Retransmission SSRC.
* @typedef {SSRC} RtxSSRC
*/
/**
* Simulcast SSRC.
* @typedef {SSRC} SimSSRC
*/
module.exports = setSimulcastInMediaSection;
//# sourceMappingURL=simulcast.js.map