diff --git a/index.html b/index.html
index 34de5c15e..f8d10c785 100644
--- a/index.html
+++ b/index.html
@@ -19,7 +19,7 @@
-
+
diff --git a/libs/app.bundle.js b/libs/app.bundle.js
index 529f7b839..1030b3000 100644
--- a/libs/app.bundle.js
+++ b/libs/app.bundle.js
@@ -705,28 +705,26 @@ var eventEmitter = new EventEmitter();
function getMediaStreamUsage()
{
var result = {
- audio: 1,
- video: 1
+ audio: true,
+ video: true
};
- if( config.startAudioMuted === true)
- result.audio = 0;
- if( config.startVideoMuted === true)
- result.video = 0;
/** There are some issues with the desktop sharing
* when this property is enabled.
+ * WARNING: We must change the implementation to start video/audio if we
+ * receive from the focus that the peer is not muted.
- if(result.audio > 0 && result.video > 0)
- return result;
var isSecureConnection = window.location.protocol == "https:";
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
{
- if(result.audio === 0)
- result.audio = -1;
- if(result.video === 0)
- result.video = -1;
- }**/
+ result = {
+ audio: false,
+ video: false
+ };
+
+ }
+ **/
return result;
}
@@ -1276,7 +1274,9 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
* We ask for audio and video combined stream in order to get permissions and
* not to ask twice.
*/
-RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) {
+RTCUtils.prototype.obtainAudioAndVideoPermissions =
+ function(devices, callback, usageOptions)
+{
var self = this;
// Get AV
@@ -1297,7 +1297,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback,
for(var i = 0; i < devices.length; i++)
{
var device = devices[i];
- if(usageOptions[device] !== -1)
+ if(usageOptions[device] === true)
newDevices.push(device);
}
else
@@ -1431,11 +1431,12 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
videoStream = stream.videoStream;
}
- var audioMuted = (usageOptions && usageOptions.audio != 1),
- videoMuted = (usageOptions && usageOptions.video != 1);
+ var audioMuted = (usageOptions && usageOptions.audio === false),
+ videoMuted = (usageOptions && usageOptions.video === false);
+
+ var audioGUM = (!usageOptions || usageOptions.audio !== false),
+ videoGUM = (!usageOptions || usageOptions.video !== false);
- var audioGUM = (!usageOptions || usageOptions.audio != -1),
- videoGUM = (!usageOptions || usageOptions.video != -1);
this.service.createLocalStream(audioStream, "audio", null, null,
audioMuted, audioGUM);
@@ -1505,10 +1506,8 @@ var roomName = null;
function notifyForInitialMute()
{
- if(config.startAudioMuted || config.startVideoMuted)
- {
- messageHandler.notify(null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000});
- }
+ messageHandler.notify(null, "notify.mutedTitle", "connected",
+ "notify.muted", null, {timeOut: 120000});
}
function setupPrezi()
@@ -1725,6 +1724,9 @@ function registerListeners() {
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
onDtmfSupportChanged);
+ APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
+ SettingsMenu.setStartMuted(audio, video);
+ });
}
@@ -1879,8 +1881,6 @@ UI.start = function (init) {
SettingsMenu.init();
- notifyForInitialMute();
-
};
function chatAddError(errorMessage, originalText)
@@ -1956,6 +1956,7 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
console.info("My role changed, new role: " + info.role);
onModeratorStatusChanged(isModerator);
VideoLayout.showModeratorIndicator();
+ SettingsMenu.onRoleChanged();
if (isModerator) {
Authentication.closeAuthenticationWindow();
@@ -2200,6 +2201,12 @@ UI.getRoomName = function () {
return roomName;
};
+UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
+ if(muteAudio || muteVideo) notifyForInitialMute();
+ if(muteAudio) UI.setAudioMuted(true);
+ if(muteVideo) UI.setVideoMute(true);
+}
+
/**
* Mutes/unmutes the local video.
*/
@@ -5090,7 +5097,7 @@ function generateLanguagesSelectBox()
var SettingsMenu = {
init: function () {
- $("#updateSettings").before(generateLanguagesSelectBox());
+ $("#startMutedOptions").before(generateLanguagesSelectBox());
APP.translation.translateElement($("#languages_selectbox"));
$('#settingsmenu>input').keyup(function(event){
if(event.keyCode === 13) {//enter
@@ -5098,11 +5105,36 @@ var SettingsMenu = {
}
});
+ if(APP.xmpp.isModerator())
+ {
+ $("#startMutedOptions").css("display", "block");
+ }
+ else
+ {
+ $("#startMutedOptions").css("display", "none");
+ }
+
$("#updateSettings").click(function () {
SettingsMenu.update();
});
},
+ onRoleChanged: function () {
+ if(APP.xmpp.isModerator())
+ {
+ $("#startMutedOptions").css("display", "block");
+ }
+ else
+ {
+ $("#startMutedOptions").css("display", "none");
+ }
+ },
+
+ setStartMuted: function (audio, video) {
+ $("#startAudioMuted").attr("checked", audio);
+ $("#startVideoMuted").attr("checked", video);
+ },
+
update: function() {
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
@@ -5119,6 +5151,10 @@ var SettingsMenu = {
APP.xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(newEmail);
+ var startAudioMuted = ($("#startAudioMuted").is(":checked"));
+ var startVideoMuted = ($("#startVideoMuted").is(":checked"));
+ APP.xmpp.addToPresence("startMuted",
+ [startAudioMuted, startVideoMuted]);
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
},
@@ -15638,6 +15674,20 @@ var Moderator = {
{ name: 'openSctp', value: config.openSctp})
.up();
}
+ if(config.startAudioMuted !== undefined)
+ {
+ elem.c(
+ 'property',
+ { name: 'startAudioMuted', value: config.startAudioMuted})
+ .up();
+ }
+ if(config.startVideoMuted !== undefined)
+ {
+ elem.c(
+ 'property',
+ { name: 'startVideoMuted', value: config.startVideoMuted})
+ .up();
+ }
elem.up();
return elem;
},
@@ -16187,6 +16237,13 @@ module.exports = function(XMPP, eventEmitter) {
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
}
+ var startMuted = $(pres).find('>startmuted');
+ if (startMuted.length)
+ {
+ eventEmitter.emit(XMPPEvents.START_MUTED,
+ startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
+ }
+
var devices = $(pres).find('>devices');
if(devices.length)
{
@@ -16541,6 +16598,15 @@ module.exports = function(XMPP, eventEmitter) {
|| 'sendrecv' }
).up();
}
+ pres.up();
+ }
+
+ if(this.presMap["startMuted"] !== undefined)
+ {
+ pres.c("startmuted", {audio: this.presMap["startMuted"].audio,
+ video: this.presMap["startMuted"].video,
+ xmlns: "http://jitsi.org/jitmeet/start-muted"});
+ delete this.presMap["startMuted"];
}
pres.up();
@@ -16621,6 +16687,9 @@ module.exports = function(XMPP, eventEmitter) {
addUserIdToPresence: function (userId) {
this.presMap['userId'] = userId;
},
+ addStartMutedToPresence: function (audio, video) {
+ this.presMap["startMuted"] = {audio: audio, video: video};
+ },
isModerator: function () {
return this.role === 'moderator';
},
@@ -16808,6 +16877,14 @@ module.exports = function(XMPP, eventEmitter)
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
switch (action) {
case 'session-initiate':
+ var startMuted = $(iq).find('jingle>startmuted');
+ if(startMuted && startMuted.length > 0)
+ {
+ var audioMuted = startMuted.attr("audio");
+ var videoMuted = startMuted.attr("video");
+ APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
+ (videoMuted === "true"));
+ }
sess = new JingleSession(
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
this.connection, XMPP);
@@ -17450,8 +17527,13 @@ function registerListeners() {
});
}
-function setupEvents() {
- $(window).bind('beforeunload', function () {
+var unload = (function () {
+ var unloaded = false;
+
+ return function () {
+ if (unloaded) { return; }
+ unloaded = true;
+
if (connection && connection.connected) {
// ensure signout
$.ajax({
@@ -17460,24 +17542,41 @@ function setupEvents() {
async: false,
cache: false,
contentType: 'application/xml',
- data: "
" +
- "" +
- "",
+ data: "" +
+ "" +
+ "",
success: function (data) {
console.log('signed out');
console.log(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log('signout error',
- textStatus + ' (' + errorThrown + ')');
+ textStatus + ' (' + errorThrown + ')');
}
});
}
XMPP.disposeConference(true);
- });
+ };
+})();
+
+function setupEvents() {
+ // In recent versions of FF the 'beforeunload' event is not fired when the
+ // window or the tab is closed. It is only fired when we leave the page
+ // (change URL). If this participant doesn't unload properly, then it
+ // becomes a ghost for the rest of the participants that stay in the
+ // conference. Thankfully handling the 'unload' event in addition to the
+ // 'beforeunload' event seems to garante the execution of the 'unload'
+ // method at least once.
+ //
+ // The 'unload' method can safely be run multiple times, it will actually do
+ // something only the first time that it's run, so we're don't have to worry
+ // about browsers that fire both events.
+
+ $(window).bind('beforeunload', unload);
+ $(window).bind('unload', unload);
}
var XMPP = {
@@ -17741,6 +17840,12 @@ var XMPP = {
case "devices":
connection.emuc.addDevicesToPresence(value);
break;
+ case "startMuted":
+ if(!Moderator.isModerator())
+ return;
+ connection.emuc.addStartMutedToPresence(value[0],
+ value[1]);
+ break;
default :
console.log("Unknown tag for presence: " + name);
return;
@@ -28054,6 +28159,8 @@ module.exports = function arrayEquals(array) {
exports.Interop = require('./interop');
},{"./interop":88}],88:[function(require,module,exports){
+"use strict";
+
var transform = require('./transform');
var arrayEquals = require('./array-equals');
@@ -28122,46 +28229,47 @@ Interop.prototype.toPlanB = function(desc) {
session.media = [];
// Associative array that maps channel types to channel objects for fast
- // access to channel objects by their type, e.g. channels['audio']->channel
+ // access to channel objects by their type, e.g. type2bl['audio']->channel
// obj.
- var channels = {};
+ var type2bl = {};
// Used to build the group:BUNDLE value after the channels construction
// loop.
var types = [];
- // Implode the Unified Plan m-lines/tracks into Plan B "channels".
- media.forEach(function(mLine) {
+ // Implode the Unified Plan m-lines/tracks into Plan B channels.
+ media.forEach(function(unifiedLine) {
// rtcp-mux is required in the Plan B SDP.
- if (typeof mLine.rtcpMux !== 'string' ||
- mLine.rtcpMux !== 'rtcp-mux') {
+ if ((typeof unifiedLine.rtcpMux !== 'string' ||
+ unifiedLine.rtcpMux !== 'rtcp-mux') &&
+ unifiedLine.direction !== 'inactive') {
throw new Error('Cannot convert to Plan B because m-lines ' +
'without the rtcp-mux attribute were found.');
}
- // If we don't have a channel for this mLine.type, then use this mLine
+ // If we don't have a channel for this unifiedLine.type, then use this unifiedLine
// as the channel basis.
- if (typeof channels[mLine.type] === 'undefined') {
- channels[mLine.type] = mLine;
+ if (typeof type2bl[unifiedLine.type] === 'undefined') {
+ type2bl[unifiedLine.type] = unifiedLine;
}
// Add sources to the channel and handle a=msid.
- if (typeof mLine.sources === 'object') {
- Object.keys(mLine.sources).forEach(function(ssrc) {
- if (typeof channels[mLine.type].sources !== 'object')
- channels[mLine.type].sources = {};
+ if (typeof unifiedLine.sources === 'object') {
+ Object.keys(unifiedLine.sources).forEach(function(ssrc) {
+ if (typeof type2bl[unifiedLine.type].sources !== 'object')
+ type2bl[unifiedLine.type].sources = {};
// Assign the sources to the channel.
- channels[mLine.type].sources[ssrc] = mLine.sources[ssrc];
+ type2bl[unifiedLine.type].sources[ssrc] = unifiedLine.sources[ssrc];
- if (typeof mLine.msid !== 'undefined') {
+ if (typeof unifiedLine.msid !== 'undefined') {
// In Plan B the msid is an SSRC attribute. Also, we don't
// care about the obsolete label and mslabel attributes.
//
- // Note that it is not guaranteed that the mLine will have
+ // Note that it is not guaranteed that the unifiedLine will have
// an msid. recvonly channels in particular don't have one.
- channels[mLine.type].sources[ssrc].msid = mLine.msid;
+ type2bl[unifiedLine.type].sources[ssrc].msid = unifiedLine.msid;
}
// NOTE ssrcs in ssrc groups will share msids, as
// draft-uberti-rtcweb-plan-00 mandates.
@@ -28169,39 +28277,39 @@ Interop.prototype.toPlanB = function(desc) {
}
// Add ssrc groups to the channel.
- if (typeof mLine.ssrcGroups !== 'undefined' &&
- Array.isArray(mLine.ssrcGroups)) {
+ if (typeof unifiedLine.ssrcGroups !== 'undefined' &&
+ Array.isArray(unifiedLine.ssrcGroups)) {
// Create the ssrcGroups array, if it's not defined.
- if (typeof channel.ssrcGroups === 'undefined' ||
- !Array.isArray(channel.ssrcGroups)) {
- channel.ssrcGroups = [];
+ if (typeof type2bl[unifiedLine.type].ssrcGroups === 'undefined' ||
+ !Array.isArray(type2bl[unifiedLine.type].ssrcGroups)) {
+ type2bl[unifiedLine.type].ssrcGroups = [];
}
- channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups);
+ type2bl[unifiedLine.type].ssrcGroups = type2bl[unifiedLine.type].ssrcGroups.concat(unifiedLine.ssrcGroups);
}
- if (channels[mLine.type] === mLine) {
+ if (type2bl[unifiedLine.type] === unifiedLine) {
// Copy ICE related stuff from the principal media line.
- mLine.candidates = media[0].candidates;
- mLine.iceUfrag = media[0].iceUfrag;
- mLine.icePwd = media[0].icePwd;
- mLine.fingerprint = media[0].fingerprint;
+ unifiedLine.candidates = media[0].candidates;
+ unifiedLine.iceUfrag = media[0].iceUfrag;
+ unifiedLine.icePwd = media[0].icePwd;
+ unifiedLine.fingerprint = media[0].fingerprint;
// Plan B mids are in ['audio', 'video', 'data']
- mLine.mid = mLine.type;
+ unifiedLine.mid = unifiedLine.type;
// Plan B doesn't support/need the bundle-only attribute.
- delete mLine.bundleOnly;
+ delete unifiedLine.bundleOnly;
// In Plan B the msid is an SSRC attribute.
- delete mLine.msid;
+ delete unifiedLine.msid;
// Used to build the group:BUNDLE value after this loop.
- types.push(mLine.type);
+ types.push(unifiedLine.type);
// Add the channel to the new media array.
- session.media.push(mLine);
+ session.media.push(unifiedLine);
}
});
@@ -28317,34 +28425,35 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// A helper map that sends mids to m-line objects. We use it later to
// rebuild the Unified Plan style session.media array.
- var mid2ml = {};
- session.media.forEach(function(channel) {
- if (typeof channel.rtcpMux !== 'string' ||
- channel.rtcpMux !== 'rtcp-mux') {
+ var mid2ul = {};
+ session.media.forEach(function(bLine) {
+ if ((typeof bLine.rtcpMux !== 'string' ||
+ bLine.rtcpMux !== 'rtcp-mux') &&
+ bLine.direction !== 'inactive') {
throw new Error("Cannot convert to Unified Plan because m-lines " +
"without the rtcp-mux attribute were found.");
}
// 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;
- var fingerprint = channel.fingerprint;
- var port = channel.port;
+ var sources = bLine.sources;
+ var ssrcGroups = bLine.ssrcGroups;
+ var candidates = bLine.candidates;
+ var iceUfrag = bLine.iceUfrag;
+ var icePwd = bLine.icePwd;
+ var fingerprint = bLine.fingerprint;
+ var port = bLine.port;
- // We'll use the "channel" object as a prototype for each new "mLine"
+ // We'll use the "bLine" 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;
- delete channel.fingerprint;
- delete channel.port;
- delete channel.mid;
+ delete bLine.sources;
+ delete bLine.ssrcGroups;
+ delete bLine.candidates;
+ delete bLine.iceUfrag;
+ delete bLine.icePwd;
+ delete bLine.fingerprint;
+ delete bLine.port;
+ delete bLine.mid;
// inverted ssrc group map
var ssrc2group = {};
@@ -28378,11 +28487,11 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// Explode the Plan B channel sources with one m-line per source.
Object.keys(sources).forEach(function(ssrc) {
- // The m-line for this SSRC. We either create it from scratch
- // or, if it's a grouped SSRC, we re-use a related mline. In
- // other words, if the source is grouped with another source,
- // put the two together in the same m-line.
- var mLine;
+ // The (unified) m-line for this SSRC. We either create it from
+ // scratch or, if it's a grouped SSRC, we re-use a related
+ // mline. In other words, if the source is grouped with another
+ // source, put the two together in the same m-line.
+ var unifiedLine;
if (typeof ssrc2group[ssrc] !== 'undefined' &&
Array.isArray(ssrc2group[ssrc])) {
ssrc2group[ssrc].some(function (ssrcGroup) {
@@ -28390,21 +28499,21 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// again here.
return ssrcGroup.ssrcs.some(function (related) {
if (typeof ssrc2ml[related] === 'object') {
- mLine = ssrc2ml[related];
+ unifiedLine = ssrc2ml[related];
return true;
}
});
});
}
- if (typeof mLine === 'object') {
+ if (typeof unifiedLine === 'object') {
// the m-line already exists. Just add the source.
- mLine.sources[ssrc] = sources[ssrc];
+ unifiedLine.sources[ssrc] = sources[ssrc];
delete sources[ssrc].msid;
} else {
- // Use the "channel" as a prototype for the "mLine".
- mLine = Object.create(channel);
- ssrc2ml[ssrc] = mLine;
+ // Use the "bLine" as a prototype for the "unifiedLine".
+ unifiedLine = Object.create(bLine);
+ ssrc2ml[ssrc] = unifiedLine;
if (typeof sources[ssrc].msid !== 'undefined') {
// Assign the msid of the source to the m-line. Note
@@ -28412,14 +28521,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// msid. In particular "recvonly" sources don't have an
// msid. Note that "recvonly" is a term only defined
// for m-lines.
- mLine.msid = sources[ssrc].msid;
+ unifiedLine.msid = sources[ssrc].msid;
delete sources[ssrc].msid;
}
// We assign one SSRC per media line.
- mLine.sources = {};
- mLine.sources[ssrc] = sources[ssrc];
- mLine.ssrcGroups = ssrc2group[ssrc];
+ unifiedLine.sources = {};
+ unifiedLine.sources[ssrc] = sources[ssrc];
+ unifiedLine.ssrcGroups = ssrc2group[ssrc];
// Use the cached Unified Plan SDP (if it exists) to assign
// SSRCs to mids.
@@ -28431,14 +28540,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
if (typeof m.sources === 'object') {
Object.keys(m.sources).forEach(function (s) {
if (s === ssrc) {
- mLine.mid = m.mid;
+ unifiedLine.mid = m.mid;
}
});
}
});
}
- if (typeof mLine.mid === 'undefined') {
+ if (typeof unifiedLine.mid === 'undefined') {
// If this is an SSRC that we see for the first time
// assign it a new mid. This is typically the case when
@@ -28451,23 +28560,23 @@ Interop.prototype.toUnifiedPlan = function(desc) {
//
// Because FF generates answers in Unified Plan style,
// we MUST already have a cached answer with all the
- // local SSRCs mapped to some mLine/mid.
+ // local SSRCs mapped to some m-line/mid.
if (desc.type === 'answer') {
throw new Error("An unmapped SSRC was found.");
}
- mLine.mid = [channel.type, '-', ssrc].join('');
+ unifiedLine.mid = [bLine.type, '-', ssrc].join('');
}
// Include the candidates in the 1st media line.
- mLine.candidates = candidates;
- mLine.iceUfrag = iceUfrag;
- mLine.icePwd = icePwd;
- mLine.fingerprint = fingerprint;
- mLine.port = port;
+ unifiedLine.candidates = candidates;
+ unifiedLine.iceUfrag = iceUfrag;
+ unifiedLine.icePwd = icePwd;
+ unifiedLine.fingerprint = fingerprint;
+ unifiedLine.port = port;
- mid2ml[mLine.mid] = mLine;
+ mid2ul[unifiedLine.mid] = unifiedLine;
}
});
}
@@ -28481,71 +28590,57 @@ Interop.prototype.toUnifiedPlan = function(desc) {
if (desc.type === 'answer') {
// The media lines in the answer must match the media lines in the
- // offer. The order is important too. Here we use the cached offer to
- // find the m-lines that are missing (from the converted answer), and
- // use the cached answer to complete the converted answer.
+ // offer. The order is important too. Here we assume that Firefox is the
+ // answerer, so we merely have to use the reconstructed (unified) answer
+ // to update the cached (unified) answer accordingly.
+ //
+ // In the general case, one would have to use the cached (unified) offer
+ // to find the m-lines that are missing from the reconstructed answer,
+ // potentially grabbing them from the cached (unified) answer. One has
+ // to be carefull with this approach because inactive m-lines do not
+ // always have an mid, making it tricky (impossible?) to find where
+ // exactly and which m-lines are missing from the reconstructed answer.
- if (typeof this.cache['offer'] === 'undefined') {
- throw new Error("An answer is being processed but we couldn't " +
- "find a cached offer.");
- }
+ for (var i = 0; i < cached.media.length; i++) {
+ var unifiedLine = cached.media[i];
- var cachedOffer = transform.parse(this.cache['offer']);
+ if (typeof mid2ul[unifiedLine.mid] === 'undefined') {
- if (typeof cachedOffer === 'undefined' ||
- typeof cachedOffer.media === 'undefined' ||
- !Array.isArray(cachedOffer.media)) {
- // FIXME(gp) is this really a problem in the general case?
- throw new Error("The cached offer has no media.");
- }
+ // The mid isn't in the reconstructed (unified) answer.
+ // This is either a (unified) m-line containing a remote
+ // track only, or a (unified) m-line containing a remote
+ // track and a local track that has been removed.
+ // In either case, it MUST exist in the cached
+ // (unified) answer.
+ //
+ // In case this is a removed local track, clean-up
+ // the (unified) m-line and make sure it's 'recvonly' or
+ // 'inactive'.
- cachedOffer.media.forEach(function(mo) {
-
- var mLine;
- cached.media.some(function(ma) {
- if (mo.mid == ma.mid) {
- if (typeof mid2ml[mo.mid] === 'undefined') {
-
- // This is either an m-line containing a remote
- // track only, or an m-line containing a remote
- // track and a local track that has been removed.
- // In either case, it MUST exist in the cached
- // answer.
- //
- // In case this is a removed local track, clean-up
- // the m-line and make sure it's 'recvonly'.
-
- // TODO sendonly -> inactive makes more sense.
- delete ma.msid;
- delete ma.sources;
- delete ma.ssrcGroups;
- if (!ma.direction
- || ma.direction === 'sendonly'
- || ma.direction === 'sendrecv')
- ma.direction = 'recvonly';
- } else {
- // This is an m-line/channel that contains a local
- // track (sendrecv or sendonly channel) or it's a
- // recvonly m-line/channel. In either case, since we're
- // going from PlanB -> Unified Plan this m-line MUST
- // exist in the cached answer.
- }
-
- // assign the found object.
- mLine = ma;
- return true;
- }
- });
-
- if (typeof mLine === 'undefined') {
- throw new Error("The cached offer contains an m-line that " +
- "doesn't exist neither in the cached answer nor in " +
- "the converted answer.");
+ delete unifiedLine.msid;
+ delete unifiedLine.sources;
+ delete unifiedLine.ssrcGroups;
+ if (!unifiedLine.direction
+ || unifiedLine.direction === 'sendrecv')
+ unifiedLine.direction = 'recvonly';
+ if (!unifiedLine.direction
+ || unifiedLine.direction === 'sendonly')
+ unifiedLine.direction = 'inactive';
+ } else {
+ // This is an (unified) m-line/channel that contains a local
+ // track (sendrecv or sendonly channel) or it's a unified
+ // recvonly m-line/channel. In either case, since we're
+ // going from PlanB -> Unified Plan this m-line MUST
+ // exist in the cached answer.
}
- session.media.push(mLine);
- mids.push(mLine.mid);
- });
+ session.media.push(unifiedLine);
+
+ if (typeof unifiedLine.mid === 'string') {
+ // inactive lines don't/may not have an mid.
+ mids.push(unifiedLine.mid);
+ }
+ }
} else {
// SDP offer/answer (and the JSEP spec) forbids removing an m-section
@@ -28559,41 +28654,48 @@ Interop.prototype.toUnifiedPlan = function(desc) {
if (typeof cached !== 'undefined' &&
typeof cached.media !== 'undefined' &&
Array.isArray(cached.media)) {
- cached.media.forEach(function(pm) {
- mids.push(pm.mid);
- if (typeof mid2ml[pm.mid] !== 'undefined') {
- session.media.push(mid2ml[pm.mid]);
+ cached.media.forEach(function(unifiedLine) {
+ mids.push(unifiedLine.mid);
+ if (typeof mid2ul[unifiedLine.mid] !== 'undefined') {
+ session.media.push(mid2ul[unifiedLine.mid]);
} else {
- delete pm.msid;
- delete pm.sources;
- delete pm.ssrcGroups;
- pm.direction = 'recvonly';
- session.media.push(pm);
+ delete unifiedLine.msid;
+ delete unifiedLine.sources;
+ delete unifiedLine.ssrcGroups;
+ if (!unifiedLine.direction
+ || unifiedLine.direction === 'sendrecv')
+ unifiedLine.direction = 'recvonly';
+ if (!unifiedLine.direction
+ || unifiedLine.direction === 'sendonly')
+ unifiedLine.direction = 'inactive';
+ session.media.push(unifiedLine);
}
});
}
// Add all the remaining (new) m-lines of the transformed SDP.
- Object.keys(mid2ml).forEach(function(mid) {
+ Object.keys(mid2ul).forEach(function(mid) {
if (mids.indexOf(mid) === -1) {
mids.push(mid);
- if (typeof mid2ml[mid].msid === 'undefined') {
+ if (typeof mid2ul[mid].direction === 'recvonly') {
// This is a remote recvonly channel. Add its SSRC to the
- // sendrecv channel.
- // TODO(gp) what if there is no sendrecv channel?
- session.media.some(function (ml) {
- if (ml.direction === 'sendrecv' && ml.type == mid2ml[mid].type) {
+ // appropriate sendrecv or sendonly channel.
+ // TODO(gp) what if we don't have sendrecv/sendonly channel?
+ session.media.some(function (unifiedLine) {
+ if ((unifiedLine.direction === 'sendrecv' ||
+ unifiedLine.direction === 'sendonly') &&
+ unifiedLine.type === mid2ul[mid].type) {
- // this shouldn't have any ssrc-groups
- Object.keys(mid2ml[mid].sources).forEach(function (ssrc) {
- ml.sources[ssrc] = mid2ml[mid].sources[ssrc];
+ // mid2ul[mid] shouldn't have any ssrc-groups
+ Object.keys(mid2ul[mid].sources).forEach(function (ssrc) {
+ unifiedLine.sources[ssrc] = mid2ul[mid].sources[ssrc];
});
return true;
}
});
} else {
- session.media.push(mid2ml[mid]);
+ session.media.push(mid2ul[mid]);
}
}
});
@@ -29388,7 +29490,8 @@ var XMPPEvents = {
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
ETHERPAD: "xmpp.etherpad",
- DEVICE_AVAILABLE: "xmpp.device_available"
+ DEVICE_AVAILABLE: "xmpp.device_available",
+ START_MUTED: "xmpp.start_muted"
};
module.exports = XMPPEvents;
},{}],106:[function(require,module,exports){