瀏覽代碼

Creates desktop sharing module.

j8
hristoterezov 10 年之前
父節點
當前提交
ee94eca733

+ 3
- 7
app.js 查看文件

@@ -415,7 +415,7 @@ $(document).bind('setLocalDescription.jingle', function (event, sid) {
415 415
     if (newssrcs.length > 0) {
416 416
         for (var i = 1; i <= newssrcs.length; i ++) {
417 417
             // Change video type to screen
418
-            if (newssrcs[i-1].type === 'video' && isUsingScreenStream) {
418
+            if (newssrcs[i-1].type === 'video' && desktopsharing.isUsingScreenStream()) {
419 419
                 newssrcs[i-1].type = 'screen';
420 420
             }
421 421
             connection.emuc.addMediaToPresence(i,
@@ -585,7 +585,7 @@ function isVideoSrcDesktop(jid) {
585 585
     if (connection.emuc.myroomjid &&
586 586
         Strophe.getResourceFromJid(connection.emuc.myroomjid) === jid) {
587 587
         // local video
588
-        isDesktop = isUsingScreenStream;
588
+        isDesktop = desktopsharing.isUsingScreenStream();
589 589
     } else {
590 590
         // Do we have associations...
591 591
         var videoSsrc = jid2Ssrc[jid];
@@ -709,11 +709,7 @@ $(document).ready(function () {
709 709
     Moderator.init();
710 710
 
711 711
     // Set default desktop sharing method
712
-    setDesktopSharing(config.desktopSharing);
713
-    // Initialize Chrome extension inline installs
714
-    if (config.chromeExtensionId) {
715
-        initInlineInstalls();
716
-    }
712
+    desktopsharing.init();
717 713
 });
718 714
 
719 715
 $(window).bind('beforeunload', function () {

+ 8
- 7
index.html 查看文件

@@ -17,7 +17,7 @@
17 17
     <script src="libs/strophe/strophe.caps.jsonly.min.js?v=1"></script>
18 18
     <script src="libs/strophe/strophe.jingle.js?v=3"></script>
19 19
     <script src="libs/strophe/strophe.jingle.sdp.js?v=5"></script>
20
-    <script src="libs/strophe/strophe.jingle.session.js?v=5"></script>
20
+    <script src="libs/strophe/strophe.jingle.session.js?v=6"></script>
21 21
     <script src="libs/strophe/strophe.util.js"></script>
22 22
     <script src="libs/jquery-ui.js"></script>
23 23
     <script src="libs/rayo.js?v=1"></script>
@@ -27,17 +27,18 @@
27 27
     <script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
28 28
     <script src="interface_config.js?v=5"></script>
29 29
     <script src="service/RTC/RTCBrowserType.js?v=1"></script>
30
-    <script src="service/RTC/StreamEventTypes.js?v=1"></script>
30
+    <script src="service/RTC/StreamEventTypes.js?v=2"></script>
31 31
     <script src="service/RTC/MediaStreamTypes.js?v=1"></script>
32
-    <script src="libs/modules/simulcast.bundle.js?v=2"></script>
32
+    <script src="service/desktopsharing/DesktopSharingEventTypes.js?v=1"></script>
33
+    <script src="libs/modules/simulcast.bundle.js?v=3"></script>
33 34
     <script src="libs/modules/connectionquality.bundle.js?v=1"></script>
34
-    <script src="libs/modules/UI.bundle.js?v=4"></script>
35
+    <script src="libs/modules/UI.bundle.js?v=5"></script>
35 36
     <script src="libs/modules/statistics.bundle.js?v=1"></script>
36
-    <script src="libs/modules/RTC.bundle.js?v=3"></script>
37
+    <script src="libs/modules/RTC.bundle.js?v=4"></script>
37 38
     <script src="muc.js?v=17"></script><!-- simple MUC library -->
38 39
     <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
39
-    <script src="desktopsharing.js?v=3"></script><!-- desktop sharing -->
40
-    <script src="app.js?v=25"></script><!-- application logic -->
40
+    <script src="libs/modules/desktopsharing.bundle.js?v=3"></script><!-- desktop sharing -->
41
+    <script src="app.js?v=26"></script><!-- application logic -->
41 42
     <script src="libs/modules/API.bundle.js?v=1"></script>
42 43
     <script src="util.js?v=7"></script><!-- utility functions -->
43 44
     <script src="moderatemuc.js?v=4"></script><!-- moderator plugin -->

+ 28
- 4
libs/modules/RTC.bundle.js 查看文件

@@ -410,7 +410,7 @@ var RTC = {
410 410
 
411 411
         eventEmitter.removeListener(eventType, listener);
412 412
     },
413
-    createLocalStream: function (stream, type) {
413
+    createLocalStream: function (stream, type, change) {
414 414
 
415 415
         var localStream =  new LocalStream(stream, type, eventEmitter);
416 416
         //in firefox we have only one stream object
@@ -425,8 +425,11 @@ var RTC = {
425 425
         {
426 426
             this.localVideo = localStream;
427 427
         }
428
-        eventEmitter.emit(StreamEventTypes.EVENT_TYPE_LOCAL_CREATED,
429
-            localStream);
428
+        var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
429
+        if(change)
430
+            eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
431
+
432
+        eventEmitter.emit(eventType, localStream);
430 433
         return localStream;
431 434
     },
432 435
     removeLocalStream: function (stream) {
@@ -482,6 +485,10 @@ var RTC = {
482 485
         this.dispose();
483 486
     },
484 487
     start: function () {
488
+        desktopsharing.addListener(
489
+            function (stream, isUsingScreenStream, callback) {
490
+                RTC.changeLocalVideo(stream, isUsingScreenStream, callback);
491
+            }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
485 492
         this.rtcUtils = new RTCUtils(this);
486 493
         this.rtcUtils.obtainAudioAndVideoPermissions();
487 494
     },
@@ -512,9 +519,26 @@ var RTC = {
512 519
         this.localStreams = [];
513 520
 
514 521
         //in firefox we have only one stream object
515
-        if(this.localAudio.getOriginalStream() != new_stream)
522
+        if (this.localAudio.getOriginalStream() != new_stream)
516 523
             this.localStreams.push(this.localAudio);
517 524
         this.localStreams.push(this.localVideo);
525
+    },
526
+    changeLocalVideo: function (stream, isUsingScreenStream, callback) {
527
+        var oldStream = this.localVideo.getOriginalStream();
528
+        var type = (isUsingScreenStream? "desktop" : "video");
529
+        RTC.localVideo = this.createLocalStream(stream, type, true);
530
+        // Stop the stream to trigger onended event for old stream
531
+        oldStream.stop();
532
+        if (activecall) {
533
+            // FIXME: will block switchInProgress on true value in case of exception
534
+            activecall.switchStreams(stream, oldStream, callback);
535
+        } else {
536
+            // We are done immediately
537
+            console.error("No conference handler");
538
+            UI.messageHandler.showError('Error',
539
+                'Unable to switch video stream.');
540
+            callback();
541
+        }
518 542
     }
519 543
 
520 544
 };

+ 49
- 35
libs/modules/UI.bundle.js 查看文件

@@ -45,26 +45,28 @@ function setupToolbars() {
45 45
     BottomToolbar.init();
46 46
 }
47 47
 
48
+function streamHandler(stream) {
49
+    switch (stream.type)
50
+    {
51
+        case "audio":
52
+            VideoLayout.changeLocalAudio(stream);
53
+            break;
54
+        case "video":
55
+            VideoLayout.changeLocalVideo(stream);
56
+            break;
57
+        case "stream":
58
+            VideoLayout.changeLocalStream(stream);
59
+            break;
60
+        case "desktop":
61
+            VideoLayout.changeLocalVideo(stream);
62
+            break;
63
+    }
64
+}
48 65
 
49 66
 function registerListeners() {
50
-    RTC.addStreamListener(function (stream) {
51
-        switch (stream.type)
52
-        {
53
-            case "audio":
54
-                VideoLayout.changeLocalAudio(stream.getOriginalStream());
55
-                break;
56
-            case "video":
57
-                VideoLayout.changeLocalVideo(stream.getOriginalStream(), true);
58
-                break;
59
-            case "stream":
60
-                VideoLayout.changeLocalStream(stream.getOriginalStream());
61
-                break;
62
-            case "desktop":
63
-                VideoLayout.changeLocalVideo(stream, !isUsingScreenStream);
64
-                break;
65
-        }
66
-    }, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
67
+    RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
67 68
 
69
+    RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
68 70
     RTC.addStreamListener(function (stream) {
69 71
         VideoLayout.onRemoteStreamAdded(stream);
70 72
     }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
@@ -97,6 +99,13 @@ function registerListeners() {
97 99
         AudioLevels.updateAudioLevel(resourceJid, audioLevel,
98 100
             UI.getLargeVideoState().userResourceJid);
99 101
     });
102
+    desktopsharing.addListener(function () {
103
+        ToolbarToggler.showDesktopSharingButton();
104
+    }, DesktopSharingEventTypes.INIT);
105
+    desktopsharing.addListener(
106
+        Toolbar.changeDesktopSharingButtonState,
107
+        DesktopSharingEventTypes.SWITCHING_DONE);
108
+
100 109
 
101 110
 }
102 111
 
@@ -446,10 +455,6 @@ UI.setRecordingButtonState = function (state) {
446 455
     Toolbar.setRecordingButtonState(state);
447 456
 };
448 457
 
449
-UI.changeDesktopSharingButtonState = function (isUsingScreenStream) {
450
-    Toolbar.changeDesktopSharingButtonState(isUsingScreenStream);
451
-};
452
-
453 458
 UI.inputDisplayNameHandler = function (value) {
454 459
     VideoLayout.inputDisplayNameHandler(value);
455 460
 };
@@ -506,10 +511,6 @@ UI.showLocalAudioIndicator = function (mute) {
506 511
     VideoLayout.showLocalAudioIndicator(mute);
507 512
 };
508 513
 
509
-UI.changeLocalVideo = function (stream, flipx) {
510
-    VideoLayout.changeLocalVideo(stream, flipx);
511
-};
512
-
513 514
 UI.generateRoomName = function() {
514 515
     var roomnode = null;
515 516
     var path = window.location.pathname;
@@ -556,7 +557,6 @@ UI.dockToolbar = function (isDock) {
556 557
     return ToolbarToggler.dockToolbar(isDock);
557 558
 };
558 559
 
559
-
560 560
 function dump(elem, filename) {
561 561
     elem = elem.parentNode;
562 562
     elem.download = filename || 'meetlog.json';
@@ -2830,11 +2830,19 @@ var BottomToolbar = (function (my) {
2830 2830
 module.exports = BottomToolbar;
2831 2831
 
2832 2832
 },{"../side_pannels/SidePanelToggler":7}],16:[function(require,module,exports){
2833
-/* global $, interfaceConfig, Moderator, showDesktopSharingButton */
2833
+/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
2834 2834
 
2835 2835
 var toolbarTimeoutObject,
2836 2836
     toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
2837 2837
 
2838
+function showDesktopSharingButton() {
2839
+    if (desktopsharing.isDesktopSharingEnabled()) {
2840
+        $('#desktopsharing').css({display: "inline"});
2841
+    } else {
2842
+        $('#desktopsharing').css({display: "none"});
2843
+    }
2844
+}
2845
+
2838 2846
 /**
2839 2847
  * Hides the toolbar.
2840 2848
  */
@@ -2929,7 +2937,9 @@ var ToolbarToggler = {
2929 2937
                 toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
2930 2938
             }
2931 2939
         }
2932
-    }
2940
+    },
2941
+
2942
+    showDesktopSharingButton: showDesktopSharingButton
2933 2943
 
2934 2944
 };
2935 2945
 
@@ -2977,7 +2987,7 @@ var buttonHandlers =
2977 2987
         return Etherpad.toggleEtherpad(0);
2978 2988
     },
2979 2989
     "toolbar_button_desktopsharing": function () {
2980
-        return toggleScreenSharing();
2990
+        return desktopsharing.toggleScreenSharing();
2981 2991
     },
2982 2992
     "toolbar_button_fullScreen": function()
2983 2993
     {
@@ -3199,7 +3209,7 @@ var Toolbar = (function (my) {
3199 3209
             inviteLink.select();
3200 3210
             document.getElementById('jqi_state0_buttonInvite').disabled = false;
3201 3211
         }
3202
-    }
3212
+    };
3203 3213
 
3204 3214
     /**
3205 3215
      * Disables and enables some of the buttons.
@@ -4591,11 +4601,11 @@ var VideoLayout = (function (my) {
4591 4601
     };
4592 4602
 
4593 4603
     my.changeLocalStream = function (stream) {
4594
-        VideoLayout.changeLocalVideo(stream, true);
4604
+        VideoLayout.changeLocalVideo(stream);
4595 4605
     };
4596 4606
 
4597 4607
     my.changeLocalAudio = function(stream) {
4598
-        RTC.attachMediaStream($('#localAudio'), stream);
4608
+        RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
4599 4609
         document.getElementById('localAudio').autoplay = true;
4600 4610
         document.getElementById('localAudio').volume = 0;
4601 4611
         if (preMuted) {
@@ -4604,9 +4614,13 @@ var VideoLayout = (function (my) {
4604 4614
         }
4605 4615
     };
4606 4616
 
4607
-    my.changeLocalVideo = function(stream, flipX) {
4617
+    my.changeLocalVideo = function(stream) {
4618
+        var flipX = true;
4619
+        if(stream.type == "desktop")
4620
+            flipX = false;
4608 4621
         var localVideo = document.createElement('video');
4609
-        localVideo.id = 'localVideo_' + RTC.getStreamID(stream);
4622
+        localVideo.id = 'localVideo_' +
4623
+            RTC.getStreamID(stream.getOriginalStream());
4610 4624
         localVideo.autoplay = true;
4611 4625
         localVideo.volume = 0; // is it required if audio is separated ?
4612 4626
         localVideo.oncontextmenu = function () { return false; };
@@ -4654,7 +4668,7 @@ var VideoLayout = (function (my) {
4654 4668
             }
4655 4669
         );
4656 4670
         // Add stream ended handler
4657
-        stream.onended = function () {
4671
+        stream.getOriginalStream().onended = function () {
4658 4672
             localVideoContainer.removeChild(localVideo);
4659 4673
             VideoLayout.updateRemovedVideo(RTC.getVideoSrc(localVideo));
4660 4674
         };

+ 630
- 0
libs/modules/desktopsharing.bundle.js 查看文件

@@ -0,0 +1,630 @@
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.desktopsharing=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
+/* global $, alert, changeLocalVideo, chrome, config, connection, getConferenceHandler, getUserMediaWithConstraints */
3
+/**
4
+ * Indicates that desktop stream is currently in use(for toggle purpose).
5
+ * @type {boolean}
6
+ */
7
+var isUsingScreenStream = false;
8
+/**
9
+ * Indicates that switch stream operation is in progress and prevent from triggering new events.
10
+ * @type {boolean}
11
+ */
12
+var switchInProgress = false;
13
+
14
+/**
15
+ * Method used to get screen sharing stream.
16
+ *
17
+ * @type {function (stream_callback, failure_callback}
18
+ */
19
+var obtainDesktopStream = null;
20
+
21
+/**
22
+ * Flag used to cache desktop sharing enabled state. Do not use directly as it can be <tt>null</tt>.
23
+ * @type {null|boolean}
24
+ */
25
+var _desktopSharingEnabled = null;
26
+
27
+var EventEmitter = require("events");
28
+
29
+var eventEmitter = new EventEmitter();
30
+
31
+/**
32
+ * Method obtains desktop stream from WebRTC 'screen' source.
33
+ * Flag 'chrome://flags/#enable-usermedia-screen-capture' must be enabled.
34
+ */
35
+function obtainWebRTCScreen(streamCallback, failCallback) {
36
+    RTC.getUserMediaWithConstraints(
37
+        ['screen'],
38
+        streamCallback,
39
+        failCallback
40
+    );
41
+}
42
+
43
+/**
44
+ * Constructs inline install URL for Chrome desktop streaming extension.
45
+ * The 'chromeExtensionId' must be defined in config.js.
46
+ * @returns {string}
47
+ */
48
+function getWebStoreInstallUrl()
49
+{
50
+    return "https://chrome.google.com/webstore/detail/" + config.chromeExtensionId;
51
+}
52
+
53
+/**
54
+ * Checks whether extension update is required.
55
+ * @param minVersion minimal required version
56
+ * @param extVersion current extension version
57
+ * @returns {boolean}
58
+ */
59
+function isUpdateRequired(minVersion, extVersion)
60
+{
61
+    try
62
+    {
63
+        var s1 = minVersion.split('.');
64
+        var s2 = extVersion.split('.');
65
+
66
+        var len = Math.max(s1.length, s2.length);
67
+        for (var i = 0; i < len; i++)
68
+        {
69
+            var n1 = 0,
70
+                n2 = 0;
71
+
72
+            if (i < s1.length)
73
+                n1 = parseInt(s1[i]);
74
+            if (i < s2.length)
75
+                n2 = parseInt(s2[i]);
76
+
77
+            if (isNaN(n1) || isNaN(n2))
78
+            {
79
+                return true;
80
+            }
81
+            else if (n1 !== n2)
82
+            {
83
+                return n1 > n2;
84
+            }
85
+        }
86
+
87
+        // will happen if boths version has identical numbers in
88
+        // their components (even if one of them is longer, has more components)
89
+        return false;
90
+    }
91
+    catch (e)
92
+    {
93
+        console.error("Failed to parse extension version", e);
94
+        UI.messageHandler.showError('Error',
95
+            'Error when trying to detect desktopsharing extension.');
96
+        return true;
97
+    }
98
+}
99
+
100
+
101
+function checkExtInstalled(isInstalledCallback) {
102
+    if (!chrome.runtime) {
103
+        // No API, so no extension for sure
104
+        isInstalledCallback(false);
105
+        return;
106
+    }
107
+    chrome.runtime.sendMessage(
108
+        config.chromeExtensionId,
109
+        { getVersion: true },
110
+        function (response) {
111
+            if (!response || !response.version) {
112
+                // Communication failure - assume that no endpoint exists
113
+                console.warn("Extension not installed?: " + chrome.runtime.lastError);
114
+                isInstalledCallback(false);
115
+            } else {
116
+                // Check installed extension version
117
+                var extVersion = response.version;
118
+                console.log('Extension version is: ' + extVersion);
119
+                var updateRequired = isUpdateRequired(config.minChromeExtVersion, extVersion);
120
+                if (updateRequired) {
121
+                    alert(
122
+                        'Jitsi Desktop Streamer requires update. ' +
123
+                        'Changes will take effect after next Chrome restart.');
124
+                }
125
+                isInstalledCallback(!updateRequired);
126
+            }
127
+        }
128
+    );
129
+}
130
+
131
+function doGetStreamFromExtension(streamCallback, failCallback) {
132
+    // Sends 'getStream' msg to the extension. Extension id must be defined in the config.
133
+    chrome.runtime.sendMessage(
134
+        config.chromeExtensionId,
135
+        { getStream: true, sources: config.desktopSharingSources },
136
+        function (response) {
137
+            if (!response) {
138
+                failCallback(chrome.runtime.lastError);
139
+                return;
140
+            }
141
+            console.log("Response from extension: " + response);
142
+            if (response.streamId) {
143
+                RTC.getUserMediaWithConstraints(
144
+                    ['desktop'],
145
+                    function (stream) {
146
+                        streamCallback(stream);
147
+                    },
148
+                    failCallback,
149
+                    null, null, null,
150
+                    response.streamId);
151
+            } else {
152
+                failCallback("Extension failed to get the stream");
153
+            }
154
+        }
155
+    );
156
+}
157
+/**
158
+ * Asks Chrome extension to call chooseDesktopMedia and gets chrome 'desktop' stream for returned stream token.
159
+ */
160
+function obtainScreenFromExtension(streamCallback, failCallback) {
161
+    checkExtInstalled(
162
+        function (isInstalled) {
163
+            if (isInstalled) {
164
+                doGetStreamFromExtension(streamCallback, failCallback);
165
+            } else {
166
+                chrome.webstore.install(
167
+                    getWebStoreInstallUrl(),
168
+                    function (arg) {
169
+                        console.log("Extension installed successfully", arg);
170
+                        // We need to reload the page in order to get the access to chrome.runtime
171
+                        window.location.reload(false);
172
+                    },
173
+                    function (arg) {
174
+                        console.log("Failed to install the extension", arg);
175
+                        failCallback(arg);
176
+                        UI.messageHandler.showError('Error',
177
+                            'Failed to install desktop sharing extension');
178
+                    }
179
+                );
180
+            }
181
+        }
182
+    );
183
+}
184
+
185
+/**
186
+ * Call this method to toggle desktop sharing feature.
187
+ * @param method pass "ext" to use chrome extension for desktop capture(chrome extension required),
188
+ *        pass "webrtc" to use WebRTC "screen" desktop source('chrome://flags/#enable-usermedia-screen-capture'
189
+ *        must be enabled), pass any other string or nothing in order to disable this feature completely.
190
+ */
191
+function setDesktopSharing(method) {
192
+    // Check if we are running chrome
193
+    if (!navigator.webkitGetUserMedia) {
194
+        obtainDesktopStream = null;
195
+        console.info("Desktop sharing disabled");
196
+    } else if (method == "ext") {
197
+        obtainDesktopStream = obtainScreenFromExtension;
198
+        console.info("Using Chrome extension for desktop sharing");
199
+    } else if (method == "webrtc") {
200
+        obtainDesktopStream = obtainWebRTCScreen;
201
+        console.info("Using Chrome WebRTC for desktop sharing");
202
+    }
203
+
204
+    // Reset enabled cache
205
+    _desktopSharingEnabled = null;
206
+}
207
+
208
+/**
209
+ * Initializes <link rel=chrome-webstore-item /> with extension id set in config.js to support inline installs.
210
+ * Host site must be selected as main website of published extension.
211
+ */
212
+function initInlineInstalls()
213
+{
214
+    $("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
215
+}
216
+
217
+function getSwitchStreamFailed(error) {
218
+    console.error("Failed to obtain the stream to switch to", error);
219
+    switchInProgress = false;
220
+}
221
+
222
+function streamSwitchDone() {
223
+    switchInProgress = false;
224
+    eventEmitter.emit(
225
+        DesktopSharingEventTypes.SWITCHING_DONE,
226
+        isUsingScreenStream);
227
+}
228
+
229
+function newStreamCreated(stream)
230
+{
231
+    eventEmitter.emit(DesktopSharingEventTypes.NEW_STREAM_CREATED,
232
+        stream, isUsingScreenStream, streamSwitchDone);
233
+}
234
+
235
+
236
+module.exports = {
237
+    isUsingScreenStream: function () {
238
+        return isUsingScreenStream;
239
+    },
240
+
241
+    /**
242
+     * @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
243
+     */
244
+    isDesktopSharingEnabled: function () {
245
+        if (_desktopSharingEnabled === null) {
246
+            if (obtainDesktopStream === obtainScreenFromExtension) {
247
+                // Parse chrome version
248
+                var userAgent = navigator.userAgent.toLowerCase();
249
+                // We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
250
+                var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
251
+                console.log("Chrome version" + userAgent, ver);
252
+                _desktopSharingEnabled = ver >= 34;
253
+            } else {
254
+                _desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
255
+            }
256
+        }
257
+        return _desktopSharingEnabled;
258
+    },
259
+    
260
+    init: function () {
261
+        setDesktopSharing(config.desktopSharing);
262
+
263
+        // Initialize Chrome extension inline installs
264
+        if (config.chromeExtensionId) {
265
+            initInlineInstalls();
266
+        }
267
+
268
+        eventEmitter.emit(DesktopSharingEventTypes.INIT);
269
+    },
270
+
271
+    addListener: function(listener, type)
272
+    {
273
+        eventEmitter.on(type, listener);
274
+    },
275
+
276
+    removeListener: function (listener,type) {
277
+        eventEmitter.removeListener(type, listener);
278
+    },
279
+
280
+    /*
281
+     * Toggles screen sharing.
282
+     */
283
+    toggleScreenSharing: function () {
284
+        if (switchInProgress || !obtainDesktopStream) {
285
+            console.warn("Switch in progress or no method defined");
286
+            return;
287
+        }
288
+        switchInProgress = true;
289
+
290
+        if (!isUsingScreenStream)
291
+        {
292
+            // Switch to desktop stream
293
+            obtainDesktopStream(
294
+                function (stream) {
295
+                    // We now use screen stream
296
+                    isUsingScreenStream = true;
297
+                    // Hook 'ended' event to restore camera when screen stream stops
298
+                    stream.addEventListener('ended',
299
+                        function (e) {
300
+                            if (!switchInProgress && isUsingScreenStream) {
301
+                                toggleScreenSharing();
302
+                            }
303
+                        }
304
+                    );
305
+                    newStreamCreated(stream);
306
+                },
307
+                getSwitchStreamFailed);
308
+        } else {
309
+            // Disable screen stream
310
+            RTC.getUserMediaWithConstraints(
311
+                ['video'],
312
+                function (stream) {
313
+                    // We are now using camera stream
314
+                    isUsingScreenStream = false;
315
+                    newStreamCreated(stream);
316
+                },
317
+                getSwitchStreamFailed, config.resolution || '360'
318
+            );
319
+        }
320
+    }
321
+};
322
+
323
+
324
+},{"events":2}],2:[function(require,module,exports){
325
+// Copyright Joyent, Inc. and other Node contributors.
326
+//
327
+// Permission is hereby granted, free of charge, to any person obtaining a
328
+// copy of this software and associated documentation files (the
329
+// "Software"), to deal in the Software without restriction, including
330
+// without limitation the rights to use, copy, modify, merge, publish,
331
+// distribute, sublicense, and/or sell copies of the Software, and to permit
332
+// persons to whom the Software is furnished to do so, subject to the
333
+// following conditions:
334
+//
335
+// The above copyright notice and this permission notice shall be included
336
+// in all copies or substantial portions of the Software.
337
+//
338
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
339
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
340
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
341
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
342
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
343
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
344
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
345
+
346
+function EventEmitter() {
347
+  this._events = this._events || {};
348
+  this._maxListeners = this._maxListeners || undefined;
349
+}
350
+module.exports = EventEmitter;
351
+
352
+// Backwards-compat with node 0.10.x
353
+EventEmitter.EventEmitter = EventEmitter;
354
+
355
+EventEmitter.prototype._events = undefined;
356
+EventEmitter.prototype._maxListeners = undefined;
357
+
358
+// By default EventEmitters will print a warning if more than 10 listeners are
359
+// added to it. This is a useful default which helps finding memory leaks.
360
+EventEmitter.defaultMaxListeners = 10;
361
+
362
+// Obviously not all Emitters should be limited to 10. This function allows
363
+// that to be increased. Set to zero for unlimited.
364
+EventEmitter.prototype.setMaxListeners = function(n) {
365
+  if (!isNumber(n) || n < 0 || isNaN(n))
366
+    throw TypeError('n must be a positive number');
367
+  this._maxListeners = n;
368
+  return this;
369
+};
370
+
371
+EventEmitter.prototype.emit = function(type) {
372
+  var er, handler, len, args, i, listeners;
373
+
374
+  if (!this._events)
375
+    this._events = {};
376
+
377
+  // If there is no 'error' event listener then throw.
378
+  if (type === 'error') {
379
+    if (!this._events.error ||
380
+        (isObject(this._events.error) && !this._events.error.length)) {
381
+      er = arguments[1];
382
+      if (er instanceof Error) {
383
+        throw er; // Unhandled 'error' event
384
+      } else {
385
+        throw TypeError('Uncaught, unspecified "error" event.');
386
+      }
387
+      return false;
388
+    }
389
+  }
390
+
391
+  handler = this._events[type];
392
+
393
+  if (isUndefined(handler))
394
+    return false;
395
+
396
+  if (isFunction(handler)) {
397
+    switch (arguments.length) {
398
+      // fast cases
399
+      case 1:
400
+        handler.call(this);
401
+        break;
402
+      case 2:
403
+        handler.call(this, arguments[1]);
404
+        break;
405
+      case 3:
406
+        handler.call(this, arguments[1], arguments[2]);
407
+        break;
408
+      // slower
409
+      default:
410
+        len = arguments.length;
411
+        args = new Array(len - 1);
412
+        for (i = 1; i < len; i++)
413
+          args[i - 1] = arguments[i];
414
+        handler.apply(this, args);
415
+    }
416
+  } else if (isObject(handler)) {
417
+    len = arguments.length;
418
+    args = new Array(len - 1);
419
+    for (i = 1; i < len; i++)
420
+      args[i - 1] = arguments[i];
421
+
422
+    listeners = handler.slice();
423
+    len = listeners.length;
424
+    for (i = 0; i < len; i++)
425
+      listeners[i].apply(this, args);
426
+  }
427
+
428
+  return true;
429
+};
430
+
431
+EventEmitter.prototype.addListener = function(type, listener) {
432
+  var m;
433
+
434
+  if (!isFunction(listener))
435
+    throw TypeError('listener must be a function');
436
+
437
+  if (!this._events)
438
+    this._events = {};
439
+
440
+  // To avoid recursion in the case that type === "newListener"! Before
441
+  // adding it to the listeners, first emit "newListener".
442
+  if (this._events.newListener)
443
+    this.emit('newListener', type,
444
+              isFunction(listener.listener) ?
445
+              listener.listener : listener);
446
+
447
+  if (!this._events[type])
448
+    // Optimize the case of one listener. Don't need the extra array object.
449
+    this._events[type] = listener;
450
+  else if (isObject(this._events[type]))
451
+    // If we've already got an array, just append.
452
+    this._events[type].push(listener);
453
+  else
454
+    // Adding the second element, need to change to array.
455
+    this._events[type] = [this._events[type], listener];
456
+
457
+  // Check for listener leak
458
+  if (isObject(this._events[type]) && !this._events[type].warned) {
459
+    var m;
460
+    if (!isUndefined(this._maxListeners)) {
461
+      m = this._maxListeners;
462
+    } else {
463
+      m = EventEmitter.defaultMaxListeners;
464
+    }
465
+
466
+    if (m && m > 0 && this._events[type].length > m) {
467
+      this._events[type].warned = true;
468
+      console.error('(node) warning: possible EventEmitter memory ' +
469
+                    'leak detected. %d listeners added. ' +
470
+                    'Use emitter.setMaxListeners() to increase limit.',
471
+                    this._events[type].length);
472
+      if (typeof console.trace === 'function') {
473
+        // not supported in IE 10
474
+        console.trace();
475
+      }
476
+    }
477
+  }
478
+
479
+  return this;
480
+};
481
+
482
+EventEmitter.prototype.on = EventEmitter.prototype.addListener;
483
+
484
+EventEmitter.prototype.once = function(type, listener) {
485
+  if (!isFunction(listener))
486
+    throw TypeError('listener must be a function');
487
+
488
+  var fired = false;
489
+
490
+  function g() {
491
+    this.removeListener(type, g);
492
+
493
+    if (!fired) {
494
+      fired = true;
495
+      listener.apply(this, arguments);
496
+    }
497
+  }
498
+
499
+  g.listener = listener;
500
+  this.on(type, g);
501
+
502
+  return this;
503
+};
504
+
505
+// emits a 'removeListener' event iff the listener was removed
506
+EventEmitter.prototype.removeListener = function(type, listener) {
507
+  var list, position, length, i;
508
+
509
+  if (!isFunction(listener))
510
+    throw TypeError('listener must be a function');
511
+
512
+  if (!this._events || !this._events[type])
513
+    return this;
514
+
515
+  list = this._events[type];
516
+  length = list.length;
517
+  position = -1;
518
+
519
+  if (list === listener ||
520
+      (isFunction(list.listener) && list.listener === listener)) {
521
+    delete this._events[type];
522
+    if (this._events.removeListener)
523
+      this.emit('removeListener', type, listener);
524
+
525
+  } else if (isObject(list)) {
526
+    for (i = length; i-- > 0;) {
527
+      if (list[i] === listener ||
528
+          (list[i].listener && list[i].listener === listener)) {
529
+        position = i;
530
+        break;
531
+      }
532
+    }
533
+
534
+    if (position < 0)
535
+      return this;
536
+
537
+    if (list.length === 1) {
538
+      list.length = 0;
539
+      delete this._events[type];
540
+    } else {
541
+      list.splice(position, 1);
542
+    }
543
+
544
+    if (this._events.removeListener)
545
+      this.emit('removeListener', type, listener);
546
+  }
547
+
548
+  return this;
549
+};
550
+
551
+EventEmitter.prototype.removeAllListeners = function(type) {
552
+  var key, listeners;
553
+
554
+  if (!this._events)
555
+    return this;
556
+
557
+  // not listening for removeListener, no need to emit
558
+  if (!this._events.removeListener) {
559
+    if (arguments.length === 0)
560
+      this._events = {};
561
+    else if (this._events[type])
562
+      delete this._events[type];
563
+    return this;
564
+  }
565
+
566
+  // emit removeListener for all listeners on all events
567
+  if (arguments.length === 0) {
568
+    for (key in this._events) {
569
+      if (key === 'removeListener') continue;
570
+      this.removeAllListeners(key);
571
+    }
572
+    this.removeAllListeners('removeListener');
573
+    this._events = {};
574
+    return this;
575
+  }
576
+
577
+  listeners = this._events[type];
578
+
579
+  if (isFunction(listeners)) {
580
+    this.removeListener(type, listeners);
581
+  } else {
582
+    // LIFO order
583
+    while (listeners.length)
584
+      this.removeListener(type, listeners[listeners.length - 1]);
585
+  }
586
+  delete this._events[type];
587
+
588
+  return this;
589
+};
590
+
591
+EventEmitter.prototype.listeners = function(type) {
592
+  var ret;
593
+  if (!this._events || !this._events[type])
594
+    ret = [];
595
+  else if (isFunction(this._events[type]))
596
+    ret = [this._events[type]];
597
+  else
598
+    ret = this._events[type].slice();
599
+  return ret;
600
+};
601
+
602
+EventEmitter.listenerCount = function(emitter, type) {
603
+  var ret;
604
+  if (!emitter._events || !emitter._events[type])
605
+    ret = 0;
606
+  else if (isFunction(emitter._events[type]))
607
+    ret = 1;
608
+  else
609
+    ret = emitter._events[type].length;
610
+  return ret;
611
+};
612
+
613
+function isFunction(arg) {
614
+  return typeof arg === 'function';
615
+}
616
+
617
+function isNumber(arg) {
618
+  return typeof arg === 'number';
619
+}
620
+
621
+function isObject(arg) {
622
+  return typeof arg === 'object' && arg !== null;
623
+}
624
+
625
+function isUndefined(arg) {
626
+  return arg === void 0;
627
+}
628
+
629
+},{}]},{},[1])(1)
630
+});

+ 1
- 1
libs/modules/simulcast.bundle.js 查看文件

@@ -384,7 +384,7 @@ NativeSimulcastSender.prototype._localVideoSourceCache = '';
384 384
 
385 385
 NativeSimulcastSender.prototype.reset = function () {
386 386
     this._localExplosionMap = {};
387
-    this._isUsingScreenStream = isUsingScreenStream;
387
+    this._isUsingScreenStream = desktopsharing.isUsingScreenStream();
388 388
 };
389 389
 
390 390
 NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {

+ 0
- 3
libs/strophe/strophe.jingle.session.js 查看文件

@@ -920,9 +920,6 @@ JingleSession.prototype.switchStreams = function (new_stream, oldStream, success
920 920
 
921 921
     var self = this;
922 922
 
923
-    // Stop the stream to trigger onended event for old stream
924
-    oldStream.stop();
925
-
926 923
     // Remember SDP to figure out added/removed SSRCs
927 924
     var oldSdp = null;
928 925
     if(self.peerconnection) {

+ 29
- 4
modules/RTC/RTC.js 查看文件

@@ -24,7 +24,7 @@ var RTC = {
24 24
 
25 25
         eventEmitter.removeListener(eventType, listener);
26 26
     },
27
-    createLocalStream: function (stream, type) {
27
+    createLocalStream: function (stream, type, change) {
28 28
 
29 29
         var localStream =  new LocalStream(stream, type, eventEmitter);
30 30
         //in firefox we have only one stream object
@@ -39,8 +39,11 @@ var RTC = {
39 39
         {
40 40
             this.localVideo = localStream;
41 41
         }
42
-        eventEmitter.emit(StreamEventTypes.EVENT_TYPE_LOCAL_CREATED,
43
-            localStream);
42
+        var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED;
43
+        if(change)
44
+            eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED;
45
+
46
+        eventEmitter.emit(eventType, localStream);
44 47
         return localStream;
45 48
     },
46 49
     removeLocalStream: function (stream) {
@@ -96,6 +99,11 @@ var RTC = {
96 99
         this.dispose();
97 100
     },
98 101
     start: function () {
102
+        var self = this;
103
+        desktopsharing.addListener(
104
+            function (stream, isUsingScreenStream, callback) {
105
+                self.changeLocalVideo(stream, isUsingScreenStream, callback);
106
+            }, DesktopSharingEventTypes.NEW_STREAM_CREATED);
99 107
         this.rtcUtils = new RTCUtils(this);
100 108
         this.rtcUtils.obtainAudioAndVideoPermissions();
101 109
     },
@@ -126,9 +134,26 @@ var RTC = {
126 134
         this.localStreams = [];
127 135
 
128 136
         //in firefox we have only one stream object
129
-        if(this.localAudio.getOriginalStream() != new_stream)
137
+        if (this.localAudio.getOriginalStream() != new_stream)
130 138
             this.localStreams.push(this.localAudio);
131 139
         this.localStreams.push(this.localVideo);
140
+    },
141
+    changeLocalVideo: function (stream, isUsingScreenStream, callback) {
142
+        var oldStream = this.localVideo.getOriginalStream();
143
+        var type = (isUsingScreenStream? "desktop" : "video");
144
+        RTC.localVideo = this.createLocalStream(stream, type, true);
145
+        // Stop the stream to trigger onended event for old stream
146
+        oldStream.stop();
147
+        if (activecall) {
148
+            // FIXME: will block switchInProgress on true value in case of exception
149
+            activecall.switchStreams(stream, oldStream, callback);
150
+        } else {
151
+            // We are done immediately
152
+            console.error("No conference handler");
153
+            UI.messageHandler.showError('Error',
154
+                'Unable to switch video stream.');
155
+            callback();
156
+        }
132 157
     }
133 158
 
134 159
 };

+ 26
- 26
modules/UI/UI.js 查看文件

@@ -44,26 +44,28 @@ function setupToolbars() {
44 44
     BottomToolbar.init();
45 45
 }
46 46
 
47
+function streamHandler(stream) {
48
+    switch (stream.type)
49
+    {
50
+        case "audio":
51
+            VideoLayout.changeLocalAudio(stream);
52
+            break;
53
+        case "video":
54
+            VideoLayout.changeLocalVideo(stream);
55
+            break;
56
+        case "stream":
57
+            VideoLayout.changeLocalStream(stream);
58
+            break;
59
+        case "desktop":
60
+            VideoLayout.changeLocalVideo(stream);
61
+            break;
62
+    }
63
+}
47 64
 
48 65
 function registerListeners() {
49
-    RTC.addStreamListener(function (stream) {
50
-        switch (stream.type)
51
-        {
52
-            case "audio":
53
-                VideoLayout.changeLocalAudio(stream.getOriginalStream());
54
-                break;
55
-            case "video":
56
-                VideoLayout.changeLocalVideo(stream.getOriginalStream(), true);
57
-                break;
58
-            case "stream":
59
-                VideoLayout.changeLocalStream(stream.getOriginalStream());
60
-                break;
61
-            case "desktop":
62
-                VideoLayout.changeLocalVideo(stream, !isUsingScreenStream);
63
-                break;
64
-        }
65
-    }, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
66
+    RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
66 67
 
68
+    RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
67 69
     RTC.addStreamListener(function (stream) {
68 70
         VideoLayout.onRemoteStreamAdded(stream);
69 71
     }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
@@ -96,6 +98,13 @@ function registerListeners() {
96 98
         AudioLevels.updateAudioLevel(resourceJid, audioLevel,
97 99
             UI.getLargeVideoState().userResourceJid);
98 100
     });
101
+    desktopsharing.addListener(function () {
102
+        ToolbarToggler.showDesktopSharingButton();
103
+    }, DesktopSharingEventTypes.INIT);
104
+    desktopsharing.addListener(
105
+        Toolbar.changeDesktopSharingButtonState,
106
+        DesktopSharingEventTypes.SWITCHING_DONE);
107
+
99 108
 
100 109
 }
101 110
 
@@ -445,10 +454,6 @@ UI.setRecordingButtonState = function (state) {
445 454
     Toolbar.setRecordingButtonState(state);
446 455
 };
447 456
 
448
-UI.changeDesktopSharingButtonState = function (isUsingScreenStream) {
449
-    Toolbar.changeDesktopSharingButtonState(isUsingScreenStream);
450
-};
451
-
452 457
 UI.inputDisplayNameHandler = function (value) {
453 458
     VideoLayout.inputDisplayNameHandler(value);
454 459
 };
@@ -505,10 +510,6 @@ UI.showLocalAudioIndicator = function (mute) {
505 510
     VideoLayout.showLocalAudioIndicator(mute);
506 511
 };
507 512
 
508
-UI.changeLocalVideo = function (stream, flipx) {
509
-    VideoLayout.changeLocalVideo(stream, flipx);
510
-};
511
-
512 513
 UI.generateRoomName = function() {
513 514
     var roomnode = null;
514 515
     var path = window.location.pathname;
@@ -555,7 +556,6 @@ UI.dockToolbar = function (isDock) {
555 556
     return ToolbarToggler.dockToolbar(isDock);
556 557
 };
557 558
 
558
-
559 559
 function dump(elem, filename) {
560 560
     elem = elem.parentNode;
561 561
     elem.download = filename || 'meetlog.json';

+ 2
- 2
modules/UI/toolbars/Toolbar.js 查看文件

@@ -40,7 +40,7 @@ var buttonHandlers =
40 40
         return Etherpad.toggleEtherpad(0);
41 41
     },
42 42
     "toolbar_button_desktopsharing": function () {
43
-        return toggleScreenSharing();
43
+        return desktopsharing.toggleScreenSharing();
44 44
     },
45 45
     "toolbar_button_fullScreen": function()
46 46
     {
@@ -262,7 +262,7 @@ var Toolbar = (function (my) {
262 262
             inviteLink.select();
263 263
             document.getElementById('jqi_state0_buttonInvite').disabled = false;
264 264
         }
265
-    }
265
+    };
266 266
 
267 267
     /**
268 268
      * Disables and enables some of the buttons.

+ 12
- 2
modules/UI/toolbars/ToolbarToggler.js 查看文件

@@ -1,8 +1,16 @@
1
-/* global $, interfaceConfig, Moderator, showDesktopSharingButton */
1
+/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
2 2
 
3 3
 var toolbarTimeoutObject,
4 4
     toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
5 5
 
6
+function showDesktopSharingButton() {
7
+    if (desktopsharing.isDesktopSharingEnabled()) {
8
+        $('#desktopsharing').css({display: "inline"});
9
+    } else {
10
+        $('#desktopsharing').css({display: "none"});
11
+    }
12
+}
13
+
6 14
 /**
7 15
  * Hides the toolbar.
8 16
  */
@@ -97,7 +105,9 @@ var ToolbarToggler = {
97 105
                 toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
98 106
             }
99 107
         }
100
-    }
108
+    },
109
+
110
+    showDesktopSharingButton: showDesktopSharingButton
101 111
 
102 112
 };
103 113
 

+ 9
- 5
modules/UI/videolayout/VideoLayout.js 查看文件

@@ -414,11 +414,11 @@ var VideoLayout = (function (my) {
414 414
     };
415 415
 
416 416
     my.changeLocalStream = function (stream) {
417
-        VideoLayout.changeLocalVideo(stream, true);
417
+        VideoLayout.changeLocalVideo(stream);
418 418
     };
419 419
 
420 420
     my.changeLocalAudio = function(stream) {
421
-        RTC.attachMediaStream($('#localAudio'), stream);
421
+        RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
422 422
         document.getElementById('localAudio').autoplay = true;
423 423
         document.getElementById('localAudio').volume = 0;
424 424
         if (preMuted) {
@@ -427,9 +427,13 @@ var VideoLayout = (function (my) {
427 427
         }
428 428
     };
429 429
 
430
-    my.changeLocalVideo = function(stream, flipX) {
430
+    my.changeLocalVideo = function(stream) {
431
+        var flipX = true;
432
+        if(stream.type == "desktop")
433
+            flipX = false;
431 434
         var localVideo = document.createElement('video');
432
-        localVideo.id = 'localVideo_' + RTC.getStreamID(stream);
435
+        localVideo.id = 'localVideo_' +
436
+            RTC.getStreamID(stream.getOriginalStream());
433 437
         localVideo.autoplay = true;
434 438
         localVideo.volume = 0; // is it required if audio is separated ?
435 439
         localVideo.oncontextmenu = function () { return false; };
@@ -477,7 +481,7 @@ var VideoLayout = (function (my) {
477 481
             }
478 482
         );
479 483
         // Add stream ended handler
480
-        stream.onended = function () {
484
+        stream.getOriginalStream().onended = function () {
481 485
             localVideoContainer.removeChild(localVideo);
482 486
             VideoLayout.updateRemovedVideo(RTC.getVideoSrc(localVideo));
483 487
         };

desktopsharing.js → modules/desktopsharing/desktopsharing.js 查看文件

@@ -23,6 +23,10 @@ var obtainDesktopStream = null;
23 23
  */
24 24
 var _desktopSharingEnabled = null;
25 25
 
26
+var EventEmitter = require("events");
27
+
28
+var eventEmitter = new EventEmitter();
29
+
26 30
 /**
27 31
  * Method obtains desktop stream from WebRTC 'screen' source.
28 32
  * Flag 'chrome://flags/#enable-usermedia-screen-capture' must be enabled.
@@ -177,33 +181,6 @@ function obtainScreenFromExtension(streamCallback, failCallback) {
177 181
     );
178 182
 }
179 183
 
180
-/**
181
- * @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
182
- */
183
-function isDesktopSharingEnabled() {
184
-    if (_desktopSharingEnabled === null) {
185
-        if (obtainDesktopStream === obtainScreenFromExtension) {
186
-            // Parse chrome version
187
-            var userAgent = navigator.userAgent.toLowerCase();
188
-            // We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
189
-            var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
190
-            console.log("Chrome version" + userAgent, ver);
191
-            _desktopSharingEnabled = ver >= 34;
192
-        } else {
193
-            _desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
194
-        }
195
-    }
196
-    return _desktopSharingEnabled;
197
-}
198
-
199
-function showDesktopSharingButton() {
200
-    if (isDesktopSharingEnabled()) {
201
-        $('#desktopsharing').css({display: "inline"});
202
-    } else {
203
-        $('#desktopsharing').css({display: "none"});
204
-    }
205
-}
206
-
207 184
 /**
208 185
  * Call this method to toggle desktop sharing feature.
209 186
  * @param method pass "ext" to use chrome extension for desktop capture(chrome extension required),
@@ -225,8 +202,6 @@ function setDesktopSharing(method) {
225 202
 
226 203
     // Reset enabled cache
227 204
     _desktopSharingEnabled = null;
228
-
229
-    showDesktopSharingButton();
230 205
 }
231 206
 
232 207
 /**
@@ -244,73 +219,103 @@ function getSwitchStreamFailed(error) {
244 219
 }
245 220
 
246 221
 function streamSwitchDone() {
247
-    //window.setTimeout(
248
-    //    function () {
249 222
     switchInProgress = false;
250
-    UI.changeDesktopSharingButtonState(isUsingScreenStream);
251
-    //    }, 100
252
-    //);
223
+    eventEmitter.emit(
224
+        DesktopSharingEventTypes.SWITCHING_DONE,
225
+        isUsingScreenStream);
253 226
 }
254 227
 
255
-function newStreamCreated(stream) {
228
+function newStreamCreated(stream)
229
+{
230
+    eventEmitter.emit(DesktopSharingEventTypes.NEW_STREAM_CREATED,
231
+        stream, isUsingScreenStream, streamSwitchDone);
232
+}
256 233
 
257
-    var oldStream = RTC.localVideo.getOriginalStream();
258 234
 
259
-    RTC.localVideo.stream = stream;
235
+module.exports = {
236
+    isUsingScreenStream: function () {
237
+        return isUsingScreenStream;
238
+    },
260 239
 
261
-    UI.changeLocalVideo(stream, !isUsingScreenStream);
240
+    /**
241
+     * @returns {boolean} <tt>true</tt> if desktop sharing feature is available and enabled.
242
+     */
243
+    isDesktopSharingEnabled: function () {
244
+        if (_desktopSharingEnabled === null) {
245
+            if (obtainDesktopStream === obtainScreenFromExtension) {
246
+                // Parse chrome version
247
+                var userAgent = navigator.userAgent.toLowerCase();
248
+                // We can assume that user agent is chrome, because it's enforced when 'ext' streaming method is set
249
+                var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
250
+                console.log("Chrome version" + userAgent, ver);
251
+                _desktopSharingEnabled = ver >= 34;
252
+            } else {
253
+                _desktopSharingEnabled = obtainDesktopStream === obtainWebRTCScreen;
254
+            }
255
+        }
256
+        return _desktopSharingEnabled;
257
+    },
258
+    
259
+    init: function () {
260
+        setDesktopSharing(config.desktopSharing);
262 261
 
263
-    if (activecall) {
264
-        // FIXME: will block switchInProgress on true value in case of exception
265
-        activecall.switchStreams(stream, oldStream, streamSwitchDone);
266
-    } else {
267
-        // We are done immediately
268
-        console.error("No conference handler");
269
-        UI.messageHandler.showError('Error',
270
-            'Unable to switch video stream.');
271
-        streamSwitchDone();
272
-    }
273
-}
262
+        // Initialize Chrome extension inline installs
263
+        if (config.chromeExtensionId) {
264
+            initInlineInstalls();
265
+        }
274 266
 
275
-/*
276
- * Toggles screen sharing.
277
- */
278
-function toggleScreenSharing() {
279
-    if (switchInProgress || !obtainDesktopStream) {
280
-        console.warn("Switch in progress or no method defined");
281
-        return;
282
-    }
283
-    switchInProgress = true;
267
+        eventEmitter.emit(DesktopSharingEventTypes.INIT);
268
+    },
284 269
 
285
-    if (!isUsingScreenStream)
270
+    addListener: function(listener, type)
286 271
     {
287
-        // Switch to desktop stream
288
-        obtainDesktopStream(
289
-            function (stream) {
290
-                // We now use screen stream
291
-                isUsingScreenStream = true;
292
-                // Hook 'ended' event to restore camera when screen stream stops
293
-                stream.addEventListener('ended',
294
-                    function (e) {
295
-                        if (!switchInProgress && isUsingScreenStream) {
296
-                            toggleScreenSharing();
272
+        eventEmitter.on(type, listener);
273
+    },
274
+
275
+    removeListener: function (listener,type) {
276
+        eventEmitter.removeListener(type, listener);
277
+    },
278
+
279
+    /*
280
+     * Toggles screen sharing.
281
+     */
282
+    toggleScreenSharing: function () {
283
+        if (switchInProgress || !obtainDesktopStream) {
284
+            console.warn("Switch in progress or no method defined");
285
+            return;
286
+        }
287
+        switchInProgress = true;
288
+
289
+        if (!isUsingScreenStream)
290
+        {
291
+            // Switch to desktop stream
292
+            obtainDesktopStream(
293
+                function (stream) {
294
+                    // We now use screen stream
295
+                    isUsingScreenStream = true;
296
+                    // Hook 'ended' event to restore camera when screen stream stops
297
+                    stream.addEventListener('ended',
298
+                        function (e) {
299
+                            if (!switchInProgress && isUsingScreenStream) {
300
+                                toggleScreenSharing();
301
+                            }
297 302
                         }
298
-                    }
299
-                );
300
-                newStreamCreated(stream);
301
-            },
302
-            getSwitchStreamFailed);
303
-    } else {
304
-        // Disable screen stream
305
-        RTC.getUserMediaWithConstraints(
306
-            ['video'],
307
-            function (stream) {
308
-                // We are now using camera stream
309
-                isUsingScreenStream = false;
310
-                newStreamCreated(stream);
311
-            },
312
-            getSwitchStreamFailed, config.resolution || '360'
313
-        );
303
+                    );
304
+                    newStreamCreated(stream);
305
+                },
306
+                getSwitchStreamFailed);
307
+        } else {
308
+            // Disable screen stream
309
+            RTC.getUserMediaWithConstraints(
310
+                ['video'],
311
+                function (stream) {
312
+                    // We are now using camera stream
313
+                    isUsingScreenStream = false;
314
+                    newStreamCreated(stream);
315
+                },
316
+                getSwitchStreamFailed, config.resolution || '360'
317
+            );
318
+        }
314 319
     }
315
-}
320
+};
316 321
 

+ 1
- 1
modules/simulcast/SimulcastSender.js 查看文件

@@ -47,7 +47,7 @@ NativeSimulcastSender.prototype._localVideoSourceCache = '';
47 47
 
48 48
 NativeSimulcastSender.prototype.reset = function () {
49 49
     this._localExplosionMap = {};
50
-    this._isUsingScreenStream = isUsingScreenStream;
50
+    this._isUsingScreenStream = desktopsharing.isUsingScreenStream();
51 51
 };
52 52
 
53 53
 NativeSimulcastSender.prototype._cacheLocalVideoSources = function (lines) {

+ 2
- 0
service/RTC/StreamEventTypes.js 查看文件

@@ -1,6 +1,8 @@
1 1
 var StreamEventTypes = {
2 2
     EVENT_TYPE_LOCAL_CREATED: "stream.local_created",
3 3
 
4
+    EVENT_TYPE_LOCAL_CHANGED: "stream.local_changed",
5
+
4 6
     EVENT_TYPE_LOCAL_ENDED: "stream.local_ended",
5 7
 
6 8
     EVENT_TYPE_REMOTE_CREATED: "stream.remote_created",

+ 10
- 0
service/desktopsharing/DesktopSharingEventTypes.js 查看文件

@@ -0,0 +1,10 @@
1
+var DesktopSharingEventTypes = {
2
+    INIT: "ds.init",
3
+
4
+    SWITCHING_DONE: "ds.switching_done",
5
+
6
+    NEW_STREAM_CREATED: "ds.new_stream_created"
7
+};
8
+
9
+//These lines should be uncommented when require works in app.js
10
+//module.exports = NEW_STREAM_CREATED;

Loading…
取消
儲存