Update libs/app.bundle.js.

This commit is contained in:
George Politis 2015-05-20 14:03:43 +02:00
parent ee6fd63c25
commit 777422c87d
2 changed files with 297 additions and 194 deletions

View File

@ -19,7 +19,7 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib --> <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib --> <script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=5"></script> <script src="interface_config.js?v=5"></script>
<script src="libs/app.bundle.js?v=67"></script> <script src="libs/app.bundle.js?v=68"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin --> <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<link rel="stylesheet" href="css/font.css?v=7"/> <link rel="stylesheet" href="css/font.css?v=7"/>
<link rel="stylesheet" href="css/toastr.css?v=1"> <link rel="stylesheet" href="css/toastr.css?v=1">

View File

@ -705,28 +705,26 @@ var eventEmitter = new EventEmitter();
function getMediaStreamUsage() function getMediaStreamUsage()
{ {
var result = { var result = {
audio: 1, audio: true,
video: 1 video: true
}; };
if( config.startAudioMuted === true)
result.audio = 0;
if( config.startVideoMuted === true)
result.video = 0;
/** There are some issues with the desktop sharing /** There are some issues with the desktop sharing
* when this property is enabled. * 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:"; var isSecureConnection = window.location.protocol == "https:";
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection) if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
{ {
if(result.audio === 0) result = {
result.audio = -1; audio: false,
if(result.video === 0) video: false
result.video = -1; };
}**/
}
**/
return result; 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 * We ask for audio and video combined stream in order to get permissions and
* not to ask twice. * not to ask twice.
*/ */
RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) { RTCUtils.prototype.obtainAudioAndVideoPermissions =
function(devices, callback, usageOptions)
{
var self = this; var self = this;
// Get AV // Get AV
@ -1297,7 +1297,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback,
for(var i = 0; i < devices.length; i++) for(var i = 0; i < devices.length; i++)
{ {
var device = devices[i]; var device = devices[i];
if(usageOptions[device] !== -1) if(usageOptions[device] === true)
newDevices.push(device); newDevices.push(device);
} }
else else
@ -1431,11 +1431,12 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
videoStream = stream.videoStream; videoStream = stream.videoStream;
} }
var audioMuted = (usageOptions && usageOptions.audio != 1), var audioMuted = (usageOptions && usageOptions.audio === false),
videoMuted = (usageOptions && usageOptions.video != 1); 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, this.service.createLocalStream(audioStream, "audio", null, null,
audioMuted, audioGUM); audioMuted, audioGUM);
@ -1505,10 +1506,8 @@ var roomName = null;
function notifyForInitialMute() 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() function setupPrezi()
@ -1725,6 +1724,9 @@ function registerListeners() {
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED, APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
onDtmfSupportChanged); onDtmfSupportChanged);
APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
SettingsMenu.setStartMuted(audio, video);
});
} }
@ -1879,8 +1881,6 @@ UI.start = function (init) {
SettingsMenu.init(); SettingsMenu.init();
notifyForInitialMute();
}; };
function chatAddError(errorMessage, originalText) function chatAddError(errorMessage, originalText)
@ -1956,6 +1956,7 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
console.info("My role changed, new role: " + info.role); console.info("My role changed, new role: " + info.role);
onModeratorStatusChanged(isModerator); onModeratorStatusChanged(isModerator);
VideoLayout.showModeratorIndicator(); VideoLayout.showModeratorIndicator();
SettingsMenu.onRoleChanged();
if (isModerator) { if (isModerator) {
Authentication.closeAuthenticationWindow(); Authentication.closeAuthenticationWindow();
@ -2200,6 +2201,12 @@ UI.getRoomName = function () {
return roomName; 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. * Mutes/unmutes the local video.
*/ */
@ -5090,7 +5097,7 @@ function generateLanguagesSelectBox()
var SettingsMenu = { var SettingsMenu = {
init: function () { init: function () {
$("#updateSettings").before(generateLanguagesSelectBox()); $("#startMutedOptions").before(generateLanguagesSelectBox());
APP.translation.translateElement($("#languages_selectbox")); APP.translation.translateElement($("#languages_selectbox"));
$('#settingsmenu>input').keyup(function(event){ $('#settingsmenu>input').keyup(function(event){
if(event.keyCode === 13) {//enter 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 () { $("#updateSettings").click(function () {
SettingsMenu.update(); 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() { update: function() {
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value); var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value); var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
@ -5119,6 +5151,10 @@ var SettingsMenu = {
APP.xmpp.addToPresence("email", newEmail); APP.xmpp.addToPresence("email", newEmail);
var email = Settings.setEmail(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); Avatar.setUserAvatar(APP.xmpp.myJid(), email);
}, },
@ -15638,6 +15674,20 @@ var Moderator = {
{ name: 'openSctp', value: config.openSctp}) { name: 'openSctp', value: config.openSctp})
.up(); .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(); elem.up();
return elem; return elem;
}, },
@ -16187,6 +16237,13 @@ module.exports = function(XMPP, eventEmitter) {
$(document).trigger('videomuted.muc', [from, videoMuted.text()]); $(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'); var devices = $(pres).find('>devices');
if(devices.length) if(devices.length)
{ {
@ -16541,6 +16598,15 @@ module.exports = function(XMPP, eventEmitter) {
|| 'sendrecv' } || 'sendrecv' }
).up(); ).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(); pres.up();
@ -16621,6 +16687,9 @@ module.exports = function(XMPP, eventEmitter) {
addUserIdToPresence: function (userId) { addUserIdToPresence: function (userId) {
this.presMap['userId'] = userId; this.presMap['userId'] = userId;
}, },
addStartMutedToPresence: function (audio, video) {
this.presMap["startMuted"] = {audio: audio, video: video};
},
isModerator: function () { isModerator: function () {
return this.role === 'moderator'; return this.role === 'moderator';
}, },
@ -16808,6 +16877,14 @@ module.exports = function(XMPP, eventEmitter)
// see http://xmpp.org/extensions/xep-0166.html#concepts-session // see http://xmpp.org/extensions/xep-0166.html#concepts-session
switch (action) { switch (action) {
case 'session-initiate': 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( sess = new JingleSession(
$(iq).attr('to'), $(iq).find('jingle').attr('sid'), $(iq).attr('to'), $(iq).find('jingle').attr('sid'),
this.connection, XMPP); this.connection, XMPP);
@ -17450,8 +17527,13 @@ function registerListeners() {
}); });
} }
function setupEvents() { var unload = (function () {
$(window).bind('beforeunload', function () { var unloaded = false;
return function () {
if (unloaded) { return; }
unloaded = true;
if (connection && connection.connected) { if (connection && connection.connected) {
// ensure signout // ensure signout
$.ajax({ $.ajax({
@ -17460,24 +17542,41 @@ function setupEvents() {
async: false, async: false,
cache: false, cache: false,
contentType: 'application/xml', contentType: 'application/xml',
data: "<body rid='" + (connection.rid || connection._proto.rid) data: "<body rid='" + (connection.rid || connection._proto.rid) +
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='" "' xmlns='http://jabber.org/protocol/httpbind' sid='" +
+ (connection.sid || connection._proto.sid) (connection.sid || connection._proto.sid) +
+ "' type='terminate'>" + "' type='terminate'>" +
"<presence xmlns='jabber:client' type='unavailable'/>" + "<presence xmlns='jabber:client' type='unavailable'/>" +
"</body>", "</body>",
success: function (data) { success: function (data) {
console.log('signed out'); console.log('signed out');
console.log(data); console.log(data);
}, },
error: function (XMLHttpRequest, textStatus, errorThrown) { error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log('signout error', console.log('signout error',
textStatus + ' (' + errorThrown + ')'); textStatus + ' (' + errorThrown + ')');
} }
}); });
} }
XMPP.disposeConference(true); 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 = { var XMPP = {
@ -17741,6 +17840,12 @@ var XMPP = {
case "devices": case "devices":
connection.emuc.addDevicesToPresence(value); connection.emuc.addDevicesToPresence(value);
break; break;
case "startMuted":
if(!Moderator.isModerator())
return;
connection.emuc.addStartMutedToPresence(value[0],
value[1]);
break;
default : default :
console.log("Unknown tag for presence: " + name); console.log("Unknown tag for presence: " + name);
return; return;
@ -28054,6 +28159,8 @@ module.exports = function arrayEquals(array) {
exports.Interop = require('./interop'); exports.Interop = require('./interop');
},{"./interop":88}],88:[function(require,module,exports){ },{"./interop":88}],88:[function(require,module,exports){
"use strict";
var transform = require('./transform'); var transform = require('./transform');
var arrayEquals = require('./array-equals'); var arrayEquals = require('./array-equals');
@ -28122,46 +28229,47 @@ Interop.prototype.toPlanB = function(desc) {
session.media = []; session.media = [];
// Associative array that maps channel types to channel objects for fast // 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. // obj.
var channels = {}; var type2bl = {};
// Used to build the group:BUNDLE value after the channels construction // Used to build the group:BUNDLE value after the channels construction
// loop. // loop.
var types = []; var types = [];
// Implode the Unified Plan m-lines/tracks into Plan B "channels". // Implode the Unified Plan m-lines/tracks into Plan B channels.
media.forEach(function(mLine) { media.forEach(function(unifiedLine) {
// rtcp-mux is required in the Plan B SDP. // rtcp-mux is required in the Plan B SDP.
if (typeof mLine.rtcpMux !== 'string' || if ((typeof unifiedLine.rtcpMux !== 'string' ||
mLine.rtcpMux !== 'rtcp-mux') { unifiedLine.rtcpMux !== 'rtcp-mux') &&
unifiedLine.direction !== 'inactive') {
throw new Error('Cannot convert to Plan B because m-lines ' + throw new Error('Cannot convert to Plan B because m-lines ' +
'without the rtcp-mux attribute were found.'); '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. // as the channel basis.
if (typeof channels[mLine.type] === 'undefined') { if (typeof type2bl[unifiedLine.type] === 'undefined') {
channels[mLine.type] = mLine; type2bl[unifiedLine.type] = unifiedLine;
} }
// Add sources to the channel and handle a=msid. // Add sources to the channel and handle a=msid.
if (typeof mLine.sources === 'object') { if (typeof unifiedLine.sources === 'object') {
Object.keys(mLine.sources).forEach(function(ssrc) { Object.keys(unifiedLine.sources).forEach(function(ssrc) {
if (typeof channels[mLine.type].sources !== 'object') if (typeof type2bl[unifiedLine.type].sources !== 'object')
channels[mLine.type].sources = {}; type2bl[unifiedLine.type].sources = {};
// Assign the sources to the channel. // 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 // In Plan B the msid is an SSRC attribute. Also, we don't
// care about the obsolete label and mslabel attributes. // 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. // 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 // NOTE ssrcs in ssrc groups will share msids, as
// draft-uberti-rtcweb-plan-00 mandates. // draft-uberti-rtcweb-plan-00 mandates.
@ -28169,39 +28277,39 @@ Interop.prototype.toPlanB = function(desc) {
} }
// Add ssrc groups to the channel. // Add ssrc groups to the channel.
if (typeof mLine.ssrcGroups !== 'undefined' && if (typeof unifiedLine.ssrcGroups !== 'undefined' &&
Array.isArray(mLine.ssrcGroups)) { Array.isArray(unifiedLine.ssrcGroups)) {
// Create the ssrcGroups array, if it's not defined. // Create the ssrcGroups array, if it's not defined.
if (typeof channel.ssrcGroups === 'undefined' || if (typeof type2bl[unifiedLine.type].ssrcGroups === 'undefined' ||
!Array.isArray(channel.ssrcGroups)) { !Array.isArray(type2bl[unifiedLine.type].ssrcGroups)) {
channel.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. // Copy ICE related stuff from the principal media line.
mLine.candidates = media[0].candidates; unifiedLine.candidates = media[0].candidates;
mLine.iceUfrag = media[0].iceUfrag; unifiedLine.iceUfrag = media[0].iceUfrag;
mLine.icePwd = media[0].icePwd; unifiedLine.icePwd = media[0].icePwd;
mLine.fingerprint = media[0].fingerprint; unifiedLine.fingerprint = media[0].fingerprint;
// Plan B mids are in ['audio', 'video', 'data'] // 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. // 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. // 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. // 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. // 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 // A helper map that sends mids to m-line objects. We use it later to
// rebuild the Unified Plan style session.media array. // rebuild the Unified Plan style session.media array.
var mid2ml = {}; var mid2ul = {};
session.media.forEach(function(channel) { session.media.forEach(function(bLine) {
if (typeof channel.rtcpMux !== 'string' || if ((typeof bLine.rtcpMux !== 'string' ||
channel.rtcpMux !== 'rtcp-mux') { bLine.rtcpMux !== 'rtcp-mux') &&
bLine.direction !== 'inactive') {
throw new Error("Cannot convert to Unified Plan because m-lines " + throw new Error("Cannot convert to Unified Plan because m-lines " +
"without the rtcp-mux attribute were found."); "without the rtcp-mux attribute were found.");
} }
// With rtcp-mux and bundle all the channels should have the same ICE // With rtcp-mux and bundle all the channels should have the same ICE
// stuff. // stuff.
var sources = channel.sources; var sources = bLine.sources;
var ssrcGroups = channel.ssrcGroups; var ssrcGroups = bLine.ssrcGroups;
var candidates = channel.candidates; var candidates = bLine.candidates;
var iceUfrag = channel.iceUfrag; var iceUfrag = bLine.iceUfrag;
var icePwd = channel.icePwd; var icePwd = bLine.icePwd;
var fingerprint = channel.fingerprint; var fingerprint = bLine.fingerprint;
var port = channel.port; 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. // that we create, but first we need to clean it up a bit.
delete channel.sources; delete bLine.sources;
delete channel.ssrcGroups; delete bLine.ssrcGroups;
delete channel.candidates; delete bLine.candidates;
delete channel.iceUfrag; delete bLine.iceUfrag;
delete channel.icePwd; delete bLine.icePwd;
delete channel.fingerprint; delete bLine.fingerprint;
delete channel.port; delete bLine.port;
delete channel.mid; delete bLine.mid;
// inverted ssrc group map // inverted ssrc group map
var ssrc2group = {}; var ssrc2group = {};
@ -28378,11 +28487,11 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// Explode the Plan B channel sources with one m-line per source. // Explode the Plan B channel sources with one m-line per source.
Object.keys(sources).forEach(function(ssrc) { Object.keys(sources).forEach(function(ssrc) {
// The m-line for this SSRC. We either create it from scratch // The (unified) m-line for this SSRC. We either create it from
// or, if it's a grouped SSRC, we re-use a related mline. In // scratch or, if it's a grouped SSRC, we re-use a related
// other words, if the source is grouped with another source, // mline. In other words, if the source is grouped with another
// put the two together in the same m-line. // source, put the two together in the same m-line.
var mLine; var unifiedLine;
if (typeof ssrc2group[ssrc] !== 'undefined' && if (typeof ssrc2group[ssrc] !== 'undefined' &&
Array.isArray(ssrc2group[ssrc])) { Array.isArray(ssrc2group[ssrc])) {
ssrc2group[ssrc].some(function (ssrcGroup) { ssrc2group[ssrc].some(function (ssrcGroup) {
@ -28390,21 +28499,21 @@ Interop.prototype.toUnifiedPlan = function(desc) {
// again here. // again here.
return ssrcGroup.ssrcs.some(function (related) { return ssrcGroup.ssrcs.some(function (related) {
if (typeof ssrc2ml[related] === 'object') { if (typeof ssrc2ml[related] === 'object') {
mLine = ssrc2ml[related]; unifiedLine = ssrc2ml[related];
return true; return true;
} }
}); });
}); });
} }
if (typeof mLine === 'object') { if (typeof unifiedLine === 'object') {
// the m-line already exists. Just add the source. // the m-line already exists. Just add the source.
mLine.sources[ssrc] = sources[ssrc]; unifiedLine.sources[ssrc] = sources[ssrc];
delete sources[ssrc].msid; delete sources[ssrc].msid;
} else { } else {
// Use the "channel" as a prototype for the "mLine". // Use the "bLine" as a prototype for the "unifiedLine".
mLine = Object.create(channel); unifiedLine = Object.create(bLine);
ssrc2ml[ssrc] = mLine; ssrc2ml[ssrc] = unifiedLine;
if (typeof sources[ssrc].msid !== 'undefined') { if (typeof sources[ssrc].msid !== 'undefined') {
// Assign the msid of the source to the m-line. Note // 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. In particular "recvonly" sources don't have an
// msid. Note that "recvonly" is a term only defined // msid. Note that "recvonly" is a term only defined
// for m-lines. // for m-lines.
mLine.msid = sources[ssrc].msid; unifiedLine.msid = sources[ssrc].msid;
delete sources[ssrc].msid; delete sources[ssrc].msid;
} }
// We assign one SSRC per media line. // We assign one SSRC per media line.
mLine.sources = {}; unifiedLine.sources = {};
mLine.sources[ssrc] = sources[ssrc]; unifiedLine.sources[ssrc] = sources[ssrc];
mLine.ssrcGroups = ssrc2group[ssrc]; unifiedLine.ssrcGroups = ssrc2group[ssrc];
// Use the cached Unified Plan SDP (if it exists) to assign // Use the cached Unified Plan SDP (if it exists) to assign
// SSRCs to mids. // SSRCs to mids.
@ -28431,14 +28540,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
if (typeof m.sources === 'object') { if (typeof m.sources === 'object') {
Object.keys(m.sources).forEach(function (s) { Object.keys(m.sources).forEach(function (s) {
if (s === ssrc) { 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 // If this is an SSRC that we see for the first time
// assign it a new mid. This is typically the case when // 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, // Because FF generates answers in Unified Plan style,
// we MUST already have a cached answer with all the // 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') { if (desc.type === 'answer') {
throw new Error("An unmapped SSRC was found."); 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. // Include the candidates in the 1st media line.
mLine.candidates = candidates; unifiedLine.candidates = candidates;
mLine.iceUfrag = iceUfrag; unifiedLine.iceUfrag = iceUfrag;
mLine.icePwd = icePwd; unifiedLine.icePwd = icePwd;
mLine.fingerprint = fingerprint; unifiedLine.fingerprint = fingerprint;
mLine.port = port; unifiedLine.port = port;
mid2ml[mLine.mid] = mLine; mid2ul[unifiedLine.mid] = unifiedLine;
} }
}); });
} }
@ -28481,71 +28590,57 @@ Interop.prototype.toUnifiedPlan = function(desc) {
if (desc.type === 'answer') { if (desc.type === 'answer') {
// The media lines in the answer must match the media lines in the // 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 // offer. The order is important too. Here we assume that Firefox is the
// find the m-lines that are missing (from the converted answer), and // answerer, so we merely have to use the reconstructed (unified) answer
// use the cached answer to complete the converted 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') { for (var i = 0; i < cached.media.length; i++) {
throw new Error("An answer is being processed but we couldn't " + var unifiedLine = cached.media[i];
"find a cached offer.");
}
var cachedOffer = transform.parse(this.cache['offer']); if (typeof mid2ul[unifiedLine.mid] === 'undefined') {
if (typeof cachedOffer === 'undefined' || // The mid isn't in the reconstructed (unified) answer.
typeof cachedOffer.media === 'undefined' || // This is either a (unified) m-line containing a remote
!Array.isArray(cachedOffer.media)) { // track only, or a (unified) m-line containing a remote
// FIXME(gp) is this really a problem in the general case? // track and a local track that has been removed.
throw new Error("The cached offer has no media."); // 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) { delete unifiedLine.msid;
delete unifiedLine.sources;
var mLine; delete unifiedLine.ssrcGroups;
cached.media.some(function(ma) { if (!unifiedLine.direction
if (mo.mid == ma.mid) { || unifiedLine.direction === 'sendrecv')
if (typeof mid2ml[mo.mid] === 'undefined') { unifiedLine.direction = 'recvonly';
if (!unifiedLine.direction
// This is either an m-line containing a remote || unifiedLine.direction === 'sendonly')
// track only, or an m-line containing a remote unifiedLine.direction = 'inactive';
// track and a local track that has been removed. } else {
// In either case, it MUST exist in the cached // This is an (unified) m-line/channel that contains a local
// answer. // track (sendrecv or sendonly channel) or it's a unified
// // recvonly m-line/channel. In either case, since we're
// In case this is a removed local track, clean-up // going from PlanB -> Unified Plan this m-line MUST
// the m-line and make sure it's 'recvonly'. // exist in the cached answer.
// 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.");
} }
session.media.push(mLine); session.media.push(unifiedLine);
mids.push(mLine.mid);
}); if (typeof unifiedLine.mid === 'string') {
// inactive lines don't/may not have an mid.
mids.push(unifiedLine.mid);
}
}
} else { } else {
// SDP offer/answer (and the JSEP spec) forbids removing an m-section // 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' && if (typeof cached !== 'undefined' &&
typeof cached.media !== 'undefined' && typeof cached.media !== 'undefined' &&
Array.isArray(cached.media)) { Array.isArray(cached.media)) {
cached.media.forEach(function(pm) { cached.media.forEach(function(unifiedLine) {
mids.push(pm.mid); mids.push(unifiedLine.mid);
if (typeof mid2ml[pm.mid] !== 'undefined') { if (typeof mid2ul[unifiedLine.mid] !== 'undefined') {
session.media.push(mid2ml[pm.mid]); session.media.push(mid2ul[unifiedLine.mid]);
} else { } else {
delete pm.msid; delete unifiedLine.msid;
delete pm.sources; delete unifiedLine.sources;
delete pm.ssrcGroups; delete unifiedLine.ssrcGroups;
pm.direction = 'recvonly'; if (!unifiedLine.direction
session.media.push(pm); || 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. // 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) { if (mids.indexOf(mid) === -1) {
mids.push(mid); 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 // This is a remote recvonly channel. Add its SSRC to the
// sendrecv channel. // appropriate sendrecv or sendonly channel.
// TODO(gp) what if there is no sendrecv channel? // TODO(gp) what if we don't have sendrecv/sendonly channel?
session.media.some(function (ml) { session.media.some(function (unifiedLine) {
if (ml.direction === 'sendrecv' && ml.type == mid2ml[mid].type) { if ((unifiedLine.direction === 'sendrecv' ||
unifiedLine.direction === 'sendonly') &&
unifiedLine.type === mid2ul[mid].type) {
// this shouldn't have any ssrc-groups // mid2ul[mid] shouldn't have any ssrc-groups
Object.keys(mid2ml[mid].sources).forEach(function (ssrc) { Object.keys(mid2ul[mid].sources).forEach(function (ssrc) {
ml.sources[ssrc] = mid2ml[mid].sources[ssrc]; unifiedLine.sources[ssrc] = mid2ul[mid].sources[ssrc];
}); });
return true; return true;
} }
}); });
} else { } else {
session.media.push(mid2ml[mid]); session.media.push(mid2ul[mid]);
} }
} }
}); });
@ -29388,7 +29490,8 @@ var XMPPEvents = {
AUTHENTICATION_REQUIRED: "xmpp.authentication_required", AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received", CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
ETHERPAD: "xmpp.etherpad", ETHERPAD: "xmpp.etherpad",
DEVICE_AVAILABLE: "xmpp.device_available" DEVICE_AVAILABLE: "xmpp.device_available",
START_MUTED: "xmpp.start_muted"
}; };
module.exports = XMPPEvents; module.exports = XMPPEvents;
},{}],106:[function(require,module,exports){ },{}],106:[function(require,module,exports){