diff --git a/index.html b/index.html
index 6936292f4..35d4a34d1 100644
--- a/index.html
+++ b/index.html
@@ -19,7 +19,7 @@
-
+
diff --git a/libs/app.bundle.js b/libs/app.bundle.js
index c760706ba..147856e90 100644
--- a/libs/app.bundle.js
+++ b/libs/app.bundle.js
@@ -25733,11 +25733,28 @@ Interop.prototype.toPlanB = function(desc) {
// Assign the sources to the channel.
channels[mLine.type].sources[ssrc] = mLine.sources[ssrc];
- // In Plan B the msid is an SSRC attribute.
+ // In Plan B the msid is an SSRC attribute. Also, we don't care
+ // about the obsolete label and mslabel attributes.
channels[mLine.type].sources[ssrc].msid = mLine.msid;
+
+ // NOTE ssrcs in ssrc groups will share msids, as
+ // draft-uberti-rtcweb-plan-00 mandates.
});
}
+ // Add ssrc groups to the channel.
+ if (typeof mLine.ssrcGroups !== 'undefined' &&
+ Array.isArray(mLine.ssrcGroups)) {
+
+ // Create the ssrcGroups array, if it's not defined.
+ if (typeof channel.ssrcGroups === 'undefined' ||
+ !Array.isArray(channel.ssrcGroups)) {
+ channel.ssrcGroups = [];
+ }
+
+ channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups);
+ }
+
if (channels[mLine.type] === mLine) {
// Copy ICE related stuff from the principal media line.
mLine.candidates = media[0].candidates;
@@ -25760,8 +25777,6 @@ Interop.prototype.toPlanB = function(desc) {
// Add the channel to the new media array.
session.media.push(mLine);
}
-
- // TODO(gp) add support for ssrc-group.
});
// We regenerate the BUNDLE group with the new mids.
@@ -25888,6 +25903,7 @@ Interop.prototype.toPlanA = function(desc) {
// With rtcp-mux and bundle all the channels should have the same ICE
// stuff.
var sources = channel.sources;
+ var ssrcGroups = channel.ssrcGroups;
var candidates = channel.candidates;
var iceUfrag = channel.iceUfrag;
var icePwd = channel.icePwd;
@@ -25897,6 +25913,7 @@ Interop.prototype.toPlanA = function(desc) {
// We'll use the "channel" object as a prototype for each new "mLine"
// that we create, but first we need to clean it up a bit.
delete channel.sources;
+ delete channel.ssrcGroups;
delete channel.candidates;
delete channel.iceUfrag;
delete channel.icePwd;
@@ -25904,13 +25921,63 @@ Interop.prototype.toPlanA = function(desc) {
delete channel.port;
delete channel.mid;
+ // inverted ssrc group map
+ var invertedGroups = {};
+ if (typeof ssrcGroups !== 'undefined' && Array.isArray(ssrcGroups)) {
+ ssrcGroups.forEach(function (ssrcGroup) {
+
+ // TODO(gp) find out how to receive simulcast with FF. For the
+ // time being, hide it.
+ if (ssrcGroup.semantics === 'SIM') {
+ return;
+ }
+
+ if (typeof ssrcGroup.ssrcs !== 'undefined' &&
+ Array.isArray(ssrcGroup.ssrcs)) {
+ ssrcGroup.ssrcs.forEach(function (ssrc) {
+ if (typeof invertedGroups[ssrc] === 'undefined') {
+ invertedGroups[ssrc] = [];
+ }
+
+ invertedGroups[ssrc].push(ssrcGroup);
+ });
+ }
+ });
+ }
+
+ // ssrc to m-line index.
+ var mLines = {};
+
if (typeof sources === 'object') {
// Explode the Plan B channel sources with one m-line per source.
Object.keys(sources).forEach(function(ssrc) {
+ var mLine;
+ if (typeof invertedGroups[ssrc] !== 'undefined' &&
+ Array.isArray(invertedGroups[ssrc])) {
+ invertedGroups[ssrc].every(function (ssrcGroup) {
+ // ssrcGroup.ssrcs *is* an Array, no need to check
+ // again here.
+ return ssrcGroup.ssrcs.every(function (related) {
+ if (typeof mLines[related] === 'object') {
+ mLine = mLines[related];
+ return false;
+ } else {
+ return true;
+ }
+ });
+ });
+ }
+
+ if (typeof mLine === 'object') {
+ // the m-line already exists. Just add the source.
+ mLine.sources[ssrc] = sources[ssrc];
+ delete sources[ssrc].msid;
+ } else {
// Use the "channel" as a prototype for the "mLine".
- var mLine = Object.create(channel);
+ mLine = Object.create(channel);
+ mLines[ssrc] = mLine;
// Assign the msid of the source to the m-line.
mLine.msid = sources[ssrc].msid;
@@ -25919,6 +25986,7 @@ Interop.prototype.toPlanA = function(desc) {
// We assign one SSRC per media line.
mLine.sources = {};
mLine.sources[ssrc] = sources[ssrc];
+ mLine.ssrcGroups = invertedGroups[ssrc];
// Use the cached Plan A SDP (if it exists) to assign SSRCs to
// mids.
@@ -25966,9 +26034,9 @@ Interop.prototype.toPlanA = function(desc) {
mLine.port = port;
media[mLine.mid] = mLine;
+ }
});
}
- // TODO(gp) add support for ssrc-groups
});
// Rebuild the media array in the right order and add the missing mLines
@@ -26047,7 +26115,7 @@ Interop.prototype.toPlanA = function(desc) {
} else {
delete pm.msid;
delete pm.sources;
- // TODO(gp) delete ssrc-groups
+ delete pm.ssrcGroups;
pm.direction = 'recvonly';
session.media.push(pm);
}
@@ -26097,12 +26165,12 @@ var transform = require('sdp-transform');
exports.write = function(session, opts) {
- // expand sources to ssrcs
if (typeof session !== 'undefined' &&
typeof session.media !== 'undefined' &&
Array.isArray(session.media)) {
session.media.forEach(function (mLine) {
+ // expand sources to ssrcs
if (typeof mLine.sources !== 'undefined' &&
Object.keys(mLine.sources).length !== 0) {
mLine.ssrcs = [];
@@ -26118,6 +26186,17 @@ exports.write = function(session, opts) {
});
delete mLine.sources;
}
+
+ // join ssrcs in ssrc groups
+ if (typeof mLine.ssrcGroups !== 'undefined' &&
+ Array.isArray(mLine.ssrcGroups)) {
+ mLine.ssrcGroups.forEach(function (ssrcGroup) {
+ if (typeof ssrcGroup.ssrcs !== 'undefined' &&
+ Array.isArray(ssrcGroup.ssrcs)) {
+ ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');
+ }
+ });
+ }
});
}
@@ -26141,8 +26220,8 @@ exports.parse = function(sdp) {
if (typeof session !== 'undefined' && typeof session.media !== 'undefined' &&
Array.isArray(session.media)) {
- // group sources attributes by ssrc
session.media.forEach(function (mLine) {
+ // group sources attributes by ssrc
if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
mLine.sources = {};
mLine.ssrcs.forEach(function (ssrc) {
@@ -26153,6 +26232,16 @@ exports.parse = function(sdp) {
delete mLine.ssrcs;
}
+
+ // split ssrcs in ssrc groups
+ if (typeof mLine.ssrcGroups !== 'undefined' &&
+ Array.isArray(mLine.ssrcGroups)) {
+ mLine.ssrcGroups.forEach(function (ssrcGroup) {
+ if (typeof ssrcGroup.ssrcs === 'string') {
+ ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');
+ }
+ });
+ }
});
}
// split group mids
@@ -26316,10 +26405,6 @@ var grammar = module.exports = {
name: 'direction',
reg: /^(sendrecv|recvonly|sendonly|inactive)/
},
- { //a=bundle-only
- name: 'bundleOnly',
- reg: /^(bundle-only)/
- },
{ //a=ice-lite
name: 'icelite',
reg: /^(ice-lite)/
@@ -26373,6 +26458,12 @@ var grammar = module.exports = {
names: ['id', 'attribute', 'value'],
format: "ssrc:%d %s:%s"
},
+ { //a=ssrc-group:FEC 1 2
+ push: "ssrcGroups",
+ reg: /^ssrc-group:(\w*) (.*)/,
+ names: ['semantics', 'ssrcs'],
+ format: "ssrc-group:%s %s"
+ },
{ //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV
name: "msidSemantic",
reg: /^msid-semantic:\s?(\w*) (\S*)/,
@@ -26482,30 +26573,6 @@ exports.parse = function (sdp) {
});
session.media = media; // link it up
-
- // group sources attributes by ssrc
- media.forEach(function (mLine) {
- if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {
- mLine.sources = {};
- mLine.ssrcs.forEach(function (ssrc) {
- if (!mLine.sources[ssrc.id])
- mLine.sources[ssrc.id] = {};
- mLine.sources[ssrc.id][ssrc.attribute] = ssrc.value;
- });
-
- delete mLine.ssrcs;
- }
- });
-
- // split group mids
- if (typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
- session.groups.forEach(function (g) {
- if (typeof g.mids === 'string') {
- g.mids = g.mids.split(' ');
- }
- });
- }
-
return session;
};
@@ -26613,29 +26680,8 @@ module.exports = function (session, opts) {
if (mLine.payloads == null) {
mLine.payloads = "";
}
-
- // expand sources to ssrcs
- if (typeof mLine.sources !== 'undefined' && Object.keys(mLine.sources).length !== 0) {
- mLine.ssrcs = [];
- Object.keys(mLine.sources).forEach(function (ssrc) {
- var source = mLine.sources[ssrc];
- Object.keys(source).forEach(function (attribute) {
- mLine.ssrcs.push({id: ssrc, attribute: attribute, value: source[attribute]})
- });
- });
- delete mLine.sources;
- }
});
- // join group mids
- if (typeof session.groups !== 'undefined' && Array.isArray(session.groups)) {
- session.groups.forEach(function (g) {
- if (typeof g.mids !== 'undefined' && Array.isArray(g.mids)) {
- g.mids = g.mids.join(' ');
- }
- });
- }
-
var outerOrder = opts.outerOrder || defaultOuterOrder;
var innerOrder = opts.innerOrder || defaultInnerOrder;
var sdp = [];
@@ -26643,10 +26689,10 @@ module.exports = function (session, opts) {
// loop through outerOrder for matching properties on session
outerOrder.forEach(function (type) {
grammar[type].forEach(function (obj) {
- if (obj.name in session && typeof session[obj.name] !== 'undefined') {
+ if (obj.name in session && session[obj.name] != null) {
sdp.push(makeLine(type, obj, session));
}
- else if (obj.push in session && typeof session[obj.push] !== 'undefined') {
+ else if (obj.push in session && session[obj.push] != null) {
session[obj.push].forEach(function (el) {
sdp.push(makeLine(type, obj, el));
});
@@ -26660,10 +26706,10 @@ module.exports = function (session, opts) {
innerOrder.forEach(function (type) {
grammar[type].forEach(function (obj) {
- if (obj.name in mLine && typeof mLine[obj.name] !== 'undefined') {
+ if (obj.name in mLine && mLine[obj.name] != null) {
sdp.push(makeLine(type, obj, mLine));
}
- else if (obj.push in mLine && typeof mLine[obj.push] !== 'undefined') {
+ else if (obj.push in mLine && mLine[obj.push] != null) {
mLine[obj.push].forEach(function (el) {
sdp.push(makeLine(type, obj, el));
});