Sfoglia il codice sorgente

Merge branch 'master' into ssfocus

Conflicts:
	config.js
	libs/strophe/strophe.jingle.session.js
	rtp_sts.js
j8
paweldomas 11 anni fa
parent
commit
e308025143

+ 139
- 90
app.js Vedi File

@@ -12,6 +12,10 @@ var roomName = null;
12 12
 var ssrc2jid = {};
13 13
 var mediaStreams = {};
14 14
 var bridgeIsDown = false;
15
+//TODO: this array must be removed when firefox implement multistream support
16
+var notReceivedSSRCs = [];
17
+
18
+var jid2Ssrc = {};
15 19
 
16 20
 /**
17 21
  * The stats collector that process stats data and triggers updates to app.js.
@@ -30,7 +34,6 @@ var localStatsCollector = null;
30 34
  * FIXME: remove those maps
31 35
  */
32 36
 var ssrc2videoType = {};
33
-var videoSrcToSsrc = {};
34 37
 /**
35 38
  * Currently focused video "src"(displayed in large video).
36 39
  * @type {String}
@@ -74,26 +77,43 @@ function init() {
74 77
     if (RTC === null) {
75 78
         window.location.href = 'webrtcrequired.html';
76 79
         return;
77
-    } else if (RTC.browser !== 'chrome') {
80
+    } else if (RTC.browser !== 'chrome' &&
81
+        config.enableFirefoxSupport !== true) {
78 82
         window.location.href = 'chromeonly.html';
79 83
         return;
80 84
     }
81 85
 
82 86
     obtainAudioAndVideoPermissions(function (stream) {
83
-        var audioStream = new webkitMediaStream();
84
-        var videoStream = new webkitMediaStream();
85
-        var audioTracks = stream.getAudioTracks();
86
-        var videoTracks = stream.getVideoTracks();
87
-        for (var i = 0; i < audioTracks.length; i++) {
88
-            audioStream.addTrack(audioTracks[i]);
87
+        var audioStream, videoStream;
88
+        if(window.webkitMediaStream)
89
+        {
90
+            var audioStream = new webkitMediaStream();
91
+            var videoStream = new webkitMediaStream();
92
+            var audioTracks = stream.getAudioTracks();
93
+            var videoTracks = stream.getVideoTracks();
94
+            for (var i = 0; i < audioTracks.length; i++) {
95
+                audioStream.addTrack(audioTracks[i]);
96
+            }
97
+
98
+            for (i = 0; i < videoTracks.length; i++) {
99
+                videoStream.addTrack(videoTracks[i]);
100
+            }
101
+            VideoLayout.changeLocalAudio(audioStream);
102
+            startLocalRtpStatsCollector(audioStream);
103
+
104
+
105
+            VideoLayout.changeLocalVideo(videoStream, true);
89 106
         }
90
-        VideoLayout.changeLocalAudio(audioStream);
91
-        startLocalRtpStatsCollector(audioStream);
107
+        else
108
+        {
109
+            VideoLayout.changeLocalStream(stream);
110
+            startLocalRtpStatsCollector(stream);
92 111
 
93
-        for (i = 0; i < videoTracks.length; i++) {
94
-            videoStream.addTrack(videoTracks[i]);
95 112
         }
96
-        VideoLayout.changeLocalVideo(videoStream, true);
113
+
114
+
115
+
116
+
97 117
         maybeDoJoin();
98 118
     });
99 119
 
@@ -170,24 +190,33 @@ function connect(jid, password) {
170 190
  */
171 191
 function obtainAudioAndVideoPermissions(callback) {
172 192
     // Get AV
193
+    var cb = function (stream) {
194
+        console.log('got', stream, stream.getAudioTracks().length, stream.getVideoTracks().length);
195
+        callback(stream);
196
+        trackUsage('localMedia', {
197
+            audio: stream.getAudioTracks().length,
198
+            video: stream.getVideoTracks().length
199
+        });
200
+    }
173 201
     getUserMediaWithConstraints(
174 202
         ['audio', 'video'],
175
-        function (avStream) {
176
-            callback(avStream);
177
-            trackUsage('localMedia', {
178
-                audio: avStream.getAudioTracks().length,
179
-                video: avStream.getVideoTracks().length
180
-            });
181
-        },
203
+        cb,
182 204
         function (error) {
183
-            console.error('failed to obtain audio/video stream - stop', error);
184
-            trackUsage('localMediaError', {
185
-                media: error.media || 'video',
186
-                name : error.name
187
-            });
188
-            messageHandler.showError("Error",
189
-                "Failed to obtain permissions to use the local microphone" +
190
-                    "and/or camera.");
205
+            console.error('failed to obtain audio/video stream - trying audio only', error);
206
+            getUserMediaWithConstraints(
207
+                ['audio'],
208
+                cb,
209
+                function (error) {
210
+                    console.error('failed to obtain audio/video stream - stop', error);
211
+                    trackUsage('localMediaError', {
212
+                        media: error.media || 'video',
213
+                        name : error.name
214
+                    });
215
+                    messageHandler.showError("Error",
216
+                        "Failed to obtain permissions to use the local microphone" +
217
+                            "and/or camera.");
218
+                }
219
+            );
191 220
         },
192 221
         config.resolution || '360');
193 222
 }
@@ -264,8 +293,7 @@ function doJoinAfterFocus() {
264 293
     connection.emuc.doJoin(roomjid);
265 294
 }
266 295
 
267
-function waitForRemoteVideo(selector, ssrc, stream) {
268
-
296
+function waitForRemoteVideo(selector, ssrc, stream, jid) {
269 297
     // XXX(gp) so, every call to this function is *always* preceded by a call
270 298
     // to the RTC.attachMediaStream() function but that call is *not* followed
271 299
     // by an update to the videoSrcToSsrc map!
@@ -297,17 +325,17 @@ function waitForRemoteVideo(selector, ssrc, stream) {
297 325
 
298 326
         // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
299 327
         //        in order to get rid of too many maps
300
-        if (ssrc && selector.attr('src')) {
301
-            videoSrcToSsrc[selector.attr('src')] = ssrc;
328
+        if (ssrc && jid) {
329
+            jid2Ssrc[Strophe.getResourceFromJid(jid)] = ssrc;
302 330
         } else {
303
-            console.warn("No ssrc given for video", selector);
304
-            messageHandler.showError('Warning', 'No ssrc was given for the video.');
331
+            console.warn("No ssrc given for jid", jid);
332
+//            messageHandler.showError('Warning', 'No ssrc was given for the video.');
305 333
         }
306 334
 
307 335
         $(document).trigger('videoactive.jingle', [selector]);
308 336
     } else {
309 337
         setTimeout(function () {
310
-            waitForRemoteVideo(selector, ssrc, stream);
338
+            waitForRemoteVideo(selector, ssrc, stream, jid);
311 339
             }, 250);
312 340
     }
313 341
 }
@@ -320,16 +348,19 @@ function waitForPresence(data, sid) {
320 348
     var sess = connection.jingle.sessions[sid];
321 349
 
322 350
     var thessrc;
351
+
323 352
     // look up an associated JID for a stream id
324
-    if (data.stream.id.indexOf('mixedmslabel') === -1) {
353
+    if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
325 354
         // look only at a=ssrc: and _not_ at a=ssrc-group: lines
355
+
326 356
         var ssrclines
327 357
             = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc:');
328 358
         ssrclines = ssrclines.filter(function (line) {
329 359
             // NOTE(gp) previously we filtered on the mslabel, but that property
330 360
             // is not always present.
331 361
             // return line.indexOf('mslabel:' + data.stream.label) !== -1;
332
-            return line.indexOf('msid:' + data.stream.id) !== -1;
362
+
363
+            return ((line.indexOf('msid:' + data.stream.id) !== -1));
333 364
         });
334 365
         if (ssrclines.length) {
335 366
             thessrc = ssrclines[0].substring(7).split(' ')[0];
@@ -359,6 +390,27 @@ function waitForPresence(data, sid) {
359 390
         }
360 391
     }
361 392
 
393
+    //TODO: this code should be removed when firefox implement multistream support
394
+    if(RTC.browser == "firefox")
395
+    {
396
+        if((notReceivedSSRCs.length == 0) ||
397
+            !ssrc2jid[notReceivedSSRCs[notReceivedSSRCs.length - 1]])
398
+        {
399
+            // TODO(gp) limit wait duration to 1 sec.
400
+            setTimeout(function(d, s) {
401
+                return function() {
402
+                    waitForPresence(d, s);
403
+                }
404
+            }(data, sid), 250);
405
+            return;
406
+        }
407
+
408
+        thessrc = notReceivedSSRCs.pop();
409
+        if (ssrc2jid[thessrc]) {
410
+            data.peerjid = ssrc2jid[thessrc];
411
+        }
412
+    }
413
+
362 414
     // NOTE(gp) now that we have simulcast, a media stream can have more than 1
363 415
     // ssrc. We should probably take that into account in our MediaStream
364 416
     // wrapper.
@@ -404,8 +456,6 @@ function waitForPresence(data, sid) {
404 456
                                             data.stream,
405 457
                                             data.peerjid,
406 458
                                             thessrc);
407
-        if(isVideo && container.id !== 'mixedstream')
408
-             videoSrcToSsrc[$(container).find('>video')[0].src] = thessrc;
409 459
     }
410 460
 
411 461
     // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
@@ -420,25 +470,6 @@ function waitForPresence(data, sid) {
420 470
     }
421 471
 }
422 472
 
423
-/**
424
- * Returns the JID of the user to whom given <tt>videoSrc</tt> belongs.
425
- * @param videoSrc the video "src" identifier.
426
- * @returns {null | String} the JID of the user to whom given <tt>videoSrc</tt>
427
- *                   belongs.
428
- */
429
-function getJidFromVideoSrc(videoSrc)
430
-{
431
-    if (videoSrc === localVideoSrc)
432
-        return connection.emuc.myroomjid;
433
-
434
-    var ssrc = videoSrcToSsrc[videoSrc];
435
-    if (!ssrc)
436
-    {
437
-        return null;
438
-    }
439
-    return ssrc2jid[ssrc];
440
-}
441
-
442 473
 // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
443 474
 function sendKeyframe(pc) {
444 475
     console.log('sendkeyframe', pc.iceConnectionState);
@@ -637,15 +668,27 @@ $(document).bind('setLocalDescription.jingle', function (event, sid) {
637 668
     var media = simulcast.parseMedia(sess.peerconnection.localDescription);
638 669
     media.forEach(function (media) {
639 670
 
640
-        // TODO(gp) maybe exclude FID streams?
641
-        Object.keys(media.sources).forEach(function(ssrc) {
671
+        if(Object.keys(media.sources).length > 0) {
672
+            // TODO(gp) maybe exclude FID streams?
673
+            Object.keys(media.sources).forEach(function (ssrc) {
674
+                newssrcs.push({
675
+                    'ssrc': ssrc,
676
+                    'type': media.type,
677
+                    'direction': media.direction
678
+                });
679
+            });
680
+        }
681
+        else if(sess.localStreamsSSRC && sess.localStreamsSSRC[media.type])
682
+        {
642 683
             newssrcs.push({
643
-                'ssrc': ssrc,
684
+                'ssrc': sess.localStreamsSSRC[media.type],
644 685
                 'type': media.type,
645 686
                 'direction': media.direction
646 687
             });
647
-        });
688
+        }
689
+
648 690
     });
691
+
649 692
     console.log('new ssrcs', newssrcs);
650 693
 
651 694
     // Have to clear presence map to get rid of removed streams
@@ -678,20 +721,22 @@ $(document).bind('iceconnectionstatechange.jingle', function (event, sid, sessio
678 721
             var metadata = {};
679 722
             metadata.setupTime = (new Date()).getTime() - session.timeChecking;
680 723
             session.peerconnection.getStats(function (res) {
681
-                res.result().forEach(function (report) {
682
-                    if (report.type == 'googCandidatePair' && report.stat('googActiveConnection') == 'true') {
683
-                        metadata.localCandidateType = report.stat('googLocalCandidateType');
684
-                        metadata.remoteCandidateType = report.stat('googRemoteCandidateType');
685
-
686
-                        // log pair as well so we can get nice pie charts 
687
-                        metadata.candidatePair = report.stat('googLocalCandidateType') + ';' + report.stat('googRemoteCandidateType');
688
-
689
-                        if (report.stat('googRemoteAddress').indexOf('[') === 0) {
690
-                            metadata.ipv6 = true;
724
+                if(res && res.result) {
725
+                    res.result().forEach(function (report) {
726
+                        if (report.type == 'googCandidatePair' && report.stat('googActiveConnection') == 'true') {
727
+                            metadata.localCandidateType = report.stat('googLocalCandidateType');
728
+                            metadata.remoteCandidateType = report.stat('googRemoteCandidateType');
729
+
730
+                            // log pair as well so we can get nice pie charts
731
+                            metadata.candidatePair = report.stat('googLocalCandidateType') + ';' + report.stat('googRemoteCandidateType');
732
+
733
+                            if (report.stat('googRemoteAddress').indexOf('[') === 0) {
734
+                                metadata.ipv6 = true;
735
+                            }
691 736
                         }
692
-                    }
693
-                });
694
-                trackUsage('iceConnected', metadata);
737
+                    });
738
+                    trackUsage('iceConnected', metadata);
739
+                }
695 740
             });
696 741
         }
697 742
         break;
@@ -788,14 +833,13 @@ $(document).bind('left.muc', function (event, jid) {
788 833
         APIConnector.triggerEvent("participantLeft",{jid: jid});
789 834
     }
790 835
 
836
+    delete jid2Ssrc[jid];
837
+
791 838
     // Unlock large video
792
-    if (focusedVideoSrc)
839
+    if (focusedVideoSrc && focusedVideoSrc.jid === jid)
793 840
     {
794
-        if (getJidFromVideoSrc(focusedVideoSrc) === jid)
795
-        {
796
-            console.info("Focused video owner has left the conference");
797
-            focusedVideoSrc = null;
798
-        }
841
+        console.info("Focused video owner has left the conference");
842
+        focusedVideoSrc = null;
799 843
     }
800 844
 
801 845
     connection.jingle.terminateByJid(jid);
@@ -812,8 +856,6 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
812 856
     Object.keys(ssrc2jid).forEach(function (ssrc) {
813 857
         if (ssrc2jid[ssrc] == jid) {
814 858
             delete ssrc2jid[ssrc];
815
-        }
816
-        if (ssrc2videoType[ssrc] == jid) {
817 859
             delete ssrc2videoType[ssrc];
818 860
         }
819 861
     });
@@ -822,6 +864,7 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
822 864
         //console.log(jid, 'assoc ssrc', ssrc.getAttribute('type'), ssrc.getAttribute('ssrc'));
823 865
         var ssrcV = ssrc.getAttribute('ssrc');
824 866
         ssrc2jid[ssrcV] = jid;
867
+        notReceivedSSRCs.push(ssrcV);
825 868
 
826 869
         var type = ssrc.getAttribute('type');
827 870
         ssrc2videoType[ssrcV] = type;
@@ -949,16 +992,20 @@ $(document).bind('passwordrequired.main', function (event) {
949 992
  * blob:https%3A//pawel.jitsi.net/9a46e0bd-131e-4d18-9c14-a9264e8db395
950 993
  * @returns {boolean}
951 994
  */
952
-function isVideoSrcDesktop(videoSrc) {
995
+function isVideoSrcDesktop(jid) {
953 996
     // FIXME: fix this mapping mess...
954 997
     // figure out if large video is desktop stream or just a camera
998
+
999
+    if(!jid)
1000
+        return false;
955 1001
     var isDesktop = false;
956
-    if (localVideoSrc === videoSrc) {
1002
+    if (connection.emuc.myroomjid &&
1003
+        Strophe.getResourceFromJid(connection.emuc.myroomjid) === jid) {
957 1004
         // local video
958 1005
         isDesktop = isUsingScreenStream;
959 1006
     } else {
960 1007
         // Do we have associations...
961
-        var videoSsrc = videoSrcToSsrc[videoSrc];
1008
+        var videoSsrc = jid2Ssrc[jid];
962 1009
         if (videoSsrc) {
963 1010
             var videoType = ssrc2videoType[videoSsrc];
964 1011
             if (videoType) {
@@ -968,7 +1015,7 @@ function isVideoSrcDesktop(videoSrc) {
968 1015
                 console.error("No video type for ssrc: " + videoSsrc);
969 1016
             }
970 1017
         } else {
971
-            console.error("No ssrc for src: " + videoSrc);
1018
+            console.error("No ssrc for jid: " + jid);
972 1019
         }
973 1020
     }
974 1021
     return isDesktop;
@@ -1311,6 +1358,8 @@ $(document).ready(function () {
1311 1358
             VideoLayout.positionLarge(currentVideoWidth, currentVideoHeight);
1312 1359
         });
1313 1360
 
1361
+    document.getElementById('largeVideo').volume = 0;
1362
+
1314 1363
     if (!$('#settings').is(':visible')) {
1315 1364
         console.log('init');
1316 1365
         init();
@@ -1386,10 +1435,10 @@ function disposeConference(onUnload) {
1386 1435
         // FIXME: probably removing streams is not required and close() should
1387 1436
         // be enough
1388 1437
         if (connection.jingle.localAudio) {
1389
-            handler.peerconnection.removeStream(connection.jingle.localAudio);
1438
+            handler.peerconnection.removeStream(connection.jingle.localAudio, onUnload);
1390 1439
         }
1391 1440
         if (connection.jingle.localVideo) {
1392
-            handler.peerconnection.removeStream(connection.jingle.localVideo);
1441
+            handler.peerconnection.removeStream(connection.jingle.localVideo, onUnload);
1393 1442
         }
1394 1443
         handler.peerconnection.close();
1395 1444
     }
@@ -1553,7 +1602,7 @@ function onSelectedEndpointChanged(userJid)
1553 1602
                 dataChannel.send(JSON.stringify({
1554 1603
                     'colibriClass': 'SelectedEndpointChangedEvent',
1555 1604
                     'selectedEndpoint': (!userJid || userJid == null)
1556
-                        ? null : Strophe.getResourceFromJid(userJid)
1605
+                        ? null : userJid
1557 1606
                 }));
1558 1607
 
1559 1608
                 return true;

+ 5
- 3
config.js Vedi File

@@ -4,7 +4,7 @@ var config = {
4 4
         //anonymousdomain: 'guest.example.com',
5 5
         muc: 'conference.jitsi-meet.example.com', // FIXME: use XEP-0030
6 6
         bridge: 'jitsi-videobridge.jitsi-meet.example.com', // FIXME: use XEP-0030
7
-        call_control: 'callcontrol.jitsi-meet.example.com',
7
+        //call_control: 'callcontrol.jitsi-meet.example.com',
8 8
         focus: 'focus.jitsi-meet.example.com'
9 9
     },
10 10
 //  getroomnode: function (path) { return 'someprefixpossiblybasedonpath'; },
@@ -26,8 +26,10 @@ var config = {
26 26
     adaptiveSimulcast: false,
27 27
     useRtcpMux: true,
28 28
     useBundle: true,
29
-    enableRecording: true,
30
-    enableWelcomePage: false,
29
+    enableRecording: false,
30
+    enableWelcomePage: true,
31 31
     enableSimulcast: false,
32
+    enableFirefoxSupport: false, //firefox support is still experimental, only one-to-one conferences with chrome focus
33
+    // will work when simulcast, bundle, mux, lastN and SCTP are disabled.
32 34
     logStats: false // Enable logging of PeerConnection stats via the focus
33 35
 };

+ 5
- 0
css/videolayout_default.css Vedi File

@@ -104,6 +104,11 @@
104 104
     text-align: center;
105 105
 }
106 106
 
107
+#largeVideo
108
+{
109
+    object-fit: cover;
110
+}
111
+
107 112
 #presentation,
108 113
 #etherpad,
109 114
 #localVideoWrapper>video,

+ 1
- 2
data_channels.js Vedi File

@@ -26,8 +26,7 @@ function onDataChannel(event)
26 26
 
27 27
         // when the data channel becomes available, tell the bridge about video
28 28
         // selections so that it can do adaptive simulcast,
29
-        var largeVideoSrc = $('#largeVideo').attr('src');
30
-        var userJid = getJidFromVideoSrc(largeVideoSrc);
29
+        var userJid = VideoLayout.getLargeVideoState().userJid;
31 30
         // we want the notification to trigger even if userJid is undefined,
32 31
         // or null.
33 32
         onSelectedEndpointChanged(userJid);

+ 2
- 1
libs/colibri/colibri.focus.js Vedi File

@@ -453,7 +453,8 @@ ColibriFocus.prototype.createdConference = function (result) {
453 453
         'a=rtpmap:100 VP8/90000\r\n' +
454 454
         'a=rtcp-fb:100 ccm fir\r\n' +
455 455
         'a=rtcp-fb:100 nack\r\n' +
456
-        'a=rtcp-fb:100 goog-remb\r\n' +
456
+        'a=rtcp-fb:100 nack pli\r\n' +
457
+            (config.enableFirefoxSupport? "" : 'a=rtcp-fb:100 goog-remb\r\n') +
457 458
         'a=rtpmap:116 red/90000\r\n' +
458 459
         'a=rtpmap:117 ulpfec/90000\r\n' +
459 460
         (config.useRtcpMux ? 'a=rtcp-mux\r\n' : '') +

+ 70
- 4
libs/strophe/strophe.jingle.adapter.js Vedi File

@@ -141,12 +141,28 @@ if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
141 141
 TraceablePeerConnection.prototype.addStream = function (stream) {
142 142
     this.trace('addStream', stream.id);
143 143
     simulcast.resetSender();
144
-    this.peerconnection.addStream(stream);
144
+    try
145
+    {
146
+        this.peerconnection.addStream(stream);
147
+    }
148
+    catch (e)
149
+    {
150
+        console.error(e);
151
+        return;
152
+    }
145 153
 };
146 154
 
147
-TraceablePeerConnection.prototype.removeStream = function (stream) {
155
+TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams) {
148 156
     this.trace('removeStream', stream.id);
149 157
     simulcast.resetSender();
158
+    if(stopStreams) {
159
+        stream.getAudioTracks().forEach(function (track) {
160
+            track.stop();
161
+        });
162
+        stream.getVideoTracks().forEach(function (track) {
163
+            track.stop();
164
+        });
165
+    }
150 166
     this.peerconnection.removeStream(stream);
151 167
 };
152 168
 
@@ -486,6 +502,11 @@ TraceablePeerConnection.prototype.addIceCandidate = function (candidate, success
486 502
 TraceablePeerConnection.prototype.getStats = function(callback, errback) {
487 503
     if (navigator.mozGetUserMedia) {
488 504
         // ignore for now...
505
+        if(!errback)
506
+            errback = function () {
507
+
508
+            }
509
+        this.peerconnection.getStats(null,callback,errback);
489 510
     } else {
490 511
         this.peerconnection.getStats(callback);
491 512
     }
@@ -506,7 +527,40 @@ function setupRTC() {
506 527
                     element[0].mozSrcObject = stream;
507 528
                     element[0].play();
508 529
                 },
509
-                pc_constraints: {}
530
+                pc_constraints: {},
531
+                getLocalSSRC: function (session, callback) {
532
+                    session.peerconnection.getStats(function (s) {
533
+                            var ssrcs = {};
534
+                            s.forEach(function (item) {
535
+                                if (item.type == "outboundrtp" && !item.isRemote)
536
+                                {
537
+                                    ssrcs[item.id.split('_')[2]] = item.ssrc;
538
+                                }
539
+                            });
540
+                            session.localStreamsSSRC = {
541
+                                "audio": ssrcs.audio,//for stable 0
542
+                                "video": ssrcs.video// for stable 1
543
+                            };
544
+                            callback(session.localStreamsSSRC);
545
+                        },
546
+                        function () {
547
+                            callback(null);
548
+                        });
549
+                },
550
+                getStreamID: function (stream) {
551
+                    var tracks = stream.getVideoTracks();
552
+                    if(!tracks || tracks.length == 0)
553
+                    {
554
+                        tracks = stream.getAudioTracks();
555
+                    }
556
+                    return tracks[0].id.replace(/[\{,\}]/g,"");
557
+                },
558
+                getVideoSrc: function (element) {
559
+                    return element.mozSrcObject;
560
+                },
561
+                setVideoSrc: function (element, src) {
562
+                    element.mozSrcObject = src;
563
+                }
510 564
             };
511 565
             if (!MediaStream.prototype.getVideoTracks)
512 566
                 MediaStream.prototype.getVideoTracks = function () { return []; };
@@ -525,7 +579,19 @@ function setupRTC() {
525 579
                 element.attr('src', webkitURL.createObjectURL(stream));
526 580
             },
527 581
             // DTLS should now be enabled by default but..
528
-            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}
582
+            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]},
583
+            getLocalSSRC: function (session, callback) {
584
+                callback(null);
585
+            },
586
+            getStreamID: function (stream) {
587
+                return stream.id;
588
+            },
589
+            getVideoSrc: function (element) {
590
+                return element.getAttribute("src");
591
+            },
592
+            setVideoSrc: function (element, src) {
593
+                element.setAttribute("src", src);
594
+            }
529 595
         };
530 596
         if (navigator.userAgent.indexOf('Android') != -1) {
531 597
             RTC.pc_constraints = {}; // disable DTLS on Android

+ 6
- 2
libs/strophe/strophe.jingle.js Vedi File

@@ -88,7 +88,9 @@ Strophe.addConnectionPlugin('jingle', {
88 88
             case 'session-initiate':
89 89
                 sess = new JingleSession($(iq).attr('to'), $(iq).find('jingle').attr('sid'), this.connection);
90 90
                 // configure session
91
-                if (this.localAudio) {
91
+
92
+                //in firefox we have only one stream object
93
+                if (this.localAudio != this.localVideo) {
92 94
                     sess.localStreams.push(this.localAudio);
93 95
                 }
94 96
                 if (this.localVideo) {
@@ -173,7 +175,9 @@ Strophe.addConnectionPlugin('jingle', {
173 175
             Math.random().toString(36).substr(2, 12), // random string
174 176
             this.connection);
175 177
         // configure session
176
-        if (this.localAudio) {
178
+
179
+        //in firefox we have only one stream
180
+        if (this.localAudio != this.localVideo) {
177 181
             sess.localStreams.push(this.localAudio);
178 182
         }
179 183
         if (this.localVideo) {

+ 59
- 18
libs/strophe/strophe.jingle.sdp.js Vedi File

@@ -194,7 +194,8 @@ SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
194 194
 }
195 195
 
196 196
 // add content's to a jingle element
197
-SDP.prototype.toJingle = function (elem, thecreator) {
197
+SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
198
+//    console.log("SSRC" + ssrcs["audio"] + " - " + ssrcs["video"]);
198 199
     var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
199 200
     var self = this;
200 201
     // new bundle plan
@@ -221,7 +222,12 @@ SDP.prototype.toJingle = function (elem, thecreator) {
221 222
         if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
222 223
             ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
223 224
         } else {
224
-            ssrc = false;
225
+            if(ssrcs && ssrcs[mline.media])
226
+            {
227
+                ssrc = ssrcs[mline.media];
228
+            }
229
+            else
230
+                ssrc = false;
225 231
         }
226 232
 
227 233
         elem.c('content', {creator: thecreator, name: mline.media});
@@ -267,25 +273,60 @@ SDP.prototype.toJingle = function (elem, thecreator) {
267 273
                 elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
268 274
                 // FIXME: group by ssrc and support multiple different ssrcs
269 275
                 var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
270
-                ssrclines.forEach(function(line) {
271
-                    idx = line.indexOf(' ');
272
-                    var linessrc = line.substr(0, idx).substr(7);
273
-                    if (linessrc != ssrc) {
276
+                if(ssrclines.length > 0) {
277
+                    ssrclines.forEach(function (line) {
278
+                        idx = line.indexOf(' ');
279
+                        var linessrc = line.substr(0, idx).substr(7);
280
+                        if (linessrc != ssrc) {
281
+                            elem.up();
282
+                            ssrc = linessrc;
283
+                            elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
284
+                        }
285
+                        var kv = line.substr(idx + 1);
286
+                        elem.c('parameter');
287
+                        if (kv.indexOf(':') == -1) {
288
+                            elem.attrs({ name: kv });
289
+                        } else {
290
+                            elem.attrs({ name: kv.split(':', 2)[0] });
291
+                            elem.attrs({ value: kv.split(':', 2)[1] });
292
+                        }
274 293
                         elem.up();
275
-                        ssrc = linessrc;
276
-                        elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
277
-                    }
278
-                    var kv = line.substr(idx + 1);
294
+                    });
295
+                    elem.up();
296
+                }
297
+                else
298
+                {
299
+                    elem.up();
300
+                    elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
279 301
                     elem.c('parameter');
280
-                    if (kv.indexOf(':') == -1) {
281
-                        elem.attrs({ name: kv });
282
-                    } else {
283
-                        elem.attrs({ name: kv.split(':', 2)[0] });
284
-                        elem.attrs({ value: kv.split(':', 2)[1] });
285
-                    }
302
+                    elem.attrs({name: "cname", value:Math.random().toString(36).substring(7)});
286 303
                     elem.up();
287
-                });
288
-                elem.up();
304
+                    var msid = null;
305
+                    if(mline.media == "audio")
306
+                    {
307
+                        msid = connection.jingle.localAudio.getAudioTracks()[0].id;
308
+                    }
309
+                    else
310
+                    {
311
+                        msid = connection.jingle.localVideo.getVideoTracks()[0].id;
312
+                    }
313
+                    if(msid != null)
314
+                    {
315
+                        msid = msid.replace(/[\{,\}]/g,"");
316
+                        elem.c('parameter');
317
+                        elem.attrs({name: "msid", value:msid});
318
+                        elem.up();
319
+                        elem.c('parameter');
320
+                        elem.attrs({name: "mslabel", value:msid});
321
+                        elem.up();
322
+                        elem.c('parameter');
323
+                        elem.attrs({name: "label", value:msid});
324
+                        elem.up();
325
+                        elem.up();
326
+                    }
327
+
328
+
329
+                }
289 330
 
290 331
                 // XEP-0339 handle ssrc-group attributes
291 332
                 var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');

+ 84
- 50
libs/strophe/strophe.jingle.session.js Vedi File

@@ -36,6 +36,7 @@ function JingleSession(me, sid, connection) {
36 36
     this.reason = null;
37 37
 
38 38
     this.wait = true;
39
+    this.localStreamsSSRC = null;
39 40
 }
40 41
 
41 42
 JingleSession.prototype.initiate = function (peerjid, isInitiator) {
@@ -64,6 +65,7 @@ JingleSession.prototype.initiate = function (peerjid, isInitiator) {
64 65
     };
65 66
     this.peerconnection.onaddstream = function (event) {
66 67
         self.remoteStreams.push(event.stream);
68
+        console.log("REMOTE STREAM ADDED: " + event.stream + " - " + event.stream.id);
67 69
         $(document).trigger('remotestreamadded.jingle', [event, self.sid]);
68 70
     };
69 71
     this.peerconnection.onremovestream = function (event) {
@@ -128,8 +130,7 @@ JingleSession.prototype.accept = function () {
128 130
             initiator: this.initiator,
129 131
             responder: this.responder,
130 132
             sid: this.sid });
131
-    prsdp.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
132
-
133
+    prsdp.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
133 134
     var sdp = this.peerconnection.localDescription.sdp;
134 135
     while (SDPUtil.find_line(sdp, 'a=inactive')) {
135 136
         // FIXME: change any inactive to sendrecv or whatever they were originally
@@ -149,7 +150,7 @@ JingleSession.prototype.accept = function () {
149 150
                 function (stanza) {
150 151
                     var error = ($(stanza).find('error').length) ? {
151 152
                         code: $(stanza).find('error').attr('code'),
152
-                        reason: $(stanza).find('error :first')[0].tagName,
153
+                        reason: $(stanza).find('error :first')[0].tagName
153 154
                     }:{};
154 155
                     error.source = 'answer';
155 156
                     $(document).trigger('error.jingle', [self.sid, error]);
@@ -220,10 +221,10 @@ JingleSession.prototype.sendIceCandidate = function (candidate) {
220 221
                     }, 20);
221 222
 
222 223
                 }
223
-                this.drip_container.push(event.candidate);
224
+                this.drip_container.push(candidate);
224 225
                 return;
225 226
             } else {
226
-                self.sendIceCandidate([event.candidate]);
227
+                self.sendIceCandidate([candidate]);
227 228
             }
228 229
         }
229 230
     } else {
@@ -237,25 +238,43 @@ JingleSession.prototype.sendIceCandidate = function (candidate) {
237 238
                     initiator: this.initiator,
238 239
                     sid: this.sid});
239 240
             this.localSDP = new SDP(this.peerconnection.localDescription.sdp);
240
-            this.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder');
241
-            this.connection.sendIQ(init,
242
-                function () {
243
-                    //console.log('session initiate ack');
244
-                    var ack = {};
245
-                    ack.source = 'offer';
246
-                    $(document).trigger('ack.jingle', [self.sid, ack]);
247
-                },
248
-                function (stanza) {
249
-                    self.state = 'error';
250
-                    self.peerconnection.close();
251
-                    var error = ($(stanza).find('error').length) ? {
252
-                        code: $(stanza).find('error').attr('code'),
253
-                        reason: $(stanza).find('error :first')[0].tagName,
254
-                    }:{};
255
-                    error.source = 'offer';
256
-                    $(document).trigger('error.jingle', [self.sid, error]);
257
-                },
258
-                10000);
241
+            var self = this;
242
+            var sendJingle = function (ssrc) {
243
+                if(!ssrc)
244
+                    ssrc = {};
245
+                self.localSDP.toJingle(init, self.initiator == self.me ? 'initiator' : 'responder', ssrc);
246
+                self.connection.sendIQ(init,
247
+                    function () {
248
+                        //console.log('session initiate ack');
249
+                        var ack = {};
250
+                        ack.source = 'offer';
251
+                        $(document).trigger('ack.jingle', [self.sid, ack]);
252
+                    },
253
+                    function (stanza) {
254
+                        self.state = 'error';
255
+                        self.peerconnection.close();
256
+                        var error = ($(stanza).find('error').length) ? {
257
+                            code: $(stanza).find('error').attr('code'),
258
+                            reason: $(stanza).find('error :first')[0].tagName,
259
+                        }:{};
260
+                        error.source = 'offer';
261
+                        $(document).trigger('error.jingle', [self.sid, error]);
262
+                    },
263
+                    10000);
264
+            }
265
+
266
+            RTC.getLocalSSRC(this, function (ssrcs) {
267
+                if(ssrcs)
268
+                {
269
+                    sendJingle(ssrcs);
270
+                    $(document).trigger("setLocalDescription.jingle", [self.sid]);
271
+                }
272
+                else
273
+                {
274
+                    sendJingle();
275
+                }
276
+            });
277
+
259 278
         }
260 279
         this.lasticecandidate = true;
261 280
         console.log('Have we encountered any srflx candidates? ' + this.hadstuncandidate);
@@ -276,11 +295,12 @@ JingleSession.prototype.sendIceCandidates = function (candidates) {
276 295
             sid: this.sid});
277 296
     for (var mid = 0; mid < this.localSDP.media.length; mid++) {
278 297
         var cands = candidates.filter(function (el) { return el.sdpMLineIndex == mid; });
298
+        var mline = SDPUtil.parse_mline(this.localSDP.media[mid].split('\r\n')[0]);
279 299
         if (cands.length > 0) {
280 300
             var ice = SDPUtil.iceparams(this.localSDP.media[mid], this.localSDP.session);
281 301
             ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
282 302
             cand.c('content', {creator: this.initiator == this.me ? 'initiator' : 'responder',
283
-                name: cands[0].sdpMid
303
+                name: (cands[0].sdpMid? cands[0].sdpMid : mline.media)
284 304
             }).c('transport', ice);
285 305
             for (var i = 0; i < cands.length; i++) {
286 306
                 cand.c('candidate', SDPUtil.candidateToJingle(cands[i].candidate)).up();
@@ -339,14 +359,14 @@ JingleSession.prototype.createdOffer = function (sdp) {
339 359
     var self = this;
340 360
     this.localSDP = new SDP(sdp.sdp);
341 361
     //this.localSDP.mangle();
342
-    if (this.usetrickle) {
362
+    var sendJingle = function () {
343 363
         var init = $iq({to: this.peerjid,
344 364
             type: 'set'})
345 365
             .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
346 366
                 action: 'session-initiate',
347 367
                 initiator: this.initiator,
348 368
                 sid: this.sid});
349
-        this.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder');
369
+        this.localSDP.toJingle(init, this.initiator == this.me ? 'initiator' : 'responder', this.localStreamsSSRC);
350 370
         this.connection.sendIQ(init,
351 371
             function () {
352 372
                 var ack = {};
@@ -368,7 +388,16 @@ JingleSession.prototype.createdOffer = function (sdp) {
368 388
     sdp.sdp = this.localSDP.raw;
369 389
     this.peerconnection.setLocalDescription(sdp,
370 390
         function () {
371
-            $(document).trigger('setLocalDescription.jingle', [self.sid]);
391
+            if(this.usetrickle)
392
+            {
393
+                RTC.getLocalSSRC(function(ssrc)
394
+                {
395
+                    sendJingle(ssrc);
396
+                    $(document).trigger('setLocalDescription.jingle', [self.sid]);
397
+                });
398
+            }
399
+            else
400
+                $(document).trigger('setLocalDescription.jingle', [self.sid]);
372 401
             //console.log('setLocalDescription success');
373 402
         },
374 403
         function (e) {
@@ -557,21 +586,9 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
557 586
     var self = this;
558 587
     this.localSDP = new SDP(sdp.sdp);
559 588
     //this.localSDP.mangle();
560
-    var accept = null;
561 589
     this.usepranswer = provisional === true;
562 590
     if (this.usetrickle) {
563
-        if (!this.usepranswer) {
564
-            accept = $iq({to: this.peerjid,
565
-                type: 'set'})
566
-                .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
567
-                    action: 'session-accept',
568
-                    initiator: this.initiator,
569
-                    responder: this.responder,
570
-                    sid: this.sid });
571
-            var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
572
-            var publicLocalSDP = new SDP(publicLocalDesc.sdp);
573
-            publicLocalSDP.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
574
-        } else {
591
+        if (this.usepranswer) {
575 592
             sdp.type = 'pranswer';
576 593
             for (var i = 0; i < this.localSDP.media.length; i++) {
577 594
                 this.localSDP.media[i] = this.localSDP.media[i].replace('a=sendrecv\r\n', 'a=inactive\r\n');
@@ -579,13 +596,19 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
579 596
             this.localSDP.raw = this.localSDP.session + '\r\n' + this.localSDP.media.join('');
580 597
         }
581 598
     }
582
-    sdp.sdp = this.localSDP.raw;
583
-    this.peerconnection.setLocalDescription(sdp,
584
-        function () {
585
-            $(document).trigger('setLocalDescription.jingle', [self.sid]);
586
-            //console.log('setLocalDescription success');
587
-            if (accept)
588
-            {
599
+    var self = this;
600
+    var sendJingle = function (ssrcs) {
601
+
602
+                var accept = $iq({to: self.peerjid,
603
+                    type: 'set'})
604
+                    .c('jingle', {xmlns: 'urn:xmpp:jingle:1',
605
+                        action: 'session-accept',
606
+                        initiator: self.initiator,
607
+                        responder: self.responder,
608
+                        sid: self.sid });
609
+                var publicLocalDesc = simulcast.reverseTransformLocalDescription(sdp);
610
+                var publicLocalSDP = new SDP(publicLocalDesc.sdp);
611
+                publicLocalSDP.toJingle(accept, self.initiator == self.me ? 'initiator' : 'responder', ssrcs);
589 612
                 this.connection.sendIQ(accept,
590 613
                     function () {
591 614
                         var ack = {};
@@ -598,12 +621,23 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
598 621
                             reason: $(stanza).find('error :first')[0].tagName,
599 622
                         }:{};
600 623
                         error.source = 'answer';
601
-                        error.stanza = stanza;
602
-
603 624
                         $(document).trigger('error.jingle', [self.sid, error]);
604 625
                     },
605 626
                     10000);
627
+    }
628
+    sdp.sdp = this.localSDP.raw;
629
+    this.peerconnection.setLocalDescription(sdp,
630
+        function () {
631
+
632
+            //console.log('setLocalDescription success');
633
+            if (self.usetrickle && !self.usepranswer) {
634
+                RTC.getLocalSSRC(self, function (ssrc) {
635
+                    sendJingle(ssrc);
636
+                    $(document).trigger('setLocalDescription.jingle', [self.sid]);
637
+                });
606 638
             }
639
+            else
640
+                $(document).trigger('setLocalDescription.jingle', [self.sid]);
607 641
         },
608 642
         function (e) {
609 643
             console.error('setLocalDescription failed', e);

+ 5
- 2
libs/strophe/strophe.jingle.sessionbase.js Vedi File

@@ -82,14 +82,17 @@ SessionBase.prototype.switchStreams = function (new_stream, oldStream, success_c
82 82
         if(self.peerconnection.localDescription) {
83 83
             oldSdp = new SDP(self.peerconnection.localDescription.sdp);
84 84
         }
85
-        self.peerconnection.removeStream(oldStream);
85
+        self.peerconnection.removeStream(oldStream, true);
86 86
         self.peerconnection.addStream(new_stream);
87 87
     }
88 88
 
89 89
     self.connection.jingle.localVideo = new_stream;
90 90
 
91 91
     self.connection.jingle.localStreams = [];
92
-    self.connection.jingle.localStreams.push(self.connection.jingle.localAudio);
92
+
93
+    //in firefox we have only one stream object
94
+    if(self.connection.jingle.localAudio != self.connection.jingle.localVideo)
95
+        self.connection.jingle.localStreams.push(self.connection.jingle.localAudio);
93 96
     self.connection.jingle.localStreams.push(self.connection.jingle.localVideo);
94 97
 
95 98
     // Conference is not active

+ 1
- 1
libs/strophe/strophe.min.js
File diff soppresso perché troppo grande
Vedi File


+ 1
- 1
muc.js Vedi File

@@ -112,7 +112,7 @@ Strophe.addConnectionPlugin('emuc', {
112 112
             var create = $iq({type: 'set', to: this.roomjid})
113 113
                     .c('query', {xmlns: 'http://jabber.org/protocol/muc#owner'})
114 114
                     .c('x', {xmlns: 'jabber:x:data', type: 'submit'});
115
-            this.connection.send(create); // fire away
115
+            this.connection.sendIQ(create); // fire away
116 116
         }
117 117
 
118 118
         // Parse roles.

+ 157
- 50
rtp_sts.js Vedi File

@@ -80,7 +80,14 @@ PeerStats.prototype.setSsrcResolution = function (ssrc, resolution)
80 80
  */
81 81
 PeerStats.prototype.setSsrcBitrate = function (ssrc, bitrate)
82 82
 {
83
-    this.ssrc2bitrate[ssrc] = bitrate;
83
+    if(this.ssrc2bitrate[ssrc])
84
+    {
85
+        this.ssrc2bitrate[ssrc].download += bitrate.download;
86
+        this.ssrc2bitrate[ssrc].upload += bitrate.upload;
87
+    }
88
+    else {
89
+        this.ssrc2bitrate[ssrc] = bitrate;
90
+    }
84 91
 };
85 92
 
86 93
 /**
@@ -103,6 +110,7 @@ PeerStats.prototype.setSsrcAudioLevel = function (ssrc, audioLevel)
103 110
  */
104 111
 PeerStats.transport = [];
105 112
 
113
+
106 114
 /**
107 115
  * <tt>StatsCollector</tt> registers for stats updates of given
108 116
  * <tt>peerconnection</tt> in given <tt>interval</tt>. On each update particular
@@ -210,7 +218,15 @@ StatsCollector.prototype.start = function ()
210 218
             self.peerconnection.getStats(
211 219
                 function (report)
212 220
                 {
213
-                    var results = report.result();
221
+                    var results = null;
222
+                    if(!report || !report.result || typeof report.result != 'function')
223
+                    {
224
+                        results = report;
225
+                    }
226
+                    else
227
+                    {
228
+                        results = report.result();
229
+                    }
214 230
                     //console.error("Got interval report", results);
215 231
                     self.currentAudioLevelsReport = results;
216 232
                     self.processAudioLevelReport();
@@ -229,10 +245,28 @@ StatsCollector.prototype.start = function ()
229 245
             self.peerconnection.getStats(
230 246
                 function (report)
231 247
                 {
232
-                    var results = report.result();
248
+                    var results = null;
249
+                    if(!report || !report.result || typeof report.result != 'function')
250
+                    {
251
+                        //firefox
252
+                        results = report;
253
+                    }
254
+                    else
255
+                    {
256
+                        //chrome
257
+                        results = report.result();
258
+                    }
233 259
                     //console.error("Got interval report", results);
234 260
                     self.currentStatsReport = results;
235
-                    self.processStatsReport();
261
+                    try
262
+                    {
263
+                        self.processStatsReport();
264
+                    }
265
+                    catch(e)
266
+                    {
267
+                        console.error("Unsupported key:" + e);
268
+                    }
269
+
236 270
                     self.baselineStatsReport = self.currentStatsReport;
237 271
                 },
238 272
                 self.errorCallback
@@ -318,6 +352,36 @@ StatsCollector.prototype.logStats = function () {
318 352
     this.statsToBeLogged.stats = {};
319 353
     this.statsToBeLogged.timestamps = [];
320 354
 };
355
+var keyMap = {
356
+    "firefox": {
357
+        "ssrc": "ssrc",
358
+        "packetsReceived": "packetsReceived",
359
+        "packetsLost": "packetsLost",
360
+        "packetsSent": "packetsSent",
361
+        "bytesReceived": "bytesReceived",
362
+        "bytesSent": "bytesSent"
363
+    },
364
+    "chrome": {
365
+        "receiveBandwidth": "googAvailableReceiveBandwidth",
366
+        "sendBandwidth": "googAvailableSendBandwidth",
367
+        "remoteAddress": "googRemoteAddress",
368
+        "transportType": "googTransportType",
369
+        "localAddress": "googLocalAddress",
370
+        "activeConnection": "googActiveConnection",
371
+        "ssrc": "ssrc",
372
+        "packetsReceived": "packetsReceived",
373
+        "packetsSent": "packetsSent",
374
+        "packetsLost": "packetsLost",
375
+        "bytesReceived": "bytesReceived",
376
+        "bytesSent": "bytesSent",
377
+        "googFrameHeightReceived": "googFrameHeightReceived",
378
+        "googFrameWidthReceived": "googFrameWidthReceived",
379
+        "googFrameHeightSent": "googFrameHeightSent",
380
+        "googFrameWidthSent": "googFrameWidthSent",
381
+        "audioInputLevel": "audioInputLevel",
382
+        "audioOutputLevel": "audioOutputLevel"
383
+    }
384
+};
321 385
 
322 386
 /**
323 387
  * Stats processing logic.
@@ -329,23 +393,29 @@ StatsCollector.prototype.processStatsReport = function () {
329 393
 
330 394
     for (var idx in this.currentStatsReport) {
331 395
         var now = this.currentStatsReport[idx];
332
-        if (now.stat('googAvailableReceiveBandwidth') ||
333
-            now.stat('googAvailableSendBandwidth'))
334
-        {
335
-            PeerStats.bandwidth = {
336
-                "download": Math.round(
337
-                        (now.stat('googAvailableReceiveBandwidth')) / 1000),
338
-                "upload": Math.round(
339
-                        (now.stat('googAvailableSendBandwidth')) / 1000)
340
-            };
396
+        try {
397
+            if (getStatValue(now, 'receiveBandwidth') ||
398
+                getStatValue(now, 'sendBandwidth')) {
399
+                PeerStats.bandwidth = {
400
+                    "download": Math.round(
401
+                            (getStatValue(now, 'receiveBandwidth')) / 1000),
402
+                    "upload": Math.round(
403
+                            (getStatValue(now, 'sendBandwidth')) / 1000)
404
+                };
405
+            }
341 406
         }
407
+        catch(e){/*not supported*/}
342 408
 
343 409
         if(now.type == 'googCandidatePair')
344 410
         {
345
-            var ip = now.stat('googRemoteAddress');
346
-            var type = now.stat("googTransportType");
347
-            var localIP = now.stat("googLocalAddress");
348
-            var active = now.stat("googActiveConnection");
411
+            var ip, type, localIP, active;
412
+            try {
413
+                ip = getStatValue(now, 'remoteAddress');
414
+                type = getStatValue(now, "transportType");
415
+                localIP = getStatValue(now, "localAddress");
416
+                active = getStatValue(now, "activeConnection");
417
+            }
418
+            catch(e){/*not supported*/}
349 419
             if(!ip || !type || !localIP || active != "true")
350 420
                 continue;
351 421
             var addressSaved = false;
@@ -364,17 +434,32 @@ StatsCollector.prototype.processStatsReport = function () {
364 434
             continue;
365 435
         }
366 436
 
367
-        if (now.type != 'ssrc') {
437
+        if(now.type == "candidatepair")
438
+        {
439
+            if(now.state == "succeeded")
440
+                continue;
441
+
442
+            var local = this.currentStatsReport[now.localCandidateId];
443
+            var remote = this.currentStatsReport[now.remoteCandidateId];
444
+            PeerStats.transport.push({localip: local.ipAddress + ":" + local.portNumber,
445
+                ip: remote.ipAddress + ":" + remote.portNumber, type: local.transport});
446
+
447
+        }
448
+
449
+        if (now.type != 'ssrc' && now.type != "outboundrtp" &&
450
+            now.type != "inboundrtp") {
368 451
             continue;
369 452
         }
370 453
 
371 454
         var before = this.baselineStatsReport[idx];
372 455
         if (!before) {
373
-            console.warn(now.stat('ssrc') + ' not enough data');
456
+            console.warn(getStatValue(now, 'ssrc') + ' not enough data');
374 457
             continue;
375 458
         }
376 459
 
377
-        var ssrc = now.stat('ssrc');
460
+        var ssrc = getStatValue(now, 'ssrc');
461
+        if(!ssrc)
462
+            continue;
378 463
         var jid = ssrc2jid[ssrc];
379 464
         if (!jid) {
380 465
             console.warn("No jid for ssrc: " + ssrc);
@@ -390,31 +475,30 @@ StatsCollector.prototype.processStatsReport = function () {
390 475
 
391 476
         var isDownloadStream = true;
392 477
         var key = 'packetsReceived';
393
-        if (!now.stat(key))
478
+        if (!getStatValue(now, key))
394 479
         {
395 480
             isDownloadStream = false;
396 481
             key = 'packetsSent';
397
-            if (!now.stat(key))
482
+            if (!getStatValue(now, key))
398 483
             {
399
-                console.error("No packetsReceived nor packetSent stat found");
400
-                this.stop();
401
-                return;
484
+                console.warn("No packetsReceived nor packetSent stat found");
485
+                continue;
402 486
             }
403 487
         }
404
-        var packetsNow = now.stat(key);
488
+        var packetsNow = getStatValue(now, key);
405 489
         if(!packetsNow || packetsNow < 0)
406 490
             packetsNow = 0;
407 491
 
408
-        var packetsBefore = before.stat(key);
492
+        var packetsBefore = getStatValue(before, key);
409 493
         if(!packetsBefore || packetsBefore < 0)
410 494
             packetsBefore = 0;
411 495
         var packetRate = packetsNow - packetsBefore;
412 496
         if(!packetRate || packetRate < 0)
413 497
             packetRate = 0;
414
-        var currentLoss = now.stat('packetsLost');
498
+        var currentLoss = getStatValue(now, 'packetsLost');
415 499
         if(!currentLoss || currentLoss < 0)
416 500
             currentLoss = 0;
417
-        var previousLoss = before.stat('packetsLost');
501
+        var previousLoss = getStatValue(before, 'packetsLost');
418 502
         if(!previousLoss || previousLoss < 0)
419 503
             previousLoss = 0;
420 504
         var lossRate = currentLoss - previousLoss;
@@ -427,16 +511,18 @@ StatsCollector.prototype.processStatsReport = function () {
427 511
                 "packetsLost": lossRate,
428 512
                 "isDownloadStream": isDownloadStream});
429 513
 
514
+
430 515
         var bytesReceived = 0, bytesSent = 0;
431
-        if(now.stat("bytesReceived"))
516
+        if(getStatValue(now, "bytesReceived"))
432 517
         {
433
-            bytesReceived = now.stat("bytesReceived") -
434
-                before.stat("bytesReceived");
518
+            bytesReceived = getStatValue(now, "bytesReceived") -
519
+                getStatValue(before, "bytesReceived");
435 520
         }
436 521
 
437
-        if(now.stat("bytesSent"))
522
+        if(getStatValue(now, "bytesSent"))
438 523
         {
439
-            bytesSent = now.stat("bytesSent") - before.stat("bytesSent");
524
+            bytesSent = getStatValue(now, "bytesSent") -
525
+                getStatValue(before, "bytesSent");
440 526
         }
441 527
 
442 528
         var time = Math.round((now.timestamp - before.timestamp) / 1000);
@@ -461,19 +547,21 @@ StatsCollector.prototype.processStatsReport = function () {
461 547
         jidStats.setSsrcBitrate(ssrc, {
462 548
             "download": bytesReceived,
463 549
             "upload": bytesSent});
550
+
464 551
         var resolution = {height: null, width: null};
465
-        if(now.stat("googFrameHeightReceived") &&
466
-            now.stat("googFrameWidthReceived"))
467
-        {
468
-            resolution.height = now.stat("googFrameHeightReceived");
469
-            resolution.width = now.stat("googFrameWidthReceived");
470
-        }
471
-        else if(now.stat("googFrameHeightSent") &&
472
-            now.stat("googFrameWidthSent"))
473
-        {
474
-            resolution.height = now.stat("googFrameHeightSent");
475
-            resolution.width = now.stat("googFrameWidthSent");
552
+        try {
553
+            if (getStatValue(now, "googFrameHeightReceived") &&
554
+                getStatValue(now, "googFrameWidthReceived")) {
555
+                resolution.height = getStatValue(now, "googFrameHeightReceived");
556
+                resolution.width = getStatValue(now, "googFrameWidthReceived");
557
+            }
558
+            else if (getStatValue(now, "googFrameHeightSent") &&
559
+                getStatValue(now, "googFrameWidthSent")) {
560
+                resolution.height = getStatValue(now, "googFrameHeightSent");
561
+                resolution.width = getStatValue(now, "googFrameWidthSent");
562
+            }
476 563
         }
564
+        catch(e){/*not supported*/}
477 565
 
478 566
         if(resolution.height && resolution.width)
479 567
         {
@@ -515,6 +603,8 @@ StatsCollector.prototype.processStatsReport = function () {
515 603
                         self.jid2stats[jid].ssrc2bitrate[ssrc].download;
516 604
                     bitrateUpload +=
517 605
                         self.jid2stats[jid].ssrc2bitrate[ssrc].upload;
606
+
607
+                    delete self.jid2stats[jid].ssrc2bitrate[ssrc];
518 608
                 }
519 609
             );
520 610
             resolutions[jid] = self.jid2stats[jid].ssrc2resolution;
@@ -566,11 +656,11 @@ StatsCollector.prototype.processAudioLevelReport = function ()
566 656
         var before = this.baselineAudioLevelsReport[idx];
567 657
         if (!before)
568 658
         {
569
-            console.warn(now.stat('ssrc') + ' not enough data');
659
+            console.warn(getStatValue(now, 'ssrc') + ' not enough data');
570 660
             continue;
571 661
         }
572 662
 
573
-        var ssrc = now.stat('ssrc');
663
+        var ssrc = getStatValue(now, 'ssrc');
574 664
         var jid = ssrc2jid[ssrc];
575 665
         if (!jid)
576 666
         {
@@ -586,9 +676,19 @@ StatsCollector.prototype.processAudioLevelReport = function ()
586 676
         }
587 677
 
588 678
         // Audio level
589
-        var audioLevel = now.stat('audioInputLevel');
590
-        if (!audioLevel)
591
-            audioLevel = now.stat('audioOutputLevel');
679
+        var audioLevel = null;
680
+
681
+        try {
682
+            audioLevel = getStatValue(now, 'audioInputLevel');
683
+            if (!audioLevel)
684
+                audioLevel = getStatValue(now, 'audioOutputLevel');
685
+        }
686
+        catch(e) {/*not supported*/
687
+            console.warn("Audio Levels are not available in the statistics.");
688
+            clearInterval(this.audioLevelsIntervalId);
689
+            return;
690
+        }
691
+
592 692
         if (audioLevel)
593 693
         {
594 694
             // TODO: can't find specs about what this value really is,
@@ -603,3 +703,10 @@ StatsCollector.prototype.processAudioLevelReport = function ()
603 703
 
604 704
 
605 705
 };
706
+
707
+function getStatValue(item, name) {
708
+    if(!keyMap[RTC.browser][name])
709
+        throw "The property isn't supported!";
710
+    var key = keyMap[RTC.browser][name];
711
+    return RTC.browser == "chrome"? item.stat(key) : item[key];
712
+}

+ 141
- 106
videolayout.js Vedi File

@@ -9,6 +9,9 @@ var VideoLayout = (function (my) {
9 9
         updateInProgress: false,
10 10
         newSrc: ''
11 11
     };
12
+
13
+    var defaultLocalDisplayName = "Me";
14
+
12 15
     my.connectionIndicators = {};
13 16
 
14 17
     my.isInLastN = function(resource) {
@@ -17,9 +20,13 @@ var VideoLayout = (function (my) {
17 20
             || (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
18 21
     };
19 22
 
20
-    my.changeLocalAudio = function(stream) {
23
+    my.changeLocalStream = function (stream) {
21 24
         connection.jingle.localAudio = stream;
25
+        VideoLayout.changeLocalVideo(stream, true);
26
+    }
22 27
 
28
+    my.changeLocalAudio = function(stream) {
29
+        connection.jingle.localAudio = stream;
23 30
         RTC.attachMediaStream($('#localAudio'), stream);
24 31
         document.getElementById('localAudio').autoplay = true;
25 32
         document.getElementById('localAudio').volume = 0;
@@ -33,7 +40,7 @@ var VideoLayout = (function (my) {
33 40
         connection.jingle.localVideo = stream;
34 41
 
35 42
         var localVideo = document.createElement('video');
36
-        localVideo.id = 'localVideo_' + stream.id;
43
+        localVideo.id = 'localVideo_' + RTC.getStreamID(stream);
37 44
         localVideo.autoplay = true;
38 45
         localVideo.volume = 0; // is it required if audio is separated ?
39 46
         localVideo.oncontextmenu = function () { return false; };
@@ -55,10 +62,10 @@ var VideoLayout = (function (my) {
55 62
         // Add click handler to both video and video wrapper elements in case
56 63
         // there's no video.
57 64
         localVideoSelector.click(function () {
58
-            VideoLayout.handleVideoThumbClicked(localVideo.src);
65
+            VideoLayout.handleVideoThumbClicked(RTC.getVideoSrc(localVideo), false, connection.emuc.myroomjid);
59 66
         });
60 67
         $('#localVideoContainer').click(function () {
61
-            VideoLayout.handleVideoThumbClicked(localVideo.src);
68
+            VideoLayout.handleVideoThumbClicked(RTC.getVideoSrc(localVideo), false, connection.emuc.myroomjid);
62 69
         });
63 70
 
64 71
         // Add hover handler
@@ -68,14 +75,14 @@ var VideoLayout = (function (my) {
68 75
             },
69 76
             function() {
70 77
                 if (!VideoLayout.isLargeVideoVisible()
71
-                        || localVideo.src !== $('#largeVideo').attr('src'))
78
+                        || RTC.getVideoSrc(localVideo) !== RTC.getVideoSrc($('#largeVideo')[0]))
72 79
                     VideoLayout.showDisplayName('localVideoContainer', false);
73 80
             }
74 81
         );
75 82
         // Add stream ended handler
76 83
         stream.onended = function () {
77 84
             localVideoContainer.removeChild(localVideo);
78
-            VideoLayout.updateRemovedVideo(localVideo.src);
85
+            VideoLayout.updateRemovedVideo(RTC.getVideoSrc(localVideo));
79 86
         };
80 87
         // Flip video x axis if needed
81 88
         flipXLocalVideo = flipX;
@@ -86,9 +93,16 @@ var VideoLayout = (function (my) {
86 93
         var videoStream = simulcast.getLocalVideoStream();
87 94
         RTC.attachMediaStream(localVideoSelector, videoStream);
88 95
 
89
-        localVideoSrc = localVideo.src;
96
+        localVideoSrc = RTC.getVideoSrc(localVideo);
97
+
98
+        var myResourceJid = null;
99
+        if(connection.emuc.myroomjid)
100
+        {
101
+           myResourceJid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
102
+        }
103
+        VideoLayout.updateLargeVideo(localVideoSrc, 0,
104
+            myResourceJid);
90 105
 
91
-        VideoLayout.updateLargeVideo(localVideoSrc, 0);
92 106
     };
93 107
 
94 108
     /**
@@ -97,7 +111,7 @@ var VideoLayout = (function (my) {
97 111
      * @param removedVideoSrc src stream identifier of the video.
98 112
      */
99 113
     my.updateRemovedVideo = function(removedVideoSrc) {
100
-        if (removedVideoSrc === $('#largeVideo').attr('src')) {
114
+        if (removedVideoSrc === RTC.getVideoSrc($('#largeVideo')[0])) {
101 115
             // this is currently displayed as large
102 116
             // pick the last visible video in the row
103 117
             // if nobody else is left, this picks the local video
@@ -109,7 +123,7 @@ var VideoLayout = (function (my) {
109 123
                 console.info("Last visible video no longer exists");
110 124
                 pick = $('#remoteVideos>span[id!="mixedstream"]>video').get(0);
111 125
 
112
-                if (!pick || !pick.src) {
126
+                if (!pick || !RTC.getVideoSrc(pick)) {
113 127
                     // Try local video
114 128
                     console.info("Fallback to local video...");
115 129
                     pick = $('#remoteVideos>span>span>video').get(0);
@@ -118,20 +132,38 @@ var VideoLayout = (function (my) {
118 132
 
119 133
             // mute if localvideo
120 134
             if (pick) {
121
-                VideoLayout.updateLargeVideo(pick.src, pick.volume);
135
+                var container = pick.parentNode;
136
+                var jid = null;
137
+                if(container)
138
+                {
139
+                    if(container.id == "localVideoWrapper")
140
+                    {
141
+                        jid = Strophe.getResourceFromJid(connection.emuc.myroomjid);
142
+                    }
143
+                    else
144
+                    {
145
+                        jid = VideoLayout.getPeerContainerResourceJid(container);
146
+                    }
147
+                }
148
+
149
+                VideoLayout.updateLargeVideo(RTC.getVideoSrc(pick), pick.volume, jid);
122 150
             } else {
123 151
                 console.warn("Failed to elect large video");
124 152
             }
125 153
         }
126 154
     };
127 155
 
156
+    my.getLargeVideoState = function () {
157
+        return largeVideoState;
158
+    }
159
+
128 160
     /**
129 161
      * Updates the large video with the given new video source.
130 162
      */
131
-    my.updateLargeVideo = function(newSrc, vol) {
163
+    my.updateLargeVideo = function(newSrc, vol, jid) {
132 164
         console.log('hover in', newSrc);
133 165
 
134
-        if ($('#largeVideo').attr('src') != newSrc) {
166
+        if (RTC.getVideoSrc($('#largeVideo')[0]) != newSrc) {
135 167
 
136 168
             $('#activeSpeakerAvatar').css('visibility', 'hidden');
137 169
             // Due to the simulcast the localVideoSrc may have changed when the
@@ -144,15 +176,22 @@ var VideoLayout = (function (my) {
144 176
 
145 177
             largeVideoState.newSrc = newSrc;
146 178
             largeVideoState.isVisible = $('#largeVideo').is(':visible');
147
-            largeVideoState.isDesktop = isVideoSrcDesktop(newSrc);
148
-            largeVideoState.userJid = getJidFromVideoSrc(newSrc);
179
+            largeVideoState.isDesktop = isVideoSrcDesktop(jid);
180
+            if(jid2Ssrc[largeVideoState.userJid] ||
181
+                (connection && connection.emuc.myroomjid &&
182
+                    largeVideoState.userJid == Strophe.getResourceFromJid(connection.emuc.myroomjid)))
183
+            {
184
+                largeVideoState.oldJid = largeVideoState.userJid;
185
+            }
186
+            else
187
+            {
188
+                largeVideoState.oldJid = null;
189
+            }
190
+            largeVideoState.userJid = jid;
149 191
 
150 192
             // Screen stream is already rotated
151 193
             largeVideoState.flipX = (newSrc === localVideoSrc) && flipXLocalVideo;
152 194
 
153
-            var oldSrc = $('#largeVideo').attr('src');
154
-            largeVideoState.oldJid = getJidFromVideoSrc(oldSrc);
155
-
156 195
             var userChanged = false;
157 196
             if (largeVideoState.oldJid != largeVideoState.userJid) {
158 197
                 userChanged = true;
@@ -170,7 +209,8 @@ var VideoLayout = (function (my) {
170 209
 
171 210
                     if (!userChanged && largeVideoState.preload
172 211
                         && largeVideoState.preload != null
173
-                        && $(largeVideoState.preload).attr('src') == newSrc) {
212
+                        && RTC.getVideoSrc($(largeVideoState.preload)[0]) == newSrc)
213
+                    {
174 214
 
175 215
                         console.info('Switching to preloaded video');
176 216
                         var attributes = $('#largeVideo').prop("attributes");
@@ -196,7 +236,7 @@ var VideoLayout = (function (my) {
196 236
                         largeVideoState.preload = null;
197 237
                         largeVideoState.preload_ssrc = 0;
198 238
                     } else {
199
-                        $('#largeVideo').attr('src', largeVideoState.newSrc);
239
+                        RTC.setVideoSrc($('#largeVideo')[0], largeVideoState.newSrc);
200 240
                     }
201 241
 
202 242
                     var videoTransform = document.getElementById('largeVideo')
@@ -224,14 +264,12 @@ var VideoLayout = (function (my) {
224 264
                     // Only if the large video is currently visible.
225 265
                     // Disable previous dominant speaker video.
226 266
                     if (largeVideoState.oldJid) {
227
-                        var oldResourceJid = Strophe.getResourceFromJid(largeVideoState.oldJid);
228
-                        VideoLayout.enableDominantSpeaker(oldResourceJid, false);
267
+                        VideoLayout.enableDominantSpeaker(largeVideoState.oldJid, false);
229 268
                     }
230 269
 
231 270
                     // Enable new dominant speaker in the remote videos section.
232 271
                     if (largeVideoState.userJid) {
233
-                        var resourceJid = Strophe.getResourceFromJid(largeVideoState.userJid);
234
-                        VideoLayout.enableDominantSpeaker(resourceJid, true);
272
+                        VideoLayout.enableDominantSpeaker(largeVideoState.userJid, true);
235 273
                     }
236 274
 
237 275
                     if (userChanged && largeVideoState.isVisible) {
@@ -256,17 +294,20 @@ var VideoLayout = (function (my) {
256 294
         }
257 295
     };
258 296
 
259
-    my.handleVideoThumbClicked = function(videoSrc, noPinnedEndpointChangedEvent) {
297
+    my.handleVideoThumbClicked = function(videoSrc, noPinnedEndpointChangedEvent, jid) {
260 298
         // Restore style for previously focused video
261
-        var focusJid = getJidFromVideoSrc(focusedVideoSrc);
262
-        var oldContainer = getParticipantContainer(focusJid);
299
+        var oldContainer = null;
300
+        if(focusedVideoSrc) {
301
+            var focusJid = focusedVideoSrc.jid;
302
+            oldContainer = getParticipantContainer(focusJid);
303
+        }
263 304
 
264 305
         if (oldContainer) {
265 306
             oldContainer.removeClass("videoContainerFocused");
266 307
         }
267 308
 
268 309
         // Unlock current focused.
269
-        if (focusedVideoSrc === videoSrc)
310
+        if (focusedVideoSrc && focusedVideoSrc.src === videoSrc)
270 311
         {
271 312
             focusedVideoSrc = null;
272 313
             var dominantSpeakerVideo = null;
@@ -277,7 +318,7 @@ var VideoLayout = (function (my) {
277 318
                         .get(0);
278 319
 
279 320
                 if (dominantSpeakerVideo) {
280
-                    VideoLayout.updateLargeVideo(dominantSpeakerVideo.src, 1);
321
+                    VideoLayout.updateLargeVideo(RTC.getVideoSrc(dominantSpeakerVideo), 1, currentDominantSpeaker);
281 322
                 }
282 323
             }
283 324
 
@@ -288,17 +329,19 @@ var VideoLayout = (function (my) {
288 329
         }
289 330
 
290 331
         // Lock new video
291
-        focusedVideoSrc = videoSrc;
332
+        focusedVideoSrc = {
333
+            src: videoSrc,
334
+            jid: jid
335
+        };
292 336
 
293 337
         // Update focused/pinned interface.
294
-        var userJid = getJidFromVideoSrc(videoSrc);
295
-        if (userJid)
338
+        if (jid)
296 339
         {
297
-            var container = getParticipantContainer(userJid);
340
+            var container = getParticipantContainer(jid);
298 341
             container.addClass("videoContainerFocused");
299 342
 
300 343
             if (!noPinnedEndpointChangedEvent) {
301
-                $(document).trigger("pinnedendpointchanged", [userJid]);
344
+                $(document).trigger("pinnedendpointchanged", [jid]);
302 345
             }
303 346
         }
304 347
 
@@ -310,7 +353,7 @@ var VideoLayout = (function (my) {
310 353
         // this isn't a prezi.
311 354
         $(document).trigger("video.selected", [false]);
312 355
 
313
-        VideoLayout.updateLargeVideo(videoSrc, 1);
356
+        VideoLayout.updateLargeVideo(videoSrc, 1, Strophe.getResourceFromJid(jid));
314 357
 
315 358
         $('audio').each(function (idx, el) {
316 359
             if (el.id.indexOf('mixedmslabel') !== -1) {
@@ -356,8 +399,7 @@ var VideoLayout = (function (my) {
356 399
      * Shows/hides the large video.
357 400
      */
358 401
     my.setLargeVideoVisible = function(isVisible) {
359
-        var largeVideoJid = getJidFromVideoSrc($('#largeVideo').attr('src'));
360
-        var resourceJid = Strophe.getResourceFromJid(largeVideoJid);
402
+        var resourceJid = largeVideoState.userJid;
361 403
 
362 404
         if (isVisible) {
363 405
             $('#largeVideo').css({visibility: 'visible'});
@@ -457,7 +499,7 @@ var VideoLayout = (function (my) {
457 499
                         ? document.createElement('video')
458 500
                         : document.createElement('audio');
459 501
         var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_')
460
-                    + sid + '_' + stream.id;
502
+                    + sid + '_' + RTC.getStreamID(stream);
461 503
 
462 504
         element.id = id;
463 505
         element.autoplay = true;
@@ -487,10 +529,8 @@ var VideoLayout = (function (my) {
487 529
                 var videoStream = simulcast.getReceivingVideoStream(stream);
488 530
                 RTC.attachMediaStream(sel, videoStream);
489 531
 
490
-                if (isVideo) {
491
-                    waitForRemoteVideo(sel, thessrc, stream);
492
-                }
493
-
532
+                if (isVideo)
533
+                    waitForRemoteVideo(sel, thessrc, stream, peerJid);
494 534
             }
495 535
 
496 536
             stream.onended = function () {
@@ -512,7 +552,7 @@ var VideoLayout = (function (my) {
512 552
                 var videoThumb = $('#' + container.id + '>video').get(0);
513 553
 
514 554
                 if (videoThumb)
515
-                    VideoLayout.handleVideoThumbClicked(videoThumb.src);
555
+                    VideoLayout.handleVideoThumbClicked(RTC.getVideoSrc(videoThumb), false, peerJid);
516 556
 
517 557
                 event.preventDefault();
518 558
                 return false;
@@ -527,13 +567,13 @@ var VideoLayout = (function (my) {
527 567
                     var videoSrc = null;
528 568
                     if ($('#' + container.id + '>video')
529 569
                             && $('#' + container.id + '>video').length > 0) {
530
-                        videoSrc = $('#' + container.id + '>video').get(0).src;
570
+                        videoSrc = RTC.getVideoSrc($('#' + container.id + '>video').get(0));
531 571
                     }
532 572
 
533 573
                     // If the video has been "pinned" by the user we want to
534 574
                     // keep the display name on place.
535 575
                     if (!VideoLayout.isLargeVideoVisible()
536
-                            || videoSrc !== $('#largeVideo').attr('src'))
576
+                            || videoSrc !== RTC.getVideoSrc($('#largeVideo')[0]))
537 577
                         VideoLayout.showDisplayName(container.id, false);
538 578
                 }
539 579
             );
@@ -558,13 +598,11 @@ var VideoLayout = (function (my) {
558 598
         var removedVideoSrc = null;
559 599
         if (isVideo) {
560 600
             select = $('#' + container.id + '>video');
561
-            removedVideoSrc = select.get(0).src;
601
+            removedVideoSrc = RTC.getVideoSrc(select.get(0));
562 602
         }
563 603
         else
564 604
             select = $('#' + container.id + '>audio');
565 605
 
566
-        // Remove video source from the mapping.
567
-        delete videoSrcToSsrc[removedVideoSrc];
568 606
 
569 607
         // Mark video as removed to cancel waiting loop(if video is removed
570 608
         // before has started)
@@ -1004,7 +1042,6 @@ var VideoLayout = (function (my) {
1004 1042
         videoSpan = document.getElementById(videoContainerId);
1005 1043
 
1006 1044
         if (!videoSpan) {
1007
-            console.error("No video element for jid", resourceJid);
1008 1045
             return;
1009 1046
         }
1010 1047
 
@@ -1220,22 +1257,6 @@ var VideoLayout = (function (my) {
1220 1257
             return containerElement.id.substring(i + 12); 
1221 1258
     };
1222 1259
 
1223
-    my.getLargeVideoResource = function () {
1224
-        var largeVideoJid, largeVideoResource;
1225
-
1226
-        // Another approach could be to compare the srcs of the thumbnails and
1227
-        // then call getPeerContainerResourceJid.
1228
-
1229
-        var largeVideoSsrc
1230
-            = videoSrcToSsrc[$('#largeVideo').attr('src')];
1231
-
1232
-        if (largeVideoSsrc
1233
-                /* variables/state checking to prevent exceptions */
1234
-            && (largeVideoJid = ssrc2jid[largeVideoSsrc])
1235
-            && (largeVideoResource = Strophe.getResourceFromJid(largeVideoJid)))
1236
-            return largeVideoResource;
1237
-    };
1238
-
1239 1260
     /**
1240 1261
      * Adds the remote video menu element for the given <tt>jid</tt> in the
1241 1262
      * given <tt>parentElement</tt>.
@@ -1338,7 +1359,7 @@ var VideoLayout = (function (my) {
1338 1359
                     // We have a video src, great! Let's update the large video
1339 1360
                     // now.
1340 1361
 
1341
-                    VideoLayout.handleVideoThumbClicked(videoThumb.src);
1362
+                    VideoLayout.handleVideoThumbClicked(videoThumb.src, false, jid);
1342 1363
                 } else {
1343 1364
 
1344 1365
                     // If we don't have a video src for jid, there's absolutely
@@ -1474,7 +1495,7 @@ var VideoLayout = (function (my) {
1474 1495
             // Update the large video if the video source is already available,
1475 1496
             // otherwise wait for the "videoactive.jingle" event.
1476 1497
             if (video.length && video[0].currentTime > 0)
1477
-                VideoLayout.updateLargeVideo(video[0].src);
1498
+                VideoLayout.updateLargeVideo(RTC.getVideoSrc(video[0]), resourceJid);
1478 1499
         }
1479 1500
     });
1480 1501
 
@@ -1553,7 +1574,7 @@ var VideoLayout = (function (my) {
1553 1574
                 // it is no longer being received. If resourceJid was being
1554 1575
                 // displayed in the large video we have to switch to another
1555 1576
                 // user.
1556
-                var largeVideoResource = VideoLayout.getLargeVideoResource();
1577
+                var largeVideoResource = largeVideoState.userJid;
1557 1578
                 if (!updateLargeVideo && resourceJid === largeVideoResource) {
1558 1579
                     updateLargeVideo = true;
1559 1580
                 }
@@ -1578,18 +1599,17 @@ var VideoLayout = (function (my) {
1578 1599
                     var videoStream = simulcast.getReceivingVideoStream(
1579 1600
                         mediaStream.stream);
1580 1601
                     RTC.attachMediaStream(sel, videoStream);
1581
-                    videoSrcToSsrc[sel.attr('src')] = mediaStream.ssrc;
1582 1602
                     if (lastNPickupJid == mediaStream.peerjid) {
1583 1603
                         // Clean up the lastN pickup jid.
1584 1604
                         lastNPickupJid = null;
1585 1605
 
1586 1606
                         // Don't fire the events again, they've already
1587 1607
                         // been fired in the contact list click handler.
1588
-                        VideoLayout.handleVideoThumbClicked($(sel).attr('src'), false);
1608
+                        VideoLayout.handleVideoThumbClicked($(sel).attr('src'), false, mediaStream.peerjid);
1589 1609
 
1590 1610
                         updateLargeVideo = false;
1591 1611
                     }
1592
-                    waitForRemoteVideo(sel, mediaStream.ssrc, mediaStream.stream);
1612
+                    waitForRemoteVideo(sel, mediaStream.ssrc, mediaStream.stream, resourceJid);
1593 1613
                 }
1594 1614
             })
1595 1615
         }
@@ -1646,7 +1666,7 @@ var VideoLayout = (function (my) {
1646 1666
                 || (parentResourceJid
1647 1667
                 && VideoLayout.getDominantSpeakerResourceJid()
1648 1668
                     === parentResourceJid)) {
1649
-                VideoLayout.updateLargeVideo(videoelem.attr('src'), 1);
1669
+                VideoLayout.updateLargeVideo(RTC.getVideoSrc(videoelem[0]), 1, parentResourceJid);
1650 1670
             }
1651 1671
 
1652 1672
             VideoLayout.showModeratorIndicator();
@@ -1657,7 +1677,15 @@ var VideoLayout = (function (my) {
1657 1677
         endpointSimulcastLayers.forEach(function (esl) {
1658 1678
 
1659 1679
             var resource = esl.endpoint;
1660
-            if (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1) {
1680
+
1681
+            // if lastN is enabled *and* the endpoint is *not* in the lastN set,
1682
+            // then ignore the event (= do not preload anything).
1683
+            //
1684
+            // The bridge could probably stop sending this message if it's for
1685
+            // an endpoint that's not in lastN.
1686
+
1687
+            if (lastNCount != -1
1688
+                && (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
1661 1689
                 return;
1662 1690
             }
1663 1691
 
@@ -1674,12 +1702,8 @@ var VideoLayout = (function (my) {
1674 1702
                 console.info([esl, primarySSRC, msid, session, electedStream]);
1675 1703
 
1676 1704
                 var msidParts = msid.split(' ');
1677
-                var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
1678 1705
 
1679
-                // FIXME(gp) here we should use the VideoLayout.getPeerContainerResource
1680
-                // and VideoLayout.getLargeVideoResource methods.
1681
-                var preload = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
1682
-                    == ssrc2jid[videoSrcToSsrc[largeVideoState.newSrc]]);
1706
+                var preload = (Strophe.getResourceFromJid(ssrc2jid[primarySSRC]) == largeVideoState.userJid);
1683 1707
 
1684 1708
                 if (preload) {
1685 1709
                     if (largeVideoState.preload)
@@ -1691,9 +1715,7 @@ var VideoLayout = (function (my) {
1691 1715
                     // ssrcs are unique in an rtp session
1692 1716
                     largeVideoState.preload_ssrc = primarySSRC;
1693 1717
 
1694
-                    var electedStreamUrl = webkitURL.createObjectURL(electedStream);
1695
-                    largeVideoState.preload
1696
-                        .attr('src', electedStreamUrl);
1718
+                    RTC.attachMediaStream(largeVideoState.preload, electedStream)
1697 1719
                 }
1698 1720
 
1699 1721
             } else {
@@ -1709,7 +1731,19 @@ var VideoLayout = (function (my) {
1709 1731
         endpointSimulcastLayers.forEach(function (esl) {
1710 1732
 
1711 1733
             var resource = esl.endpoint;
1712
-            if (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1) {
1734
+
1735
+            // if lastN is enabled *and* the endpoint is *not* in the lastN set,
1736
+            // then ignore the event (= do not change large video/thumbnail
1737
+            // SRCs).
1738
+            //
1739
+            // Note that even if we ignore the "changed" event in this event
1740
+            // handler, the bridge must continue sending these events because
1741
+            // the simulcast code in simulcast.js uses it to know what's going
1742
+            // to be streamed by the bridge when/if the endpoint gets back into
1743
+            // the lastN set.
1744
+
1745
+            if (lastNCount != -1
1746
+                && (lastNCount < 1 || lastNEndpointsCache.indexOf(resource) === -1)) {
1713 1747
                 return;
1714 1748
             }
1715 1749
 
@@ -1729,21 +1763,15 @@ var VideoLayout = (function (my) {
1729 1763
                 var msidParts = msid.split(' ');
1730 1764
                 var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
1731 1765
 
1732
-                // FIXME(gp) here we should use the VideoLayout.getPeerContainerResource
1733
-                // and VideoLayout.getLargeVideoResource methods.
1734
-                var updateLargeVideo = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
1735
-                    == ssrc2jid[videoSrcToSsrc[largeVideoState.newSrc]]);
1736
-
1737
-                // We should only update the focused video src if it's not a
1738
-                // falsy value.
1739
-                var updateFocusedVideoSrc
1740
-                    = focusedVideoSrc && focusedVideoSrc !== ''
1741
-                        && (selRemoteVideo.attr('src') == focusedVideoSrc);
1766
+                var updateLargeVideo = (Strophe.getResourceFromJid(ssrc2jid[primarySSRC])
1767
+                    == largeVideoState.userJid);
1768
+                var updateFocusedVideoSrc = (focusedVideoSrc && focusedVideoSrc.src && focusedVideoSrc.src != '' &&
1769
+                    (RTC.getVideoSrc(selRemoteVideo[0]) == focusedVideoSrc.src));
1742 1770
 
1743 1771
                 var electedStreamUrl;
1744 1772
                 if (largeVideoState.preload_ssrc == primarySSRC)
1745 1773
                 {
1746
-                    electedStreamUrl = $(largeVideoState.preload).attr('src');
1774
+                    RTC.setVideoSrc(selRemoteVideo[0], RTC.getVideoSrc(largeVideoState.preload[0]));
1747 1775
                 }
1748 1776
                 else
1749 1777
                 {
@@ -1754,18 +1782,19 @@ var VideoLayout = (function (my) {
1754 1782
 
1755 1783
                     largeVideoState.preload_ssrc = 0;
1756 1784
 
1757
-                    electedStreamUrl = webkitURL.createObjectURL(electedStream);
1785
+                    RTC.attachMediaStream(selRemoteVideo, electedStream);
1758 1786
                 }
1759 1787
 
1760
-                selRemoteVideo.attr('src', electedStreamUrl);
1761
-                videoSrcToSsrc[selRemoteVideo.attr('src')] = primarySSRC + ''; // what we store there is typeof string.
1788
+                var jid = ssrc2jid[primarySSRC];
1789
+                jid2Ssrc[jid] = primarySSRC;
1762 1790
 
1763 1791
                 if (updateLargeVideo) {
1764
-                    VideoLayout.updateLargeVideo(electedStreamUrl);
1792
+                    VideoLayout.updateLargeVideo(RTC.getVideoSrc(selRemoteVideo[0]), null,
1793
+                        Strophe.getResourceFromJid(jid));
1765 1794
                 }
1766 1795
 
1767 1796
                 if (updateFocusedVideoSrc) {
1768
-                    focusedVideoSrc = electedStreamUrl;
1797
+                    focusedVideoSrc.src = RTC.getVideoSrc(selRemoteVideo[0]);
1769 1798
                 }
1770 1799
 
1771 1800
                 var videoId;
@@ -1889,19 +1918,25 @@ var VideoLayout = (function (my) {
1889 1918
         if(this.jid==null)
1890 1919
         {
1891 1920
             resolution = "";
1892
-            for(var i in this.resolution)
1921
+            if(this.resolution == null || !Object.keys(this.resolution)
1922
+                || Object.keys(this.resolution).length == 0)
1893 1923
             {
1894
-                resolutionValue = this.resolution[i];
1895
-                if(resolutionValue)
1924
+                resolution = "N/A";
1925
+            }
1926
+            else
1927
+                for(var i in this.resolution)
1896 1928
                 {
1897
-                    if(resolutionValue.height &&
1898
-                        resolutionValue.width)
1929
+                    resolutionValue = this.resolution[i];
1930
+                    if(resolutionValue)
1899 1931
                     {
1900
-                        resolution += (resolution == ""? "" : ", ")
1901
-                            + resolutionValue.width + "x" + resolutionValue.height;
1932
+                        if(resolutionValue.height &&
1933
+                            resolutionValue.width)
1934
+                        {
1935
+                            resolution += (resolution == ""? "" : ", ")
1936
+                                + resolutionValue.width + "x" + resolutionValue.height;
1937
+                        }
1902 1938
                     }
1903 1939
                 }
1904
-            }
1905 1940
         }
1906 1941
         else if(!resolutionValue ||
1907 1942
             !resolutionValue.height ||

Loading…
Annulla
Salva