Pārlūkot izejas kodu

Implement the functionality that allows users to start the conversation muted.

j8
hristoterezov 10 gadus atpakaļ
vecāks
revīzija
cc1ad1bc13

+ 2
- 0
config.js Parādīt failu

@@ -32,6 +32,8 @@ var config = {
32 32
     enableWelcomePage: true,
33 33
     enableSimulcast: false, // blocks FF support
34 34
     logStats: false, // Enable logging of PeerConnection stats via the focus
35
+    startVideoMuted: false,
36
+    startAudioMuted: false,
35 37
     /*noticeMessage: 'Service update is scheduled for 16th March 2015. ' +
36 38
     'During that time service will not be available. ' +
37 39
     'Apologise for inconvenience.'*/

+ 2
- 2
index.html Parādīt failu

@@ -10,7 +10,7 @@
10 10
     <meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
11 11
     <meta itemprop="image" content="/images/jitsilogo.png"/>
12 12
     <script src="libs/jquery-2.1.1.min.js"></script>
13
-    <script src="config.js?v=6"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
13
+    <script src="config.js?v=7"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
14 14
     <script src="libs/strophe/strophe.min.js?v=1"></script>
15 15
     <script src="libs/strophe/strophe.disco.min.js?v=1"></script>
16 16
     <script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
@@ -19,7 +19,7 @@
19 19
     <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
20 20
     <script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
21 21
     <script src="interface_config.js?v=5"></script>
22
-    <script src="libs/app.bundle.js?v=57"></script>
22
+    <script src="libs/app.bundle.js?v=58"></script>
23 23
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
24 24
     <link rel="stylesheet" href="css/font.css?v=7"/>
25 25
     <link rel="stylesheet" href="css/toastr.css?v=1">

+ 2
- 2
lang/main.json Parādīt failu

@@ -123,8 +123,8 @@
123 123
         "focus": "Conference focus",
124 124
         "focusFail": "__component__ not available - retry in __ms__ sec",
125 125
         "grantedTo": "Moderator rights granted to __to__!",
126
-        "grantedToUnknown": "Moderator rights granted to $t(somebody)!"
127
-
126
+        "grantedToUnknown": "Moderator rights granted to $t(somebody)!",
127
+        "muted": "You have started the conversation muted."
128 128
     },
129 129
     "dialog": {
130 130
         "kickMessage": "Ouch! You have been kicked out of the meet!",

+ 272
- 120
libs/app.bundle.js Parādīt failu

@@ -1,4 +1,4 @@
1
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.APP = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
1
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.APP=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2 2
 /* jshint -W117 */
3 3
 /* application specific logic */
4 4
 
@@ -578,12 +578,15 @@ module.exports = DataChannels;
578 578
 var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
579 579
 
580 580
 
581
-function LocalStream(stream, type, eventEmitter, videoType)
581
+function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
582 582
 {
583 583
     this.stream = stream;
584 584
     this.eventEmitter = eventEmitter;
585 585
     this.type = type;
586 586
     this.videoType = videoType;
587
+    this.isGUMStream = true;
588
+    if(isGUMStream === false)
589
+        this.isGUMStream = isGUMStream;
587 590
     var self = this;
588 591
     if(type == "audio")
589 592
     {
@@ -614,26 +617,14 @@ LocalStream.prototype.getOriginalStream = function()
614 617
 }
615 618
 
616 619
 LocalStream.prototype.isAudioStream = function () {
617
-    return (this.stream.getAudioTracks() && this.stream.getAudioTracks().length > 0);
618
-};
619
-
620
-LocalStream.prototype.mute = function()
621
-{
622
-    var ismuted = false;
623
-    var tracks = this.getTracks();
624
-
625
-    for (var idx = 0; idx < tracks.length; idx++) {
626
-        ismuted = !tracks[idx].enabled;
627
-        tracks[idx].enabled = ismuted;
628
-    }
629
-    return ismuted;
620
+    return this.type === "audio";
630 621
 };
631 622
 
632 623
 LocalStream.prototype.setMute = function(mute)
633 624
 {
634 625
 
635
-    if(window.location.protocol != "https:" ||
636
-        this.isAudioStream() || this.videoType === "screen")
626
+    if((window.location.protocol != "https:" && this.isGUMStream) ||
627
+        (this.isAudioStream() && this.isGUMStream) || this.videoType === "screen")
637 628
     {
638 629
         var tracks = this.getTracks();
639 630
 
@@ -649,9 +640,18 @@ LocalStream.prototype.setMute = function(mute)
649 640
         }
650 641
         else
651 642
         {
652
-            APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(["video"],
643
+            var self = this;
644
+            APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(
645
+                (this.isAudioStream() ? ["audio"] : ["video"]),
653 646
                 function (stream) {
654
-                    APP.RTC.changeLocalVideo(stream, false, function () {});
647
+                    if(self.isAudioStream())
648
+                    {
649
+                        APP.RTC.changeLocalAudio(stream, function () {});
650
+                    }
651
+                    else
652
+                    {
653
+                        APP.RTC.changeLocalVideo(stream, false, function () {});
654
+                    }
655 655
                 });
656 656
         }
657 657
     }
@@ -760,11 +760,41 @@ var UIEvents = require("../../service/UI/UIEvents");
760 760
 
761 761
 var eventEmitter = new EventEmitter();
762 762
 
763
+
764
+function getMediaStreamUsage()
765
+{
766
+    var result = {
767
+        audio: 1,
768
+        video: 1
769
+    };
770
+    if( config.startAudioMuted === true)
771
+        result.audio = 0;
772
+    if( config.startVideoMuted === true)
773
+        result.video = 0;
774
+
775
+    /** There are some issues with the desktop sharing
776
+     * when this property is enabled.
777
+
778
+     if(result.audio > 0 && result.video > 0)
779
+        return result;
780
+     var isSecureConnection = window.location.protocol == "https:";
781
+
782
+    if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
783
+    {
784
+        if(result.audio === 0)
785
+            result.audio = -1;
786
+        if(result.video === 0)
787
+            result.video = -1;
788
+    }**/
789
+
790
+    return result;
791
+}
792
+
763 793
 var RTC = {
764 794
     rtcUtils: null,
765 795
     devices: {
766
-        audio: false,
767
-        video: false
796
+        audio: true,
797
+        video: true
768 798
     },
769 799
     localStreams: [],
770 800
     remoteStreams: {},
@@ -782,13 +812,15 @@ var RTC = {
782 812
 
783 813
         eventEmitter.removeListener(eventType, listener);
784 814
     },
785
-    createLocalStream: function (stream, type, change, videoType) {
815
+    createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
786 816
 
787
-        var localStream =  new LocalStream(stream, type, eventEmitter, videoType);
817
+        var localStream =  new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
788 818
         //in firefox we have only one stream object
789 819
         if(this.localStreams.length == 0 ||
790 820
             this.localStreams[0].getOriginalStream() != stream)
791 821
             this.localStreams.push(localStream);
822
+        if(isMuted === true)
823
+            localStream.setMute(false);
792 824
 
793 825
         if(type == "audio")
794 826
         {
@@ -802,7 +834,7 @@ var RTC = {
802 834
         if(change)
803 835
             eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
804 836
 
805
-        eventEmitter.emit(eventType, localStream);
837
+        eventEmitter.emit(eventType, localStream, isMuted);
806 838
         return localStream;
807 839
     },
808 840
     removeLocalStream: function (stream) {
@@ -886,7 +918,8 @@ var RTC = {
886 918
         APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
887 919
             DataChannels.handlePinnedEndpointEvent);
888 920
         this.rtcUtils = new RTCUtils(this);
889
-        this.rtcUtils.obtainAudioAndVideoPermissions();
921
+        this.rtcUtils.obtainAudioAndVideoPermissions(
922
+            null, null, getMediaStreamUsage());
890 923
     },
891 924
     muteRemoteVideoStream: function (jid, value) {
892 925
         var stream;
@@ -927,12 +960,20 @@ var RTC = {
927 960
                 callback();
928 961
             };
929 962
         }
930
-        var videoStream = this.rtcUtils.createVideoStream(stream);
963
+        var videoStream = this.rtcUtils.createStream(stream, true);
931 964
         this.localVideo = this.createLocalStream(videoStream, "video", true, type);
932 965
         // Stop the stream to trigger onended event for old stream
933 966
         oldStream.stop();
934 967
         APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
935 968
     },
969
+    changeLocalAudio: function (stream, callback) {
970
+        var oldStream = this.localAudio.getOriginalStream();
971
+        var newStream = this.rtcUtils.createStream(stream);
972
+        this.localAudio = this.createLocalStream(newStream, "audio", true);
973
+        // Stop the stream to trigger onended event for old stream
974
+        oldStream.stop();
975
+        APP.xmpp.switchStreams(newStream, oldStream, callback, true);
976
+    },
936 977
     /**
937 978
      * Checks if video identified by given src is desktop stream.
938 979
      * @param videoSrc eg.
@@ -967,7 +1008,7 @@ var RTC = {
967 1008
         {
968 1009
             APP.xmpp.sendVideoInfoPresence(mute);
969 1010
             if(callback)
970
-                callback();
1011
+                callback(mute);
971 1012
         }
972 1013
         else
973 1014
         {
@@ -1138,6 +1179,8 @@ function RTCUtils(RTCService)
1138 1179
                 //
1139 1180
                 // https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
1140 1181
                 // https://github.com/webrtc/samples/issues/302
1182
+                if(!element[0])
1183
+                    return;
1141 1184
                 element[0].mozSrcObject = stream;
1142 1185
                 element[0].play();
1143 1186
             };
@@ -1150,10 +1193,13 @@ function RTCUtils(RTCService)
1150 1193
                 return tracks[0].id.replace(/[\{,\}]/g,"");
1151 1194
             };
1152 1195
             this.getVideoSrc = function (element) {
1196
+                if(!element)
1197
+                    return null;
1153 1198
                 return element.mozSrcObject;
1154 1199
             };
1155 1200
             this.setVideoSrc = function (element, src) {
1156
-                element.mozSrcObject = src;
1201
+                if(element)
1202
+                    element.mozSrcObject = src;
1157 1203
             };
1158 1204
             RTCSessionDescription = mozRTCSessionDescription;
1159 1205
             RTCIceCandidate = mozRTCIceCandidate;
@@ -1176,10 +1222,13 @@ function RTCUtils(RTCService)
1176 1222
             return stream.id.replace(/[\{,\}]/g,"");
1177 1223
         };
1178 1224
         this.getVideoSrc = function (element) {
1225
+            if(!element)
1226
+                return null;
1179 1227
             return element.getAttribute("src");
1180 1228
         };
1181 1229
         this.setVideoSrc = function (element, src) {
1182
-            element.setAttribute("src", src);
1230
+            if(element)
1231
+                element.setAttribute("src", src);
1183 1232
         };
1184 1233
         // DTLS should now be enabled by default but..
1185 1234
         this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
@@ -1286,20 +1335,43 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
1286 1335
  * We ask for audio and video combined stream in order to get permissions and
1287 1336
  * not to ask twice.
1288 1337
  */
1289
-RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback) {
1338
+RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) {
1290 1339
     var self = this;
1291 1340
     // Get AV
1292 1341
 
1342
+    var successCallback = function (stream) {
1343
+        if(callback)
1344
+            callback(stream, usageOptions);
1345
+        else
1346
+            self.successCallback(stream, usageOptions);
1347
+    };
1348
+
1293 1349
     if(!devices)
1294 1350
         devices = ['audio', 'video'];
1295 1351
 
1352
+    var newDevices = [];
1353
+
1354
+
1355
+    if(usageOptions)
1356
+        for(var i = 0; i < devices.length; i++)
1357
+        {
1358
+            var device = devices[i];
1359
+            if(usageOptions[device] !== -1)
1360
+                newDevices.push(device);
1361
+        }
1362
+    else
1363
+        newDevices = devices;
1364
+
1365
+    if(newDevices.length === 0)
1366
+    {
1367
+        successCallback();
1368
+        return;
1369
+    }
1370
+
1296 1371
     this.getUserMediaWithConstraints(
1297
-        devices,
1372
+        newDevices,
1298 1373
         function (stream) {
1299
-            if(callback)
1300
-                callback(stream);
1301
-            else
1302
-                self.successCallback(stream);
1374
+            successCallback(stream);
1303 1375
         },
1304 1376
         function (error) {
1305 1377
             self.errorCallback(error);
@@ -1307,11 +1379,11 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback)
1307 1379
         config.resolution || '360');
1308 1380
 }
1309 1381
 
1310
-RTCUtils.prototype.successCallback = function (stream) {
1382
+RTCUtils.prototype.successCallback = function (stream, usageOptions) {
1311 1383
     if(stream)
1312 1384
         console.log('got', stream, stream.getAudioTracks().length,
1313 1385
             stream.getVideoTracks().length);
1314
-    this.handleLocalStream(stream);
1386
+    this.handleLocalStream(stream, usageOptions);
1315 1387
 };
1316 1388
 
1317 1389
 RTCUtils.prototype.errorCallback = function (error) {
@@ -1349,7 +1421,7 @@ RTCUtils.prototype.errorCallback = function (error) {
1349 1421
 
1350 1422
 }
1351 1423
 
1352
-RTCUtils.prototype.handleLocalStream = function(stream)
1424
+RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
1353 1425
 {
1354 1426
     if(window.webkitMediaStream)
1355 1427
     {
@@ -1369,9 +1441,18 @@ RTCUtils.prototype.handleLocalStream = function(stream)
1369 1441
             }
1370 1442
         }
1371 1443
 
1372
-        this.service.createLocalStream(audioStream, "audio");
1444
+        var audioMuted = (usageOptions && usageOptions.audio != 1),
1445
+            videoMuted = (usageOptions && usageOptions.video != 1);
1446
+
1447
+        var audioGUM = (!usageOptions || usageOptions.audio != -1),
1448
+            videoGUM = (!usageOptions || usageOptions.video != -1);
1449
+
1373 1450
 
1374
-        this.service.createLocalStream(videoStream, "video");
1451
+        this.service.createLocalStream(audioStream, "audio", null, null,
1452
+            audioMuted, audioGUM);
1453
+
1454
+        this.service.createLocalStream(videoStream, "video", null, null,
1455
+            videoMuted, videoGUM);
1375 1456
     }
1376 1457
     else
1377 1458
     {//firefox
@@ -1380,26 +1461,26 @@ RTCUtils.prototype.handleLocalStream = function(stream)
1380 1461
 
1381 1462
 };
1382 1463
 
1383
-RTCUtils.prototype.createVideoStream = function(stream)
1464
+RTCUtils.prototype.createStream = function(stream, isVideo)
1384 1465
 {
1385
-    var videoStream = null;
1466
+    var newStream = null;
1386 1467
     if(window.webkitMediaStream)
1387 1468
     {
1388
-        videoStream = new webkitMediaStream();
1389
-        if(stream)
1469
+        newStream = new webkitMediaStream();
1470
+        if(newStream)
1390 1471
         {
1391
-            var videoTracks = stream.getVideoTracks();
1472
+            var tracks = (isVideo? stream.getVideoTracks() : stream.getAudioTracks());
1392 1473
 
1393
-            for (i = 0; i < videoTracks.length; i++) {
1394
-                videoStream.addTrack(videoTracks[i]);
1474
+            for (i = 0; i < tracks.length; i++) {
1475
+                newStream.addTrack(tracks[i]);
1395 1476
             }
1396 1477
         }
1397 1478
 
1398 1479
     }
1399 1480
     else
1400
-        videoStream = stream;
1481
+        newStream = stream;
1401 1482
 
1402
-    return videoStream;
1483
+    return newStream;
1403 1484
 };
1404 1485
 
1405 1486
 module.exports = RTCUtils;
@@ -1439,6 +1520,14 @@ var eventEmitter = new EventEmitter();
1439 1520
 var roomName = null;
1440 1521
 
1441 1522
 
1523
+function notifyForInitialMute()
1524
+{
1525
+    if(config.startAudioMuted || config.startVideoMuted)
1526
+    {
1527
+        messageHandler.notify(null, "notify.me", "connected", "notify.muted");
1528
+    }
1529
+}
1530
+
1442 1531
 function setupPrezi()
1443 1532
 {
1444 1533
     $("#reloadPresentationLink").click(function()
@@ -1461,17 +1550,17 @@ function setupToolbars() {
1461 1550
     BottomToolbar.init();
1462 1551
 }
1463 1552
 
1464
-function streamHandler(stream) {
1553
+function streamHandler(stream, isMuted) {
1465 1554
     switch (stream.type)
1466 1555
     {
1467 1556
         case "audio":
1468
-            VideoLayout.changeLocalAudio(stream);
1557
+            VideoLayout.changeLocalAudio(stream, isMuted);
1469 1558
             break;
1470 1559
         case "video":
1471
-            VideoLayout.changeLocalVideo(stream);
1560
+            VideoLayout.changeLocalVideo(stream, isMuted);
1472 1561
             break;
1473 1562
         case "stream":
1474
-            VideoLayout.changeLocalStream(stream);
1563
+            VideoLayout.changeLocalStream(stream, isMuted);
1475 1564
             break;
1476 1565
     }
1477 1566
 }
@@ -1788,6 +1877,8 @@ UI.start = function (init) {
1788 1877
 
1789 1878
     SettingsMenu.init();
1790 1879
 
1880
+    notifyForInitialMute();
1881
+
1791 1882
 };
1792 1883
 
1793 1884
 function chatAddError(errorMessage, originalText)
@@ -1821,6 +1912,9 @@ function onMucJoined(jid, info) {
1821 1912
 
1822 1913
     if (displayName)
1823 1914
         onDisplayNameChanged('localVideoContainer', displayName);
1915
+
1916
+
1917
+    VideoLayout.mucJoined();
1824 1918
 }
1825 1919
 
1826 1920
 function initEtherpad(name) {
@@ -2121,9 +2215,17 @@ UI.toggleAudio = function() {
2121 2215
 /**
2122 2216
  * Sets muted audio state for the local participant.
2123 2217
  */
2124
-UI.setAudioMuted = function (mute) {
2125
-
2126
-    if(!APP.xmpp.setAudioMute(mute, function () {
2218
+UI.setAudioMuted = function (mute, earlyMute) {
2219
+    var audioMute = null;
2220
+    if(earlyMute)
2221
+        audioMute = function (mute, cb) {
2222
+            return APP.xmpp.sendAudioInfoPresence(mute, cb);
2223
+        };
2224
+    else
2225
+        audioMute = function (mute, cb) {
2226
+            return APP.xmpp.setAudioMute(mute, cb);
2227
+        }
2228
+    if(!audioMute(mute, function () {
2127 2229
         VideoLayout.showLocalAudioIndicator(mute);
2128 2230
 
2129 2231
         UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
@@ -2171,6 +2273,9 @@ UI.setVideoMuteButtonsState = function (mute) {
2171 2273
     }
2172 2274
 }
2173 2275
 
2276
+
2277
+UI.setVideoMute = setVideoMute;
2278
+
2174 2279
 module.exports = UI;
2175 2280
 
2176 2281
 
@@ -2929,6 +3034,12 @@ function isUserMuted(jid) {
2929 3034
         }
2930 3035
     }
2931 3036
 
3037
+    if(jid && jid == APP.xmpp.myJid())
3038
+    {
3039
+        var localVideo = APP.RTC.localVideo;
3040
+        return (!localVideo || localVideo.isMuted());
3041
+    }
3042
+
2932 3043
     if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
2933 3044
         return null;
2934 3045
     }
@@ -3028,8 +3139,9 @@ var Avatar = {
3028 3139
             } else {
3029 3140
                 if (video && video.length > 0) {
3030 3141
                     setVisibility(video, !show);
3031
-                    setVisibility(avatar, show);
3032 3142
                 }
3143
+                setVisibility(avatar, show);
3144
+
3033 3145
             }
3034 3146
         }
3035 3147
     },
@@ -7261,37 +7373,19 @@ var VideoLayout = (function (my) {
7261 7373
             || (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
7262 7374
     };
7263 7375
 
7264
-    my.changeLocalStream = function (stream) {
7265
-        VideoLayout.changeLocalVideo(stream);
7376
+    my.changeLocalStream = function (stream, isMuted) {
7377
+        VideoLayout.changeLocalVideo(stream, isMuted);
7266 7378
     };
7267 7379
 
7268
-    my.changeLocalAudio = function(stream) {
7380
+    my.changeLocalAudio = function(stream, isMuted) {
7381
+        if(isMuted)
7382
+            APP.UI.setAudioMuted(true, true);
7269 7383
         APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
7270 7384
         document.getElementById('localAudio').autoplay = true;
7271 7385
         document.getElementById('localAudio').volume = 0;
7272
-        if (preMuted) {
7273
-            if(!APP.UI.setAudioMuted(true))
7274
-            {
7275
-                preMuted = mute;
7276
-            }
7277
-            preMuted = false;
7278
-        }
7279 7386
     };
7280 7387
 
7281
-    my.changeLocalVideo = function(stream) {
7282
-        var flipX = true;
7283
-        if(stream.videoType == "screen")
7284
-            flipX = false;
7285
-        var localVideo = document.createElement('video');
7286
-        localVideo.id = 'localVideo_' +
7287
-            APP.RTC.getStreamID(stream.getOriginalStream());
7288
-        localVideo.autoplay = true;
7289
-        localVideo.volume = 0; // is it required if audio is separated ?
7290
-        localVideo.oncontextmenu = function () { return false; };
7291
-
7292
-        var localVideoContainer = document.getElementById('localVideoWrapper');
7293
-        localVideoContainer.appendChild(localVideo);
7294
-
7388
+    my.changeLocalVideo = function(stream, isMuted) {
7295 7389
         // Set default display name.
7296 7390
         setDisplayName('localVideoContainer');
7297 7391
 
@@ -7302,7 +7396,7 @@ var VideoLayout = (function (my) {
7302 7396
 
7303 7397
         AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
7304 7398
 
7305
-        var localVideoSelector = $('#' + localVideo.id);
7399
+        var localVideo = null;
7306 7400
 
7307 7401
         function localVideoClick(event) {
7308 7402
             event.stopPropagation();
@@ -7311,9 +7405,7 @@ var VideoLayout = (function (my) {
7311 7405
                 false,
7312 7406
                 APP.xmpp.myResource());
7313 7407
         }
7314
-        // Add click handler to both video and video wrapper elements in case
7315
-        // there's no video.
7316
-        localVideoSelector.click(localVideoClick);
7408
+
7317 7409
         $('#localVideoContainer').click(localVideoClick);
7318 7410
 
7319 7411
         // Add hover handler
@@ -7323,24 +7415,52 @@ var VideoLayout = (function (my) {
7323 7415
             },
7324 7416
             function() {
7325 7417
                 if (!VideoLayout.isLargeVideoVisible()
7326
-                        || APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
7418
+                    || APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
7327 7419
                     VideoLayout.showDisplayName('localVideoContainer', false);
7328 7420
             }
7329 7421
         );
7330
-        // Add stream ended handler
7331
-        stream.getOriginalStream().onended = function () {
7332
-            localVideoContainer.removeChild(localVideo);
7333
-            VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
7334
-        };
7422
+
7423
+        if(isMuted)
7424
+        {
7425
+            APP.UI.setVideoMute(true);
7426
+            return;
7427
+        }
7428
+        var flipX = true;
7429
+        if(stream.videoType == "screen")
7430
+            flipX = false;
7431
+        var localVideo = document.createElement('video');
7432
+        localVideo.id = 'localVideo_' +
7433
+            APP.RTC.getStreamID(stream.getOriginalStream());
7434
+        localVideo.autoplay = true;
7435
+        localVideo.volume = 0; // is it required if audio is separated ?
7436
+        localVideo.oncontextmenu = function () { return false; };
7437
+
7438
+        var localVideoContainer = document.getElementById('localVideoWrapper');
7439
+        localVideoContainer.appendChild(localVideo);
7440
+
7441
+        var localVideoSelector = $('#' + localVideo.id);
7442
+
7443
+        // Add click handler to both video and video wrapper elements in case
7444
+        // there's no video.
7445
+        localVideoSelector.click(localVideoClick);
7446
+
7335 7447
         // Flip video x axis if needed
7336 7448
         flipXLocalVideo = flipX;
7337 7449
         if (flipX) {
7338 7450
             localVideoSelector.addClass("flipVideoX");
7339 7451
         }
7452
+
7340 7453
         // Attach WebRTC stream
7341 7454
         var videoStream = APP.simulcast.getLocalVideoStream();
7342 7455
         APP.RTC.attachMediaStream(localVideoSelector, videoStream);
7343 7456
 
7457
+        // Add stream ended handler
7458
+        stream.getOriginalStream().onended = function () {
7459
+            localVideoContainer.removeChild(localVideo);
7460
+            VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
7461
+        };
7462
+
7463
+
7344 7464
         localVideoSrc = APP.RTC.getVideoSrc(localVideo);
7345 7465
 
7346 7466
         var myResourceJid = APP.xmpp.myResource();
@@ -7350,6 +7470,14 @@ var VideoLayout = (function (my) {
7350 7470
 
7351 7471
     };
7352 7472
 
7473
+    my.mucJoined = function () {
7474
+        var myResourceJid = APP.xmpp.myResource();
7475
+
7476
+        if(!largeVideoState.userResourceJid)
7477
+            VideoLayout.updateLargeVideo(localVideoSrc, 0,
7478
+                myResourceJid, true);
7479
+    };
7480
+
7353 7481
     /**
7354 7482
      * Adds or removes icons for not available camera and microphone.
7355 7483
      * @param resourceJid the jid of user
@@ -7417,26 +7545,34 @@ var VideoLayout = (function (my) {
7417 7545
                 }
7418 7546
             }
7419 7547
 
7548
+            var src = null, volume = null;
7420 7549
             // mute if localvideo
7421 7550
             if (pick) {
7422 7551
                 var container = pick.parentNode;
7423
-                var jid = null;
7424
-                if(container)
7425
-                {
7426
-                    if(container.id == "localVideoWrapper")
7427
-                    {
7428
-                        jid = APP.xmpp.myResource();
7429
-                    }
7430
-                    else
7431
-                    {
7432
-                        jid = VideoLayout.getPeerContainerResourceJid(container);
7433
-                    }
7434
-                }
7435
-
7436
-                VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(pick), pick.volume, jid);
7552
+                src = APP.RTC.getVideoSrc(pick);
7553
+                volume = pick.volume;
7437 7554
             } else {
7438 7555
                 console.warn("Failed to elect large video");
7556
+                container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
7557
+
7558
+            }
7559
+
7560
+            var jid = null;
7561
+            if(container)
7562
+            {
7563
+                if(container.id == "localVideoWrapper")
7564
+                {
7565
+                    jid = APP.xmpp.myResource();
7566
+                }
7567
+                else
7568
+                {
7569
+                    jid = VideoLayout.getPeerContainerResourceJid(container);
7570
+                }
7439 7571
             }
7572
+            else
7573
+                return;
7574
+
7575
+            VideoLayout.updateLargeVideo(src, volume, jid);
7440 7576
         }
7441 7577
     };
7442 7578
     
@@ -7485,11 +7621,10 @@ var VideoLayout = (function (my) {
7485 7621
     /**
7486 7622
      * Updates the large video with the given new video source.
7487 7623
      */
7488
-    my.updateLargeVideo = function(newSrc, vol, resourceJid) {
7489
-        console.log('hover in', newSrc);
7490
-
7491
-        if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc) {
7624
+    my.updateLargeVideo = function(newSrc, vol, resourceJid, forceUpdate) {
7625
+        console.log('hover in', newSrc, resourceJid);
7492 7626
 
7627
+        if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc || forceUpdate) {
7493 7628
             $('#activeSpeaker').css('visibility', 'hidden');
7494 7629
             // Due to the simulcast the localVideoSrc may have changed when the
7495 7630
             // fadeOut event triggers. In that case the getJidFromVideoSrc and
@@ -7527,7 +7662,6 @@ var VideoLayout = (function (my) {
7527 7662
                 largeVideoState.updateInProgress = true;
7528 7663
 
7529 7664
                 var doUpdate = function () {
7530
-
7531 7665
                     Avatar.updateActiveSpeakerAvatarSrc(
7532 7666
                         APP.xmpp.findJidFromResource(
7533 7667
                             largeVideoState.userResourceJid));
@@ -8372,10 +8506,9 @@ var VideoLayout = (function (my) {
8372 8506
                 if (videoSpan.classList.contains("dominantspeaker"))
8373 8507
                     videoSpan.classList.remove("dominantspeaker");
8374 8508
             }
8375
-
8376
-            Avatar.showUserAvatar(
8377
-                APP.xmpp.findJidFromResource(resourceJid));
8378 8509
         }
8510
+        Avatar.showUserAvatar(
8511
+            APP.xmpp.findJidFromResource(resourceJid));
8379 8512
     };
8380 8513
 
8381 8514
     /**
@@ -13436,7 +13569,7 @@ JingleSession.prototype._modifySources = function (successCallback, queueCallbac
13436 13569
  * @param oldStream old video stream of this session.
13437 13570
  * @param success_callback callback executed after successful stream switch.
13438 13571
  */
13439
-JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
13572
+JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback, isAudio) {
13440 13573
 
13441 13574
     var self = this;
13442 13575
 
@@ -13451,7 +13584,8 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
13451 13584
             self.peerconnection.addStream(new_stream);
13452 13585
     }
13453 13586
 
13454
-    APP.RTC.switchVideoStreams(new_stream, oldStream);
13587
+    if(!isAudio)
13588
+        APP.RTC.switchVideoStreams(new_stream, oldStream);
13455 13589
 
13456 13590
     // Conference is not active
13457 13591
     if(!oldSdp || !self.peerconnection) {
@@ -15877,6 +16011,15 @@ module.exports = function(XMPP, eventEmitter) {
15877 16011
         initPresenceMap: function (myroomjid) {
15878 16012
             this.presMap['to'] = myroomjid;
15879 16013
             this.presMap['xns'] = 'http://jabber.org/protocol/muc';
16014
+            if(APP.RTC.localAudio.isMuted())
16015
+            {
16016
+                this.addAudioInfoToPresence(true);
16017
+            }
16018
+
16019
+            if(APP.RTC.localVideo.isMuted())
16020
+            {
16021
+                this.addVideoInfoToPresence(true);
16022
+            }
15880 16023
         },
15881 16024
         doJoin: function (jid, password) {
15882 16025
             this.myroomjid = jid;
@@ -17328,10 +17471,10 @@ var XMPP = {
17328 17471
     isExternalAuthEnabled: function () {
17329 17472
         return Moderator.isExternalAuthEnabled();
17330 17473
     },
17331
-    switchStreams: function (stream, oldStream, callback) {
17474
+    switchStreams: function (stream, oldStream, callback, isAudio) {
17332 17475
         if (connection && connection.jingle.activecall) {
17333 17476
             // FIXME: will block switchInProgress on true value in case of exception
17334
-            connection.jingle.activecall.switchStreams(stream, oldStream, callback);
17477
+            connection.jingle.activecall.switchStreams(stream, oldStream, callback, isAudio);
17335 17478
         } else {
17336 17479
             // We are done immediately
17337 17480
             console.warn("No conference handler or conference not started yet");
@@ -17339,6 +17482,8 @@ var XMPP = {
17339 17482
         }
17340 17483
     },
17341 17484
     sendVideoInfoPresence: function (mute) {
17485
+        if(!connection)
17486
+            return;
17342 17487
         connection.emuc.addVideoInfoToPresence(mute);
17343 17488
         connection.emuc.sendPresence();
17344 17489
     },
@@ -17382,10 +17527,17 @@ var XMPP = {
17382 17527
         // It is not clear what is the right way to handle multiple tracks.
17383 17528
         // So at least make sure that they are all muted or all unmuted and
17384 17529
         // that we send presence just once.
17385
-        APP.RTC.localAudio.mute();
17530
+        APP.RTC.localAudio.setMute(!mute);
17386 17531
         // isMuted is the opposite of audioEnabled
17387
-        connection.emuc.addAudioInfoToPresence(mute);
17388
-        connection.emuc.sendPresence();
17532
+        this.sendAudioInfoPresence(mute, callback);
17533
+        return true;
17534
+    },
17535
+    sendAudioInfoPresence: function(mute, callback)
17536
+    {
17537
+        if(connection) {
17538
+            connection.emuc.addAudioInfoToPresence(mute);
17539
+            connection.emuc.sendPresence();
17540
+        }
17389 17541
         callback();
17390 17542
         return true;
17391 17543
     },

+ 18
- 18
modules/RTC/LocalStream.js Parādīt failu

@@ -1,12 +1,15 @@
1 1
 var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js");
2 2
 
3 3
 
4
-function LocalStream(stream, type, eventEmitter, videoType)
4
+function LocalStream(stream, type, eventEmitter, videoType, isGUMStream)
5 5
 {
6 6
     this.stream = stream;
7 7
     this.eventEmitter = eventEmitter;
8 8
     this.type = type;
9 9
     this.videoType = videoType;
10
+    this.isGUMStream = true;
11
+    if(isGUMStream === false)
12
+        this.isGUMStream = isGUMStream;
10 13
     var self = this;
11 14
     if(type == "audio")
12 15
     {
@@ -37,26 +40,14 @@ LocalStream.prototype.getOriginalStream = function()
37 40
 }
38 41
 
39 42
 LocalStream.prototype.isAudioStream = function () {
40
-    return (this.stream.getAudioTracks() && this.stream.getAudioTracks().length > 0);
41
-};
42
-
43
-LocalStream.prototype.mute = function()
44
-{
45
-    var ismuted = false;
46
-    var tracks = this.getTracks();
47
-
48
-    for (var idx = 0; idx < tracks.length; idx++) {
49
-        ismuted = !tracks[idx].enabled;
50
-        tracks[idx].enabled = ismuted;
51
-    }
52
-    return ismuted;
43
+    return this.type === "audio";
53 44
 };
54 45
 
55 46
 LocalStream.prototype.setMute = function(mute)
56 47
 {
57 48
 
58
-    if(window.location.protocol != "https:" ||
59
-        this.isAudioStream() || this.videoType === "screen")
49
+    if((window.location.protocol != "https:" && this.isGUMStream) ||
50
+        (this.isAudioStream() && this.isGUMStream) || this.videoType === "screen")
60 51
     {
61 52
         var tracks = this.getTracks();
62 53
 
@@ -72,9 +63,18 @@ LocalStream.prototype.setMute = function(mute)
72 63
         }
73 64
         else
74 65
         {
75
-            APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(["video"],
66
+            var self = this;
67
+            APP.RTC.rtcUtils.obtainAudioAndVideoPermissions(
68
+                (this.isAudioStream() ? ["audio"] : ["video"]),
76 69
                 function (stream) {
77
-                    APP.RTC.changeLocalVideo(stream, false, function () {});
70
+                    if(self.isAudioStream())
71
+                    {
72
+                        APP.RTC.changeLocalAudio(stream, function () {});
73
+                    }
74
+                    else
75
+                    {
76
+                        APP.RTC.changeLocalVideo(stream, false, function () {});
77
+                    }
78 78
                 });
79 79
         }
80 80
     }

+ 49
- 8
modules/RTC/RTC.js Parādīt failu

@@ -13,11 +13,41 @@ var UIEvents = require("../../service/UI/UIEvents");
13 13
 
14 14
 var eventEmitter = new EventEmitter();
15 15
 
16
+
17
+function getMediaStreamUsage()
18
+{
19
+    var result = {
20
+        audio: 1,
21
+        video: 1
22
+    };
23
+    if( config.startAudioMuted === true)
24
+        result.audio = 0;
25
+    if( config.startVideoMuted === true)
26
+        result.video = 0;
27
+
28
+    /** There are some issues with the desktop sharing
29
+     * when this property is enabled.
30
+
31
+     if(result.audio > 0 && result.video > 0)
32
+        return result;
33
+     var isSecureConnection = window.location.protocol == "https:";
34
+
35
+    if(config.disableEarlyMediaPermissionRequests || !isSecureConnection)
36
+    {
37
+        if(result.audio === 0)
38
+            result.audio = -1;
39
+        if(result.video === 0)
40
+            result.video = -1;
41
+    }**/
42
+
43
+    return result;
44
+}
45
+
16 46
 var RTC = {
17 47
     rtcUtils: null,
18 48
     devices: {
19
-        audio: false,
20
-        video: false
49
+        audio: true,
50
+        video: true
21 51
     },
22 52
     localStreams: [],
23 53
     remoteStreams: {},
@@ -35,13 +65,15 @@ var RTC = {
35 65
 
36 66
         eventEmitter.removeListener(eventType, listener);
37 67
     },
38
-    createLocalStream: function (stream, type, change, videoType) {
68
+    createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) {
39 69
 
40
-        var localStream =  new LocalStream(stream, type, eventEmitter, videoType);
70
+        var localStream =  new LocalStream(stream, type, eventEmitter, videoType, isGUMStream);
41 71
         //in firefox we have only one stream object
42 72
         if(this.localStreams.length == 0 ||
43 73
             this.localStreams[0].getOriginalStream() != stream)
44 74
             this.localStreams.push(localStream);
75
+        if(isMuted === true)
76
+            localStream.setMute(false);
45 77
 
46 78
         if(type == "audio")
47 79
         {
@@ -55,7 +87,7 @@ var RTC = {
55 87
         if(change)
56 88
             eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
57 89
 
58
-        eventEmitter.emit(eventType, localStream);
90
+        eventEmitter.emit(eventType, localStream, isMuted);
59 91
         return localStream;
60 92
     },
61 93
     removeLocalStream: function (stream) {
@@ -139,7 +171,8 @@ var RTC = {
139 171
         APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
140 172
             DataChannels.handlePinnedEndpointEvent);
141 173
         this.rtcUtils = new RTCUtils(this);
142
-        this.rtcUtils.obtainAudioAndVideoPermissions();
174
+        this.rtcUtils.obtainAudioAndVideoPermissions(
175
+            null, null, getMediaStreamUsage());
143 176
     },
144 177
     muteRemoteVideoStream: function (jid, value) {
145 178
         var stream;
@@ -180,12 +213,20 @@ var RTC = {
180 213
                 callback();
181 214
             };
182 215
         }
183
-        var videoStream = this.rtcUtils.createVideoStream(stream);
216
+        var videoStream = this.rtcUtils.createStream(stream, true);
184 217
         this.localVideo = this.createLocalStream(videoStream, "video", true, type);
185 218
         // Stop the stream to trigger onended event for old stream
186 219
         oldStream.stop();
187 220
         APP.xmpp.switchStreams(videoStream, oldStream,localCallback);
188 221
     },
222
+    changeLocalAudio: function (stream, callback) {
223
+        var oldStream = this.localAudio.getOriginalStream();
224
+        var newStream = this.rtcUtils.createStream(stream);
225
+        this.localAudio = this.createLocalStream(newStream, "audio", true);
226
+        // Stop the stream to trigger onended event for old stream
227
+        oldStream.stop();
228
+        APP.xmpp.switchStreams(newStream, oldStream, callback, true);
229
+    },
189 230
     /**
190 231
      * Checks if video identified by given src is desktop stream.
191 232
      * @param videoSrc eg.
@@ -220,7 +261,7 @@ var RTC = {
220 261
         {
221 262
             APP.xmpp.sendVideoInfoPresence(mute);
222 263
             if(callback)
223
-                callback();
264
+                callback(mute);
224 265
         }
225 266
         else
226 267
         {

+ 62
- 22
modules/RTC/RTCUtils.js Parādīt failu

@@ -144,6 +144,8 @@ function RTCUtils(RTCService)
144 144
                 //
145 145
                 // https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
146 146
                 // https://github.com/webrtc/samples/issues/302
147
+                if(!element[0])
148
+                    return;
147 149
                 element[0].mozSrcObject = stream;
148 150
                 element[0].play();
149 151
             };
@@ -156,10 +158,13 @@ function RTCUtils(RTCService)
156 158
                 return tracks[0].id.replace(/[\{,\}]/g,"");
157 159
             };
158 160
             this.getVideoSrc = function (element) {
161
+                if(!element)
162
+                    return null;
159 163
                 return element.mozSrcObject;
160 164
             };
161 165
             this.setVideoSrc = function (element, src) {
162
-                element.mozSrcObject = src;
166
+                if(element)
167
+                    element.mozSrcObject = src;
163 168
             };
164 169
             RTCSessionDescription = mozRTCSessionDescription;
165 170
             RTCIceCandidate = mozRTCIceCandidate;
@@ -182,10 +187,13 @@ function RTCUtils(RTCService)
182 187
             return stream.id.replace(/[\{,\}]/g,"");
183 188
         };
184 189
         this.getVideoSrc = function (element) {
190
+            if(!element)
191
+                return null;
185 192
             return element.getAttribute("src");
186 193
         };
187 194
         this.setVideoSrc = function (element, src) {
188
-            element.setAttribute("src", src);
195
+            if(element)
196
+                element.setAttribute("src", src);
189 197
         };
190 198
         // DTLS should now be enabled by default but..
191 199
         this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
@@ -292,20 +300,43 @@ RTCUtils.prototype.setAvailableDevices = function (um, available) {
292 300
  * We ask for audio and video combined stream in order to get permissions and
293 301
  * not to ask twice.
294 302
  */
295
-RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback) {
303
+RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback, usageOptions) {
296 304
     var self = this;
297 305
     // Get AV
298 306
 
307
+    var successCallback = function (stream) {
308
+        if(callback)
309
+            callback(stream, usageOptions);
310
+        else
311
+            self.successCallback(stream, usageOptions);
312
+    };
313
+
299 314
     if(!devices)
300 315
         devices = ['audio', 'video'];
301 316
 
317
+    var newDevices = [];
318
+
319
+
320
+    if(usageOptions)
321
+        for(var i = 0; i < devices.length; i++)
322
+        {
323
+            var device = devices[i];
324
+            if(usageOptions[device] !== -1)
325
+                newDevices.push(device);
326
+        }
327
+    else
328
+        newDevices = devices;
329
+
330
+    if(newDevices.length === 0)
331
+    {
332
+        successCallback();
333
+        return;
334
+    }
335
+
302 336
     this.getUserMediaWithConstraints(
303
-        devices,
337
+        newDevices,
304 338
         function (stream) {
305
-            if(callback)
306
-                callback(stream);
307
-            else
308
-                self.successCallback(stream);
339
+            successCallback(stream);
309 340
         },
310 341
         function (error) {
311 342
             self.errorCallback(error);
@@ -313,11 +344,11 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions = function(devices, callback)
313 344
         config.resolution || '360');
314 345
 }
315 346
 
316
-RTCUtils.prototype.successCallback = function (stream) {
347
+RTCUtils.prototype.successCallback = function (stream, usageOptions) {
317 348
     if(stream)
318 349
         console.log('got', stream, stream.getAudioTracks().length,
319 350
             stream.getVideoTracks().length);
320
-    this.handleLocalStream(stream);
351
+    this.handleLocalStream(stream, usageOptions);
321 352
 };
322 353
 
323 354
 RTCUtils.prototype.errorCallback = function (error) {
@@ -355,7 +386,7 @@ RTCUtils.prototype.errorCallback = function (error) {
355 386
 
356 387
 }
357 388
 
358
-RTCUtils.prototype.handleLocalStream = function(stream)
389
+RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
359 390
 {
360 391
     if(window.webkitMediaStream)
361 392
     {
@@ -375,9 +406,18 @@ RTCUtils.prototype.handleLocalStream = function(stream)
375 406
             }
376 407
         }
377 408
 
378
-        this.service.createLocalStream(audioStream, "audio");
409
+        var audioMuted = (usageOptions && usageOptions.audio != 1),
410
+            videoMuted = (usageOptions && usageOptions.video != 1);
411
+
412
+        var audioGUM = (!usageOptions || usageOptions.audio != -1),
413
+            videoGUM = (!usageOptions || usageOptions.video != -1);
414
+
379 415
 
380
-        this.service.createLocalStream(videoStream, "video");
416
+        this.service.createLocalStream(audioStream, "audio", null, null,
417
+            audioMuted, audioGUM);
418
+
419
+        this.service.createLocalStream(videoStream, "video", null, null,
420
+            videoMuted, videoGUM);
381 421
     }
382 422
     else
383 423
     {//firefox
@@ -386,26 +426,26 @@ RTCUtils.prototype.handleLocalStream = function(stream)
386 426
 
387 427
 };
388 428
 
389
-RTCUtils.prototype.createVideoStream = function(stream)
429
+RTCUtils.prototype.createStream = function(stream, isVideo)
390 430
 {
391
-    var videoStream = null;
431
+    var newStream = null;
392 432
     if(window.webkitMediaStream)
393 433
     {
394
-        videoStream = new webkitMediaStream();
395
-        if(stream)
434
+        newStream = new webkitMediaStream();
435
+        if(newStream)
396 436
         {
397
-            var videoTracks = stream.getVideoTracks();
437
+            var tracks = (isVideo? stream.getVideoTracks() : stream.getAudioTracks());
398 438
 
399
-            for (i = 0; i < videoTracks.length; i++) {
400
-                videoStream.addTrack(videoTracks[i]);
439
+            for (i = 0; i < tracks.length; i++) {
440
+                newStream.addTrack(tracks[i]);
401 441
             }
402 442
         }
403 443
 
404 444
     }
405 445
     else
406
-        videoStream = stream;
446
+        newStream = stream;
407 447
 
408
-    return videoStream;
448
+    return newStream;
409 449
 };
410 450
 
411 451
 module.exports = RTCUtils;

+ 31
- 7
modules/UI/UI.js Parādīt failu

@@ -32,6 +32,14 @@ var eventEmitter = new EventEmitter();
32 32
 var roomName = null;
33 33
 
34 34
 
35
+function notifyForInitialMute()
36
+{
37
+    if(config.startAudioMuted || config.startVideoMuted)
38
+    {
39
+        messageHandler.notify(null, "notify.me", "connected", "notify.muted");
40
+    }
41
+}
42
+
35 43
 function setupPrezi()
36 44
 {
37 45
     $("#reloadPresentationLink").click(function()
@@ -54,17 +62,17 @@ function setupToolbars() {
54 62
     BottomToolbar.init();
55 63
 }
56 64
 
57
-function streamHandler(stream) {
65
+function streamHandler(stream, isMuted) {
58 66
     switch (stream.type)
59 67
     {
60 68
         case "audio":
61
-            VideoLayout.changeLocalAudio(stream);
69
+            VideoLayout.changeLocalAudio(stream, isMuted);
62 70
             break;
63 71
         case "video":
64
-            VideoLayout.changeLocalVideo(stream);
72
+            VideoLayout.changeLocalVideo(stream, isMuted);
65 73
             break;
66 74
         case "stream":
67
-            VideoLayout.changeLocalStream(stream);
75
+            VideoLayout.changeLocalStream(stream, isMuted);
68 76
             break;
69 77
     }
70 78
 }
@@ -381,6 +389,8 @@ UI.start = function (init) {
381 389
 
382 390
     SettingsMenu.init();
383 391
 
392
+    notifyForInitialMute();
393
+
384 394
 };
385 395
 
386 396
 function chatAddError(errorMessage, originalText)
@@ -414,6 +424,9 @@ function onMucJoined(jid, info) {
414 424
 
415 425
     if (displayName)
416 426
         onDisplayNameChanged('localVideoContainer', displayName);
427
+
428
+
429
+    VideoLayout.mucJoined();
417 430
 }
418 431
 
419 432
 function initEtherpad(name) {
@@ -714,9 +727,17 @@ UI.toggleAudio = function() {
714 727
 /**
715 728
  * Sets muted audio state for the local participant.
716 729
  */
717
-UI.setAudioMuted = function (mute) {
718
-
719
-    if(!APP.xmpp.setAudioMute(mute, function () {
730
+UI.setAudioMuted = function (mute, earlyMute) {
731
+    var audioMute = null;
732
+    if(earlyMute)
733
+        audioMute = function (mute, cb) {
734
+            return APP.xmpp.sendAudioInfoPresence(mute, cb);
735
+        };
736
+    else
737
+        audioMute = function (mute, cb) {
738
+            return APP.xmpp.setAudioMute(mute, cb);
739
+        }
740
+    if(!audioMute(mute, function () {
720 741
         VideoLayout.showLocalAudioIndicator(mute);
721 742
 
722 743
         UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
@@ -764,5 +785,8 @@ UI.setVideoMuteButtonsState = function (mute) {
764 785
     }
765 786
 }
766 787
 
788
+
789
+UI.setVideoMute = setVideoMute;
790
+
767 791
 module.exports = UI;
768 792
 

+ 8
- 1
modules/UI/avatar/Avatar.js Parādīt failu

@@ -20,6 +20,12 @@ function isUserMuted(jid) {
20 20
         }
21 21
     }
22 22
 
23
+    if(jid && jid == APP.xmpp.myJid())
24
+    {
25
+        var localVideo = APP.RTC.localVideo;
26
+        return (!localVideo || localVideo.isMuted());
27
+    }
28
+
23 29
     if (!APP.RTC.remoteStreams[jid] || !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) {
24 30
         return null;
25 31
     }
@@ -119,8 +125,9 @@ var Avatar = {
119 125
             } else {
120 126
                 if (video && video.length > 0) {
121 127
                     setVisibility(video, !show);
122
-                    setVisibility(avatar, show);
123 128
                 }
129
+                setVisibility(avatar, show);
130
+
124 131
             }
125 132
         }
126 133
     },

+ 77
- 56
modules/UI/videolayout/VideoLayout.js Parādīt failu

@@ -550,37 +550,19 @@ var VideoLayout = (function (my) {
550 550
             || (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1);
551 551
     };
552 552
 
553
-    my.changeLocalStream = function (stream) {
554
-        VideoLayout.changeLocalVideo(stream);
553
+    my.changeLocalStream = function (stream, isMuted) {
554
+        VideoLayout.changeLocalVideo(stream, isMuted);
555 555
     };
556 556
 
557
-    my.changeLocalAudio = function(stream) {
557
+    my.changeLocalAudio = function(stream, isMuted) {
558
+        if(isMuted)
559
+            APP.UI.setAudioMuted(true, true);
558 560
         APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
559 561
         document.getElementById('localAudio').autoplay = true;
560 562
         document.getElementById('localAudio').volume = 0;
561
-        if (preMuted) {
562
-            if(!APP.UI.setAudioMuted(true))
563
-            {
564
-                preMuted = mute;
565
-            }
566
-            preMuted = false;
567
-        }
568 563
     };
569 564
 
570
-    my.changeLocalVideo = function(stream) {
571
-        var flipX = true;
572
-        if(stream.videoType == "screen")
573
-            flipX = false;
574
-        var localVideo = document.createElement('video');
575
-        localVideo.id = 'localVideo_' +
576
-            APP.RTC.getStreamID(stream.getOriginalStream());
577
-        localVideo.autoplay = true;
578
-        localVideo.volume = 0; // is it required if audio is separated ?
579
-        localVideo.oncontextmenu = function () { return false; };
580
-
581
-        var localVideoContainer = document.getElementById('localVideoWrapper');
582
-        localVideoContainer.appendChild(localVideo);
583
-
565
+    my.changeLocalVideo = function(stream, isMuted) {
584 566
         // Set default display name.
585 567
         setDisplayName('localVideoContainer');
586 568
 
@@ -591,7 +573,7 @@ var VideoLayout = (function (my) {
591 573
 
592 574
         AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
593 575
 
594
-        var localVideoSelector = $('#' + localVideo.id);
576
+        var localVideo = null;
595 577
 
596 578
         function localVideoClick(event) {
597 579
             event.stopPropagation();
@@ -600,9 +582,7 @@ var VideoLayout = (function (my) {
600 582
                 false,
601 583
                 APP.xmpp.myResource());
602 584
         }
603
-        // Add click handler to both video and video wrapper elements in case
604
-        // there's no video.
605
-        localVideoSelector.click(localVideoClick);
585
+
606 586
         $('#localVideoContainer').click(localVideoClick);
607 587
 
608 588
         // Add hover handler
@@ -612,24 +592,52 @@ var VideoLayout = (function (my) {
612 592
             },
613 593
             function() {
614 594
                 if (!VideoLayout.isLargeVideoVisible()
615
-                        || APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
595
+                    || APP.RTC.getVideoSrc(localVideo) !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
616 596
                     VideoLayout.showDisplayName('localVideoContainer', false);
617 597
             }
618 598
         );
619
-        // Add stream ended handler
620
-        stream.getOriginalStream().onended = function () {
621
-            localVideoContainer.removeChild(localVideo);
622
-            VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
623
-        };
599
+
600
+        if(isMuted)
601
+        {
602
+            APP.UI.setVideoMute(true);
603
+            return;
604
+        }
605
+        var flipX = true;
606
+        if(stream.videoType == "screen")
607
+            flipX = false;
608
+        var localVideo = document.createElement('video');
609
+        localVideo.id = 'localVideo_' +
610
+            APP.RTC.getStreamID(stream.getOriginalStream());
611
+        localVideo.autoplay = true;
612
+        localVideo.volume = 0; // is it required if audio is separated ?
613
+        localVideo.oncontextmenu = function () { return false; };
614
+
615
+        var localVideoContainer = document.getElementById('localVideoWrapper');
616
+        localVideoContainer.appendChild(localVideo);
617
+
618
+        var localVideoSelector = $('#' + localVideo.id);
619
+
620
+        // Add click handler to both video and video wrapper elements in case
621
+        // there's no video.
622
+        localVideoSelector.click(localVideoClick);
623
+
624 624
         // Flip video x axis if needed
625 625
         flipXLocalVideo = flipX;
626 626
         if (flipX) {
627 627
             localVideoSelector.addClass("flipVideoX");
628 628
         }
629
+
629 630
         // Attach WebRTC stream
630 631
         var videoStream = APP.simulcast.getLocalVideoStream();
631 632
         APP.RTC.attachMediaStream(localVideoSelector, videoStream);
632 633
 
634
+        // Add stream ended handler
635
+        stream.getOriginalStream().onended = function () {
636
+            localVideoContainer.removeChild(localVideo);
637
+            VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
638
+        };
639
+
640
+
633 641
         localVideoSrc = APP.RTC.getVideoSrc(localVideo);
634 642
 
635 643
         var myResourceJid = APP.xmpp.myResource();
@@ -639,6 +647,14 @@ var VideoLayout = (function (my) {
639 647
 
640 648
     };
641 649
 
650
+    my.mucJoined = function () {
651
+        var myResourceJid = APP.xmpp.myResource();
652
+
653
+        if(!largeVideoState.userResourceJid)
654
+            VideoLayout.updateLargeVideo(localVideoSrc, 0,
655
+                myResourceJid, true);
656
+    };
657
+
642 658
     /**
643 659
      * Adds or removes icons for not available camera and microphone.
644 660
      * @param resourceJid the jid of user
@@ -706,26 +722,34 @@ var VideoLayout = (function (my) {
706 722
                 }
707 723
             }
708 724
 
725
+            var src = null, volume = null;
709 726
             // mute if localvideo
710 727
             if (pick) {
711 728
                 var container = pick.parentNode;
712
-                var jid = null;
713
-                if(container)
714
-                {
715
-                    if(container.id == "localVideoWrapper")
716
-                    {
717
-                        jid = APP.xmpp.myResource();
718
-                    }
719
-                    else
720
-                    {
721
-                        jid = VideoLayout.getPeerContainerResourceJid(container);
722
-                    }
723
-                }
724
-
725
-                VideoLayout.updateLargeVideo(APP.RTC.getVideoSrc(pick), pick.volume, jid);
729
+                src = APP.RTC.getVideoSrc(pick);
730
+                volume = pick.volume;
726 731
             } else {
727 732
                 console.warn("Failed to elect large video");
733
+                container = $('#remoteVideos>span[id!="mixedstream"]:visible:last').get(0);
734
+
728 735
             }
736
+
737
+            var jid = null;
738
+            if(container)
739
+            {
740
+                if(container.id == "localVideoWrapper")
741
+                {
742
+                    jid = APP.xmpp.myResource();
743
+                }
744
+                else
745
+                {
746
+                    jid = VideoLayout.getPeerContainerResourceJid(container);
747
+                }
748
+            }
749
+            else
750
+                return;
751
+
752
+            VideoLayout.updateLargeVideo(src, volume, jid);
729 753
         }
730 754
     };
731 755
     
@@ -774,11 +798,10 @@ var VideoLayout = (function (my) {
774 798
     /**
775 799
      * Updates the large video with the given new video source.
776 800
      */
777
-    my.updateLargeVideo = function(newSrc, vol, resourceJid) {
778
-        console.log('hover in', newSrc);
779
-
780
-        if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc) {
801
+    my.updateLargeVideo = function(newSrc, vol, resourceJid, forceUpdate) {
802
+        console.log('hover in', newSrc, resourceJid);
781 803
 
804
+        if (APP.RTC.getVideoSrc($('#largeVideo')[0]) !== newSrc || forceUpdate) {
782 805
             $('#activeSpeaker').css('visibility', 'hidden');
783 806
             // Due to the simulcast the localVideoSrc may have changed when the
784 807
             // fadeOut event triggers. In that case the getJidFromVideoSrc and
@@ -816,7 +839,6 @@ var VideoLayout = (function (my) {
816 839
                 largeVideoState.updateInProgress = true;
817 840
 
818 841
                 var doUpdate = function () {
819
-
820 842
                     Avatar.updateActiveSpeakerAvatarSrc(
821 843
                         APP.xmpp.findJidFromResource(
822 844
                             largeVideoState.userResourceJid));
@@ -1661,10 +1683,9 @@ var VideoLayout = (function (my) {
1661 1683
                 if (videoSpan.classList.contains("dominantspeaker"))
1662 1684
                     videoSpan.classList.remove("dominantspeaker");
1663 1685
             }
1664
-
1665
-            Avatar.showUserAvatar(
1666
-                APP.xmpp.findJidFromResource(resourceJid));
1667 1686
         }
1687
+        Avatar.showUserAvatar(
1688
+            APP.xmpp.findJidFromResource(resourceJid));
1668 1689
     };
1669 1690
 
1670 1691
     /**

+ 3
- 2
modules/xmpp/JingleSession.js Parādīt failu

@@ -955,7 +955,7 @@ JingleSession.prototype._modifySources = function (successCallback, queueCallbac
955 955
  * @param oldStream old video stream of this session.
956 956
  * @param success_callback callback executed after successful stream switch.
957 957
  */
958
-JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback) {
958
+JingleSession.prototype.switchStreams = function (new_stream, oldStream, success_callback, isAudio) {
959 959
 
960 960
     var self = this;
961 961
 
@@ -970,7 +970,8 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
970 970
             self.peerconnection.addStream(new_stream);
971 971
     }
972 972
 
973
-    APP.RTC.switchVideoStreams(new_stream, oldStream);
973
+    if(!isAudio)
974
+        APP.RTC.switchVideoStreams(new_stream, oldStream);
974 975
 
975 976
     // Conference is not active
976 977
     if(!oldSdp || !self.peerconnection) {

+ 9
- 0
modules/xmpp/strophe.emuc.js Parādīt failu

@@ -28,6 +28,15 @@ module.exports = function(XMPP, eventEmitter) {
28 28
         initPresenceMap: function (myroomjid) {
29 29
             this.presMap['to'] = myroomjid;
30 30
             this.presMap['xns'] = 'http://jabber.org/protocol/muc';
31
+            if(APP.RTC.localAudio.isMuted())
32
+            {
33
+                this.addAudioInfoToPresence(true);
34
+            }
35
+
36
+            if(APP.RTC.localVideo.isMuted())
37
+            {
38
+                this.addVideoInfoToPresence(true);
39
+            }
31 40
         },
32 41
         doJoin: function (jid, password) {
33 42
             this.myroomjid = jid;

+ 14
- 5
modules/xmpp/xmpp.js Parādīt failu

@@ -261,10 +261,10 @@ var XMPP = {
261 261
     isExternalAuthEnabled: function () {
262 262
         return Moderator.isExternalAuthEnabled();
263 263
     },
264
-    switchStreams: function (stream, oldStream, callback) {
264
+    switchStreams: function (stream, oldStream, callback, isAudio) {
265 265
         if (connection && connection.jingle.activecall) {
266 266
             // FIXME: will block switchInProgress on true value in case of exception
267
-            connection.jingle.activecall.switchStreams(stream, oldStream, callback);
267
+            connection.jingle.activecall.switchStreams(stream, oldStream, callback, isAudio);
268 268
         } else {
269 269
             // We are done immediately
270 270
             console.warn("No conference handler or conference not started yet");
@@ -272,6 +272,8 @@ var XMPP = {
272 272
         }
273 273
     },
274 274
     sendVideoInfoPresence: function (mute) {
275
+        if(!connection)
276
+            return;
275 277
         connection.emuc.addVideoInfoToPresence(mute);
276 278
         connection.emuc.sendPresence();
277 279
     },
@@ -315,10 +317,17 @@ var XMPP = {
315 317
         // It is not clear what is the right way to handle multiple tracks.
316 318
         // So at least make sure that they are all muted or all unmuted and
317 319
         // that we send presence just once.
318
-        APP.RTC.localAudio.mute();
320
+        APP.RTC.localAudio.setMute(!mute);
319 321
         // isMuted is the opposite of audioEnabled
320
-        connection.emuc.addAudioInfoToPresence(mute);
321
-        connection.emuc.sendPresence();
322
+        this.sendAudioInfoPresence(mute, callback);
323
+        return true;
324
+    },
325
+    sendAudioInfoPresence: function(mute, callback)
326
+    {
327
+        if(connection) {
328
+            connection.emuc.addAudioInfoToPresence(mute);
329
+            connection.emuc.sendPresence();
330
+        }
322 331
         callback();
323 332
         return true;
324 333
     },

Notiek ielāde…
Atcelt
Saglabāt