Ver código fonte

Adds IE support through Temasys WebRTC plugin.

master
paweldomas 10 anos atrás
pai
commit
ae759fab5b

+ 1
- 4
app.js Ver arquivo

@@ -21,14 +21,11 @@ var APP =
21 21
 
22 22
 function init() {
23 23
 
24
+    APP.desktopsharing.init();
24 25
     APP.RTC.start();
25 26
     APP.xmpp.start();
26 27
     APP.statistics.start();
27 28
     APP.connectionquality.init();
28
-
29
-    // Set default desktop sharing method
30
-    APP.desktopsharing.init();
31
-
32 29
     APP.keyboardshortcut.init();
33 30
     APP.members.start();
34 31
 }

+ 15
- 9
css/videolayout_default.css Ver arquivo

@@ -60,28 +60,31 @@
60 60
 }
61 61
 
62 62
 #remoteVideos .videocontainer:hover {
63
-    -webkit-box-shadow: inset 0 0 10px #FFFFFF, 0 0 10px #FFFFFF;
63
+    box-shadow:  inset 0 0 10px #FFFFFF, 0 0 10px #FFFFFF;
64 64
     border: 2px solid #FFFFFF;
65 65
 }
66 66
 
67 67
 #remoteVideos .videocontainer.videoContainerFocused {
68
-    -webkit-box-shadow: inset 0 0 28px #006d91;
68
+    box-shadow: inset 0 0 28px #006d91;
69 69
     border: 2px solid #006d91;
70 70
 }
71 71
 
72 72
 #remoteVideos .videocontainer.videoContainerFocused:hover {
73
-    -webkit-box-shadow: inset 0 0 5px #FFFFFF, 0 0 10px #FFFFFF, inset 0 0 60px #006d91;
73
+    box-shadow: inset 0 0 5px #FFFFFF, 0 0 10px #FFFFFF, inset 0 0 60px #006d91;
74 74
     border: 2px solid #FFFFFF;
75 75
 }
76 76
 
77 77
 #localVideoWrapper {
78 78
     display:inline-block;
79 79
     -webkit-mask-box-image: url(../images/videomask.svg);
80
-    border-radius:0px !important;
80
+    border-radius:4px !important;
81 81
     border: 0px !important;
82 82
 }
83 83
 
84
-#remoteVideos .videocontainer>video {
84
+/* With TemasysWebRTC plugin <object/> element is used
85
+   instead of <video/> */
86
+#remoteVideos .videocontainer>video,
87
+#remoteVideos .videocontainer>object {
85 88
     border-radius:4px;
86 89
 }
87 90
 
@@ -92,8 +95,9 @@
92 95
     -o-transform: scale(-1, 1);
93 96
 }
94 97
 
95
-#localVideoWrapper>video {
96
-    border-radius:0px !important;
98
+#localVideoWrapper>video,
99
+#localVideoWrapper>object {
100
+    border-radius:4px !important;
97 101
 }
98 102
 
99 103
 #largeVideo,
@@ -110,8 +114,10 @@
110 114
 #presentation,
111 115
 #etherpad,
112 116
 #localVideoWrapper>video,
117
+#localVideoWrapper>object,
113 118
 #localVideoWrapper,
114
-.videocontainer>video {
119
+.videocontainer>video,
120
+.videocontainer>object {
115 121
     position: absolute;
116 122
     left: 0;
117 123
     top: 0;
@@ -363,7 +369,7 @@
363 369
     margin-right: 40%;
364 370
     text-align: center;
365 371
     background: linear-gradient(to bottom, rgba(255,255,255,.85) , rgba(255,255,255,.35));
366
-    -webkit-box-shadow: 0 0 2px #000000, 0 0 10px #000000;
372
+    box-shadow: 0 0 2px #000000, 0 0 10px #000000;
367 373
     border-bottom-left-radius: 12px;
368 374
     border-bottom-right-radius: 12px;
369 375
     display: none;

+ 2
- 2
index.html Ver arquivo

@@ -22,12 +22,12 @@
22 22
     <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
23 23
     <script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
24 24
     <script src="interface_config.js?v=5"></script>
25
-    <script src="libs/app.bundle.js?v=98"></script>
25
+    <script src="libs/app.bundle.js?v=99"></script>
26 26
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
27 27
     <link rel="stylesheet" href="css/font.css?v=7"/>
28 28
     <link rel="stylesheet" href="css/toastr.css?v=1">
29 29
     <link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=30"/>
30
-    <link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=17" id="videolayout_default"/>
30
+    <link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=18" id="videolayout_default"/>
31 31
     <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
32 32
     <link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
33 33
     <link rel="stylesheet" href="css/modaldialog.css?v=3">

+ 2049
- 338
libs/app.bundle.js
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 25
- 7
modules/RTC/RTC.js Ver arquivo

@@ -1,4 +1,5 @@
1 1
 var EventEmitter = require("events");
2
+var RTCBrowserType = require("./RTCBrowserType");
2 3
 var RTCUtils = require("./RTCUtils.js");
3 4
 var LocalStream = require("./LocalStream.js");
4 5
 var DataChannels = require("./DataChannels");
@@ -99,7 +100,7 @@ var RTC = {
99 100
     },
100 101
     createRemoteStream: function (data, sid, thessrc) {
101 102
         var remoteStream = new MediaStream(data, sid, thessrc,
102
-            this.getBrowserType(), eventEmitter);
103
+            RTCBrowserType.getBrowserType(), eventEmitter);
103 104
         var jid = data.peerjid || APP.xmpp.myJid();
104 105
         if(!this.remoteStreams[jid]) {
105 106
             this.remoteStreams[jid] = {};
@@ -108,9 +109,6 @@ var RTC = {
108 109
         eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream);
109 110
         return remoteStream;
110 111
     },
111
-    getBrowserType: function () {
112
-        return this.rtcUtils.browser;
113
-    },
114 112
     getPCConstraints: function () {
115 113
         return this.rtcUtils.pc_constraints;
116 114
     },
@@ -133,6 +131,9 @@ var RTC = {
133 131
     setVideoSrc: function (element, src) {
134 132
         this.rtcUtils.setVideoSrc(element, src);
135 133
     },
134
+    getVideoElementName: function () {
135
+        return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video';
136
+    },
136 137
     dispose: function() {
137 138
         if (this.rtcUtils) {
138 139
             this.rtcUtils = null;
@@ -168,9 +169,22 @@ var RTC = {
168 169
             DataChannels.handleSelectedEndpointEvent);
169 170
         APP.UI.addListener(UIEvents.PINNED_ENDPOINT,
170 171
             DataChannels.handlePinnedEndpointEvent);
171
-        this.rtcUtils = new RTCUtils(this);
172
-        this.rtcUtils.obtainAudioAndVideoPermissions(
173
-            null, null, getMediaStreamUsage());
172
+
173
+        // In case of IE we continue from 'onReady' callback
174
+        // passed to RTCUtils constructor. It will be invoked by Temasys plugin
175
+        // once it is initialized.
176
+        var onReady = function () {
177
+            eventEmitter.emit(RTCEvents.RTC_READY, true);
178
+            self.rtcUtils.obtainAudioAndVideoPermissions(
179
+                null, null, getMediaStreamUsage());
180
+        };
181
+
182
+        this.rtcUtils = new RTCUtils(this, onReady);
183
+
184
+        // Call onReady() if Temasys plugin is not used
185
+        if (!RTCBrowserType.isTemasysPluginUsed()) {
186
+            onReady();
187
+        }
174 188
     },
175 189
     muteRemoteVideoStream: function (jid, value) {
176 190
         var stream;
@@ -211,6 +225,10 @@ var RTC = {
211 225
                 callback();
212 226
             };
213 227
         }
228
+        // FIXME: Workaround for FF/IE/Safari
229
+        if (stream && stream.videoStream) {
230
+            stream = stream.videoStream;
231
+        }
214 232
         var videoStream = this.rtcUtils.createStream(stream, true);
215 233
         this.localVideo = this.createLocalStream(videoStream, "video", true, type);
216 234
         // Stop the stream to trigger onended event for old stream

+ 152
- 0
modules/RTC/RTCBrowserType.js Ver arquivo

@@ -0,0 +1,152 @@
1
+
2
+var currentBrowser;
3
+
4
+var browserVersion;
5
+
6
+var RTCBrowserType = {
7
+
8
+    RTC_BROWSER_CHROME: "rtc_browser.chrome",
9
+
10
+    RTC_BROWSER_OPERA: "rtc_browser.opera",
11
+
12
+    RTC_BROWSER_FIREFOX: "rtc_browser.firefox",
13
+
14
+    RTC_BROWSER_IEXPLORER: "rtc_browser.iexplorer",
15
+
16
+    RTC_BROWSER_SAFARI: "rtc_browser.safari",
17
+
18
+    getBrowserType: function () {
19
+        return currentBrowser;
20
+    },
21
+
22
+    isChrome: function () {
23
+        return currentBrowser === RTCBrowserType.RTC_BROWSER_CHROME;
24
+    },
25
+
26
+    isOpera: function () {
27
+        return currentBrowser === RTCBrowserType.RTC_BROWSER_OPERA;
28
+    },
29
+    isFirefox: function () {
30
+        return currentBrowser === RTCBrowserType.RTC_BROWSER_FIREFOX;
31
+    },
32
+
33
+    isIExplorer: function () {
34
+        return currentBrowser === RTCBrowserType.RTC_BROWSER_IEXPLORER;
35
+    },
36
+
37
+    isSafari: function () {
38
+        return currentBrowser === RTCBrowserType.RTC_BROWSER_SAFARI;
39
+    },
40
+    isTemasysPluginUsed: function () {
41
+        return RTCBrowserType.isIExplorer() || RTCBrowserType.isSafari();
42
+    },
43
+    getFirefoxVersion: function () {
44
+        return RTCBrowserType.isFirefox() ? browserVersion : null;
45
+    },
46
+
47
+    getChromeVersion: function () {
48
+        return RTCBrowserType.isChrome() ? browserVersion : null;
49
+    }
50
+
51
+    // Add version getters for other browsers when needed
52
+};
53
+
54
+// detectOpera() must be called before detectChrome() !!!
55
+// otherwise Opera wil be detected as Chrome
56
+function detectChrome() {
57
+    if (navigator.webkitGetUserMedia) {
58
+        currentBrowser = RTCBrowserType.RTC_BROWSER_CHROME;
59
+        var userAgent = navigator.userAgent.toLowerCase();
60
+        // We can assume that user agent is chrome, because it's
61
+        // enforced when 'ext' streaming method is set
62
+        var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
63
+        console.log("This appears to be Chrome, ver: " + ver);
64
+        return ver;
65
+    }
66
+    return null;
67
+}
68
+
69
+function detectOpera() {
70
+    var userAgent = navigator.userAgent;
71
+    if (userAgent.match(/Opera|OPR/)) {
72
+        currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA;
73
+        var version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2];
74
+        console.info("This appears to be Opera, ver: " + version);
75
+        return version;
76
+    }
77
+    return null;
78
+}
79
+
80
+function detectFirefox() {
81
+    if (navigator.mozGetUserMedia) {
82
+        currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX;
83
+        var version = parseInt(
84
+            navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
85
+        console.log('This appears to be Firefox, ver: ' + version);
86
+        return version;
87
+    }
88
+    return null;
89
+}
90
+
91
+function detectSafari() {
92
+    if ((/^((?!chrome).)*safari/i.test(navigator.userAgent))) {
93
+        currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI;
94
+        console.info("This appears to be Safari");
95
+        // FIXME detect Safari version when needed
96
+        return 1;
97
+    }
98
+    return null;
99
+}
100
+
101
+function detectIE() {
102
+    var version;
103
+    var ua = window.navigator.userAgent;
104
+
105
+    var msie = ua.indexOf('MSIE ');
106
+    if (msie > 0) {
107
+        // IE 10 or older => return version number
108
+        version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
109
+    }
110
+
111
+    var trident = ua.indexOf('Trident/');
112
+    if (!version && trident > 0) {
113
+        // IE 11 => return version number
114
+        var rv = ua.indexOf('rv:');
115
+        version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
116
+    }
117
+
118
+    var edge = ua.indexOf('Edge/');
119
+    if (!version && edge > 0) {
120
+        // IE 12 => return version number
121
+        version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
122
+    }
123
+
124
+    if (version) {
125
+        currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER;
126
+        console.info("This appears to be IExplorer, ver: " + version);
127
+    }
128
+    return version;
129
+}
130
+
131
+function detectBrowser() {
132
+    var version;
133
+    var detectors = [
134
+        detectOpera,
135
+        detectChrome,
136
+        detectFirefox,
137
+        detectIE,
138
+        detectSafari
139
+    ];
140
+    // Try all browser detectors
141
+    for (var i = 0; i < detectors.length; i++) {
142
+        version = detectors[i]();
143
+        if (version)
144
+            return version;
145
+    }
146
+    console.error("Failed to detect browser type");
147
+    return undefined;
148
+}
149
+
150
+browserVersion = detectBrowser();
151
+
152
+module.exports = RTCBrowserType;

+ 166
- 72
modules/RTC/RTCUtils.js Ver arquivo

@@ -1,5 +1,7 @@
1
-var RTCBrowserType = require("../../service/RTC/RTCBrowserType.js");
1
+var RTCBrowserType = require("./RTCBrowserType");
2 2
 var Resolutions = require("../../service/RTC/Resolutions");
3
+var AdapterJS = require("./adapter.screenshare");
4
+var SDPUtil = require("../xmpp/SDPUtil");
3 5
 
4 6
 var currentResolution = null;
5 7
 
@@ -58,16 +60,30 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid
58 60
         constraints.audio = { mandatory: {}, optional: []};// same behaviour as true
59 61
     }
60 62
     if (um.indexOf('screen') >= 0) {
61
-        constraints.video = {
62
-            mandatory: {
63
-                chromeMediaSource: "screen",
64
-                googLeakyBucket: true,
65
-                maxWidth: window.screen.width,
66
-                maxHeight: window.screen.height,
67
-                maxFrameRate: 3
68
-            },
69
-            optional: []
70
-        };
63
+        if (RTCBrowserType.isChrome()) {
64
+            constraints.video = {
65
+                mandatory: {
66
+                    chromeMediaSource: "screen",
67
+                    googLeakyBucket: true,
68
+                    maxWidth: window.screen.width,
69
+                    maxHeight: window.screen.height,
70
+                    maxFrameRate: 3
71
+                },
72
+                optional: []
73
+            };
74
+        } else if (RTCBrowserType.isTemasysPluginUsed()) {
75
+            constraints.video = {
76
+                optional: [
77
+                    {
78
+                        sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey
79
+                    }
80
+                ]
81
+            };
82
+        } else {
83
+            console.error(
84
+                "'screen' WebRTC media source is supported only in Chrome" +
85
+                " and with Temasys plugin");
86
+        }
71 87
     }
72 88
     if (um.indexOf('desktop') >= 0) {
73 89
         constraints.video = {
@@ -124,16 +140,14 @@ function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid
124 140
 }
125 141
 
126 142
 
127
-function RTCUtils(RTCService)
143
+function RTCUtils(RTCService, onTemasysPluginReady)
128 144
 {
145
+    var self = this;
129 146
     this.service = RTCService;
130
-    if (navigator.mozGetUserMedia) {
131
-        console.log('This appears to be Firefox');
132
-        var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
133
-        if (version >= 40
134
-            && config.useBundle && config.useRtcpMux) {
147
+    if (RTCBrowserType.isFirefox()) {
148
+        var FFversion = RTCBrowserType.getFirefoxVersion();
149
+        if (FFversion >= 40 && config.useBundle && config.useRtcpMux) {
135 150
             this.peerconnection = mozRTCPeerConnection;
136
-            this.browser = RTCBrowserType.RTC_BROWSER_FIREFOX;
137 151
             this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
138 152
             this.pc_constraints = {};
139 153
             this.attachMediaStream =  function (element, stream) {
@@ -155,7 +169,7 @@ function RTCUtils(RTCService)
155 169
                 {
156 170
                     tracks = stream.getAudioTracks();
157 171
                 }
158
-                return tracks[0].id.replace(/[\{,\}]/g,"");
172
+                return SDPUtil.filter_special_chars(tracks[0].id);
159 173
             };
160 174
             this.getVideoSrc = function (element) {
161 175
                 if(!element)
@@ -169,14 +183,16 @@ function RTCUtils(RTCService)
169 183
             RTCSessionDescription = mozRTCSessionDescription;
170 184
             RTCIceCandidate = mozRTCIceCandidate;
171 185
         } else {
186
+            console.error(
187
+                "Firefox requirements not met, ver: " + FFversion +
188
+                ", bundle: " + config.useBundle +
189
+                ", rtcp-mux: " + config.useRtcpMux);
172 190
             window.location.href = 'unsupported_browser.html';
173 191
             return;
174 192
         }
175 193
 
176
-    } else if (navigator.webkitGetUserMedia) {
177
-        console.log('This appears to be Chrome');
194
+    } else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) {
178 195
         this.peerconnection = webkitRTCPeerConnection;
179
-        this.browser = RTCBrowserType.RTC_BROWSER_CHROME;
180 196
         this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
181 197
         this.attachMediaStream = function (element, stream) {
182 198
             element.attr('src', webkitURL.createObjectURL(stream));
@@ -184,7 +200,7 @@ function RTCUtils(RTCService)
184 200
         this.getStreamID = function (stream) {
185 201
             // streams from FF endpoints have the characters '{' and '}'
186 202
             // that make jQuery choke.
187
-            return stream.id.replace(/[\{,\}]/g,"");
203
+            return SDPUtil.filter_special_chars(stream.id);
188 204
         };
189 205
         this.getVideoSrc = function (element) {
190 206
             if(!element)
@@ -211,12 +227,62 @@ function RTCUtils(RTCService)
211 227
             };
212 228
         }
213 229
     }
214
-    else
215
-    {
216
-        try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
230
+    // Detect IE/Safari
231
+    else if (RTCBrowserType.isTemasysPluginUsed()) {
232
+
233
+        AdapterJS.WebRTCPlugin.setLogLevel(
234
+            AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS.VERBOSE);
217 235
 
236
+        AdapterJS.webRTCReady(function (isPlugin) {
237
+
238
+            self.peerconnection = RTCPeerConnection;
239
+            self.getUserMedia = getUserMedia;
240
+            self.attachMediaStream = function (element, stream) {
241
+
242
+                if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
243
+                    return;
244
+                }
245
+
246
+                attachMediaStream(element[0], stream);
247
+            };
248
+            self.getStreamID = function (stream) {
249
+                var id = SDPUtil.filter_special_chars(stream.label);
250
+                return id;
251
+            };
252
+            self.getVideoSrc = function (element) {
253
+                if (!element) {
254
+                    console.warn("Attempt to get video SRC of null element");
255
+                    return null;
256
+                }
257
+                var src = null;
258
+                var children = element.children;
259
+                for (var i = 0; i !== children.length; ++i) {
260
+                    if (children[i].name === 'streamId') {
261
+                        return children[i].value;
262
+                    }
263
+                }
264
+                //console.info(element.id + " SRC: " + src);
265
+                return null;
266
+            };
267
+            self.setVideoSrc = function (element, src) {
268
+                //console.info("Set video src: ", element, src);
269
+                if (!src) {
270
+                    console.warn("Not attaching video stream, 'src' is null");
271
+                    return;
272
+                }
273
+                AdapterJS.WebRTCPlugin.WaitForPluginReady();
274
+                var stream = AdapterJS.WebRTCPlugin.plugin
275
+                    .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, src);
276
+                attachMediaStream(element, stream);
277
+            };
278
+
279
+            onTemasysPluginReady(isPlugin);
280
+        });
281
+    } else {
282
+        try {
283
+            console.log('Browser does not appear to be WebRTC-capable');
284
+        } catch (e) { }
218 285
         window.location.href = 'unsupported_browser.html';
219
-        return;
220 286
     }
221 287
 }
222 288
 
@@ -232,7 +298,7 @@ RTCUtils.prototype.getUserMediaWithConstraints = function(
232 298
     var constraints = getConstraints(
233 299
         um, resolution, bandwidth, fps, desktopStream, isAndroid);
234 300
 
235
-    var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
301
+    console.info("Get media constraints", constraints);
236 302
 
237 303
     var self = this;
238 304
 
@@ -311,9 +377,9 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
311 377
         return;
312 378
     }
313 379
 
314
-    if (navigator.mozGetUserMedia) {
380
+    if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) {
315 381
 
316
-        // With FF we can't split the stream into audio and video because FF
382
+        // With FF/IE we can't split the stream into audio and video because FF
317 383
         // doesn't support media stream constructors. So, we need to get the
318 384
         // audio stream separately from the video stream using two distinct GUM
319 385
         // calls. Not very user friendly :-( but we don't have many other
@@ -321,31 +387,41 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
321 387
         //
322 388
         // Note that we pack those 2 streams in a single object and pass it to
323 389
         // the successCallback method.
324
-
325
-        self.getUserMediaWithConstraints(
326
-            ['audio'],
327
-            function (audioStream) {
328
-                self.getUserMediaWithConstraints(
329
-                    ['video'],
330
-                    function (videoStream) {
331
-                        return self.successCallback({
332
-                            audioStream: audioStream,
333
-                            videoStream: videoStream
334
-                        });
335
-                    },
336
-                    function (error) {
337
-                        console.error('failed to obtain video stream - stop',
338
-                            error);
339
-                        return self.successCallback(null);
340
-                    },
341
-                    config.resolution || '360');
342
-            },
343
-            function (error) {
344
-                console.error('failed to obtain audio stream - stop',
345
-                        error);
346
-                return self.successCallback(null);
347
-            }
348
-        );
390
+        var obtainVideo = function (audioStream) {
391
+            self.getUserMediaWithConstraints(
392
+                ['video'],
393
+                function (videoStream) {
394
+                    return successCallback({
395
+                        audioStream: audioStream,
396
+                        videoStream: videoStream
397
+                    });
398
+                },
399
+                function (error) {
400
+                    console.error(
401
+                        'failed to obtain video stream - stop', error);
402
+                    self.errorCallback(error);
403
+                },
404
+                config.resolution || '360');
405
+        };
406
+        var obtainAudio = function () {
407
+            self.getUserMediaWithConstraints(
408
+                ['audio'],
409
+                function (audioStream) {
410
+                    if (newDevices.indexOf('video') !== -1)
411
+                        obtainVideo(audioStream);
412
+                },
413
+                function (error) {
414
+                    console.error(
415
+                        'failed to obtain audio stream - stop', error);
416
+                    self.errorCallback(error);
417
+                }
418
+            );
419
+        };
420
+        if (newDevices.indexOf('audio') !== -1) {
421
+            obtainAudio();
422
+        } else {
423
+            obtainVideo(null);
424
+        }
349 425
     } else {
350 426
         this.getUserMediaWithConstraints(
351 427
         newDevices,
@@ -358,12 +434,12 @@ RTCUtils.prototype.obtainAudioAndVideoPermissions =
358 434
         config.resolution || '360');
359 435
     }
360 436
 
361
-}
437
+};
362 438
 
363 439
 RTCUtils.prototype.successCallback = function (stream, usageOptions) {
364
-    // If this is FF, the stream parameter is *not* a MediaStream object, it's
365
-    // an object with two properties: audioStream, videoStream.
366
-    if(stream && !navigator.mozGetUserMedia)
440
+    // If this is FF or IE, the stream parameter is *not* a MediaStream object,
441
+    // it's an object with two properties: audioStream, videoStream.
442
+    if (stream && stream.getAudioTracks && stream.getVideoTracks)
367 443
         console.log('got', stream, stream.getAudioTracks().length,
368 444
             stream.getVideoTracks().length);
369 445
     this.handleLocalStream(stream, usageOptions);
@@ -427,10 +503,17 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
427 503
             }
428 504
         }
429 505
     }
430
-    else
431
-    {//firefox
432
-        audioStream = stream.audioStream;
433
-        videoStream = stream.videoStream;
506
+    else if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed())
507
+    {   // Firefox and Temasys plugin
508
+        if (stream && stream.audioStream)
509
+            audioStream = stream.audioStream;
510
+        else
511
+            audioStream = new DummyMediaStream("dummyAudio");
512
+
513
+        if (stream && stream.videoStream)
514
+            videoStream = stream.videoStream;
515
+        else
516
+            videoStream = new DummyMediaStream("dummyVideo");
434 517
     }
435 518
 
436 519
     var audioMuted = (usageOptions && usageOptions.audio === false),
@@ -447,15 +530,20 @@ RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
447 530
         videoMuted, videoGUM);
448 531
 };
449 532
 
450
-RTCUtils.prototype.createStream = function(stream, isVideo)
451
-{
533
+function DummyMediaStream(id) {
534
+    this.id = id;
535
+    this.label = id;
536
+    this.stop = function() { };
537
+    this.getAudioTracks = function() { return []; }
538
+    this.getVideoTracks = function() { return []; }
539
+}
540
+
541
+RTCUtils.prototype.createStream = function(stream, isVideo) {
452 542
     var newStream = null;
453
-    if(window.webkitMediaStream)
454
-    {
543
+    if (window.webkitMediaStream) {
455 544
         newStream = new webkitMediaStream();
456
-        if(newStream)
457
-        {
458
-            var tracks = (isVideo? stream.getVideoTracks() : stream.getAudioTracks());
545
+        if (newStream) {
546
+            var tracks = (isVideo ? stream.getVideoTracks() : stream.getAudioTracks());
459 547
 
460 548
             for (i = 0; i < tracks.length; i++) {
461 549
                 newStream.addTrack(tracks[i]);
@@ -463,8 +551,14 @@ RTCUtils.prototype.createStream = function(stream, isVideo)
463 551
         }
464 552
 
465 553
     }
466
-    else
467
-        newStream = stream;
554
+    else {
555
+        // FIXME: this is duplicated with 'handleLocalStream' !!!
556
+        if (stream) {
557
+            newStream = stream;
558
+        } else {
559
+            newStream = new DummyMediaStream(isVideo ? "dummyVideo" : "dummyAudio");
560
+        }
561
+    }
468 562
 
469 563
     return newStream;
470 564
 };

+ 1310
- 0
modules/RTC/adapter.screenshare.js
Diferenças do arquivo suprimidas por serem muito extensas
Ver arquivo


+ 5
- 2
modules/UI/UI.js Ver arquivo

@@ -24,6 +24,7 @@ var CQEvents = require("../../service/connectionquality/CQEvents");
24 24
 var DesktopSharingEventTypes
25 25
     = require("../../service/desktopsharing/DesktopSharingEventTypes");
26 26
 var RTCEvents = require("../../service/RTC/RTCEvents");
27
+var RTCBrowserType = require("../RTC/RTCBrowserType");
27 28
 var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
28 29
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
29 30
 var MemberEvents = require("../../service/members/Events");
@@ -253,7 +254,7 @@ function registerListeners() {
253 254
         {
254 255
             // might need to update the direction if participant just went from sendrecv to recvonly
255 256
             if (stream.type === 'video' || stream.type === 'screen') {
256
-                var el = $('#participant_'  + Strophe.getResourceFromJid(jid) + '>video');
257
+                var el = $('#participant_' + Strophe.getResourceFromJid(jid) + '>' + APP.RTC.getVideoElementName());
257 258
                 switch (stream.direction) {
258 259
                     case 'sendrecv':
259 260
                         el.show();
@@ -405,7 +406,9 @@ UI.start = function (init) {
405 406
         $('#notice').css({display: 'block'});
406 407
     }
407 408
 
408
-    document.getElementById('largeVideo').volume = 0;
409
+    if (!RTCBrowserType.isIExplorer()) {
410
+        document.getElementById('largeVideo').volume = 0;
411
+    }
409 412
 
410 413
     if(config.requireDisplayName) {
411 414
         var currentSettings = Settings.getSettings();

+ 4
- 1
modules/UI/videolayout/ConnectionIndicator.js Ver arquivo

@@ -336,7 +336,10 @@ ConnectionIndicator.prototype.create = function () {
336 336
  */
337 337
 ConnectionIndicator.prototype.remove = function()
338 338
 {
339
-    this.connectionIndicatorContainer.remove();
339
+    if (this.connectionIndicatorContainer.parentNode) {
340
+        this.connectionIndicatorContainer.parentNode.removeChild(
341
+            this.connectionIndicatorContainer);
342
+    }
340 343
     this.popover.forceHide();
341 344
 
342 345
 };

+ 18
- 14
modules/UI/videolayout/LargeVideo.js Ver arquivo

@@ -1,9 +1,11 @@
1 1
 var Avatar = require("../avatar/Avatar");
2
+var RTCBrowserType = require("../../RTC/RTCBrowserType");
2 3
 var UIUtil = require("../util/UIUtil");
3 4
 var UIEvents = require("../../../service/UI/UIEvents");
4 5
 var xmpp = require("../../xmpp/xmpp");
5 6
 
6
-var video = $('#largeVideo');
7
+// FIXME: With Temasys we have to re-select everytime
8
+//var video = $('#largeVideo');
7 9
 
8 10
 var currentVideoWidth = null;
9 11
 var currentVideoHeight = null;
@@ -244,9 +246,7 @@ function changeVideo(isVisible) {
244 246
     }
245 247
 
246 248
     if (isVisible) {
247
-        // using "this" should be ok because we're called
248
-        // from within the fadeOut event.
249
-        $(this).fadeIn(300);
249
+        $('#largeVideo').fadeIn(300);
250 250
     }
251 251
 
252 252
     if(oldSmallVideo)
@@ -260,12 +260,16 @@ var LargeVideo = {
260 260
         this.eventEmitter = emitter;
261 261
         var self = this;
262 262
         // Listen for large video size updates
263
-        document.getElementById('largeVideo')
264
-            .addEventListener('loadedmetadata', function (e) {
265
-                currentVideoWidth = this.videoWidth;
266
-                currentVideoHeight = this.videoHeight;
267
-                self.position(currentVideoWidth, currentVideoHeight);
268
-            });
263
+        var largeVideo = $('#largeVideo')[0];
264
+        var onplaying = function (arg1, arg2, arg3) {
265
+            // re-select
266
+            if (RTCBrowserType.isTemasysPluginUsed())
267
+                largeVideo = $('#largeVideo')[0];
268
+            currentVideoWidth = largeVideo.videoWidth;
269
+            currentVideoHeight = largeVideo.videoHeight;
270
+            self.position(currentVideoWidth, currentVideoHeight);
271
+        };
272
+        largeVideo.onplaying = onplaying;
269 273
     },
270 274
     /**
271 275
      * Indicates if the large video is currently visible.
@@ -273,14 +277,14 @@ var LargeVideo = {
273 277
      * @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
274 278
      */
275 279
     isLargeVideoVisible: function() {
276
-        return video.is(':visible');
280
+        return $('#largeVideo').is(':visible');
277 281
     },
278 282
     /**
279 283
      * Updates the large video with the given new video source.
280 284
      */
281 285
     updateLargeVideo: function(resourceJid, forceUpdate) {
282
-        console.log('hover in', resourceJid);
283 286
         var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
287
+        console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
284 288
 
285 289
         if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
286 290
             || forceUpdate) {
@@ -303,8 +307,8 @@ var LargeVideo = {
303 307
                 this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
304 308
                     resourceJid);
305 309
             }
306
-
307
-            video.fadeOut(300, changeVideo.bind(video, this.isLargeVideoVisible()));
310
+            $('#largeVideo').fadeOut(300,
311
+                changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
308 312
         } else {
309 313
             if(currentSmallVideo) {
310 314
                 currentSmallVideo.showAvatar();

+ 18
- 5
modules/UI/videolayout/LocalVideo.js Ver arquivo

@@ -3,6 +3,7 @@ var ConnectionIndicator = require("./ConnectionIndicator");
3 3
 var NicknameHandler = require("../util/NicknameHandler");
4 4
 var UIUtil = require("../util/UIUtil");
5 5
 var LargeVideo = require("./LargeVideo");
6
+var RTCBrowserType = require("../../RTC/RTCBrowserType");
6 7
 
7 8
 function LocalVideo(VideoLayout)
8 9
 {
@@ -163,13 +164,18 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
163 164
     var self = this;
164 165
 
165 166
     function localVideoClick(event) {
166
-        event.stopPropagation();
167
+        // FIXME: with Temasys plugin event arg is not an event, but
168
+        // the clicked object itself, so we have to skip this call
169
+        if (event.stopPropagation) {
170
+            event.stopPropagation();
171
+        }
167 172
         self.VideoLayout.handleVideoThumbClicked(
168 173
             false,
169 174
             APP.xmpp.myResource());
170 175
     }
171 176
 
172
-    $('#localVideoContainer').click(localVideoClick);
177
+    $('#localVideoContainer').off('click');
178
+    $('#localVideoContainer').on('click', localVideoClick);
173 179
 
174 180
     // Add hover handler
175 181
     $('#localVideoContainer').hover(
@@ -192,8 +198,10 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
192 198
     var localVideo = document.createElement('video');
193 199
     localVideo.id = 'localVideo_' +
194 200
         APP.RTC.getStreamID(stream.getOriginalStream());
195
-    localVideo.autoplay = true;
196
-    localVideo.volume = 0; // is it required if audio is separated ?
201
+    if (!RTCBrowserType.isIExplorer()) {
202
+        localVideo.autoplay = true;
203
+        localVideo.volume = 0; // is it required if audio is separated ?
204
+    }
197 205
     localVideo.oncontextmenu = function () { return false; };
198 206
 
199 207
     var localVideoContainer = document.getElementById('localVideoWrapper');
@@ -203,7 +211,9 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
203 211
 
204 212
     // Add click handler to both video and video wrapper elements in case
205 213
     // there's no video.
206
-    localVideoSelector.click(localVideoClick);
214
+
215
+    // onclick has to be used with Temasys plugin
216
+    localVideo.onclick = localVideoClick;
207 217
 
208 218
     if (this.flipX) {
209 219
         localVideoSelector.addClass("flipVideoX");
@@ -214,6 +224,9 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
214 224
 
215 225
     // Add stream ended handler
216 226
     stream.getOriginalStream().onended = function () {
227
+        // We have to re-select after attach when Temasys plugin is used,
228
+        // because <video> element is replaced with <object>
229
+        localVideo = $('#' + localVideo.id)[0];
217 230
         localVideoContainer.removeChild(localVideo);
218 231
         self.VideoLayout.updateRemovedVideo(APP.xmpp.myResource());
219 232
     };

+ 38
- 14
modules/UI/videolayout/RemoteVideo.js Ver arquivo

@@ -3,6 +3,7 @@ var SmallVideo = require("./SmallVideo");
3 3
 var AudioLevels = require("../audio_levels/AudioLevels");
4 4
 var LargeVideo = require("./LargeVideo");
5 5
 var Avatar = require("../avatar/Avatar");
6
+var RTCBrowserType = require("../../RTC/RTCBrowserType");
6 7
 
7 8
 function RemoteVideo(peerJid, VideoLayout)
8 9
 {
@@ -146,14 +147,15 @@ RemoteVideo.prototype.removeRemoteStreamElement = function (stream, isVideo, id)
146 147
     select.remove();
147 148
 
148 149
     var audioCount = $('#' + this.videoSpanId + '>audio').length;
149
-    var videoCount = $('#' + this.videoSpanId + '>video').length;
150
+    var videoCount = $('#' + this.videoSpanId + '>' + APP.RTC.getVideoElementName()).length;
150 151
 
151 152
     if (!audioCount && !videoCount) {
152 153
         console.log("Remove whole user", this.videoSpanId);
153 154
         if(this.connectionIndicator)
154 155
             this.connectionIndicator.remove();
155 156
         // Remove whole container
156
-        this.container.remove();
157
+        if (this.container.parentNode)
158
+            this.container.parentNode.removeChild(this.container);
157 159
 
158 160
         this.VideoLayout.resizeThumbnails();
159 161
     }
@@ -185,11 +187,21 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
185 187
         if (isVideo && stream.id !== 'mixedmslabel') {
186 188
             var onPlayingHandler = function () {
187 189
                 // FIXME: why do i have to do this for FF?
188
-                APP.RTC.attachMediaStream(sel, stream);
190
+                if (RTCBrowserType.isFirefox()) {
191
+                    APP.RTC.attachMediaStream(sel, stream);
192
+                }
193
+                if (RTCBrowserType.isTemasysPluginUsed()) {
194
+                    sel = $('#' + newElementId);
195
+                }
189 196
                 self.VideoLayout.videoactive(sel, self.resourceJid);
190
-                sel.off("playing", onPlayingHandler);
197
+                sel[0].onplaying = null;
198
+                if (RTCBrowserType.isTemasysPluginUsed()) {
199
+                    // 'currentTime' is used to check if the video has started
200
+                    // and the value is not set by the plugin, so we do it
201
+                    sel[0].currentTime = 1;
202
+                }
191 203
             };
192
-            sel.on("playing", onPlayingHandler);
204
+            sel[0].onplaying = onPlayingHandler;
193 205
         }
194 206
 
195 207
         APP.RTC.attachMediaStream(sel, stream);
@@ -203,23 +215,35 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
203 215
 
204 216
     };
205 217
 
218
+    // Name of video element name is different for IE/Safari
219
+    var videoElem = APP.RTC.getVideoElementName();
220
+
206 221
     // Add click handler.
207
-    this.container.onclick = function (event) {
222
+    var onClickHandler = function (event) {
208 223
         /*
209 224
          * FIXME It turns out that videoThumb may not exist (if there is
210 225
          * no actual video).
211 226
          */
212
-        var videoThumb = $('#' + self.videoSpanId + '>video').get(0);
227
+        var videoThumb = $('#' + self.videoSpanId + '>' + videoElem).get(0);
213 228
         if (videoThumb) {
214 229
             self.VideoLayout.handleVideoThumbClicked(
215 230
                 false,
216 231
                 self.resourceJid);
217 232
         }
218
-
219
-        event.stopPropagation();
220
-        event.preventDefault();
233
+        // On IE we need to populate this handler on video <object>
234
+        // and it does not give event instance as an argument,
235
+        // so we check here for methods.
236
+        if (event.stopPropagation && event.preventDefault) {
237
+            event.stopPropagation();
238
+            event.preventDefault();
239
+        }
221 240
         return false;
222 241
     };
242
+    this.container.onclick = onClickHandler;
243
+    // reselect
244
+    if (RTCBrowserType.isTemasysPluginUsed())
245
+        sel = $('#' + newElementId);
246
+    sel[0].onclick = onClickHandler;
223 247
 
224 248
     //FIXME
225 249
     // Add hover handler
@@ -229,9 +253,9 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
229 253
         },
230 254
         function() {
231 255
             var videoSrc = null;
232
-            if ($('#' + self.videoSpanId + '>video')
233
-                && $('#' + self.videoSpanId + '>video').length > 0) {
234
-                videoSrc = APP.RTC.getVideoSrc($('#' + self.videoSpanId + '>video').get(0));
256
+            var videoSelector = $('#' + self.videoSpanId + '>' + videoElem);
257
+            if (videoSelector && videoSelector.length > 0) {
258
+                videoSrc = APP.RTC.getVideoSrc(videoSelector.get(0));
235 259
             }
236 260
 
237 261
             // If the video has been "pinned" by the user we want to
@@ -241,7 +265,7 @@ RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
241 265
                 self.showDisplayName(false);
242 266
         }
243 267
     );
244
-}
268
+},
245 269
 
246 270
 /**
247 271
  * Show/hide peer container for the given resourceJid.

+ 13
- 6
modules/UI/videolayout/SmallVideo.js Ver arquivo

@@ -1,5 +1,6 @@
1 1
 var UIUtil = require("../util/UIUtil");
2 2
 var LargeVideo = require("./LargeVideo");
3
+var RTCBrowserType = require("../../RTC/RTCBrowserType");
3 4
 
4 5
 function SmallVideo(){
5 6
     this.isMuted = false;
@@ -96,7 +97,9 @@ SmallVideo.createStreamElement = function (sid, stream) {
96 97
         + sid + '_' + APP.RTC.getStreamID(stream);
97 98
 
98 99
     element.id = id;
99
-    element.autoplay = true;
100
+    if (!RTCBrowserType.isIExplorer()) {
101
+        element.autoplay = true;
102
+    }
100 103
     element.oncontextmenu = function () { return false; };
101 104
 
102 105
     return element;
@@ -204,7 +207,7 @@ SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
204 207
         return;
205 208
     }
206 209
 
207
-    var video = $('#' + this.videoSpanId + '>video');
210
+    var video = $('#' + this.videoSpanId + '>' + APP.RTC.getVideoElementName());
208 211
 
209 212
     if (video && video.length > 0) {
210 213
         if (isEnable) {
@@ -275,8 +278,10 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
275 278
 
276 279
 
277 280
 SmallVideo.prototype.getSrc = function () {
278
-    return APP.RTC.getVideoSrc($('#' + this.videoSpanId).find("video").get(0));
279
-}
281
+    var videoElement = APP.RTC.getVideoElementName();
282
+    return APP.RTC.getVideoSrc(
283
+            $('#' + this.videoSpanId).find(videoElement).get(0));
284
+},
280 285
 
281 286
 SmallVideo.prototype.focus = function(isFocused)
282 287
 {
@@ -290,7 +295,8 @@ SmallVideo.prototype.focus = function(isFocused)
290 295
 }
291 296
 
292 297
 SmallVideo.prototype.hasVideo = function () {
293
-    return $("#" + this.videoSpanId).find("video").length !== 0;
298
+    return $("#" + this.videoSpanId).find(
299
+                APP.RTC.getVideoElementName()).length !== 0;
294 300
 }
295 301
 
296 302
 /**
@@ -302,7 +308,8 @@ SmallVideo.prototype.showAvatar = function (show) {
302 308
     if(!this.hasAvatar)
303 309
         return;
304 310
 
305
-    var video = $('#' + this.videoSpanId).find("video");
311
+    var videoElem = APP.RTC.getVideoElementName();
312
+    var video = $('#' + this.videoSpanId).find(videoElem);
306 313
     var avatar = $('#avatar_' + this.resourceJid);
307 314
 
308 315
     if (show === undefined || show === null) {

+ 32
- 14
modules/UI/videolayout/VideoLayout.js Ver arquivo

@@ -3,6 +3,10 @@ var Avatar = require("../avatar/Avatar");
3 3
 var ContactList = require("../side_pannels/contactlist/ContactList");
4 4
 var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
5 5
 var UIEvents = require("../../../service/UI/UIEvents");
6
+
7
+var RTC = require("../../RTC/RTC");
8
+var RTCBrowserType = require('../../RTC/RTCBrowserType');
9
+
6 10
 var RemoteVideo = require("./RemoteVideo");
7 11
 var LargeVideo = require("./LargeVideo");
8 12
 var LocalVideo = require("./LocalVideo");
@@ -48,11 +52,15 @@ var VideoLayout = (function (my) {
48 52
     };
49 53
 
50 54
     my.changeLocalAudio = function(stream, isMuted) {
51
-        if(isMuted)
55
+        if (isMuted)
52 56
             APP.UI.setAudioMuted(true, true);
53 57
         APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
54
-        document.getElementById('localAudio').autoplay = true;
55
-        document.getElementById('localAudio').volume = 0;
58
+        var localAudio = document.getElementById('localAudio');
59
+        // Writing volume not allowed in IE
60
+        if (!RTCBrowserType.isIExplorer()) {
61
+            localAudio.autoplay = true;
62
+            localAudio.volume = 0;
63
+        }
56 64
     };
57 65
 
58 66
     my.changeLocalVideo = function(stream, isMuted) {
@@ -107,22 +115,26 @@ var VideoLayout = (function (my) {
107 115
      * @param removedVideoSrc src stream identifier of the video.
108 116
      */
109 117
     my.updateRemovedVideo = function(resourceJid) {
118
+
119
+        var videoElem = RTC.getVideoElementName();
120
+
110 121
         if (resourceJid === LargeVideo.getResourceJid()) {
111 122
             // this is currently displayed as large
112 123
             // pick the last visible video in the row
113 124
             // if nobody else is left, this picks the local video
114 125
             var pick
115
-                = $('#remoteVideos>span[id!="mixedstream"]:visible:last>video')
116
-                    .get(0);
126
+                = $('#remoteVideos>' +
127
+                    'span[id!="mixedstream"]:visible:last>' + videoElem).get(0);
117 128
 
118 129
             if (!pick) {
119 130
                 console.info("Last visible video no longer exists");
120
-                pick = $('#remoteVideos>span[id!="mixedstream"]>video').get(0);
131
+                pick = $('#remoteVideos>' +
132
+                    'span[id!="mixedstream"]>' + videoElem).get(0);
121 133
 
122 134
                 if (!pick || !APP.RTC.getVideoSrc(pick)) {
123 135
                     // Try local video
124 136
                     console.info("Fallback to local video...");
125
-                    pick = $('#remoteVideos>span>span>video').get(0);
137
+                    pick = $('#remoteVideos>span>span>' + videoElem).get(0);
126 138
                 }
127 139
             }
128 140
 
@@ -223,10 +235,13 @@ var VideoLayout = (function (my) {
223 235
 
224 236
         LargeVideo.updateLargeVideo(resourceJid);
225 237
 
226
-        $('audio').each(function (idx, el) {
227
-            el.volume = 0;
228
-            el.volume = 1;
229
-        });
238
+        // Writing volume not allowed in IE
239
+        if (!RTCBrowserType.isIExplorer()) {
240
+            $('audio').each(function (idx, el) {
241
+                el.volume = 0;
242
+                el.volume = 1;
243
+            });
244
+        }
230 245
     };
231 246
 
232 247
 
@@ -452,7 +467,8 @@ var VideoLayout = (function (my) {
452 467
         var resource = Strophe.getResourceFromJid(jid);
453 468
         var videoContainer = $("#participant_" + resource);
454 469
         if (videoContainer.length > 0) {
455
-            var videoThumb = $('video', videoContainer).get(0);
470
+            var videoThumb
471
+                    = $(RTC.getVideoElementName(), videoContainer).get(0);
456 472
             // It is not always the case that a videoThumb exists (if there is
457 473
             // no actual video).
458 474
             if (videoThumb) {
@@ -571,7 +587,8 @@ var VideoLayout = (function (my) {
571 587
         // since we don't want to switch to local video.
572 588
         if (container && !focusedVideoResourceJid)
573 589
         {
574
-            var video = container.getElementsByTagName("video");
590
+            var video
591
+                = container.getElementsByTagName(RTC.getVideoElementName());
575 592
 
576 593
             // Update the large video if the video source is already available,
577 594
             // otherwise wait for the "videoactive.jingle" event.
@@ -673,7 +690,8 @@ var VideoLayout = (function (my) {
673 690
 
674 691
                     var jid = APP.xmpp.findJidFromResource(resourceJid);
675 692
                     var mediaStream = APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE];
676
-                    var sel = $('#participant_' + resourceJid + '>video');
693
+                    var sel = $('#participant_' + resourceJid +
694
+                                '>' + RTC.getVideoElementName());
677 695
 
678 696
                     APP.RTC.attachMediaStream(sel, mediaStream.stream);
679 697
                     if (lastNPickupJid == mediaStream.peerjid) {

+ 81
- 62
modules/desktopsharing/desktopsharing.js Ver arquivo

@@ -31,13 +31,7 @@ var extInstalled = false;
31 31
  */
32 32
 var extUpdateRequired = false;
33 33
 
34
-/**
35
- * Flag used to cache desktop sharing enabled state. Do not use directly as
36
- * it can be <tt>null</tt>.
37
- *
38
- * @type {null|boolean}
39
- */
40
-var _desktopSharingEnabled = null;
34
+var AdapterJS = require("../RTC/adapter.screenshare");
41 35
 
42 36
 var EventEmitter = require("events");
43 37
 
@@ -46,6 +40,10 @@ var eventEmitter = new EventEmitter();
46 40
 var DesktopSharingEventTypes
47 41
     = require("../../service/desktopsharing/DesktopSharingEventTypes");
48 42
 
43
+var RTCBrowserType = require("../RTC/RTCBrowserType");
44
+
45
+var RTCEvents = require("../../service/RTC/RTCEvents");
46
+
49 47
 /**
50 48
  * Method obtains desktop stream from WebRTC 'screen' source.
51 49
  * Flag 'chrome://flags/#enable-usermedia-screen-capture' must be enabled.
@@ -116,7 +114,7 @@ function isUpdateRequired(minVersion, extVersion)
116 114
     }
117 115
 }
118 116
 
119
-function checkExtInstalled(callback) {
117
+function checkChromeExtInstalled(callback) {
120 118
     if (!chrome.runtime) {
121 119
         // No API, so no extension for sure
122 120
         callback(false, false);
@@ -213,20 +211,39 @@ function obtainScreenFromExtension(streamCallback, failCallback) {
213 211
  *        feature completely.
214 212
  */
215 213
 function setDesktopSharing(method) {
216
-    // Check if we are running chrome
217
-    if (!navigator.webkitGetUserMedia) {
218
-        obtainDesktopStream = null;
219
-        console.info("Desktop sharing disabled");
220
-    } else if (method == "ext") {
221
-        obtainDesktopStream = obtainScreenFromExtension;
222
-        console.info("Using Chrome extension for desktop sharing");
223
-    } else if (method == "webrtc") {
224
-        obtainDesktopStream = obtainWebRTCScreen;
225
-        console.info("Using Chrome WebRTC for desktop sharing");
214
+
215
+    obtainDesktopStream = null;
216
+
217
+    // When TemasysWebRTC plugin is used we always use getUserMedia, so we don't
218
+    // care about 'method' parameter
219
+    if (RTCBrowserType.isTemasysPluginUsed()) {
220
+        if (!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature) {
221
+            console.info("Screensharing not supported by this plugin version");
222
+        } else if (!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
223
+            console.info(
224
+            "Screensharing not available with Temasys plugin on this site");
225
+        } else {
226
+            obtainDesktopStream = obtainWebRTCScreen;
227
+            console.info("Using Temasys plugin for desktop sharing");
228
+        }
229
+    } else if (RTCBrowserType.isChrome()) {
230
+        if (method == "ext") {
231
+            if (RTCBrowserType.getChromeVersion() >= 34) {
232
+                obtainDesktopStream = obtainScreenFromExtension;
233
+                console.info("Using Chrome extension for desktop sharing");
234
+                initChromeExtension();
235
+            } else {
236
+                console.info("Chrome extension not supported until ver 34");
237
+            }
238
+        } else if (method == "webrtc") {
239
+            obtainDesktopStream = obtainWebRTCScreen;
240
+            console.info("Using Chrome WebRTC for desktop sharing");
241
+        }
226 242
     }
227 243
 
228
-    // Reset enabled cache
229
-    _desktopSharingEnabled = null;
244
+    if (!obtainDesktopStream) {
245
+        console.info("Desktop sharing disabled");
246
+    }
230 247
 }
231 248
 
232 249
 /**
@@ -239,6 +256,19 @@ function initInlineInstalls()
239 256
     $("link[rel=chrome-webstore-item]").attr("href", getWebStoreInstallUrl());
240 257
 }
241 258
 
259
+function initChromeExtension() {
260
+    // Initialize Chrome extension inline installs
261
+    initInlineInstalls();
262
+    // Check if extension is installed
263
+    checkChromeExtInstalled(function (installed, updateRequired) {
264
+        extInstalled = installed;
265
+        extUpdateRequired = updateRequired;
266
+        console.info(
267
+            "Chrome extension installed: " + extInstalled +
268
+            " updateRequired: " + extUpdateRequired);
269
+    });
270
+}
271
+
242 272
 function getVideoStreamFailed(error) {
243 273
     console.error("Failed to obtain the stream to switch to", error);
244 274
     switchInProgress = false;
@@ -264,6 +294,25 @@ function newStreamCreated(stream)
264 294
         stream, isUsingScreenStream, streamSwitchDone);
265 295
 }
266 296
 
297
+function onEndedHandler(stream) {
298
+    if (!switchInProgress && isUsingScreenStream) {
299
+        APP.desktopsharing.toggleScreenSharing();
300
+    }
301
+    //FIXME: to be verified
302
+    if (stream.removeEventListener) {
303
+        stream.removeEventListener('ended', onEndedHandler);
304
+    } else {
305
+        stream.detachEvent('ended', onEndedHandler);
306
+    }
307
+}
308
+
309
+// Called when RTC finishes initialization
310
+function onWebRtcReady() {
311
+
312
+    setDesktopSharing(config.desktopSharing);
313
+
314
+    eventEmitter.emit(DesktopSharingEventTypes.INIT);
315
+}
267 316
 
268 317
 module.exports = {
269 318
     isUsingScreenStream: function () {
@@ -274,43 +323,10 @@ module.exports = {
274 323
      * @returns {boolean} <tt>true</tt> if desktop sharing feature is available
275 324
      *          and enabled.
276 325
      */
277
-    isDesktopSharingEnabled: function () {
278
-        if (_desktopSharingEnabled === null) {
279
-            if (obtainDesktopStream === obtainScreenFromExtension) {
280
-                // Parse chrome version
281
-                var userAgent = navigator.userAgent.toLowerCase();
282
-                // We can assume that user agent is chrome, because it's
283
-                // enforced when 'ext' streaming method is set
284
-                var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10);
285
-                console.log("Chrome version" + userAgent, ver);
286
-                _desktopSharingEnabled = ver >= 34;
287
-            } else {
288
-                _desktopSharingEnabled =
289
-                    obtainDesktopStream === obtainWebRTCScreen;
290
-            }
291
-        }
292
-        return _desktopSharingEnabled;
293
-    },
326
+    isDesktopSharingEnabled: function () { return !!obtainDesktopStream; },
294 327
     
295 328
     init: function () {
296
-        setDesktopSharing(config.desktopSharing);
297
-
298
-        // Initialize Chrome extension inline installs
299
-        if (config.chromeExtensionId) {
300
-
301
-            initInlineInstalls();
302
-
303
-            // Check if extension is installed
304
-            checkExtInstalled(function (installed, updateRequired) {
305
-                extInstalled = installed;
306
-                extUpdateRequired = updateRequired;
307
-                console.info(
308
-                    "Chrome extension installed: " + extInstalled +
309
-                    " updateRequired: " + extUpdateRequired);
310
-            });
311
-        }
312
-
313
-        eventEmitter.emit(DesktopSharingEventTypes.INIT);
329
+        APP.RTC.addListener(RTCEvents.RTC_READY, onWebRtcReady);
314 330
     },
315 331
 
316 332
     addListener: function (listener, type)
@@ -341,13 +357,16 @@ module.exports = {
341 357
                     isUsingScreenStream = true;
342 358
                     // Hook 'ended' event to restore camera
343 359
                     // when screen stream stops
344
-                    stream.addEventListener('ended',
345
-                        function (e) {
346
-                            if (!switchInProgress && isUsingScreenStream) {
347
-                                APP.desktopsharing.toggleScreenSharing();
348
-                            }
349
-                        }
350
-                    );
360
+                    //FIXME: to be verified
361
+                    if (stream.addEventListener) {
362
+                        stream.addEventListener('ended', function () {
363
+                            onEndedHandler(stream);
364
+                        });
365
+                    } else {
366
+                        stream.attachEvent('ended', function () {
367
+                            onEndedHandler(stream);
368
+                        });
369
+                    }
351 370
                     newStreamCreated(stream);
352 371
                 },
353 372
                 getDesktopStreamFailed);

+ 8
- 4
modules/statistics/RTPStatsCollector.js Ver arquivo

@@ -1,6 +1,6 @@
1 1
 /* global ssrc2jid */
2 2
 /* jshint -W117 */
3
-var RTCBrowserType = require("../../service/RTC/RTCBrowserType");
3
+var RTCBrowserType = require("../RTC/RTCBrowserType");
4 4
 
5 5
 
6 6
 /**
@@ -17,10 +17,12 @@ function calculatePacketLoss(lostPackets, totalPackets) {
17 17
 }
18 18
 
19 19
 function getStatValue(item, name) {
20
-    if(!keyMap[APP.RTC.getBrowserType()][name])
20
+    var browserType = RTCBrowserType.getBrowserType();
21
+    if (!keyMap[browserType][name])
21 22
         throw "The property isn't supported!";
22
-    var key = keyMap[APP.RTC.getBrowserType()][name];
23
-    return APP.RTC.getBrowserType() == RTCBrowserType.RTC_BROWSER_CHROME? item.stat(key) : item[key];
23
+    var key = keyMap[browserType][name];
24
+    return (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) ?
25
+        item.stat(key) : item[key];
24 26
 }
25 27
 
26 28
 /**
@@ -415,6 +417,8 @@ keyMap[RTCBrowserType.RTC_BROWSER_CHROME] = {
415 417
     "audioInputLevel": "audioInputLevel",
416 418
     "audioOutputLevel": "audioOutputLevel"
417 419
 };
420
+keyMap[RTCBrowserType.RTC_BROWSER_OPERA] =
421
+    keyMap[RTCBrowserType.RTC_BROWSER_CHROME];
418 422
 
419 423
 
420 424
 /**

+ 9
- 3
modules/xmpp/JingleSession.js Ver arquivo

@@ -6,6 +6,7 @@ var SDP = require("./SDP");
6 6
 var async = require("async");
7 7
 var transform = require("sdp-transform");
8 8
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
9
+var RTCBrowserType = require("../RTC/RTCBrowserType");
9 10
 
10 11
 // Jingle stuff
11 12
 function JingleSession(me, sid, connection, service, eventEmitter) {
@@ -1332,9 +1333,10 @@ JingleSession.prototype.remoteStreamAdded = function (data, times) {
1332 1333
     var self = this;
1333 1334
     var thessrc;
1334 1335
     var ssrc2jid = this.connection.emuc.ssrc2jid;
1336
+    var streamId = APP.RTC.getStreamID(data.stream);
1335 1337
 
1336 1338
     // look up an associated JID for a stream id
1337
-    if (data.stream.id && data.stream.id.indexOf('mixedmslabel') === -1) {
1339
+    if (streamId && streamId.indexOf('mixedmslabel') === -1) {
1338 1340
         // look only at a=ssrc: and _not_ at a=ssrc-group: lines
1339 1341
 
1340 1342
         var ssrclines
@@ -1344,7 +1346,11 @@ JingleSession.prototype.remoteStreamAdded = function (data, times) {
1344 1346
             // is not always present.
1345 1347
             // return line.indexOf('mslabel:' + data.stream.label) !== -1;
1346 1348
 
1347
-            return ((line.indexOf('msid:' + data.stream.id) !== -1));
1349
+            if (RTCBrowserType.isTemasysPluginUsed()) {
1350
+                return ((line.indexOf('mslabel:' + streamId) !== -1));
1351
+            } else {
1352
+                return ((line.indexOf('msid:' + streamId) !== -1));
1353
+            }
1348 1354
         });
1349 1355
         if (ssrclines.length) {
1350 1356
             thessrc = ssrclines[0].substring(7).split(' ')[0];
@@ -1379,7 +1385,7 @@ JingleSession.prototype.remoteStreamAdded = function (data, times) {
1379 1385
             }
1380 1386
 
1381 1387
             // ok to overwrite the one from focus? might save work in colibri.js
1382
-            console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
1388
+            console.log('associated jid', ssrc2jid[thessrc], thessrc);
1383 1389
             if (ssrc2jid[thessrc]) {
1384 1390
                 data.peerjid = ssrc2jid[thessrc];
1385 1391
             }

+ 13
- 6
modules/xmpp/SDP.js Ver arquivo

@@ -218,8 +218,12 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
218 218
                         if (kv.indexOf(':') == -1) {
219 219
                             elem.attrs({ name: kv });
220 220
                         } else {
221
-                            elem.attrs({ name: kv.split(':', 2)[0] });
222
-                            elem.attrs({ value: kv.split(':', 2)[1] });
221
+                            var k = kv.split(':', 2)[0];
222
+                            elem.attrs({ name: k });
223
+
224
+                            var v = kv.split(':', 2)[1];
225
+                            v = SDPUtil.filter_special_chars(v);
226
+                            elem.attrs({ value: v });
223 227
                         }
224 228
                         elem.up();
225 229
                     });
@@ -243,7 +247,7 @@ SDP.prototype.toJingle = function (elem, thecreator, ssrcs) {
243 247
                     }
244 248
                     if(msid != null)
245 249
                     {
246
-                        msid = msid.replace(/[\{,\}]/g,"");
250
+                        msid = SDPUtil.filter_special_chars(msid);
247 251
                         elem.c('parameter');
248 252
                         elem.attrs({name: "msid", value:msid});
249 253
                         elem.up();
@@ -605,9 +609,12 @@ SDP.prototype.jingle2media = function (content) {
605 609
     tmp.each(function () {
606 610
         var ssrc = this.getAttribute('ssrc');
607 611
         $(this).find('>parameter').each(function () {
608
-            media += 'a=ssrc:' + ssrc + ' ' + this.getAttribute('name');
609
-            if (this.getAttribute('value') && this.getAttribute('value').length)
610
-                media += ':' + this.getAttribute('value');
612
+            var name = this.getAttribute('name');
613
+            var value = this.getAttribute('value');
614
+            value = SDPUtil.filter_special_chars(value);
615
+            media += 'a=ssrc:' + ssrc + ' ' + name;
616
+            if (value && value.length)
617
+                media += ':' + value;
611 618
             media += '\r\n';
612 619
         });
613 620
     });

+ 8
- 2
modules/xmpp/SDPDiffer.js Ver arquivo

@@ -1,3 +1,6 @@
1
+
2
+var SDPUtil = require("./SDPUtil");
3
+
1 4
 function SDPDiffer(mySDP, otherSDP) {
2 5
     this.mySDP = mySDP;
3 6
     this.otherSDP = otherSDP;
@@ -130,8 +133,11 @@ SDPDiffer.prototype.toJingle = function(modify) {
130 133
                 if (kv.indexOf(':') == -1) {
131 134
                     modify.attrs({ name: kv });
132 135
                 } else {
133
-                    modify.attrs({ name: kv.split(':', 2)[0] });
134
-                    modify.attrs({ value: kv.split(':', 2)[1] });
136
+                    var nv = kv.split(':', 2);
137
+                    var name = nv[0];
138
+                    var value = SDPUtil.filter_special_chars(nv[1]);
139
+                    modify.attrs({ name: name });
140
+                    modify.attrs({ value: value });
135 141
                 }
136 142
                 modify.up(); // end of parameter
137 143
             });

+ 3
- 0
modules/xmpp/SDPUtil.js Ver arquivo

@@ -1,4 +1,7 @@
1 1
 SDPUtil = {
2
+    filter_special_chars: function (text) {
3
+        return text.replace(/[\\\/\{,\}\+]/g, "");
4
+    },
2 5
     iceparams: function (mediadesc, sessiondesc) {
3 6
         var data = null;
4 7
         if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&

+ 36
- 7
modules/xmpp/TraceablePeerConnection.js Ver arquivo

@@ -1,9 +1,18 @@
1
+var RTC = require('../RTC/RTC');
2
+var RTCBrowserType = require("../RTC/RTCBrowserType.js");
1 3
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
2 4
 
3 5
 function TraceablePeerConnection(ice_config, constraints, session) {
4 6
     var self = this;
5
-    var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
6
-    this.peerconnection = new RTCPeerconnection(ice_config, constraints);
7
+    var RTCPeerconnectionType = null;
8
+    if (RTCBrowserType.isFirefox()) {
9
+        RTCPeerconnectionType = mozRTCPeerConnection;
10
+    } else if (RTCBrowserType.isTemasysPluginUsed()) {
11
+        RTCPeerconnectionType = RTCPeerConnection;
12
+    } else {
13
+        RTCPeerconnectionType = webkitRTCPeerConnection;
14
+    }
15
+    this.peerconnection = new RTCPeerconnectionType(ice_config, constraints);
7 16
     this.updateLog = [];
8 17
     this.stats = {};
9 18
     this.statsinterval = null;
@@ -15,7 +24,15 @@ function TraceablePeerConnection(ice_config, constraints, session) {
15 24
 
16 25
     // override as desired
17 26
     this.trace = function (what, info) {
18
-        //console.warn('WTRACE', what, info);
27
+        /*console.warn('WTRACE', what, info);
28
+        if (info && RTCBrowserType.isIExplorer()) {
29
+            if (info.length > 1024) {
30
+                console.warn('WTRACE', what, info.substr(1024));
31
+            }
32
+            if (info.length > 2048) {
33
+                console.warn('WTRACE', what, info.substr(2048));
34
+            }
35
+        }*/
19 36
         self.updateLog.push({
20 37
             time: new Date(),
21 38
             type: what,
@@ -24,7 +41,9 @@ function TraceablePeerConnection(ice_config, constraints, session) {
24 41
     };
25 42
     this.onicecandidate = null;
26 43
     this.peerconnection.onicecandidate = function (event) {
27
-        self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
44
+        // FIXME: this causes stack overflow with Temasys Plugin
45
+        if (!RTCBrowserType.isTemasysPluginUsed())
46
+            self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
28 47
         if (self.onicecandidate !== null) {
29 48
             self.onicecandidate(event);
30 49
         }
@@ -155,16 +174,26 @@ TraceablePeerConnection.prototype.removeStream = function (stream, stopStreams)
155 174
     this.trace('removeStream', stream.id);
156 175
     if(stopStreams) {
157 176
         stream.getAudioTracks().forEach(function (track) {
158
-            track.stop();
177
+            // stop() not supported with IE
178
+            if (track.stop) {
179
+                track.stop();
180
+            }
159 181
         });
160 182
         stream.getVideoTracks().forEach(function (track) {
161
-            track.stop();
183
+            // stop() not supported with IE
184
+            if (track.stop) {
185
+                track.stop();
186
+            }
162 187
         });
188
+        if (stream.stop) {
189
+            stream.stop();
190
+        }
163 191
     }
164 192
 
165 193
     try {
166 194
         // FF doesn't support this yet.
167
-        this.peerconnection.removeStream(stream);
195
+        if (this.peerconnection.removeStream)
196
+            this.peerconnection.removeStream(stream);
168 197
     } catch (e) {
169 198
         console.error(e);
170 199
     }

+ 2
- 3
modules/xmpp/strophe.emuc.js Ver arquivo

@@ -28,12 +28,11 @@ 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())
31
+            if (APP.RTC.localAudio && APP.RTC.localAudio.isMuted())
32 32
             {
33 33
                 this.addAudioInfoToPresence(true);
34 34
             }
35
-
36
-            if(APP.RTC.localVideo.isMuted())
35
+            if (APP.RTC.localVideo && APP.RTC.localVideo.isMuted())
37 36
             {
38 37
                 this.addVideoInfoToPresence(true);
39 38
             }

+ 0
- 7
service/RTC/RTCBrowserType.js Ver arquivo

@@ -1,7 +0,0 @@
1
-var RTCBrowserType = {
2
-    RTC_BROWSER_CHROME: "rtc_browser.chrome",
3
-
4
-    RTC_BROWSER_FIREFOX: "rtc_browser.firefox"
5
-};
6
-
7
-module.exports = RTCBrowserType;

+ 1
- 0
service/RTC/RTCEvents.js Ver arquivo

@@ -1,4 +1,5 @@
1 1
 var RTCEvents = {
2
+    RTC_READY: "rtc.ready",
2 3
     LASTN_CHANGED: "rtc.lastn_changed",
3 4
     DOMINANTSPEAKER_CHANGED: "rtc.dominantspeaker_changed",
4 5
     LASTN_ENDPOINT_CHANGED: "rtc.lastn_endpoint_changed",

Carregando…
Cancelar
Salvar