|
@@ -705,28 +705,26 @@ var eventEmitter = new EventEmitter();
|
705
|
705
|
function getMediaStreamUsage()
|
706
|
706
|
{
|
707
|
707
|
var result = {
|
708
|
|
- audio: 1,
|
709
|
|
- video: 1
|
|
708
|
+ audio: true,
|
|
709
|
+ video: true
|
710
|
710
|
};
|
711
|
|
- if( config.startAudioMuted === true)
|
712
|
|
- result.audio = 0;
|
713
|
|
- if( config.startVideoMuted === true)
|
714
|
|
- result.video = 0;
|
715
|
711
|
|
716
|
712
|
/** There are some issues with the desktop sharing
|
717
|
713
|
* when this property is enabled.
|
|
714
|
+ * WARNING: We must change the implementation to start video/audio if we
|
|
715
|
+ * receive from the focus that the peer is not muted.
|
718
|
716
|
|
719
|
|
- if(result.audio > 0 && result.video > 0)
|
720
|
|
- return result;
|
721
|
717
|
var isSecureConnection = window.location.protocol == "https:";
|
722
|
718
|
|
723
|
719
|
if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
|
724
|
720
|
{
|
725
|
|
- if(result.audio === 0)
|
726
|
|
- result.audio = -1;
|
727
|
|
- if(result.video === 0)
|
728
|
|
- result.video = -1;
|
729
|
|
- }**/
|
|
721
|
+ result = {
|
|
722
|
+ audio: false,
|
|
723
|
+ video: false
|
|
724
|
+ };
|
|
725
|
+
|
|
726
|
+ }
|
|
727
|
+ **/
|
730
|
728
|
|
731
|
729
|
return result;
|
732
|
730
|
}
|
|
@@ -1276,7 +1274,9 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
|
1276
|
1274
|
* We ask for audio and video combined stream in order to get permissions and
|
1277
|
1275
|
* not to ask twice.
|
1278
|
1276
|
*/
|
1279
|
|
-RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) {
|
|
1277
|
+RTCUtils.prototype.obtainAudioAndVideoPermissions =
|
|
1278
|
+ function(devices, callback, usageOptions)
|
|
1279
|
+{
|
1280
|
1280
|
var self = this;
|
1281
|
1281
|
// Get AV
|
1282
|
1282
|
|
|
@@ -1297,7 +1297,7 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback,
|
1297
|
1297
|
for(var i = 0; i < devices.length; i++)
|
1298
|
1298
|
{
|
1299
|
1299
|
var device = devices[i];
|
1300
|
|
- if(usageOptions[device] !== -1)
|
|
1300
|
+ if(usageOptions[device] === true)
|
1301
|
1301
|
newDevices.push(device);
|
1302
|
1302
|
}
|
1303
|
1303
|
else
|
|
@@ -1431,11 +1431,12 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
|
1431
|
1431
|
videoStream = stream.videoStream;
|
1432
|
1432
|
}
|
1433
|
1433
|
|
1434
|
|
- var audioMuted = (usageOptions && usageOptions.audio != 1),
|
1435
|
|
- videoMuted = (usageOptions && usageOptions.video != 1);
|
|
1434
|
+ var audioMuted = (usageOptions && usageOptions.audio === false),
|
|
1435
|
+ videoMuted = (usageOptions && usageOptions.video === false);
|
|
1436
|
+
|
|
1437
|
+ var audioGUM = (!usageOptions || usageOptions.audio !== false),
|
|
1438
|
+ videoGUM = (!usageOptions || usageOptions.video !== false);
|
1436
|
1439
|
|
1437
|
|
- var audioGUM = (!usageOptions || usageOptions.audio != -1),
|
1438
|
|
- videoGUM = (!usageOptions || usageOptions.video != -1);
|
1439
|
1440
|
|
1440
|
1441
|
this.service.createLocalStream(audioStream, "audio", null, null,
|
1441
|
1442
|
audioMuted, audioGUM);
|
|
@@ -1505,10 +1506,8 @@ var roomName = null;
|
1505
|
1506
|
|
1506
|
1507
|
function notifyForInitialMute()
|
1507
|
1508
|
{
|
1508
|
|
- if(config.startAudioMuted || config.startVideoMuted)
|
1509
|
|
- {
|
1510
|
|
- messageHandler.notify(null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000});
|
1511
|
|
- }
|
|
1509
|
+ messageHandler.notify(null, "notify.mutedTitle", "connected",
|
|
1510
|
+ "notify.muted", null, {timeOut: 120000});
|
1512
|
1511
|
}
|
1513
|
1512
|
|
1514
|
1513
|
function setupPrezi()
|
|
@@ -1725,6 +1724,9 @@ function registerListeners() {
|
1725
|
1724
|
|
1726
|
1725
|
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
|
1727
|
1726
|
onDtmfSupportChanged);
|
|
1727
|
+ APP.xmpp.addListener(XMPPEvents.START_MUTED, function (audio, video) {
|
|
1728
|
+ SettingsMenu.setStartMuted(audio, video);
|
|
1729
|
+ });
|
1728
|
1730
|
}
|
1729
|
1731
|
|
1730
|
1732
|
|
|
@@ -1879,8 +1881,6 @@ UI.start = function (init) {
|
1879
|
1881
|
|
1880
|
1882
|
SettingsMenu.init();
|
1881
|
1883
|
|
1882
|
|
- notifyForInitialMute();
|
1883
|
|
-
|
1884
|
1884
|
};
|
1885
|
1885
|
|
1886
|
1886
|
function chatAddError(errorMessage, originalText)
|
|
@@ -1956,6 +1956,7 @@ function onLocalRoleChanged(jid, info, pres, isModerator)
|
1956
|
1956
|
console.info("My role changed, new role: " + info.role);
|
1957
|
1957
|
onModeratorStatusChanged(isModerator);
|
1958
|
1958
|
VideoLayout.showModeratorIndicator();
|
|
1959
|
+ SettingsMenu.onRoleChanged();
|
1959
|
1960
|
|
1960
|
1961
|
if (isModerator) {
|
1961
|
1962
|
Authentication.closeAuthenticationWindow();
|
|
@@ -2200,6 +2201,12 @@ UI.getRoomName = function () {
|
2200
|
2201
|
return roomName;
|
2201
|
2202
|
};
|
2202
|
2203
|
|
|
2204
|
+UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
|
|
2205
|
+ if(muteAudio || muteVideo) notifyForInitialMute();
|
|
2206
|
+ if(muteAudio) UI.setAudioMuted(true);
|
|
2207
|
+ if(muteVideo) UI.setVideoMute(true);
|
|
2208
|
+}
|
|
2209
|
+
|
2203
|
2210
|
/**
|
2204
|
2211
|
* Mutes/unmutes the local video.
|
2205
|
2212
|
*/
|
|
@@ -5090,7 +5097,7 @@ function generateLanguagesSelectBox()
|
5090
|
5097
|
var SettingsMenu = {
|
5091
|
5098
|
|
5092
|
5099
|
init: function () {
|
5093
|
|
- $("#updateSettings").before(generateLanguagesSelectBox());
|
|
5100
|
+ $("#startMutedOptions").before(generateLanguagesSelectBox());
|
5094
|
5101
|
APP.translation.translateElement($("#languages_selectbox"));
|
5095
|
5102
|
$('#settingsmenu>input').keyup(function(event){
|
5096
|
5103
|
if(event.keyCode === 13) {//enter
|
|
@@ -5098,11 +5105,36 @@ var SettingsMenu = {
|
5098
|
5105
|
}
|
5099
|
5106
|
});
|
5100
|
5107
|
|
|
5108
|
+ if(APP.xmpp.isModerator())
|
|
5109
|
+ {
|
|
5110
|
+ $("#startMutedOptions").css("display", "block");
|
|
5111
|
+ }
|
|
5112
|
+ else
|
|
5113
|
+ {
|
|
5114
|
+ $("#startMutedOptions").css("display", "none");
|
|
5115
|
+ }
|
|
5116
|
+
|
5101
|
5117
|
$("#updateSettings").click(function () {
|
5102
|
5118
|
SettingsMenu.update();
|
5103
|
5119
|
});
|
5104
|
5120
|
},
|
5105
|
5121
|
|
|
5122
|
+ onRoleChanged: function () {
|
|
5123
|
+ if(APP.xmpp.isModerator())
|
|
5124
|
+ {
|
|
5125
|
+ $("#startMutedOptions").css("display", "block");
|
|
5126
|
+ }
|
|
5127
|
+ else
|
|
5128
|
+ {
|
|
5129
|
+ $("#startMutedOptions").css("display", "none");
|
|
5130
|
+ }
|
|
5131
|
+ },
|
|
5132
|
+
|
|
5133
|
+ setStartMuted: function (audio, video) {
|
|
5134
|
+ $("#startAudioMuted").attr("checked", audio);
|
|
5135
|
+ $("#startVideoMuted").attr("checked", video);
|
|
5136
|
+ },
|
|
5137
|
+
|
5106
|
5138
|
update: function() {
|
5107
|
5139
|
var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
|
5108
|
5140
|
var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
|
|
@@ -5119,6 +5151,10 @@ var SettingsMenu = {
|
5119
|
5151
|
APP.xmpp.addToPresence("email", newEmail);
|
5120
|
5152
|
var email = Settings.setEmail(newEmail);
|
5121
|
5153
|
|
|
5154
|
+ var startAudioMuted = ($("#startAudioMuted").is(":checked"));
|
|
5155
|
+ var startVideoMuted = ($("#startVideoMuted").is(":checked"));
|
|
5156
|
+ APP.xmpp.addToPresence("startMuted",
|
|
5157
|
+ [startAudioMuted, startVideoMuted]);
|
5122
|
5158
|
|
5123
|
5159
|
Avatar.setUserAvatar(APP.xmpp.myJid(), email);
|
5124
|
5160
|
},
|
|
@@ -15638,6 +15674,20 @@ var Moderator = {
|
15638
|
15674
|
{ name: 'openSctp', value: config.openSctp})
|
15639
|
15675
|
.up();
|
15640
|
15676
|
}
|
|
15677
|
+ if(config.startAudioMuted !== undefined)
|
|
15678
|
+ {
|
|
15679
|
+ elem.c(
|
|
15680
|
+ 'property',
|
|
15681
|
+ { name: 'startAudioMuted', value: config.startAudioMuted})
|
|
15682
|
+ .up();
|
|
15683
|
+ }
|
|
15684
|
+ if(config.startVideoMuted !== undefined)
|
|
15685
|
+ {
|
|
15686
|
+ elem.c(
|
|
15687
|
+ 'property',
|
|
15688
|
+ { name: 'startVideoMuted', value: config.startVideoMuted})
|
|
15689
|
+ .up();
|
|
15690
|
+ }
|
15641
|
15691
|
elem.up();
|
15642
|
15692
|
return elem;
|
15643
|
15693
|
},
|
|
@@ -16187,6 +16237,13 @@ module.exports = function(XMPP, eventEmitter) {
|
16187
|
16237
|
$(document).trigger('videomuted.muc', [from, videoMuted.text()]);
|
16188
|
16238
|
}
|
16189
|
16239
|
|
|
16240
|
+ var startMuted = $(pres).find('>startmuted');
|
|
16241
|
+ if (startMuted.length)
|
|
16242
|
+ {
|
|
16243
|
+ eventEmitter.emit(XMPPEvents.START_MUTED,
|
|
16244
|
+ startMuted.attr("audio") === "true", startMuted.attr("video") === "true");
|
|
16245
|
+ }
|
|
16246
|
+
|
16190
|
16247
|
var devices = $(pres).find('>devices');
|
16191
|
16248
|
if(devices.length)
|
16192
|
16249
|
{
|
|
@@ -16541,6 +16598,15 @@ module.exports = function(XMPP, eventEmitter) {
|
16541
|
16598
|
|| 'sendrecv' }
|
16542
|
16599
|
).up();
|
16543
|
16600
|
}
|
|
16601
|
+ pres.up();
|
|
16602
|
+ }
|
|
16603
|
+
|
|
16604
|
+ if(this.presMap["startMuted"] !== undefined)
|
|
16605
|
+ {
|
|
16606
|
+ pres.c("startmuted", {audio: this.presMap["startMuted"].audio,
|
|
16607
|
+ video: this.presMap["startMuted"].video,
|
|
16608
|
+ xmlns: "http://jitsi.org/jitmeet/start-muted"});
|
|
16609
|
+ delete this.presMap["startMuted"];
|
16544
|
16610
|
}
|
16545
|
16611
|
|
16546
|
16612
|
pres.up();
|
|
@@ -16621,6 +16687,9 @@ module.exports = function(XMPP, eventEmitter) {
|
16621
|
16687
|
addUserIdToPresence: function (userId) {
|
16622
|
16688
|
this.presMap['userId'] = userId;
|
16623
|
16689
|
},
|
|
16690
|
+ addStartMutedToPresence: function (audio, video) {
|
|
16691
|
+ this.presMap["startMuted"] = {audio: audio, video: video};
|
|
16692
|
+ },
|
16624
|
16693
|
isModerator: function () {
|
16625
|
16694
|
return this.role === 'moderator';
|
16626
|
16695
|
},
|
|
@@ -16808,6 +16877,14 @@ module.exports = function(XMPP, eventEmitter)
|
16808
|
16877
|
// see http://xmpp.org/extensions/xep-0166.html#concepts-session
|
16809
|
16878
|
switch (action) {
|
16810
|
16879
|
case 'session-initiate':
|
|
16880
|
+ var startMuted = $(iq).find('jingle>startmuted');
|
|
16881
|
+ if(startMuted && startMuted.length > 0)
|
|
16882
|
+ {
|
|
16883
|
+ var audioMuted = startMuted.attr("audio");
|
|
16884
|
+ var videoMuted = startMuted.attr("video");
|
|
16885
|
+ APP.UI.setInitialMuteFromFocus((audioMuted === "true"),
|
|
16886
|
+ (videoMuted === "true"));
|
|
16887
|
+ }
|
16811
|
16888
|
sess = new JingleSession(
|
16812
|
16889
|
$(iq).attr('to'), $(iq).find('jingle').attr('sid'),
|
16813
|
16890
|
this.connection, XMPP);
|
|
@@ -17450,8 +17527,13 @@ function registerListeners() {
|
17450
|
17527
|
});
|
17451
|
17528
|
}
|
17452
|
17529
|
|
17453
|
|
-function setupEvents() {
|
17454
|
|
- $(window).bind('beforeunload', function () {
|
|
17530
|
+var unload = (function () {
|
|
17531
|
+ var unloaded = false;
|
|
17532
|
+
|
|
17533
|
+ return function () {
|
|
17534
|
+ if (unloaded) { return; }
|
|
17535
|
+ unloaded = true;
|
|
17536
|
+
|
17455
|
17537
|
if (connection && connection.connected) {
|
17456
|
17538
|
// ensure signout
|
17457
|
17539
|
$.ajax({
|
|
@@ -17460,24 +17542,41 @@ function setupEvents() {
|
17460
|
17542
|
async: false,
|
17461
|
17543
|
cache: false,
|
17462
|
17544
|
contentType: 'application/xml',
|
17463
|
|
- data: "<body rid='" + (connection.rid || connection._proto.rid)
|
17464
|
|
- + "' xmlns='http://jabber.org/protocol/httpbind' sid='"
|
17465
|
|
- + (connection.sid || connection._proto.sid)
|
17466
|
|
- + "' type='terminate'>" +
|
17467
|
|
- "<presence xmlns='jabber:client' type='unavailable'/>" +
|
17468
|
|
- "</body>",
|
|
17545
|
+ data: "<body rid='" + (connection.rid || connection._proto.rid) +
|
|
17546
|
+ "' xmlns='http://jabber.org/protocol/httpbind' sid='" +
|
|
17547
|
+ (connection.sid || connection._proto.sid) +
|
|
17548
|
+ "' type='terminate'>" +
|
|
17549
|
+ "<presence xmlns='jabber:client' type='unavailable'/>" +
|
|
17550
|
+ "</body>",
|
17469
|
17551
|
success: function (data) {
|
17470
|
17552
|
console.log('signed out');
|
17471
|
17553
|
console.log(data);
|
17472
|
17554
|
},
|
17473
|
17555
|
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
17474
|
17556
|
console.log('signout error',
|
17475
|
|
- textStatus + ' (' + errorThrown + ')');
|
|
17557
|
+ textStatus + ' (' + errorThrown + ')');
|
17476
|
17558
|
}
|
17477
|
17559
|
});
|
17478
|
17560
|
}
|
17479
|
17561
|
XMPP.disposeConference(true);
|
17480
|
|
- });
|
|
17562
|
+ };
|
|
17563
|
+})();
|
|
17564
|
+
|
|
17565
|
+function setupEvents() {
|
|
17566
|
+ // In recent versions of FF the 'beforeunload' event is not fired when the
|
|
17567
|
+ // window or the tab is closed. It is only fired when we leave the page
|
|
17568
|
+ // (change URL). If this participant doesn't unload properly, then it
|
|
17569
|
+ // becomes a ghost for the rest of the participants that stay in the
|
|
17570
|
+ // conference. Thankfully handling the 'unload' event in addition to the
|
|
17571
|
+ // 'beforeunload' event seems to garante the execution of the 'unload'
|
|
17572
|
+ // method at least once.
|
|
17573
|
+ //
|
|
17574
|
+ // The 'unload' method can safely be run multiple times, it will actually do
|
|
17575
|
+ // something only the first time that it's run, so we're don't have to worry
|
|
17576
|
+ // about browsers that fire both events.
|
|
17577
|
+
|
|
17578
|
+ $(window).bind('beforeunload', unload);
|
|
17579
|
+ $(window).bind('unload', unload);
|
17481
|
17580
|
}
|
17482
|
17581
|
|
17483
|
17582
|
var XMPP = {
|
|
@@ -17741,6 +17840,12 @@ var XMPP = {
|
17741
|
17840
|
case "devices":
|
17742
|
17841
|
connection.emuc.addDevicesToPresence(value);
|
17743
|
17842
|
break;
|
|
17843
|
+ case "startMuted":
|
|
17844
|
+ if(!Moderator.isModerator())
|
|
17845
|
+ return;
|
|
17846
|
+ connection.emuc.addStartMutedToPresence(value[0],
|
|
17847
|
+ value[1]);
|
|
17848
|
+ break;
|
17744
|
17849
|
default :
|
17745
|
17850
|
console.log("Unknown tag for presence: " + name);
|
17746
|
17851
|
return;
|
|
@@ -28054,6 +28159,8 @@ module.exports = function arrayEquals(array) {
|
28054
|
28159
|
exports.Interop = require('./interop');
|
28055
|
28160
|
|
28056
|
28161
|
},{"./interop":88}],88:[function(require,module,exports){
|
|
28162
|
+"use strict";
|
|
28163
|
+
|
28057
|
28164
|
var transform = require('./transform');
|
28058
|
28165
|
var arrayEquals = require('./array-equals');
|
28059
|
28166
|
|
|
@@ -28122,46 +28229,47 @@ Interop.prototype.toPlanB = function(desc) {
|
28122
|
28229
|
session.media = [];
|
28123
|
28230
|
|
28124
|
28231
|
// Associative array that maps channel types to channel objects for fast
|
28125
|
|
- // access to channel objects by their type, e.g. channels['audio']->channel
|
|
28232
|
+ // access to channel objects by their type, e.g. type2bl['audio']->channel
|
28126
|
28233
|
// obj.
|
28127
|
|
- var channels = {};
|
|
28234
|
+ var type2bl = {};
|
28128
|
28235
|
|
28129
|
28236
|
// Used to build the group:BUNDLE value after the channels construction
|
28130
|
28237
|
// loop.
|
28131
|
28238
|
var types = [];
|
28132
|
28239
|
|
28133
|
|
- // Implode the Unified Plan m-lines/tracks into Plan B "channels".
|
28134
|
|
- media.forEach(function(mLine) {
|
|
28240
|
+ // Implode the Unified Plan m-lines/tracks into Plan B channels.
|
|
28241
|
+ media.forEach(function(unifiedLine) {
|
28135
|
28242
|
|
28136
|
28243
|
// rtcp-mux is required in the Plan B SDP.
|
28137
|
|
- if (typeof mLine.rtcpMux !== 'string' ||
|
28138
|
|
- mLine.rtcpMux !== 'rtcp-mux') {
|
|
28244
|
+ if ((typeof unifiedLine.rtcpMux !== 'string' ||
|
|
28245
|
+ unifiedLine.rtcpMux !== 'rtcp-mux') &&
|
|
28246
|
+ unifiedLine.direction !== 'inactive') {
|
28139
|
28247
|
throw new Error('Cannot convert to Plan B because m-lines ' +
|
28140
|
28248
|
'without the rtcp-mux attribute were found.');
|
28141
|
28249
|
}
|
28142
|
28250
|
|
28143
|
|
- // If we don't have a channel for this mLine.type, then use this mLine
|
|
28251
|
+ // If we don't have a channel for this unifiedLine.type, then use this unifiedLine
|
28144
|
28252
|
// as the channel basis.
|
28145
|
|
- if (typeof channels[mLine.type] === 'undefined') {
|
28146
|
|
- channels[mLine.type] = mLine;
|
|
28253
|
+ if (typeof type2bl[unifiedLine.type] === 'undefined') {
|
|
28254
|
+ type2bl[unifiedLine.type] = unifiedLine;
|
28147
|
28255
|
}
|
28148
|
28256
|
|
28149
|
28257
|
// Add sources to the channel and handle a=msid.
|
28150
|
|
- if (typeof mLine.sources === 'object') {
|
28151
|
|
- Object.keys(mLine.sources).forEach(function(ssrc) {
|
28152
|
|
- if (typeof channels[mLine.type].sources !== 'object')
|
28153
|
|
- channels[mLine.type].sources = {};
|
|
28258
|
+ if (typeof unifiedLine.sources === 'object') {
|
|
28259
|
+ Object.keys(unifiedLine.sources).forEach(function(ssrc) {
|
|
28260
|
+ if (typeof type2bl[unifiedLine.type].sources !== 'object')
|
|
28261
|
+ type2bl[unifiedLine.type].sources = {};
|
28154
|
28262
|
|
28155
|
28263
|
// Assign the sources to the channel.
|
28156
|
|
- channels[mLine.type].sources[ssrc] = mLine.sources[ssrc];
|
|
28264
|
+ type2bl[unifiedLine.type].sources[ssrc] = unifiedLine.sources[ssrc];
|
28157
|
28265
|
|
28158
|
|
- if (typeof mLine.msid !== 'undefined') {
|
|
28266
|
+ if (typeof unifiedLine.msid !== 'undefined') {
|
28159
|
28267
|
// In Plan B the msid is an SSRC attribute. Also, we don't
|
28160
|
28268
|
// care about the obsolete label and mslabel attributes.
|
28161
|
28269
|
//
|
28162
|
|
- // Note that it is not guaranteed that the mLine will have
|
|
28270
|
+ // Note that it is not guaranteed that the unifiedLine will have
|
28163
|
28271
|
// an msid. recvonly channels in particular don't have one.
|
28164
|
|
- channels[mLine.type].sources[ssrc].msid = mLine.msid;
|
|
28272
|
+ type2bl[unifiedLine.type].sources[ssrc].msid = unifiedLine.msid;
|
28165
|
28273
|
}
|
28166
|
28274
|
// NOTE ssrcs in ssrc groups will share msids, as
|
28167
|
28275
|
// draft-uberti-rtcweb-plan-00 mandates.
|
|
@@ -28169,39 +28277,39 @@ Interop.prototype.toPlanB = function(desc) {
|
28169
|
28277
|
}
|
28170
|
28278
|
|
28171
|
28279
|
// Add ssrc groups to the channel.
|
28172
|
|
- if (typeof mLine.ssrcGroups !== 'undefined' &&
|
28173
|
|
- Array.isArray(mLine.ssrcGroups)) {
|
|
28280
|
+ if (typeof unifiedLine.ssrcGroups !== 'undefined' &&
|
|
28281
|
+ Array.isArray(unifiedLine.ssrcGroups)) {
|
28174
|
28282
|
|
28175
|
28283
|
// Create the ssrcGroups array, if it's not defined.
|
28176
|
|
- if (typeof channel.ssrcGroups === 'undefined' ||
|
28177
|
|
- !Array.isArray(channel.ssrcGroups)) {
|
28178
|
|
- channel.ssrcGroups = [];
|
|
28284
|
+ if (typeof type2bl[unifiedLine.type].ssrcGroups === 'undefined' ||
|
|
28285
|
+ !Array.isArray(type2bl[unifiedLine.type].ssrcGroups)) {
|
|
28286
|
+ type2bl[unifiedLine.type].ssrcGroups = [];
|
28179
|
28287
|
}
|
28180
|
28288
|
|
28181
|
|
- channel.ssrcGroups = channel.ssrcGroups.concat(mLine.ssrcGroups);
|
|
28289
|
+ type2bl[unifiedLine.type].ssrcGroups = type2bl[unifiedLine.type].ssrcGroups.concat(unifiedLine.ssrcGroups);
|
28182
|
28290
|
}
|
28183
|
28291
|
|
28184
|
|
- if (channels[mLine.type] === mLine) {
|
|
28292
|
+ if (type2bl[unifiedLine.type] === unifiedLine) {
|
28185
|
28293
|
// Copy ICE related stuff from the principal media line.
|
28186
|
|
- mLine.candidates = media[0].candidates;
|
28187
|
|
- mLine.iceUfrag = media[0].iceUfrag;
|
28188
|
|
- mLine.icePwd = media[0].icePwd;
|
28189
|
|
- mLine.fingerprint = media[0].fingerprint;
|
|
28294
|
+ unifiedLine.candidates = media[0].candidates;
|
|
28295
|
+ unifiedLine.iceUfrag = media[0].iceUfrag;
|
|
28296
|
+ unifiedLine.icePwd = media[0].icePwd;
|
|
28297
|
+ unifiedLine.fingerprint = media[0].fingerprint;
|
28190
|
28298
|
|
28191
|
28299
|
// Plan B mids are in ['audio', 'video', 'data']
|
28192
|
|
- mLine.mid = mLine.type;
|
|
28300
|
+ unifiedLine.mid = unifiedLine.type;
|
28193
|
28301
|
|
28194
|
28302
|
// Plan B doesn't support/need the bundle-only attribute.
|
28195
|
|
- delete mLine.bundleOnly;
|
|
28303
|
+ delete unifiedLine.bundleOnly;
|
28196
|
28304
|
|
28197
|
28305
|
// In Plan B the msid is an SSRC attribute.
|
28198
|
|
- delete mLine.msid;
|
|
28306
|
+ delete unifiedLine.msid;
|
28199
|
28307
|
|
28200
|
28308
|
// Used to build the group:BUNDLE value after this loop.
|
28201
|
|
- types.push(mLine.type);
|
|
28309
|
+ types.push(unifiedLine.type);
|
28202
|
28310
|
|
28203
|
28311
|
// Add the channel to the new media array.
|
28204
|
|
- session.media.push(mLine);
|
|
28312
|
+ session.media.push(unifiedLine);
|
28205
|
28313
|
}
|
28206
|
28314
|
});
|
28207
|
28315
|
|
|
@@ -28317,34 +28425,35 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28317
|
28425
|
|
28318
|
28426
|
// A helper map that sends mids to m-line objects. We use it later to
|
28319
|
28427
|
// rebuild the Unified Plan style session.media array.
|
28320
|
|
- var mid2ml = {};
|
28321
|
|
- session.media.forEach(function(channel) {
|
28322
|
|
- if (typeof channel.rtcpMux !== 'string' ||
|
28323
|
|
- channel.rtcpMux !== 'rtcp-mux') {
|
|
28428
|
+ var mid2ul = {};
|
|
28429
|
+ session.media.forEach(function(bLine) {
|
|
28430
|
+ if ((typeof bLine.rtcpMux !== 'string' ||
|
|
28431
|
+ bLine.rtcpMux !== 'rtcp-mux') &&
|
|
28432
|
+ bLine.direction !== 'inactive') {
|
28324
|
28433
|
throw new Error("Cannot convert to Unified Plan because m-lines " +
|
28325
|
28434
|
"without the rtcp-mux attribute were found.");
|
28326
|
28435
|
}
|
28327
|
28436
|
|
28328
|
28437
|
// With rtcp-mux and bundle all the channels should have the same ICE
|
28329
|
28438
|
// stuff.
|
28330
|
|
- var sources = channel.sources;
|
28331
|
|
- var ssrcGroups = channel.ssrcGroups;
|
28332
|
|
- var candidates = channel.candidates;
|
28333
|
|
- var iceUfrag = channel.iceUfrag;
|
28334
|
|
- var icePwd = channel.icePwd;
|
28335
|
|
- var fingerprint = channel.fingerprint;
|
28336
|
|
- var port = channel.port;
|
28337
|
|
-
|
28338
|
|
- // We'll use the "channel" object as a prototype for each new "mLine"
|
|
28439
|
+ var sources = bLine.sources;
|
|
28440
|
+ var ssrcGroups = bLine.ssrcGroups;
|
|
28441
|
+ var candidates = bLine.candidates;
|
|
28442
|
+ var iceUfrag = bLine.iceUfrag;
|
|
28443
|
+ var icePwd = bLine.icePwd;
|
|
28444
|
+ var fingerprint = bLine.fingerprint;
|
|
28445
|
+ var port = bLine.port;
|
|
28446
|
+
|
|
28447
|
+ // We'll use the "bLine" object as a prototype for each new "mLine"
|
28339
|
28448
|
// that we create, but first we need to clean it up a bit.
|
28340
|
|
- delete channel.sources;
|
28341
|
|
- delete channel.ssrcGroups;
|
28342
|
|
- delete channel.candidates;
|
28343
|
|
- delete channel.iceUfrag;
|
28344
|
|
- delete channel.icePwd;
|
28345
|
|
- delete channel.fingerprint;
|
28346
|
|
- delete channel.port;
|
28347
|
|
- delete channel.mid;
|
|
28449
|
+ delete bLine.sources;
|
|
28450
|
+ delete bLine.ssrcGroups;
|
|
28451
|
+ delete bLine.candidates;
|
|
28452
|
+ delete bLine.iceUfrag;
|
|
28453
|
+ delete bLine.icePwd;
|
|
28454
|
+ delete bLine.fingerprint;
|
|
28455
|
+ delete bLine.port;
|
|
28456
|
+ delete bLine.mid;
|
28348
|
28457
|
|
28349
|
28458
|
// inverted ssrc group map
|
28350
|
28459
|
var ssrc2group = {};
|
|
@@ -28378,11 +28487,11 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28378
|
28487
|
// Explode the Plan B channel sources with one m-line per source.
|
28379
|
28488
|
Object.keys(sources).forEach(function(ssrc) {
|
28380
|
28489
|
|
28381
|
|
- // The m-line for this SSRC. We either create it from scratch
|
28382
|
|
- // or, if it's a grouped SSRC, we re-use a related mline. In
|
28383
|
|
- // other words, if the source is grouped with another source,
|
28384
|
|
- // put the two together in the same m-line.
|
28385
|
|
- var mLine;
|
|
28490
|
+ // The (unified) m-line for this SSRC. We either create it from
|
|
28491
|
+ // scratch or, if it's a grouped SSRC, we re-use a related
|
|
28492
|
+ // mline. In other words, if the source is grouped with another
|
|
28493
|
+ // source, put the two together in the same m-line.
|
|
28494
|
+ var unifiedLine;
|
28386
|
28495
|
if (typeof ssrc2group[ssrc] !== 'undefined' &&
|
28387
|
28496
|
Array.isArray(ssrc2group[ssrc])) {
|
28388
|
28497
|
ssrc2group[ssrc].some(function (ssrcGroup) {
|
|
@@ -28390,21 +28499,21 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28390
|
28499
|
// again here.
|
28391
|
28500
|
return ssrcGroup.ssrcs.some(function (related) {
|
28392
|
28501
|
if (typeof ssrc2ml[related] === 'object') {
|
28393
|
|
- mLine = ssrc2ml[related];
|
|
28502
|
+ unifiedLine = ssrc2ml[related];
|
28394
|
28503
|
return true;
|
28395
|
28504
|
}
|
28396
|
28505
|
});
|
28397
|
28506
|
});
|
28398
|
28507
|
}
|
28399
|
28508
|
|
28400
|
|
- if (typeof mLine === 'object') {
|
|
28509
|
+ if (typeof unifiedLine === 'object') {
|
28401
|
28510
|
// the m-line already exists. Just add the source.
|
28402
|
|
- mLine.sources[ssrc] = sources[ssrc];
|
|
28511
|
+ unifiedLine.sources[ssrc] = sources[ssrc];
|
28403
|
28512
|
delete sources[ssrc].msid;
|
28404
|
28513
|
} else {
|
28405
|
|
- // Use the "channel" as a prototype for the "mLine".
|
28406
|
|
- mLine = Object.create(channel);
|
28407
|
|
- ssrc2ml[ssrc] = mLine;
|
|
28514
|
+ // Use the "bLine" as a prototype for the "unifiedLine".
|
|
28515
|
+ unifiedLine = Object.create(bLine);
|
|
28516
|
+ ssrc2ml[ssrc] = unifiedLine;
|
28408
|
28517
|
|
28409
|
28518
|
if (typeof sources[ssrc].msid !== 'undefined') {
|
28410
|
28519
|
// Assign the msid of the source to the m-line. Note
|
|
@@ -28412,14 +28521,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28412
|
28521
|
// msid. In particular "recvonly" sources don't have an
|
28413
|
28522
|
// msid. Note that "recvonly" is a term only defined
|
28414
|
28523
|
// for m-lines.
|
28415
|
|
- mLine.msid = sources[ssrc].msid;
|
|
28524
|
+ unifiedLine.msid = sources[ssrc].msid;
|
28416
|
28525
|
delete sources[ssrc].msid;
|
28417
|
28526
|
}
|
28418
|
28527
|
|
28419
|
28528
|
// We assign one SSRC per media line.
|
28420
|
|
- mLine.sources = {};
|
28421
|
|
- mLine.sources[ssrc] = sources[ssrc];
|
28422
|
|
- mLine.ssrcGroups = ssrc2group[ssrc];
|
|
28529
|
+ unifiedLine.sources = {};
|
|
28530
|
+ unifiedLine.sources[ssrc] = sources[ssrc];
|
|
28531
|
+ unifiedLine.ssrcGroups = ssrc2group[ssrc];
|
28423
|
28532
|
|
28424
|
28533
|
// Use the cached Unified Plan SDP (if it exists) to assign
|
28425
|
28534
|
// SSRCs to mids.
|
|
@@ -28431,14 +28540,14 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28431
|
28540
|
if (typeof m.sources === 'object') {
|
28432
|
28541
|
Object.keys(m.sources).forEach(function (s) {
|
28433
|
28542
|
if (s === ssrc) {
|
28434
|
|
- mLine.mid = m.mid;
|
|
28543
|
+ unifiedLine.mid = m.mid;
|
28435
|
28544
|
}
|
28436
|
28545
|
});
|
28437
|
28546
|
}
|
28438
|
28547
|
});
|
28439
|
28548
|
}
|
28440
|
28549
|
|
28441
|
|
- if (typeof mLine.mid === 'undefined') {
|
|
28550
|
+ if (typeof unifiedLine.mid === 'undefined') {
|
28442
|
28551
|
|
28443
|
28552
|
// If this is an SSRC that we see for the first time
|
28444
|
28553
|
// assign it a new mid. This is typically the case when
|
|
@@ -28451,23 +28560,23 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28451
|
28560
|
//
|
28452
|
28561
|
// Because FF generates answers in Unified Plan style,
|
28453
|
28562
|
// we MUST already have a cached answer with all the
|
28454
|
|
- // local SSRCs mapped to some mLine/mid.
|
|
28563
|
+ // local SSRCs mapped to some m-line/mid.
|
28455
|
28564
|
|
28456
|
28565
|
if (desc.type === 'answer') {
|
28457
|
28566
|
throw new Error("An unmapped SSRC was found.");
|
28458
|
28567
|
}
|
28459
|
28568
|
|
28460
|
|
- mLine.mid = [channel.type, '-', ssrc].join('');
|
|
28569
|
+ unifiedLine.mid = [bLine.type, '-', ssrc].join('');
|
28461
|
28570
|
}
|
28462
|
28571
|
|
28463
|
28572
|
// Include the candidates in the 1st media line.
|
28464
|
|
- mLine.candidates = candidates;
|
28465
|
|
- mLine.iceUfrag = iceUfrag;
|
28466
|
|
- mLine.icePwd = icePwd;
|
28467
|
|
- mLine.fingerprint = fingerprint;
|
28468
|
|
- mLine.port = port;
|
|
28573
|
+ unifiedLine.candidates = candidates;
|
|
28574
|
+ unifiedLine.iceUfrag = iceUfrag;
|
|
28575
|
+ unifiedLine.icePwd = icePwd;
|
|
28576
|
+ unifiedLine.fingerprint = fingerprint;
|
|
28577
|
+ unifiedLine.port = port;
|
28469
|
28578
|
|
28470
|
|
- mid2ml[mLine.mid] = mLine;
|
|
28579
|
+ mid2ul[unifiedLine.mid] = unifiedLine;
|
28471
|
28580
|
}
|
28472
|
28581
|
});
|
28473
|
28582
|
}
|
|
@@ -28481,71 +28590,57 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28481
|
28590
|
if (desc.type === 'answer') {
|
28482
|
28591
|
|
28483
|
28592
|
// The media lines in the answer must match the media lines in the
|
28484
|
|
- // offer. The order is important too. Here we use the cached offer to
|
28485
|
|
- // find the m-lines that are missing (from the converted answer), and
|
28486
|
|
- // use the cached answer to complete the converted answer.
|
28487
|
|
-
|
28488
|
|
- if (typeof this.cache['offer'] === 'undefined') {
|
28489
|
|
- throw new Error("An answer is being processed but we couldn't " +
|
28490
|
|
- "find a cached offer.");
|
28491
|
|
- }
|
28492
|
|
-
|
28493
|
|
- var cachedOffer = transform.parse(this.cache['offer']);
|
28494
|
|
-
|
28495
|
|
- if (typeof cachedOffer === 'undefined' ||
|
28496
|
|
- typeof cachedOffer.media === 'undefined' ||
|
28497
|
|
- !Array.isArray(cachedOffer.media)) {
|
28498
|
|
- // FIXME(gp) is this really a problem in the general case?
|
28499
|
|
- throw new Error("The cached offer has no media.");
|
28500
|
|
- }
|
28501
|
|
-
|
28502
|
|
- cachedOffer.media.forEach(function(mo) {
|
28503
|
|
-
|
28504
|
|
- var mLine;
|
28505
|
|
- cached.media.some(function(ma) {
|
28506
|
|
- if (mo.mid == ma.mid) {
|
28507
|
|
- if (typeof mid2ml[mo.mid] === 'undefined') {
|
28508
|
|
-
|
28509
|
|
- // This is either an m-line containing a remote
|
28510
|
|
- // track only, or an m-line containing a remote
|
28511
|
|
- // track and a local track that has been removed.
|
28512
|
|
- // In either case, it MUST exist in the cached
|
28513
|
|
- // answer.
|
28514
|
|
- //
|
28515
|
|
- // In case this is a removed local track, clean-up
|
28516
|
|
- // the m-line and make sure it's 'recvonly'.
|
28517
|
|
-
|
28518
|
|
- // TODO sendonly -> inactive makes more sense.
|
28519
|
|
- delete ma.msid;
|
28520
|
|
- delete ma.sources;
|
28521
|
|
- delete ma.ssrcGroups;
|
28522
|
|
- if (!ma.direction
|
28523
|
|
- || ma.direction === 'sendonly'
|
28524
|
|
- || ma.direction === 'sendrecv')
|
28525
|
|
- ma.direction = 'recvonly';
|
28526
|
|
- } else {
|
28527
|
|
- // This is an m-line/channel that contains a local
|
28528
|
|
- // track (sendrecv or sendonly channel) or it's a
|
28529
|
|
- // recvonly m-line/channel. In either case, since we're
|
28530
|
|
- // going from PlanB -> Unified Plan this m-line MUST
|
28531
|
|
- // exist in the cached answer.
|
28532
|
|
- }
|
|
28593
|
+ // offer. The order is important too. Here we assume that Firefox is the
|
|
28594
|
+ // answerer, so we merely have to use the reconstructed (unified) answer
|
|
28595
|
+ // to update the cached (unified) answer accordingly.
|
|
28596
|
+ //
|
|
28597
|
+ // In the general case, one would have to use the cached (unified) offer
|
|
28598
|
+ // to find the m-lines that are missing from the reconstructed answer,
|
|
28599
|
+ // potentially grabbing them from the cached (unified) answer. One has
|
|
28600
|
+ // to be carefull with this approach because inactive m-lines do not
|
|
28601
|
+ // always have an mid, making it tricky (impossible?) to find where
|
|
28602
|
+ // exactly and which m-lines are missing from the reconstructed answer.
|
|
28603
|
+
|
|
28604
|
+ for (var i = 0; i < cached.media.length; i++) {
|
|
28605
|
+ var unifiedLine = cached.media[i];
|
|
28606
|
+
|
|
28607
|
+ if (typeof mid2ul[unifiedLine.mid] === 'undefined') {
|
|
28608
|
+
|
|
28609
|
+ // The mid isn't in the reconstructed (unified) answer.
|
|
28610
|
+ // This is either a (unified) m-line containing a remote
|
|
28611
|
+ // track only, or a (unified) m-line containing a remote
|
|
28612
|
+ // track and a local track that has been removed.
|
|
28613
|
+ // In either case, it MUST exist in the cached
|
|
28614
|
+ // (unified) answer.
|
|
28615
|
+ //
|
|
28616
|
+ // In case this is a removed local track, clean-up
|
|
28617
|
+ // the (unified) m-line and make sure it's 'recvonly' or
|
|
28618
|
+ // 'inactive'.
|
|
28619
|
+
|
|
28620
|
+ delete unifiedLine.msid;
|
|
28621
|
+ delete unifiedLine.sources;
|
|
28622
|
+ delete unifiedLine.ssrcGroups;
|
|
28623
|
+ if (!unifiedLine.direction
|
|
28624
|
+ || unifiedLine.direction === 'sendrecv')
|
|
28625
|
+ unifiedLine.direction = 'recvonly';
|
|
28626
|
+ if (!unifiedLine.direction
|
|
28627
|
+ || unifiedLine.direction === 'sendonly')
|
|
28628
|
+ unifiedLine.direction = 'inactive';
|
|
28629
|
+ } else {
|
|
28630
|
+ // This is an (unified) m-line/channel that contains a local
|
|
28631
|
+ // track (sendrecv or sendonly channel) or it's a unified
|
|
28632
|
+ // recvonly m-line/channel. In either case, since we're
|
|
28633
|
+ // going from PlanB -> Unified Plan this m-line MUST
|
|
28634
|
+ // exist in the cached answer.
|
|
28635
|
+ }
|
28533
|
28636
|
|
28534
|
|
- // assign the found object.
|
28535
|
|
- mLine = ma;
|
28536
|
|
- return true;
|
28537
|
|
- }
|
28538
|
|
- });
|
|
28637
|
+ session.media.push(unifiedLine);
|
28539
|
28638
|
|
28540
|
|
- if (typeof mLine === 'undefined') {
|
28541
|
|
- throw new Error("The cached offer contains an m-line that " +
|
28542
|
|
- "doesn't exist neither in the cached answer nor in " +
|
28543
|
|
- "the converted answer.");
|
|
28639
|
+ if (typeof unifiedLine.mid === 'string') {
|
|
28640
|
+ // inactive lines don't/may not have an mid.
|
|
28641
|
+ mids.push(unifiedLine.mid);
|
28544
|
28642
|
}
|
28545
|
|
-
|
28546
|
|
- session.media.push(mLine);
|
28547
|
|
- mids.push(mLine.mid);
|
28548
|
|
- });
|
|
28643
|
+ }
|
28549
|
28644
|
} else {
|
28550
|
28645
|
|
28551
|
28646
|
// SDP offer/answer (and the JSEP spec) forbids removing an m-section
|
|
@@ -28559,41 +28654,48 @@ Interop.prototype.toUnifiedPlan = function(desc) {
|
28559
|
28654
|
if (typeof cached !== 'undefined' &&
|
28560
|
28655
|
typeof cached.media !== 'undefined' &&
|
28561
|
28656
|
Array.isArray(cached.media)) {
|
28562
|
|
- cached.media.forEach(function(pm) {
|
28563
|
|
- mids.push(pm.mid);
|
28564
|
|
- if (typeof mid2ml[pm.mid] !== 'undefined') {
|
28565
|
|
- session.media.push(mid2ml[pm.mid]);
|
|
28657
|
+ cached.media.forEach(function(unifiedLine) {
|
|
28658
|
+ mids.push(unifiedLine.mid);
|
|
28659
|
+ if (typeof mid2ul[unifiedLine.mid] !== 'undefined') {
|
|
28660
|
+ session.media.push(mid2ul[unifiedLine.mid]);
|
28566
|
28661
|
} else {
|
28567
|
|
- delete pm.msid;
|
28568
|
|
- delete pm.sources;
|
28569
|
|
- delete pm.ssrcGroups;
|
28570
|
|
- pm.direction = 'recvonly';
|
28571
|
|
- session.media.push(pm);
|
|
28662
|
+ delete unifiedLine.msid;
|
|
28663
|
+ delete unifiedLine.sources;
|
|
28664
|
+ delete unifiedLine.ssrcGroups;
|
|
28665
|
+ if (!unifiedLine.direction
|
|
28666
|
+ || unifiedLine.direction === 'sendrecv')
|
|
28667
|
+ unifiedLine.direction = 'recvonly';
|
|
28668
|
+ if (!unifiedLine.direction
|
|
28669
|
+ || unifiedLine.direction === 'sendonly')
|
|
28670
|
+ unifiedLine.direction = 'inactive';
|
|
28671
|
+ session.media.push(unifiedLine);
|
28572
|
28672
|
}
|
28573
|
28673
|
});
|
28574
|
28674
|
}
|
28575
|
28675
|
|
28576
|
28676
|
// Add all the remaining (new) m-lines of the transformed SDP.
|
28577
|
|
- Object.keys(mid2ml).forEach(function(mid) {
|
|
28677
|
+ Object.keys(mid2ul).forEach(function(mid) {
|
28578
|
28678
|
if (mids.indexOf(mid) === -1) {
|
28579
|
28679
|
mids.push(mid);
|
28580
|
|
- if (typeof mid2ml[mid].msid === 'undefined') {
|
|
28680
|
+ if (typeof mid2ul[mid].direction === 'recvonly') {
|
28581
|
28681
|
// This is a remote recvonly channel. Add its SSRC to the
|
28582
|
|
- // sendrecv channel.
|
28583
|
|
- // TODO(gp) what if there is no sendrecv channel?
|
28584
|
|
- session.media.some(function (ml) {
|
28585
|
|
- if (ml.direction === 'sendrecv' && ml.type == mid2ml[mid].type) {
|
28586
|
|
-
|
28587
|
|
- // this shouldn't have any ssrc-groups
|
28588
|
|
- Object.keys(mid2ml[mid].sources).forEach(function (ssrc) {
|
28589
|
|
- ml.sources[ssrc] = mid2ml[mid].sources[ssrc];
|
|
28682
|
+ // appropriate sendrecv or sendonly channel.
|
|
28683
|
+ // TODO(gp) what if we don't have sendrecv/sendonly channel?
|
|
28684
|
+ session.media.some(function (unifiedLine) {
|
|
28685
|
+ if ((unifiedLine.direction === 'sendrecv' ||
|
|
28686
|
+ unifiedLine.direction === 'sendonly') &&
|
|
28687
|
+ unifiedLine.type === mid2ul[mid].type) {
|
|
28688
|
+
|
|
28689
|
+ // mid2ul[mid] shouldn't have any ssrc-groups
|
|
28690
|
+ Object.keys(mid2ul[mid].sources).forEach(function (ssrc) {
|
|
28691
|
+ unifiedLine.sources[ssrc] = mid2ul[mid].sources[ssrc];
|
28590
|
28692
|
});
|
28591
|
28693
|
|
28592
|
28694
|
return true;
|
28593
|
28695
|
}
|
28594
|
28696
|
});
|
28595
|
28697
|
} else {
|
28596
|
|
- session.media.push(mid2ml[mid]);
|
|
28698
|
+ session.media.push(mid2ul[mid]);
|
28597
|
28699
|
}
|
28598
|
28700
|
}
|
28599
|
28701
|
});
|
|
@@ -29388,7 +29490,8 @@ var XMPPEvents = {
|
29388
|
29490
|
AUTHENTICATION_REQUIRED: "xmpp.authentication_required",
|
29389
|
29491
|
CHAT_ERROR_RECEIVED: "xmpp.chat_error_received",
|
29390
|
29492
|
ETHERPAD: "xmpp.etherpad",
|
29391
|
|
- DEVICE_AVAILABLE: "xmpp.device_available"
|
|
29493
|
+ DEVICE_AVAILABLE: "xmpp.device_available",
|
|
29494
|
+ START_MUTED: "xmpp.start_muted"
|
29392
|
29495
|
};
|
29393
|
29496
|
module.exports = XMPPEvents;
|
29394
|
29497
|
},{}],106:[function(require,module,exports){
|