Browse Source

Removes some global variables. Fixes recording.

master
hristoterezov 10 years ago
parent
commit
6c4a5bd2bc

+ 0
- 16
app.js View File

1
 /* jshint -W117 */
1
 /* jshint -W117 */
2
 /* application specific logic */
2
 /* application specific logic */
3
 var nickname = null;
3
 var nickname = null;
4
-var focusMucJid = null;
5
 var ssrc2jid = {};
4
 var ssrc2jid = {};
6
-//TODO: this array must be removed when firefox implement multistream support
7
-var notReceivedSSRCs = [];
8
-
9
-var jid2Ssrc = {};
10
-
11
-/**
12
- * Indicates whether ssrc is camera video or desktop stream.
13
- * FIXME: remove those maps
14
- */
15
-var ssrc2videoType = {};
16
-/**
17
- * Currently focused video "src"(displayed in large video).
18
- * @type {String}
19
- */
20
-var focusedVideoInfo = null;
21
 
5
 
22
 function init() {
6
 function init() {
23
 
7
 

+ 5
- 5
index.html View File

27
     <script src="service/desktopsharing/DesktopSharingEventTypes.js?v=1"></script>
27
     <script src="service/desktopsharing/DesktopSharingEventTypes.js?v=1"></script>
28
     <script src="libs/modules/simulcast.bundle.js?v=4"></script>
28
     <script src="libs/modules/simulcast.bundle.js?v=4"></script>
29
     <script src="libs/modules/connectionquality.bundle.js?v=2"></script>
29
     <script src="libs/modules/connectionquality.bundle.js?v=2"></script>
30
-    <script src="libs/modules/UI.bundle.js?v=6"></script>
31
-    <script src="libs/modules/statistics.bundle.js?v=2"></script>
32
-    <script src="libs/modules/RTC.bundle.js?v=5"></script>
30
+    <script src="libs/modules/UI.bundle.js?v=7"></script>
31
+    <script src="libs/modules/statistics.bundle.js?v=3"></script>
32
+    <script src="libs/modules/RTC.bundle.js?v=6"></script>
33
     <script src="libs/modules/desktopsharing.bundle.js?v=3"></script><!-- desktop sharing -->
33
     <script src="libs/modules/desktopsharing.bundle.js?v=3"></script><!-- desktop sharing -->
34
     <script src="util.js?v=7"></script><!-- utility functions -->
34
     <script src="util.js?v=7"></script><!-- utility functions -->
35
-    <script src="libs/modules/xmpp.bundle.js?v=1"></script>
36
-    <script src="app.js?v=27"></script><!-- application logic -->
35
+    <script src="libs/modules/xmpp.bundle.js?v=2"></script>
36
+    <script src="app.js?v=28"></script><!-- application logic -->
37
     <script src="libs/modules/API.bundle.js?v=1"></script>
37
     <script src="libs/modules/API.bundle.js?v=1"></script>
38
 
38
 
39
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
39
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->

+ 46
- 4
libs/modules/RTC.bundle.js View File

240
 },{}],2:[function(require,module,exports){
240
 },{}],2:[function(require,module,exports){
241
 //var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
241
 //var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
242
 
242
 
243
-function LocalStream(stream, type, eventEmitter)
243
+function LocalStream(stream, type, eventEmitter, videoType)
244
 {
244
 {
245
     this.stream = stream;
245
     this.stream = stream;
246
     this.eventEmitter = eventEmitter;
246
     this.eventEmitter = eventEmitter;
247
     this.type = type;
247
     this.type = type;
248
+    this.videoType = videoType;
248
     var self = this;
249
     var self = this;
249
     if(type == "audio")
250
     if(type == "audio")
250
     {
251
     {
359
     this.ssrc = ssrc;
360
     this.ssrc = ssrc;
360
     this.type = (this.stream.getVideoTracks().length > 0)?
361
     this.type = (this.stream.getVideoTracks().length > 0)?
361
         MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
362
         MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
363
+    this.videoType = null;
362
     this.muted = false;
364
     this.muted = false;
363
     if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX)
365
     if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX)
364
     {
366
     {
493
             function (stream, isUsingScreenStream, callback) {
495
             function (stream, isUsingScreenStream, callback) {
494
                 self.changeLocalVideo(stream, isUsingScreenStream, callback);
496
                 self.changeLocalVideo(stream, isUsingScreenStream, callback);
495
             }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
497
             }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
498
+        xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
499
+            for(var i = 0; i < changedStreams.length; i++) {
500
+                var type = changedStreams[i].type;
501
+                if (type != "audio") {
502
+                    var peerStreams = self.remoteStreams[jid];
503
+                    if(!peerStreams)
504
+                        continue;
505
+                    var videoStream = peerStreams[MediaStreamType.VIDEO_TYPE];
506
+                    if(!videoStream)
507
+                        continue;
508
+                    videoStream.videoType = changedStreams[i].type;
509
+                }
510
+            }
511
+        })
496
         this.rtcUtils = new RTCUtils(this);
512
         this.rtcUtils = new RTCUtils(this);
497
         this.rtcUtils.obtainAudioAndVideoPermissions();
513
         this.rtcUtils.obtainAudioAndVideoPermissions();
498
     },
514
     },
529
     },
545
     },
530
     changeLocalVideo: function (stream, isUsingScreenStream, callback) {
546
     changeLocalVideo: function (stream, isUsingScreenStream, callback) {
531
         var oldStream = this.localVideo.getOriginalStream();
547
         var oldStream = this.localVideo.getOriginalStream();
532
-        var type = (isUsingScreenStream? "desktop" : "video");
533
-        RTC.localVideo = this.createLocalStream(stream, type, true);
548
+        var type = (isUsingScreenStream? "screen" : "video");
549
+        RTC.localVideo = this.createLocalStream(stream, "video", true, type);
534
         // Stop the stream to trigger onended event for old stream
550
         // Stop the stream to trigger onended event for old stream
535
         oldStream.stop();
551
         oldStream.stop();
536
         xmpp.switchStreams(stream, oldStream,callback);
552
         xmpp.switchStreams(stream, oldStream,callback);
537
-    }
553
+    },
554
+    /**
555
+     * Checks if video identified by given src is desktop stream.
556
+     * @param videoSrc eg.
557
+     * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
558
+     * @returns {boolean}
559
+     */
560
+    isVideoSrcDesktop: function (jid) {
561
+        if(!jid)
562
+            return false;
563
+        var isDesktop = false;
564
+        var stream = null;
565
+        if (xmpp.myJid() &&
566
+            xmpp.myResource() === jid) {
567
+            // local video
568
+            stream = this.localVideo;
569
+        } else {
570
+            var peerStreams = this.remoteStreams[jid];
571
+            if(!peerStreams)
572
+                return false;
573
+            stream = peerStreams[MediaStreamType.VIDEO_TYPE];
574
+        }
538
 
575
 
576
+        if(stream)
577
+            isDesktop = (stream.videoType === "screen");
578
+
579
+        return isDesktop;
580
+    }
539
 };
581
 };
540
 
582
 
541
 module.exports = RTC;
583
 module.exports = RTC;

+ 18
- 62
libs/modules/UI.bundle.js View File

59
         case "stream":
59
         case "stream":
60
             VideoLayout.changeLocalStream(stream);
60
             VideoLayout.changeLocalStream(stream);
61
             break;
61
             break;
62
-        case "desktop":
63
-            VideoLayout.changeLocalVideo(stream);
64
-            break;
65
     }
62
     }
66
 }
63
 }
67
 
64
 
355
         }
352
         }
356
     }, 10);
353
     }, 10);
357
 
354
 
358
-    // Unlock large video
359
-    if (focusedVideoInfo && focusedVideoInfo.jid === jid)
360
-    {
361
-        console.info("Focused video owner has left the conference");
362
-        focusedVideoInfo = null;
363
-    }
355
+    VideoLayout.participantLeft(jid);
364
 
356
 
365
 };
357
 };
366
 
358
 
4402
     updateInProgress: false,
4394
     updateInProgress: false,
4403
     newSrc: ''
4395
     newSrc: ''
4404
 };
4396
 };
4397
+/**
4398
+ * Currently focused video "src"(displayed in large video).
4399
+ * @type {String}
4400
+ */
4401
+var focusedVideoInfo = null;
4405
 
4402
 
4406
 /**
4403
 /**
4407
  * Indicates if we have muted our audio before the conference has started.
4404
  * Indicates if we have muted our audio before the conference has started.
4479
     if (selector[0].currentTime > 0) {
4476
     if (selector[0].currentTime > 0) {
4480
         var videoStream = simulcast.getReceivingVideoStream(stream);
4477
         var videoStream = simulcast.getReceivingVideoStream(stream);
4481
         RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
4478
         RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
4482
-
4483
-        // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
4484
-        //        in order to get rid of too many maps
4485
-        if (ssrc && jid) {
4486
-            jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
4487
-        } else {
4488
-            console.warn("No ssrc given for jid", jid);
4489
-        }
4490
-
4491
         videoactive(selector);
4479
         videoactive(selector);
4492
     } else {
4480
     } else {
4493
         setTimeout(function () {
4481
         setTimeout(function () {
4879
 }
4867
 }
4880
 
4868
 
4881
 
4869
 
4882
-/**
4883
- * Checks if video identified by given src is desktop stream.
4884
- * @param videoSrc eg.
4885
- * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
4886
- * @returns {boolean}
4887
- */
4888
-function isVideoSrcDesktop(jid) {
4889
-    // FIXME: fix this mapping mess...
4890
-    // figure out if large video is desktop stream or just a camera
4891
-
4892
-    if(!jid)
4893
-        return false;
4894
-    var isDesktop = false;
4895
-    if (xmpp.myJid() &&
4896
-        xmpp.myResource() === jid) {
4897
-        // local video
4898
-        isDesktop = desktopsharing.isUsingScreenStream();
4899
-    } else {
4900
-        // Do we have associations...
4901
-        var videoSsrc = jid2Ssrc[jid];
4902
-        if (videoSsrc) {
4903
-            var videoType = ssrc2videoType[videoSsrc];
4904
-            if (videoType) {
4905
-                // Finally there...
4906
-                isDesktop = videoType === 'screen';
4907
-            } else {
4908
-                console.error("No video type for ssrc: " + videoSsrc);
4909
-            }
4910
-        } else {
4911
-            console.error("No ssrc for jid: " + jid);
4912
-        }
4913
-    }
4914
-    return isDesktop;
4915
-}
4916
-
4917
-
4918
-
4919
 var VideoLayout = (function (my) {
4870
 var VideoLayout = (function (my) {
4920
     my.connectionIndicators = {};
4871
     my.connectionIndicators = {};
4921
 
4872
 
4958
 
4909
 
4959
     my.changeLocalVideo = function(stream) {
4910
     my.changeLocalVideo = function(stream) {
4960
         var flipX = true;
4911
         var flipX = true;
4961
-        if(stream.type == "desktop")
4912
+        if(stream.videoType == "screen")
4962
             flipX = false;
4913
             flipX = false;
4963
         var localVideo = document.createElement('video');
4914
         var localVideo = document.createElement('video');
4964
         localVideo.id = 'localVideo_' +
4915
         localVideo.id = 'localVideo_' +
5141
 
5092
 
5142
             largeVideoState.newSrc = newSrc;
5093
             largeVideoState.newSrc = newSrc;
5143
             largeVideoState.isVisible = $('#largeVideo').is(':visible');
5094
             largeVideoState.isVisible = $('#largeVideo').is(':visible');
5144
-            largeVideoState.isDesktop = isVideoSrcDesktop(resourceJid);
5145
-            if(jid2Ssrc[largeVideoState.userResourceJid] ||
5146
-                (xmpp.myResource() &&
5147
-                    largeVideoState.userResourceJid ===
5148
-                    xmpp.myResource())) {
5095
+            largeVideoState.isDesktop = RTC.isVideoSrcDesktop(resourceJid);
5096
+            if(largeVideoState.userResourceJid) {
5149
                 largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
5097
                 largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
5150
             } else {
5098
             } else {
5151
                 largeVideoState.oldResourceJid = null;
5099
                 largeVideoState.oldResourceJid = null;
6520
                 }
6468
                 }
6521
 
6469
 
6522
                 var jid = ssrc2jid[primarySSRC];
6470
                 var jid = ssrc2jid[primarySSRC];
6523
-                jid2Ssrc[jid] = primarySSRC;
6524
 
6471
 
6525
                 if (updateLargeVideo) {
6472
                 if (updateLargeVideo) {
6526
                     VideoLayout.updateLargeVideo(RTC.getVideoSrc(selRemoteVideo[0]), null,
6473
                     VideoLayout.updateLargeVideo(RTC.getVideoSrc(selRemoteVideo[0]), null,
6618
         }
6565
         }
6619
     };
6566
     };
6620
 
6567
 
6568
+    my.participantLeft = function (jid) {
6569
+        // Unlock large video
6570
+        if (focusedVideoInfo && focusedVideoInfo.jid === jid)
6571
+        {
6572
+            console.info("Focused video owner has left the conference");
6573
+            focusedVideoInfo = null;
6574
+        }
6575
+    }
6576
+
6621
     return my;
6577
     return my;
6622
 }(VideoLayout || {}));
6578
 }(VideoLayout || {}));
6623
 
6579
 

+ 1
- 1
libs/modules/statistics.bundle.js View File

130
 
130
 
131
 module.exports = LocalStatsCollector;
131
 module.exports = LocalStatsCollector;
132
 },{}],2:[function(require,module,exports){
132
 },{}],2:[function(require,module,exports){
133
-/* global focusMucJid, ssrc2jid */
133
+/* global ssrc2jid */
134
 /* jshint -W117 */
134
 /* jshint -W117 */
135
 /**
135
 /**
136
  * Calculates packet lost percent using the number of lost packets and the
136
  * Calculates packet lost percent using the number of lost packets and the

+ 27
- 24
libs/modules/xmpp.bundle.js View File

54
     this.videoMuteByUser = false;
54
     this.videoMuteByUser = false;
55
 }
55
 }
56
 
56
 
57
+//TODO: this array must be removed when firefox implement multistream support
58
+JingleSession.notReceivedSSRCs = [];
59
+
57
 JingleSession.prototype.initiate = function (peerjid, isInitiator) {
60
 JingleSession.prototype.initiate = function (peerjid, isInitiator) {
58
     var self = this;
61
     var self = this;
59
     if (this.state !== null) {
62
     if (this.state !== null) {
1355
     //TODO: this code should be removed when firefox implement multistream support
1358
     //TODO: this code should be removed when firefox implement multistream support
1356
     if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
1359
     if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
1357
     {
1360
     {
1358
-        if((notReceivedSSRCs.length == 0) ||
1359
-            !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
1361
+        if((JingleSession.notReceivedSSRCs.length == 0) ||
1362
+            !ssrc2jid[JingleSession.notReceivedSSRCs[JingleSession.notReceivedSSRCs.length - 1]])
1360
         {
1363
         {
1361
             // TODO(gp) limit wait duration to 1 sec.
1364
             // TODO(gp) limit wait duration to 1 sec.
1362
             setTimeout(function(d) {
1365
             setTimeout(function(d) {
1367
             return;
1370
             return;
1368
         }
1371
         }
1369
 
1372
 
1370
-        thessrc = notReceivedSSRCs.pop();
1373
+        thessrc = JingleSession.notReceivedSSRCs.pop();
1371
         if (ssrc2jid[thessrc]) {
1374
         if (ssrc2jid[thessrc]) {
1372
             data.peerjid = ssrc2jid[thessrc];
1375
             data.peerjid = ssrc2jid[thessrc];
1373
         }
1376
         }
3079
     recordingToken = token;
3082
     recordingToken = token;
3080
 }
3083
 }
3081
 
3084
 
3082
-function setRecording(state, token, callback) {
3085
+function setRecording(state, token, callback, connection) {
3083
     if (useJirecon){
3086
     if (useJirecon){
3084
-        this.setRecordingJirecon(state, token, callback);
3087
+        this.setRecordingJirecon(state, token, callback, connection);
3085
     } else {
3088
     } else {
3086
-        this.setRecordingColibri(state, token, callback);
3089
+        this.setRecordingColibri(state, token, callback, connection);
3087
     }
3090
     }
3088
 }
3091
 }
3089
 
3092
 
3090
-function setRecordingJirecon(state, token, callback) {
3093
+function setRecordingJirecon(state, token, callback, connection) {
3091
     if (state == recordingEnabled){
3094
     if (state == recordingEnabled){
3092
         return;
3095
         return;
3093
     }
3096
     }
3126
 // Sends a COLIBRI message which enables or disables (according to 'state')
3129
 // Sends a COLIBRI message which enables or disables (according to 'state')
3127
 // the recording on the bridge. Waits for the result IQ and calls 'callback'
3130
 // the recording on the bridge. Waits for the result IQ and calls 'callback'
3128
 // with the new recording state, according to the IQ.
3131
 // with the new recording state, according to the IQ.
3129
-function setRecordingColibri(state, token, callback) {
3130
-    var elem = $iq({to: focusMucJid, type: 'set'});
3132
+function setRecordingColibri(state, token, callback, connection) {
3133
+    var elem = $iq({to: connection.emuc.focusMucJid, type: 'set'});
3131
     elem.c('conference', {
3134
     elem.c('conference', {
3132
         xmlns: 'http://jitsi.org/protocol/colibri'
3135
         xmlns: 'http://jitsi.org/protocol/colibri'
3133
     });
3136
     });
3151
 
3154
 
3152
 var Recording = {
3155
 var Recording = {
3153
     toggleRecording: function (tokenEmptyCallback,
3156
     toggleRecording: function (tokenEmptyCallback,
3154
-                               startingCallback, startedCallback) {
3157
+                               startingCallback, startedCallback, connection) {
3155
         if (!Moderator.isModerator()) {
3158
         if (!Moderator.isModerator()) {
3156
             console.log(
3159
             console.log(
3157
                     'non-focus, or conference not yet organized:' +
3160
                     'non-focus, or conference not yet organized:' +
3199
                 }
3202
                 }
3200
                 startedCallback(state);
3203
                 startedCallback(state);
3201
 
3204
 
3202
-            }
3205
+            },
3206
+            connection
3203
         );
3207
         );
3204
     }
3208
     }
3205
 
3209
 
3215
 var bridgeIsDown = false;
3219
 var bridgeIsDown = false;
3216
 
3220
 
3217
 var Moderator = require("./moderator");
3221
 var Moderator = require("./moderator");
3222
+var JingleSession = require("./JingleSession");
3218
 
3223
 
3219
 module.exports = function(XMPP, eventEmitter) {
3224
 module.exports = function(XMPP, eventEmitter) {
3220
     Strophe.addConnectionPlugin('emuc', {
3225
     Strophe.addConnectionPlugin('emuc', {
3228
         joined: false,
3233
         joined: false,
3229
         isOwner: false,
3234
         isOwner: false,
3230
         role: null,
3235
         role: null,
3236
+        focusMucJid: null,
3231
         init: function (conn) {
3237
         init: function (conn) {
3232
             this.connection = conn;
3238
             this.connection = conn;
3233
         },
3239
         },
3400
                 this.list_members.push(from);
3406
                 this.list_members.push(from);
3401
                 console.log('entered', from, member);
3407
                 console.log('entered', from, member);
3402
                 if (member.isFocus) {
3408
                 if (member.isFocus) {
3403
-                    focusMucJid = from;
3409
+                    this.focusMucJid = from;
3404
                     console.info("Ignore focus: " + from + ", real JID: " + member.jid);
3410
                     console.info("Ignore focus: " + from + ", real JID: " + member.jid);
3405
                 }
3411
                 }
3406
                 else {
3412
                 else {
3753
 
3759
 
3754
             API.triggerEvent("participantLeft", {jid: jid});
3760
             API.triggerEvent("participantLeft", {jid: jid});
3755
 
3761
 
3756
-            delete jid2Ssrc[jid];
3757
-
3758
             this.connection.jingle.terminateByJid(jid);
3762
             this.connection.jingle.terminateByJid(jid);
3759
 
3763
 
3760
             if (this.getPrezi(jid)) {
3764
             if (this.getPrezi(jid)) {
3777
             Object.keys(ssrc2jid).forEach(function (ssrc) {
3781
             Object.keys(ssrc2jid).forEach(function (ssrc) {
3778
                 if (ssrc2jid[ssrc] == jid) {
3782
                 if (ssrc2jid[ssrc] == jid) {
3779
                     delete ssrc2jid[ssrc];
3783
                     delete ssrc2jid[ssrc];
3780
-                    delete ssrc2videoType[ssrc];
3781
                 }
3784
                 }
3782
             });
3785
             });
3783
 
3786
 
3786
                 //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
3789
                 //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
3787
                 var ssrcV = ssrc.getAttribute('ssrc');
3790
                 var ssrcV = ssrc.getAttribute('ssrc');
3788
                 ssrc2jid[ssrcV] = from;
3791
                 ssrc2jid[ssrcV] = from;
3789
-                notReceivedSSRCs.push(ssrcV);
3792
+                JingleSession.notReceivedSSRCs.push(ssrcV);
3793
+
3790
 
3794
 
3791
                 var type = ssrc.getAttribute('type');
3795
                 var type = ssrc.getAttribute('type');
3792
-                ssrc2videoType[ssrcV] = type;
3793
 
3796
 
3794
                 var direction = ssrc.getAttribute('direction');
3797
                 var direction = ssrc.getAttribute('direction');
3795
 
3798
 
3822
 };
3825
 };
3823
 
3826
 
3824
 
3827
 
3825
-},{"./moderator":6}],9:[function(require,module,exports){
3828
+},{"./JingleSession":1,"./moderator":6}],9:[function(require,module,exports){
3826
 /* jshint -W117 */
3829
 /* jshint -W117 */
3827
 
3830
 
3828
 var JingleSession = require("./JingleSession");
3831
 var JingleSession = require("./JingleSession");
4202
         },
4205
         },
4203
         setMute: function (jid, mute) {
4206
         setMute: function (jid, mute) {
4204
             console.info("set mute", mute);
4207
             console.info("set mute", mute);
4205
-            var iqToFocus = $iq({to: focusMucJid, type: 'set'})
4208
+            var iqToFocus = $iq({to: this.connection.emuc.focusMucJid, type: 'set'})
4206
                 .c('mute', {
4209
                 .c('mute', {
4207
                     xmlns: 'http://jitsi.org/jitmeet/audio',
4210
                     xmlns: 'http://jitsi.org/jitmeet/audio',
4208
                     jid: jid
4211
                     jid: jid
4221
         },
4224
         },
4222
         onMute: function (iq) {
4225
         onMute: function (iq) {
4223
             var from = iq.getAttribute('from');
4226
             var from = iq.getAttribute('from');
4224
-            if (from !== focusMucJid) {
4227
+            if (from !== this.connection.emuc.focusMucJid) {
4225
                 console.warn("Ignored mute from non focus peer");
4228
                 console.warn("Ignored mute from non focus peer");
4226
                 return false;
4229
                 return false;
4227
             }
4230
             }
4264
                 var req = $iq(
4267
                 var req = $iq(
4265
                     {
4268
                     {
4266
                         type: 'set',
4269
                         type: 'set',
4267
-                        to: focusMucJid
4270
+                        to: this.connection.emuc.focusMucJid
4268
                     }
4271
                     }
4269
                 );
4272
                 );
4270
                 req.c('dial',
4273
                 req.c('dial',
4707
     toggleRecording: function (tokenEmptyCallback,
4710
     toggleRecording: function (tokenEmptyCallback,
4708
                                startingCallback, startedCallback) {
4711
                                startingCallback, startedCallback) {
4709
         Recording.toggleRecording(tokenEmptyCallback,
4712
         Recording.toggleRecording(tokenEmptyCallback,
4710
-            startingCallback, startedCallback);
4713
+            startingCallback, startedCallback, connection);
4711
     },
4714
     },
4712
     addToPresence: function (name, value, dontSend) {
4715
     addToPresence: function (name, value, dontSend) {
4713
         switch (name)
4716
         switch (name)
4737
             connection.emuc.sendPresence();
4740
             connection.emuc.sendPresence();
4738
     },
4741
     },
4739
     sendLogs: function (data) {
4742
     sendLogs: function (data) {
4740
-        if(!focusMucJid)
4743
+        if(!connection.emuc.focusMucJid)
4741
             return;
4744
             return;
4742
 
4745
 
4743
         var deflate = true;
4746
         var deflate = true;
4748
         }
4751
         }
4749
         content = Base64.encode(content);
4752
         content = Base64.encode(content);
4750
         // XEP-0337-ish
4753
         // XEP-0337-ish
4751
-        var message = $msg({to: focusMucJid, type: 'normal'});
4754
+        var message = $msg({to: connection.emuc.focusMucJid, type: 'normal'});
4752
         message.c('log', { xmlns: 'urn:xmpp:eventlog',
4755
         message.c('log', { xmlns: 'urn:xmpp:eventlog',
4753
             id: 'PeerConnectionStats'});
4756
             id: 'PeerConnectionStats'});
4754
         message.c('message').t(content).up();
4757
         message.c('message').t(content).up();

+ 2
- 1
modules/RTC/LocalStream.js View File

1
 //var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
1
 //var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
2
 
2
 
3
-function LocalStream(stream, type, eventEmitter)
3
+function LocalStream(stream, type, eventEmitter, videoType)
4
 {
4
 {
5
     this.stream = stream;
5
     this.stream = stream;
6
     this.eventEmitter = eventEmitter;
6
     this.eventEmitter = eventEmitter;
7
     this.type = type;
7
     this.type = type;
8
+    this.videoType = videoType;
8
     var self = this;
9
     var self = this;
9
     if(type == "audio")
10
     if(type == "audio")
10
     {
11
     {

+ 1
- 0
modules/RTC/MediaStream.js View File

32
     this.ssrc = ssrc;
32
     this.ssrc = ssrc;
33
     this.type = (this.stream.getVideoTracks().length > 0)?
33
     this.type = (this.stream.getVideoTracks().length > 0)?
34
         MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
34
         MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE;
35
+    this.videoType = null;
35
     this.muted = false;
36
     this.muted = false;
36
     if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX)
37
     if(browser == RTCBrowserType.RTC_BROWSER_FIREFOX)
37
     {
38
     {

+ 43
- 3
modules/RTC/RTC.js View File

106
             function (stream, isUsingScreenStream, callback) {
106
             function (stream, isUsingScreenStream, callback) {
107
                 self.changeLocalVideo(stream, isUsingScreenStream, callback);
107
                 self.changeLocalVideo(stream, isUsingScreenStream, callback);
108
             }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
108
             }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
109
+        xmpp.addListener(XMPPEvents.CHANGED_STREAMS, function (jid, changedStreams) {
110
+            for(var i = 0; i < changedStreams.length; i++) {
111
+                var type = changedStreams[i].type;
112
+                if (type != "audio") {
113
+                    var peerStreams = self.remoteStreams[jid];
114
+                    if(!peerStreams)
115
+                        continue;
116
+                    var videoStream = peerStreams[MediaStreamType.VIDEO_TYPE];
117
+                    if(!videoStream)
118
+                        continue;
119
+                    videoStream.videoType = changedStreams[i].type;
120
+                }
121
+            }
122
+        })
109
         this.rtcUtils = new RTCUtils(this);
123
         this.rtcUtils = new RTCUtils(this);
110
         this.rtcUtils.obtainAudioAndVideoPermissions();
124
         this.rtcUtils.obtainAudioAndVideoPermissions();
111
     },
125
     },
142
     },
156
     },
143
     changeLocalVideo: function (stream, isUsingScreenStream, callback) {
157
     changeLocalVideo: function (stream, isUsingScreenStream, callback) {
144
         var oldStream = this.localVideo.getOriginalStream();
158
         var oldStream = this.localVideo.getOriginalStream();
145
-        var type = (isUsingScreenStream? "desktop" : "video");
146
-        RTC.localVideo = this.createLocalStream(stream, type, true);
159
+        var type = (isUsingScreenStream? "screen" : "video");
160
+        RTC.localVideo = this.createLocalStream(stream, "video", true, type);
147
         // Stop the stream to trigger onended event for old stream
161
         // Stop the stream to trigger onended event for old stream
148
         oldStream.stop();
162
         oldStream.stop();
149
         xmpp.switchStreams(stream, oldStream,callback);
163
         xmpp.switchStreams(stream, oldStream,callback);
150
-    }
164
+    },
165
+    /**
166
+     * Checks if video identified by given src is desktop stream.
167
+     * @param videoSrc eg.
168
+     * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
169
+     * @returns {boolean}
170
+     */
171
+    isVideoSrcDesktop: function (jid) {
172
+        if(!jid)
173
+            return false;
174
+        var isDesktop = false;
175
+        var stream = null;
176
+        if (xmpp.myJid() &&
177
+            xmpp.myResource() === jid) {
178
+            // local video
179
+            stream = this.localVideo;
180
+        } else {
181
+            var peerStreams = this.remoteStreams[jid];
182
+            if(!peerStreams)
183
+                return false;
184
+            stream = peerStreams[MediaStreamType.VIDEO_TYPE];
185
+        }
186
+
187
+        if(stream)
188
+            isDesktop = (stream.videoType === "screen");
151
 
189
 
190
+        return isDesktop;
191
+    }
152
 };
192
 };
153
 
193
 
154
 module.exports = RTC;
194
 module.exports = RTC;

+ 1
- 9
modules/UI/UI.js View File

58
         case "stream":
58
         case "stream":
59
             VideoLayout.changeLocalStream(stream);
59
             VideoLayout.changeLocalStream(stream);
60
             break;
60
             break;
61
-        case "desktop":
62
-            VideoLayout.changeLocalVideo(stream);
63
-            break;
64
     }
61
     }
65
 }
62
 }
66
 
63
 
354
         }
351
         }
355
     }, 10);
352
     }, 10);
356
 
353
 
357
-    // Unlock large video
358
-    if (focusedVideoInfo && focusedVideoInfo.jid === jid)
359
-    {
360
-        console.info("Focused video owner has left the conference");
361
-        focusedVideoInfo = null;
362
-    }
354
+    VideoLayout.participantLeft(jid);
363
 
355
 
364
 };
356
 };
365
 
357
 

+ 17
- 53
modules/UI/videolayout/VideoLayout.js View File

15
     updateInProgress: false,
15
     updateInProgress: false,
16
     newSrc: ''
16
     newSrc: ''
17
 };
17
 };
18
+/**
19
+ * Currently focused video "src"(displayed in large video).
20
+ * @type {String}
21
+ */
22
+var focusedVideoInfo = null;
18
 
23
 
19
 /**
24
 /**
20
  * Indicates if we have muted our audio before the conference has started.
25
  * Indicates if we have muted our audio before the conference has started.
92
     if (selector[0].currentTime > 0) {
97
     if (selector[0].currentTime > 0) {
93
         var videoStream = simulcast.getReceivingVideoStream(stream);
98
         var videoStream = simulcast.getReceivingVideoStream(stream);
94
         RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
99
         RTC.attachMediaStream(selector, videoStream); // FIXME: why do i have to do this for FF?
95
-
96
-        // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
97
-        //        in order to get rid of too many maps
98
-        if (ssrc && jid) {
99
-            jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
100
-        } else {
101
-            console.warn("No ssrc given for jid", jid);
102
-        }
103
-
104
         videoactive(selector);
100
         videoactive(selector);
105
     } else {
101
     } else {
106
         setTimeout(function () {
102
         setTimeout(function () {
492
 }
488
 }
493
 
489
 
494
 
490
 
495
-/**
496
- * Checks if video identified by given src is desktop stream.
497
- * @param videoSrc eg.
498
- * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
499
- * @returns {boolean}
500
- */
501
-function isVideoSrcDesktop(jid) {
502
-    // FIXME: fix this mapping mess...
503
-    // figure out if large video is desktop stream or just a camera
504
-
505
-    if(!jid)
506
-        return false;
507
-    var isDesktop = false;
508
-    if (xmpp.myJid() &&
509
-        xmpp.myResource() === jid) {
510
-        // local video
511
-        isDesktop = desktopsharing.isUsingScreenStream();
512
-    } else {
513
-        // Do we have associations...
514
-        var videoSsrc = jid2Ssrc[jid];
515
-        if (videoSsrc) {
516
-            var videoType = ssrc2videoType[videoSsrc];
517
-            if (videoType) {
518
-                // Finally there...
519
-                isDesktop = videoType === 'screen';
520
-            } else {
521
-                console.error("No video type for ssrc: " + videoSsrc);
522
-            }
523
-        } else {
524
-            console.error("No ssrc for jid: " + jid);
525
-        }
526
-    }
527
-    return isDesktop;
528
-}
529
-
530
-
531
-
532
 var VideoLayout = (function (my) {
491
 var VideoLayout = (function (my) {
533
     my.connectionIndicators = {};
492
     my.connectionIndicators = {};
534
 
493
 
571
 
530
 
572
     my.changeLocalVideo = function(stream) {
531
     my.changeLocalVideo = function(stream) {
573
         var flipX = true;
532
         var flipX = true;
574
-        if(stream.type == "desktop")
533
+        if(stream.videoType == "screen")
575
             flipX = false;
534
             flipX = false;
576
         var localVideo = document.createElement('video');
535
         var localVideo = document.createElement('video');
577
         localVideo.id = 'localVideo_' +
536
         localVideo.id = 'localVideo_' +
754
 
713
 
755
             largeVideoState.newSrc = newSrc;
714
             largeVideoState.newSrc = newSrc;
756
             largeVideoState.isVisible = $('#largeVideo').is(':visible');
715
             largeVideoState.isVisible = $('#largeVideo').is(':visible');
757
-            largeVideoState.isDesktop = isVideoSrcDesktop(resourceJid);
758
-            if(jid2Ssrc[largeVideoState.userResourceJid] ||
759
-                (xmpp.myResource() &&
760
-                    largeVideoState.userResourceJid ===
761
-                    xmpp.myResource())) {
716
+            largeVideoState.isDesktop = RTC.isVideoSrcDesktop(resourceJid);
717
+            if(largeVideoState.userResourceJid) {
762
                 largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
718
                 largeVideoState.oldResourceJid = largeVideoState.userResourceJid;
763
             } else {
719
             } else {
764
                 largeVideoState.oldResourceJid = null;
720
                 largeVideoState.oldResourceJid = null;
2133
                 }
2089
                 }
2134
 
2090
 
2135
                 var jid = ssrc2jid[primarySSRC];
2091
                 var jid = ssrc2jid[primarySSRC];
2136
-                jid2Ssrc[jid] = primarySSRC;
2137
 
2092
 
2138
                 if (updateLargeVideo) {
2093
                 if (updateLargeVideo) {
2139
                     VideoLayout.updateLargeVideo(RTC.getVideoSrc(selRemoteVideo[0]), null,
2094
                     VideoLayout.updateLargeVideo(RTC.getVideoSrc(selRemoteVideo[0]), null,
2231
         }
2186
         }
2232
     };
2187
     };
2233
 
2188
 
2189
+    my.participantLeft = function (jid) {
2190
+        // Unlock large video
2191
+        if (focusedVideoInfo && focusedVideoInfo.jid === jid)
2192
+        {
2193
+            console.info("Focused video owner has left the conference");
2194
+            focusedVideoInfo = null;
2195
+        }
2196
+    }
2197
+
2234
     return my;
2198
     return my;
2235
 }(VideoLayout || {}));
2199
 }(VideoLayout || {}));
2236
 
2200
 

+ 1
- 1
modules/statistics/RTPStatsCollector.js View File

1
-/* global focusMucJid, ssrc2jid */
1
+/* global ssrc2jid */
2
 /* jshint -W117 */
2
 /* jshint -W117 */
3
 /**
3
 /**
4
  * Calculates packet lost percent using the number of lost packets and the
4
  * Calculates packet lost percent using the number of lost packets and the

+ 6
- 3
modules/xmpp/JingleSession.js View File

53
     this.videoMuteByUser = false;
53
     this.videoMuteByUser = false;
54
 }
54
 }
55
 
55
 
56
+//TODO: this array must be removed when firefox implement multistream support
57
+JingleSession.notReceivedSSRCs = [];
58
+
56
 JingleSession.prototype.initiate = function (peerjid, isInitiator) {
59
 JingleSession.prototype.initiate = function (peerjid, isInitiator) {
57
     var self = this;
60
     var self = this;
58
     if (this.state !== null) {
61
     if (this.state !== null) {
1354
     //TODO: this code should be removed when firefox implement multistream support
1357
     //TODO: this code should be removed when firefox implement multistream support
1355
     if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
1358
     if(RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_FIREFOX)
1356
     {
1359
     {
1357
-        if((notReceivedSSRCs.length == 0) ||
1358
-            !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
1360
+        if((JingleSession.notReceivedSSRCs.length == 0) ||
1361
+            !ssrc2jid[JingleSession.notReceivedSSRCs[JingleSession.notReceivedSSRCs.length - 1]])
1359
         {
1362
         {
1360
             // TODO(gp) limit wait duration to 1 sec.
1363
             // TODO(gp) limit wait duration to 1 sec.
1361
             setTimeout(function(d) {
1364
             setTimeout(function(d) {
1366
             return;
1369
             return;
1367
         }
1370
         }
1368
 
1371
 
1369
-        thessrc = notReceivedSSRCs.pop();
1372
+        thessrc = JingleSession.notReceivedSSRCs.pop();
1370
         if (ssrc2jid[thessrc]) {
1373
         if (ssrc2jid[thessrc]) {
1371
             data.peerjid = ssrc2jid[thessrc];
1374
             data.peerjid = ssrc2jid[thessrc];
1372
         }
1375
         }

+ 9
- 8
modules/xmpp/recording.js View File

23
     recordingToken = token;
23
     recordingToken = token;
24
 }
24
 }
25
 
25
 
26
-function setRecording(state, token, callback) {
26
+function setRecording(state, token, callback, connection) {
27
     if (useJirecon){
27
     if (useJirecon){
28
-        this.setRecordingJirecon(state, token, callback);
28
+        this.setRecordingJirecon(state, token, callback, connection);
29
     } else {
29
     } else {
30
-        this.setRecordingColibri(state, token, callback);
30
+        this.setRecordingColibri(state, token, callback, connection);
31
     }
31
     }
32
 }
32
 }
33
 
33
 
34
-function setRecordingJirecon(state, token, callback) {
34
+function setRecordingJirecon(state, token, callback, connection) {
35
     if (state == recordingEnabled){
35
     if (state == recordingEnabled){
36
         return;
36
         return;
37
     }
37
     }
70
 // Sends a COLIBRI message which enables or disables (according to 'state')
70
 // Sends a COLIBRI message which enables or disables (according to 'state')
71
 // the recording on the bridge. Waits for the result IQ and calls 'callback'
71
 // the recording on the bridge. Waits for the result IQ and calls 'callback'
72
 // with the new recording state, according to the IQ.
72
 // with the new recording state, according to the IQ.
73
-function setRecordingColibri(state, token, callback) {
74
-    var elem = $iq({to: focusMucJid, type: 'set'});
73
+function setRecordingColibri(state, token, callback, connection) {
74
+    var elem = $iq({to: connection.emuc.focusMucJid, type: 'set'});
75
     elem.c('conference', {
75
     elem.c('conference', {
76
         xmlns: 'http://jitsi.org/protocol/colibri'
76
         xmlns: 'http://jitsi.org/protocol/colibri'
77
     });
77
     });
95
 
95
 
96
 var Recording = {
96
 var Recording = {
97
     toggleRecording: function (tokenEmptyCallback,
97
     toggleRecording: function (tokenEmptyCallback,
98
-                               startingCallback, startedCallback) {
98
+                               startingCallback, startedCallback, connection) {
99
         if (!Moderator.isModerator()) {
99
         if (!Moderator.isModerator()) {
100
             console.log(
100
             console.log(
101
                     'non-focus, or conference not yet organized:' +
101
                     'non-focus, or conference not yet organized:' +
143
                 }
143
                 }
144
                 startedCallback(state);
144
                 startedCallback(state);
145
 
145
 
146
-            }
146
+            },
147
+            connection
147
         );
148
         );
148
     }
149
     }
149
 
150
 

+ 5
- 6
modules/xmpp/strophe.emuc.js View File

6
 var bridgeIsDown = false;
6
 var bridgeIsDown = false;
7
 
7
 
8
 var Moderator = require("./moderator");
8
 var Moderator = require("./moderator");
9
+var JingleSession = require("./JingleSession");
9
 
10
 
10
 module.exports = function(XMPP, eventEmitter) {
11
 module.exports = function(XMPP, eventEmitter) {
11
     Strophe.addConnectionPlugin('emuc', {
12
     Strophe.addConnectionPlugin('emuc', {
19
         joined: false,
20
         joined: false,
20
         isOwner: false,
21
         isOwner: false,
21
         role: null,
22
         role: null,
23
+        focusMucJid: null,
22
         init: function (conn) {
24
         init: function (conn) {
23
             this.connection = conn;
25
             this.connection = conn;
24
         },
26
         },
191
                 this.list_members.push(from);
193
                 this.list_members.push(from);
192
                 console.log('entered', from, member);
194
                 console.log('entered', from, member);
193
                 if (member.isFocus) {
195
                 if (member.isFocus) {
194
-                    focusMucJid = from;
196
+                    this.focusMucJid = from;
195
                     console.info("Ignore focus: " + from + ", real JID: " + member.jid);
197
                     console.info("Ignore focus: " + from + ", real JID: " + member.jid);
196
                 }
198
                 }
197
                 else {
199
                 else {
544
 
546
 
545
             API.triggerEvent("participantLeft", {jid: jid});
547
             API.triggerEvent("participantLeft", {jid: jid});
546
 
548
 
547
-            delete jid2Ssrc[jid];
548
-
549
             this.connection.jingle.terminateByJid(jid);
549
             this.connection.jingle.terminateByJid(jid);
550
 
550
 
551
             if (this.getPrezi(jid)) {
551
             if (this.getPrezi(jid)) {
568
             Object.keys(ssrc2jid).forEach(function (ssrc) {
568
             Object.keys(ssrc2jid).forEach(function (ssrc) {
569
                 if (ssrc2jid[ssrc] == jid) {
569
                 if (ssrc2jid[ssrc] == jid) {
570
                     delete ssrc2jid[ssrc];
570
                     delete ssrc2jid[ssrc];
571
-                    delete ssrc2videoType[ssrc];
572
                 }
571
                 }
573
             });
572
             });
574
 
573
 
577
                 //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
576
                 //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
578
                 var ssrcV = ssrc.getAttribute('ssrc');
577
                 var ssrcV = ssrc.getAttribute('ssrc');
579
                 ssrc2jid[ssrcV] = from;
578
                 ssrc2jid[ssrcV] = from;
580
-                notReceivedSSRCs.push(ssrcV);
579
+                JingleSession.notReceivedSSRCs.push(ssrcV);
580
+
581
 
581
 
582
                 var type = ssrc.getAttribute('type');
582
                 var type = ssrc.getAttribute('type');
583
-                ssrc2videoType[ssrcV] = type;
584
 
583
 
585
                 var direction = ssrc.getAttribute('direction');
584
                 var direction = ssrc.getAttribute('direction');
586
 
585
 

+ 2
- 2
modules/xmpp/strophe.moderate.js View File

18
         },
18
         },
19
         setMute: function (jid, mute) {
19
         setMute: function (jid, mute) {
20
             console.info("set mute", mute);
20
             console.info("set mute", mute);
21
-            var iqToFocus = $iq({to: focusMucJid, type: 'set'})
21
+            var iqToFocus = $iq({to: this.connection.emuc.focusMucJid, type: 'set'})
22
                 .c('mute', {
22
                 .c('mute', {
23
                     xmlns: 'http://jitsi.org/jitmeet/audio',
23
                     xmlns: 'http://jitsi.org/jitmeet/audio',
24
                     jid: jid
24
                     jid: jid
37
         },
37
         },
38
         onMute: function (iq) {
38
         onMute: function (iq) {
39
             var from = iq.getAttribute('from');
39
             var from = iq.getAttribute('from');
40
-            if (from !== focusMucJid) {
40
+            if (from !== this.connection.emuc.focusMucJid) {
41
                 console.warn("Ignored mute from non focus peer");
41
                 console.warn("Ignored mute from non focus peer");
42
                 return false;
42
                 return false;
43
             }
43
             }

+ 1
- 1
modules/xmpp/strophe.rayo.js View File

21
                 var req = $iq(
21
                 var req = $iq(
22
                     {
22
                     {
23
                         type: 'set',
23
                         type: 'set',
24
-                        to: focusMucJid
24
+                        to: this.connection.emuc.focusMucJid
25
                     }
25
                     }
26
                 );
26
                 );
27
                 req.c('dial',
27
                 req.c('dial',

+ 3
- 3
modules/xmpp/xmpp.js View File

323
     toggleRecording: function (tokenEmptyCallback,
323
     toggleRecording: function (tokenEmptyCallback,
324
                                startingCallback, startedCallback) {
324
                                startingCallback, startedCallback) {
325
         Recording.toggleRecording(tokenEmptyCallback,
325
         Recording.toggleRecording(tokenEmptyCallback,
326
-            startingCallback, startedCallback);
326
+            startingCallback, startedCallback, connection);
327
     },
327
     },
328
     addToPresence: function (name, value, dontSend) {
328
     addToPresence: function (name, value, dontSend) {
329
         switch (name)
329
         switch (name)
353
             connection.emuc.sendPresence();
353
             connection.emuc.sendPresence();
354
     },
354
     },
355
     sendLogs: function (data) {
355
     sendLogs: function (data) {
356
-        if(!focusMucJid)
356
+        if(!connection.emuc.focusMucJid)
357
             return;
357
             return;
358
 
358
 
359
         var deflate = true;
359
         var deflate = true;
364
         }
364
         }
365
         content = Base64.encode(content);
365
         content = Base64.encode(content);
366
         // XEP-0337-ish
366
         // XEP-0337-ish
367
-        var message = $msg({to: focusMucJid, type: 'normal'});
367
+        var message = $msg({to: connection.emuc.focusMucJid, type: 'normal'});
368
         message.c('log', { xmlns: 'urn:xmpp:eventlog',
368
         message.c('log', { xmlns: 'urn:xmpp:eventlog',
369
             id: 'PeerConnectionStats'});
369
             id: 'PeerConnectionStats'});
370
         message.c('message').t(content).up();
370
         message.c('message').t(content).up();

Loading…
Cancel
Save