Sfoglia il codice sorgente

Merge pull request #509 from ibc/datachannel-websocket

Make it possible to use WebSocket instead of DataChannel
master
Saúl Ibarra Corretgé 8 anni fa
parent
commit
5af1e2f6e9

+ 40
- 4
JitsiConference.js Vedi File

@@ -60,6 +60,10 @@ const logger = getLogger(__filename);
60 60
  * "Math.random() < forceJVB121Ratio" will determine whether a 2 people
61 61
  * conference should be moved to the JVB instead of P2P. The decision is made on
62 62
  * the responder side, after ICE succeeds on the P2P connection.
63
+ * @param {*} [options.config.openBridgeChannel] Which kind of communication to
64
+ * open with the videobridge. Values can be "datachannel", "websocket", true
65
+ * (treat it as "datachannel"), undefined (treat it as "datachannel") and false
66
+ * (don't open any channel).
63 67
  * @constructor
64 68
  *
65 69
  * FIXME Make all methods which are called from lib-internal classes
@@ -313,7 +317,7 @@ JitsiConference.prototype.leave = function() {
313 317
 
314 318
     this.getLocalTracks().forEach(track => this.onLocalTrackRemoved(track));
315 319
 
316
-    this.rtc.closeAllDataChannels();
320
+    this.rtc.closeBridgeChannel();
317 321
     if (this.statistics) {
318 322
         this.statistics.dispose();
319 323
     }
@@ -1349,8 +1353,6 @@ JitsiConference.prototype.onIncomingCall
1349 1353
         GlobalOnErrorHandler.callErrorHandler(error);
1350 1354
     }
1351 1355
 
1352
-    this.rtc.initializeDataChannels(jingleSession.peerconnection);
1353
-
1354 1356
     // Add local tracks to the session
1355 1357
     try {
1356 1358
         jingleSession.acceptOffer(
@@ -1362,6 +1364,9 @@ JitsiConference.prototype.onIncomingCall
1362 1364
                 if (this.isP2PActive() && this.jvbJingleSession) {
1363 1365
                     this._suspendMediaTransferForJvbConnection();
1364 1366
                 }
1367
+
1368
+                // Open a channel with the videobridge.
1369
+                this._setBridgeChannel();
1365 1370
             },
1366 1371
             error => {
1367 1372
                 GlobalOnErrorHandler.callErrorHandler(error);
@@ -1386,6 +1391,37 @@ JitsiConference.prototype.onIncomingCall
1386 1391
     }
1387 1392
 };
1388 1393
 
1394
+/**
1395
+ * Sets the BridgeChannel.
1396
+ */
1397
+JitsiConference.prototype._setBridgeChannel = function() {
1398
+    const jingleSession = this.jvbJingleSession;
1399
+    const wsUrl = jingleSession.bridgeWebSocketUrl;
1400
+    let bridgeChannelType;
1401
+
1402
+    switch (this.options.config.openBridgeChannel) {
1403
+    case 'datachannel':
1404
+    case true:
1405
+    case undefined:
1406
+        bridgeChannelType = 'datachannel';
1407
+        break;
1408
+    case 'websocket':
1409
+        bridgeChannelType = 'websocket';
1410
+        break;
1411
+    }
1412
+
1413
+    if (bridgeChannelType === 'datachannel'
1414
+        && !RTCBrowserType.supportsDataChannels()) {
1415
+        bridgeChannelType = 'websocket';
1416
+    }
1417
+
1418
+    if (bridgeChannelType === 'datachannel') {
1419
+        this.rtc.initializeBridgeChannel(jingleSession.peerconnection, null);
1420
+    } else if (bridgeChannelType === 'websocket' && wsUrl) {
1421
+        this.rtc.initializeBridgeChannel(null, wsUrl);
1422
+    }
1423
+};
1424
+
1389 1425
 /**
1390 1426
  * Rejects incoming Jingle call with 'security-error'. Method should be used to
1391 1427
  * reject calls initiated by unauthorised entities.
@@ -1879,7 +1915,7 @@ JitsiConference.prototype._fireIncompatibleVersionsEvent = function() {
1879 1915
  * @throws NetworkError or InvalidStateError or Error if the operation fails.
1880 1916
  */
1881 1917
 JitsiConference.prototype.sendEndpointMessage = function(to, payload) {
1882
-    this.rtc.sendDataChannelMessage(to, payload);
1918
+    this.rtc.sendChannelMessage(to, payload);
1883 1919
 };
1884 1920
 
1885 1921
 /**

+ 6
- 4
JitsiConferenceEventManager.js Vedi File

@@ -59,10 +59,12 @@ JitsiConferenceEventManager.prototype.setupChatRoomListeners = function() {
59 59
 
60 60
     chatRoom.addListener(XMPPEvents.ICE_RESTARTING, jingleSession => {
61 61
         if (!jingleSession.isP2P) {
62
-            // All data channels have to be closed, before ICE restart
63
-            // otherwise Chrome will not trigger "opened" event for the channel
64
-            // established with the new bridge
65
-            conference.rtc.closeAllDataChannels();
62
+            // If using DataChannel as bridge channel, it must be closed
63
+            // before ICE restart, otherwise Chrome will not trigger "opened"
64
+            // event for the channel established with the new bridge.
65
+            // TODO: This may be bypassed when using a WebSocket as bridge
66
+            // channel.
67
+            conference.rtc.closeBridgeChannel();
66 68
         }
67 69
 
68 70
         // else: there are no DataChannels in P2P session (at least for now)

+ 1
- 1
doc/API.md Vedi File

@@ -217,7 +217,7 @@ This objects represents the server connection. You can create new ```JitsiConnec
217 217
 4. initJitsiConference(name, options) - creates new ```JitsiConference``` object.
218 218
     - name - the name of the conference
219 219
     - options - JS object with configuration options for the conference. You can change the following properties there:
220
-        1. openSctp - boolean property. Enables/disables datachannel support. **NOTE: we recommend to set that option to true**
220
+        1. openBridgeChannel - Enables/disables bridge channel. Values can be "datachannel", "websocket", true (treat it as "datachannel"), undefined (treat it as "datachannel") and false (don't open any channel). **NOTE: we recommend to set that option to true**
221 221
         2. recordingType - the type of recording to be used
222 222
         3. jirecon
223 223
         4. callStatsID - callstats credentials

+ 1
- 1
doc/example/example.js Vedi File

@@ -12,7 +12,7 @@ const options = {
12 12
 };
13 13
 
14 14
 const confOptions = {
15
-    openSctp: true
15
+    openBridgeChannel: true
16 16
 };
17 17
 
18 18
 let connection = null;

+ 293
- 0
modules/RTC/BridgeChannel.js Vedi File

@@ -0,0 +1,293 @@
1
+import { getLogger } from 'jitsi-meet-logger';
2
+
3
+import RTCEvents from '../../service/RTC/RTCEvents';
4
+import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
5
+
6
+const logger = getLogger(__filename);
7
+
8
+/**
9
+ * Handles a WebRTC RTCPeerConnection or a WebSocket instance to communicate
10
+ * with the videobridge.
11
+ */
12
+export default class BridgeChannel {
13
+    /**
14
+     * Binds "ondatachannel" event listener on the given RTCPeerConnection
15
+     * instance, or creates a WebSocket connection with the videobridge.
16
+     * At least one of both, peerconnection or wsUrl parameters, must be
17
+     * given.
18
+     * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection
19
+     * instance.
20
+     * @param {string} [wsUrl] WebSocket URL.
21
+     * @param {EventEmitter} eventEmitter EventEmitter instance.
22
+     */
23
+    constructor(peerconnection, wsUrl, emitter) {
24
+        if (!peerconnection && !wsUrl) {
25
+            throw new TypeError(
26
+                'At least peerconnection or wsUrl must be given');
27
+        } else if (peerconnection && wsUrl) {
28
+            throw new TypeError(
29
+                'Just one of peerconnection or wsUrl must be given');
30
+        }
31
+
32
+        if (peerconnection) {
33
+            logger.debug('constructor() with peerconnection');
34
+        } else {
35
+            logger.debug(`constructor() with wsUrl:"${wsUrl}"`);
36
+        }
37
+
38
+        // The underlying WebRTC RTCDataChannel or WebSocket instance.
39
+        // @type {RTCDataChannel|WebSocket}
40
+        this._channel = null;
41
+
42
+        // @type {EventEmitter}
43
+        this._eventEmitter = emitter;
44
+
45
+        // Whether a RTCDataChannel or WebSocket is internally used.
46
+        // @type {string} "datachannel" / "websocket"
47
+        this._mode = null;
48
+
49
+        // If a RTCPeerConnection is given, listen for new RTCDataChannel
50
+        // event.
51
+        if (peerconnection) {
52
+            peerconnection.ondatachannel = event => {
53
+                // NOTE: We assume that the "onDataChannel" event just fires
54
+                // once.
55
+
56
+                const datachannel = event.channel;
57
+
58
+                // Handle the RTCDataChannel.
59
+                this._handleChannel(datachannel);
60
+                this._mode = 'datachannel';
61
+            };
62
+
63
+        // Otherwise create a WebSocket connection.
64
+        } else if (wsUrl) {
65
+            // Create a WebSocket instance.
66
+            const ws = new WebSocket(wsUrl);
67
+
68
+            // Handle the WebSocket.
69
+            this._handleChannel(ws);
70
+            this._mode = 'websocket';
71
+        }
72
+    }
73
+
74
+    /**
75
+     * The channel mode.
76
+     * @return {string} "datachannel" or "websocket" (or null if not yet set).
77
+     */
78
+    get mode() {
79
+        return this._mode;
80
+    }
81
+
82
+    /**
83
+     * Closes the currently opened channel.
84
+     */
85
+    close() {
86
+        if (this._channel) {
87
+            try {
88
+                this._channel.close();
89
+            } catch (error) {} // eslint-disable-line no-empty
90
+
91
+            this._channel = null;
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Whether there is an underlying RTCDataChannel or WebSocket and it's
97
+     * open.
98
+     * @return {boolean}
99
+     */
100
+    isOpen() {
101
+        return this._channel && (this._channel.readyState === 'open'
102
+            || this._channel.readyState === WebSocket.OPEN);
103
+    }
104
+
105
+    /**
106
+     * Sends message via the channel.
107
+     * @param {string} to The id of the endpoint that should receive the
108
+     * message. If "" the message will be sent to all participants.
109
+     * @param  {object} payload The payload of the message.
110
+     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
111
+     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})
112
+     * or from WebSocket#send or Error with "No opened channel" message.
113
+     */
114
+    sendMessage(to, payload) {
115
+        this._send({
116
+            colibriClass: 'EndpointMessage',
117
+            msgPayload: payload,
118
+            to
119
+        });
120
+    }
121
+
122
+    /**
123
+     * Sends a "lastN value changed" message via the channel.
124
+     * @param {number} value The new value for lastN. -1 means unlimited.
125
+     */
126
+    sendSetLastNMessage(value) {
127
+        const jsonObject = {
128
+            colibriClass: 'LastNChangedEvent',
129
+            lastN: value
130
+        };
131
+
132
+        this._send(jsonObject);
133
+        logger.log(`Channel lastN set to: ${value}`);
134
+    }
135
+
136
+    /**
137
+     * Sends a "pinned endpoint changed" message via the channel.
138
+     * @param {string} endpointId The id of the pinned endpoint.
139
+     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
140
+     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})
141
+     * or from WebSocket#send or Error with "No opened channel" message.
142
+     */
143
+    sendPinnedEndpointMessage(endpointId) {
144
+        logger.log(
145
+            'sending pinned changed notification to the bridge for endpoint ',
146
+            endpointId);
147
+
148
+        this._send({
149
+            colibriClass: 'PinnedEndpointChangedEvent',
150
+            pinnedEndpoint: endpointId || null
151
+        });
152
+    }
153
+
154
+    /**
155
+     * Sends a "selected endpoint changed" message via the channel.
156
+     * @param {string} endpointId The id of the selected endpoint.
157
+     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
158
+     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})
159
+     * or from WebSocket#send or Error with "No opened channel" message.
160
+     */
161
+    sendSelectedEndpointMessage(endpointId) {
162
+        logger.log(
163
+            'sending selected changed notification to the bridge for endpoint ',
164
+            endpointId);
165
+
166
+        this._send({
167
+            colibriClass: 'SelectedEndpointChangedEvent',
168
+            selectedEndpoint: endpointId || null
169
+        });
170
+    }
171
+
172
+    /**
173
+     * Set events on the given RTCDataChannel or WebSocket instance.
174
+     */
175
+    _handleChannel(channel) {
176
+        const emitter = this._eventEmitter;
177
+
178
+        channel.onopen = () => {
179
+            logger.info('Channel opened by the Videobridge!', channel);
180
+
181
+            // Code sample for sending string and/or binary data.
182
+            // Sends string message to the bridge:
183
+            //     channel.send("Hello bridge!");
184
+            // Sends 12 bytes binary message to the bridge:
185
+            //     channel.send(new ArrayBuffer(12));
186
+
187
+            emitter.emit(RTCEvents.DATA_CHANNEL_OPEN);
188
+        };
189
+
190
+        channel.onerror = error => {
191
+            logger.error('Channel error:', error, channel);
192
+        };
193
+
194
+        channel.onmessage = ({ data }) => {
195
+            // JSON object.
196
+            let obj;
197
+
198
+            try {
199
+                obj = JSON.parse(data);
200
+            } catch (error) {
201
+                GlobalOnErrorHandler.callErrorHandler(error);
202
+                logger.error(
203
+                    'Failed to parse channel message as JSON: ',
204
+                    data, channel, error);
205
+
206
+                return;
207
+            }
208
+
209
+            const colibriClass = obj.colibriClass;
210
+
211
+            switch (colibriClass) {
212
+            case 'DominantSpeakerEndpointChangeEvent': {
213
+                // Endpoint ID from the Videobridge.
214
+                const dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint;
215
+
216
+                logger.info(
217
+                    'Channel new dominant speaker event: ',
218
+                    dominantSpeakerEndpoint);
219
+                emitter.emit(
220
+                    RTCEvents.DOMINANT_SPEAKER_CHANGED,
221
+                    dominantSpeakerEndpoint);
222
+                break;
223
+            }
224
+            case 'EndpointConnectivityStatusChangeEvent': {
225
+                const endpoint = obj.endpoint;
226
+                const isActive = obj.active === 'true';
227
+
228
+                logger.info(
229
+                    `Endpoint connection status changed: ${endpoint} active ? ${
230
+                        isActive}`);
231
+                emitter.emit(RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,
232
+                    endpoint, isActive);
233
+
234
+                break;
235
+            }
236
+            case 'EndpointMessage': {
237
+                emitter.emit(
238
+                    RTCEvents.ENDPOINT_MESSAGE_RECEIVED, obj.from,
239
+                    obj.msgPayload);
240
+
241
+                break;
242
+            }
243
+            case 'LastNEndpointsChangeEvent': {
244
+                // The new/latest list of last-n endpoint IDs.
245
+                const lastNEndpoints = obj.lastNEndpoints;
246
+
247
+                logger.info('Channel new last-n event: ',
248
+                    lastNEndpoints, obj);
249
+                emitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,
250
+                    lastNEndpoints, obj);
251
+
252
+                break;
253
+            }
254
+            default: {
255
+                logger.debug('Channel JSON-formatted message: ', obj);
256
+
257
+                // The received message appears to be appropriately formatted
258
+                // (i.e. is a JSON object which assigns a value to the
259
+                // mandatory property colibriClass) so don't just swallow it,
260
+                // expose it to public consumption.
261
+                emitter.emit(`rtc.datachannel.${colibriClass}`, obj);
262
+            }
263
+            }
264
+        };
265
+
266
+        channel.onclose = () => {
267
+            logger.info('Channel closed', channel);
268
+
269
+            // Remove the channel.
270
+            this._channel = null;
271
+        };
272
+
273
+        // Store the channel.
274
+        this._channel = channel;
275
+    }
276
+
277
+    /**
278
+     * Sends passed object via the channel.
279
+     * @param {object} jsonObject The object that will be sent.
280
+     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
281
+     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})
282
+     * or from WebSocket#send or Error with "No opened channel" message.
283
+     */
284
+    _send(jsonObject) {
285
+        const channel = this._channel;
286
+
287
+        if (!this.isOpen()) {
288
+            throw new Error('No opened channel');
289
+        }
290
+
291
+        channel.send(JSON.stringify(jsonObject));
292
+    }
293
+}

+ 0
- 276
modules/RTC/DataChannels.js Vedi File

@@ -1,276 +0,0 @@
1
-// cache datachannels to avoid garbage collection
2
-// https://code.google.com/p/chromium/issues/detail?id=405545
3
-
4
-const logger = require('jitsi-meet-logger').getLogger(__filename);
5
-const RTCEvents = require('../../service/RTC/RTCEvents');
6
-const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
7
-
8
-/**
9
- * Binds "ondatachannel" event listener to given PeerConnection instance.
10
- * @param peerConnection WebRTC peer connection instance.
11
- */
12
-function DataChannels(peerConnection, emitter) {
13
-    peerConnection.ondatachannel = this.onDataChannel.bind(this);
14
-    this.eventEmitter = emitter;
15
-
16
-    this._dataChannels = [];
17
-
18
-    // Sample code for opening new data channel from Jitsi Meet to the bridge.
19
-    // Although it's not a requirement to open separate channels from both
20
-    // bridge and peer as single channel can be used for sending and receiving
21
-    // data. So either channel opened by the bridge or the one opened here is
22
-    // enough for communication with the bridge.
23
-    /* var dataChannelOptions =
24
-     {
25
-     reliable: true
26
-     };
27
-     var dataChannel
28
-     = peerConnection.createDataChannel("myChannel", dataChannelOptions);
29
-
30
-     // Can be used only when is in open state
31
-     dataChannel.onopen = function ()
32
-     {
33
-     dataChannel.send("My channel !!!");
34
-     };
35
-     dataChannel.onmessage = function (event)
36
-     {
37
-     var msgData = event.data;
38
-     logger.info("Got My Data Channel Message:", msgData, dataChannel);
39
-     };*/
40
-}
41
-
42
-/**
43
- * Callback triggered by PeerConnection when new data channel is opened
44
- * on the bridge.
45
- * @param event the event info object.
46
- */
47
-DataChannels.prototype.onDataChannel = function(event) {
48
-    const dataChannel = event.channel;
49
-    const self = this;
50
-
51
-    dataChannel.onopen = function() {
52
-        logger.info('Data channel opened by the Videobridge!');
53
-
54
-        // Code sample for sending string and/or binary data
55
-        // Sends String message to the bridge
56
-        // dataChannel.send("Hello bridge!");
57
-        // Sends 12 bytes binary message to the bridge
58
-        // dataChannel.send(new ArrayBuffer(12));
59
-
60
-        self.eventEmitter.emit(RTCEvents.DATA_CHANNEL_OPEN);
61
-    };
62
-
63
-    dataChannel.onerror = function(error) {
64
-        // FIXME: this one seems to be generated a bit too often right now
65
-        // so we are temporarily commenting it before we have more clarity
66
-        // on which of the errors we absolutely need to report
67
-        // GlobalOnErrorHandler.callErrorHandler(
68
-        //        new Error("Data Channel Error:" + error));
69
-        logger.error('Data Channel Error:', error, dataChannel);
70
-    };
71
-
72
-    dataChannel.onmessage = function({ data }) {
73
-        // JSON
74
-        let obj;
75
-
76
-        try {
77
-            obj = JSON.parse(data);
78
-        } catch (e) {
79
-            GlobalOnErrorHandler.callErrorHandler(e);
80
-            logger.error(
81
-                'Failed to parse data channel message as JSON: ',
82
-                data,
83
-                dataChannel,
84
-                e);
85
-        }
86
-        if ((typeof obj !== 'undefined') && (obj !== null)) {
87
-            const colibriClass = obj.colibriClass;
88
-
89
-            if (colibriClass === 'DominantSpeakerEndpointChangeEvent') {
90
-                // Endpoint ID from the Videobridge.
91
-                const dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint;
92
-
93
-                logger.info(
94
-                    'Data channel new dominant speaker event: ',
95
-                    dominantSpeakerEndpoint);
96
-                self.eventEmitter.emit(RTCEvents.DOMINANT_SPEAKER_CHANGED,
97
-                  dominantSpeakerEndpoint);
98
-            } else if (colibriClass === 'LastNEndpointsChangeEvent') {
99
-                // The new/latest list of last-n endpoint IDs.
100
-                const lastNEndpoints = obj.lastNEndpoints;
101
-
102
-                logger.info('Data channel new last-n event: ',
103
-                    lastNEndpoints, obj);
104
-                self.eventEmitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,
105
-                    lastNEndpoints, obj);
106
-            } else if (colibriClass === 'EndpointMessage') {
107
-                self.eventEmitter.emit(
108
-                    RTCEvents.ENDPOINT_MESSAGE_RECEIVED, obj.from,
109
-                    obj.msgPayload);
110
-            } else if (colibriClass
111
-                    === 'EndpointConnectivityStatusChangeEvent') {
112
-                const endpoint = obj.endpoint;
113
-                const isActive = obj.active === 'true';
114
-
115
-                logger.info(
116
-                    `Endpoint connection status changed: ${endpoint} active ? ${
117
-                        isActive}`);
118
-                self.eventEmitter.emit(RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,
119
-                    endpoint, isActive);
120
-            } else {
121
-                logger.debug('Data channel JSON-formatted message: ', obj);
122
-
123
-                // The received message appears to be appropriately formatted
124
-                // (i.e. is a JSON object which assigns a value to the mandatory
125
-                // property colibriClass) so don't just swallow it, expose it to
126
-                // public consumption.
127
-                self.eventEmitter.emit(`rtc.datachannel.${colibriClass}`, obj);
128
-            }
129
-        }
130
-    };
131
-
132
-    dataChannel.onclose = function() {
133
-        logger.info('The Data Channel closed', dataChannel);
134
-        const idx = self._dataChannels.indexOf(dataChannel);
135
-
136
-        if (idx > -1) {
137
-            self._dataChannels = self._dataChannels.splice(idx, 1);
138
-        }
139
-    };
140
-    this._dataChannels.push(dataChannel);
141
-};
142
-
143
-/**
144
- * Closes all currently opened data channels.
145
- */
146
-DataChannels.prototype.closeAllChannels = function() {
147
-    this._dataChannels.forEach(dc => {
148
-        // the DC will be removed from the array on 'onclose' event
149
-        dc.close();
150
-    });
151
-};
152
-
153
-/**
154
- * Sends a "selected endpoint changed" message via the data channel.
155
- * @param endpointId {string} the id of the selected endpoint
156
- * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
157
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send})
158
- * or Error with "No opened data channels found!" message.
159
- */
160
-DataChannels.prototype.sendSelectedEndpointMessage = function(endpointId) {
161
-    this._onXXXEndpointChanged('selected', endpointId);
162
-};
163
-
164
-/**
165
- * Sends a "pinned endpoint changed" message via the data channel.
166
- * @param endpointId {string} the id of the pinned endpoint
167
- * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
168
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send})
169
- * or Error with "No opened data channels found!" message.
170
- */
171
-DataChannels.prototype.sendPinnedEndpointMessage = function(endpointId) {
172
-    this._onXXXEndpointChanged('pinned', endpointId);
173
-};
174
-
175
-/**
176
- * Notifies Videobridge about a change in the value of a specific
177
- * endpoint-related property such as selected endpoint and pinned endpoint.
178
- *
179
- * @param xxx the name of the endpoint-related property whose value changed
180
- * @param userResource the new value of the endpoint-related property after the
181
- * change
182
- * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
183
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send})
184
- * or Error with "No opened data channels found!" message.
185
- */
186
-DataChannels.prototype._onXXXEndpointChanged = function(xxx, userResource) {
187
-    // Derive the correct words from xxx such as selected and Selected, pinned
188
-    // and Pinned.
189
-    const head = xxx.charAt(0);
190
-    const tail = xxx.substring(1);
191
-    const lower = head.toLowerCase() + tail;
192
-    const upper = head.toUpperCase() + tail;
193
-
194
-    logger.log(
195
-            `sending ${lower} endpoint changed notification to the bridge: `,
196
-            userResource);
197
-
198
-    const jsonObject = {};
199
-
200
-    jsonObject.colibriClass = `${upper}EndpointChangedEvent`;
201
-    jsonObject[`${lower}Endpoint`]
202
-        = userResource ? userResource : null;
203
-
204
-    this.send(jsonObject);
205
-
206
-    // Notify Videobridge about the specified endpoint change.
207
-    logger.log(`${lower} endpoint changed: `, userResource);
208
-};
209
-
210
-DataChannels.prototype._some = function(callback, thisArg) {
211
-    const dataChannels = this._dataChannels;
212
-
213
-    if (dataChannels && dataChannels.length !== 0) {
214
-        if (thisArg) {
215
-            return dataChannels.some(callback, thisArg);
216
-        }
217
-
218
-        return dataChannels.some(callback);
219
-
220
-    }
221
-
222
-    return false;
223
-
224
-};
225
-
226
-/**
227
- * Sends passed object via the first found open datachannel
228
- * @param jsonObject {object} the object that will be sent
229
- * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
230
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send})
231
- * or Error with "No opened data channels found!" message.
232
- */
233
-DataChannels.prototype.send = function(jsonObject) {
234
-    if (!this._some(dataChannel => {
235
-        if (dataChannel.readyState === 'open') {
236
-            dataChannel.send(JSON.stringify(jsonObject));
237
-
238
-            return true;
239
-        }
240
-    })) {
241
-        throw new Error('No opened data channels found!');
242
-    }
243
-};
244
-
245
-/**
246
- * Sends message via the datachannels.
247
- * @param to {string} the id of the endpoint that should receive the message.
248
- * If "" the message will be sent to all participants.
249
- * @param payload {object} the payload of the message.
250
- * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see
251
- * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel/send})
252
- * or Error with "No opened data channels found!" message.
253
- */
254
-DataChannels.prototype.sendDataChannelMessage = function(to, payload) {
255
-    this.send({
256
-        colibriClass: 'EndpointMessage',
257
-        to,
258
-        msgPayload: payload
259
-    });
260
-};
261
-
262
-/**
263
- * Sends a "lastN value changed" message via the data channel.
264
- * @param value {int} The new value for lastN. -1 means unlimited.
265
- */
266
-DataChannels.prototype.sendSetLastNMessage = function(value) {
267
-    const jsonObject = {
268
-        colibriClass: 'LastNChangedEvent',
269
-        lastN: value
270
-    };
271
-
272
-    this.send(jsonObject);
273
-    logger.log(`Channel lastN set to: ${value}`);
274
-};
275
-
276
-module.exports = DataChannels;

+ 147
- 131
modules/RTC/RTC.js Vedi File

@@ -2,7 +2,7 @@
2 2
 
3 3
 import { getLogger } from 'jitsi-meet-logger';
4 4
 
5
-import DataChannels from './DataChannels';
5
+import BridgeChannel from './BridgeChannel';
6 6
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
7 7
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
8 8
 import JitsiLocalTrack from './JitsiLocalTrack';
@@ -81,18 +81,24 @@ export default class RTC extends Listenable {
81 81
 
82 82
         this.options = options;
83 83
 
84
-        // A flag whether we had received that the data channel had opened
85
-        // we can get this flag out of sync if for some reason data channel got
86
-        // closed from server, a desired behaviour so we can see errors when
87
-        // this happen
88
-        this.dataChannelsOpen = false;
84
+        // BridgeChannel instance.
85
+        // @private
86
+        // @type {BridgeChannel}
87
+        this._channel = null;
88
+
89
+        // A flag whether we had received that the channel had opened we can
90
+        // get this flag out of sync if for some reason channel got closed
91
+        // from server, a desired behaviour so we can see errors when this
92
+        // happen.
93
+        // @private
94
+        // @type {boolean}
95
+        this._channelOpen = false;
89 96
 
90 97
         /**
91 98
          * The value specified to the last invocation of setLastN before the
92
-         * data channels completed opening. If non-null, the value will be sent
93
-         * through a data channel (once) as soon as it opens and will then be
99
+         * channel completed opening. If non-null, the value will be sent
100
+         * through a channel (once) as soon as it opens and will then be
94 101
          * discarded.
95
-         *
96 102
          * @private
97 103
          * @type {number}
98 104
          */
@@ -100,7 +106,7 @@ export default class RTC extends Listenable {
100 106
 
101 107
         /**
102 108
          * Defines the last N endpoints list. It can be null or an array once
103
-         * initialised with a datachannel last N event.
109
+         * initialised with a channel last N event.
104 110
          * @type {Array<string>|null}
105 111
          * @private
106 112
          */
@@ -142,13 +148,13 @@ export default class RTC extends Listenable {
142 148
 
143 149
     /**
144 150
      * Creates the local MediaStreams.
145
-     * @param {Object} [options] optional parameters
146
-     * @param {Array} options.devices the devices that will be requested
147
-     * @param {string} options.resolution resolution constraints
148
-     * @param {bool} options.dontCreateJitsiTrack if <tt>true</tt> objects with
149
-     * the following structure {stream: the Media Stream, type: "audio" or
150
-     * "video", videoType: "camera" or "desktop"} will be returned trough the
151
-     * Promise, otherwise JitsiTrack objects will be returned.
151
+     * @param {object} [options] Optional parameters.
152
+     * @param {array} options.devices The devices that will be requested.
153
+     * @param {string} options.resolution Resolution constraints.
154
+     * @param {bool} options.dontCreateJitsiTrack If <tt>true</tt> objects with
155
+     *     the following structure {stream: the Media Stream, type: "audio" or
156
+     *     "video", videoType: "camera" or "desktop"} will be returned trough
157
+     *     the Promise, otherwise JitsiTrack objects will be returned.
152 158
      * @param {string} options.cameraDeviceId
153 159
      * @param {string} options.micDeviceId
154 160
      * @returns {*} Promise object that will receive the new JitsiTracks
@@ -168,59 +174,62 @@ export default class RTC extends Listenable {
168 174
     }
169 175
 
170 176
     /**
171
-     * Initializes the data channels of this instance.
172
-     * @param peerconnection the associated PeerConnection.
173
-     */
174
-    initializeDataChannels(peerconnection) {
175
-        if (this.options.config.openSctp) {
176
-            this.dataChannels = new DataChannels(peerconnection,
177
-                this.eventEmitter);
178
-
179
-            this._dataChannelOpenListener = () => {
180
-                // mark that dataChannel is opened
181
-                this.dataChannelsOpen = true;
182
-
183
-                // when the data channel becomes available, tell the bridge
184
-                // about video selections so that it can do adaptive simulcast,
185
-                // we want the notification to trigger even if userJid
186
-                // is undefined, or null.
187
-                try {
188
-                    this.dataChannels.sendPinnedEndpointMessage(
189
-                        this._pinnedEndpoint);
190
-                    this.dataChannels.sendSelectedEndpointMessage(
191
-                        this._selectedEndpoint);
192
-                } catch (error) {
193
-                    GlobalOnErrorHandler.callErrorHandler(error);
194
-                    logger.error(
195
-                        `Cannot send selected(${this._selectedEndpoint})`
196
-                        + `pinned(${this._pinnedEndpoint}) endpoint message.`,
197
-                        error);
198
-                }
199
-
200
-                this.removeListener(RTCEvents.DATA_CHANNEL_OPEN,
201
-                    this._dataChannelOpenListener);
202
-                this._dataChannelOpenListener = null;
203
-
204
-                // If setLastN was invoked before the data channels completed
205
-                // opening, apply the specified value now that the data channels
206
-                // are open. NOTE that -1 is the default value assumed by both
207
-                // RTC module and the JVB.
208
-                if (this._lastN !== -1) {
209
-                    this.dataChannels.sendSetLastNMessage(this._lastN);
210
-                }
211
-            };
212
-            this.addListener(RTCEvents.DATA_CHANNEL_OPEN,
213
-                this._dataChannelOpenListener);
214
-
215
-            // Add Last N change listener.
216
-            this.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
217
-                this._lastNChangeListener);
218
-        }
177
+     * Initializes the bridge channel of this instance.
178
+     * At least one of both, peerconnection or wsUrl parameters, must be
179
+     * given.
180
+     * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection
181
+     * instance.
182
+     * @param {string} [wsUrl] WebSocket URL.
183
+     */
184
+    initializeBridgeChannel(peerconnection, wsUrl) {
185
+        this._channel = new BridgeChannel(
186
+            peerconnection, wsUrl, this.eventEmitter);
187
+
188
+        this._channelOpenListener = () => {
189
+            // Mark that channel as opened.
190
+            this._channelOpen = true;
191
+
192
+            // When the channel becomes available, tell the bridge about
193
+            // video selections so that it can do adaptive simulcast,
194
+            // we want the notification to trigger even if userJid
195
+            // is undefined, or null.
196
+            try {
197
+                this._channel.sendPinnedEndpointMessage(
198
+                    this._pinnedEndpoint);
199
+                this._channel.sendSelectedEndpointMessage(
200
+                    this._selectedEndpoint);
201
+            } catch (error) {
202
+                GlobalOnErrorHandler.callErrorHandler(error);
203
+                logger.error(
204
+                    `Cannot send selected(${this._selectedEndpoint})`
205
+                    + `pinned(${this._pinnedEndpoint}) endpoint message.`,
206
+                    error);
207
+            }
208
+
209
+            this.removeListener(RTCEvents.DATA_CHANNEL_OPEN,
210
+                this._channelOpenListener);
211
+            this._channelOpenListener = null;
212
+
213
+            // If setLastN was invoked before the bridge channel completed
214
+            // opening, apply the specified value now that the channel
215
+            // is open. NOTE that -1 is the default value assumed by both
216
+            // RTC module and the JVB.
217
+            if (this._lastN !== -1) {
218
+                this._channel.sendSetLastNMessage(this._lastN);
219
+            }
220
+        };
221
+
222
+        this.addListener(RTCEvents.DATA_CHANNEL_OPEN,
223
+            this._channelOpenListener);
224
+
225
+        // Add Last N change listener.
226
+        this.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
227
+            this._lastNChangeListener);
219 228
     }
220 229
 
221 230
     /**
222 231
      * Receives events when Last N had changed.
223
-     * @param {array} lastNEndpoints the new Last N endpoints.
232
+     * @param {array} lastNEndpoints The new Last N endpoints.
224 233
      * @private
225 234
      */
226 235
     _onLastNChanged(lastNEndpoints = []) {
@@ -247,13 +256,19 @@ export default class RTC extends Listenable {
247 256
      * PeerConnection has been closed using PeerConnection.close() method.
248 257
      */
249 258
     onCallEnded() {
250
-        if (this.dataChannels) {
251
-            // DataChannels are not explicitly closed as the PeerConnection
252
-            // is closed on call ended which triggers data channel onclose
253
-            // events. The reference is cleared to disable any logic related
254
-            // to the data channels.
255
-            this.dataChannels = null;
256
-            this.dataChannelsOpen = false;
259
+        if (this._channel) {
260
+            // The BridgeChannel is not explicitly closed as the PeerConnection
261
+            // is closed on call ended which triggers datachannel onclose
262
+            // events. If using a WebSocket, the channel must be closed since
263
+            // it is not managed by the PeerConnection.
264
+            // The reference is cleared to disable any logic related to the
265
+            // channel.
266
+            if (this._channel && this._channel.mode === 'websocket') {
267
+                this._channel.close();
268
+            }
269
+
270
+            this._channel = null;
271
+            this._channelOpen = false;
257 272
         }
258 273
     }
259 274
 
@@ -261,17 +276,17 @@ export default class RTC extends Listenable {
261 276
      * Elects the participant with the given id to be the selected participant
262 277
      * in order to always receive video for this participant (even when last n
263 278
      * is enabled).
264
-     * If there is no data channel we store it and send it through the channel
265
-     * once it is created.
266
-     * @param id {string} the user id.
279
+     * If there is no channel we store it and send it through the channel once
280
+     * it is created.
281
+     * @param {string} id The user id.
267 282
      * @throws NetworkError or InvalidStateError or Error if the operation
268 283
      * fails.
269 284
      */
270 285
     selectEndpoint(id) {
271
-        // cache the value if channel is missing, till we open it
286
+        // Cache the value if channel is missing, till we open it.
272 287
         this._selectedEndpoint = id;
273
-        if (this.dataChannels && this.dataChannelsOpen) {
274
-            this.dataChannels.sendSelectedEndpointMessage(id);
288
+        if (this._channel && this._channelOpen) {
289
+            this._channel.sendSelectedEndpointMessage(id);
275 290
         }
276 291
     }
277 292
 
@@ -279,15 +294,15 @@ export default class RTC extends Listenable {
279 294
      * Elects the participant with the given id to be the pinned participant in
280 295
      * order to always receive video for this participant (even when last n is
281 296
      * enabled).
282
-     * @param id {string} the user id
297
+     * @param {stirng} id The user id.
283 298
      * @throws NetworkError or InvalidStateError or Error if the operation
284 299
      * fails.
285 300
      */
286 301
     pinEndpoint(id) {
287
-        // cache the value if channel is missing, till we open it
302
+        // Cache the value if channel is missing, till we open it.
288 303
         this._pinnedEndpoint = id;
289
-        if (this.dataChannels && this.dataChannelsOpen) {
290
-            this.dataChannels.sendPinnedEndpointMessage(id);
304
+        if (this._channel && this._channelOpen) {
305
+            this._channel.sendPinnedEndpointMessage(id);
291 306
         }
292 307
     }
293 308
 
@@ -337,19 +352,20 @@ export default class RTC extends Listenable {
337 352
 
338 353
     /**
339 354
      * Creates new <tt>TraceablePeerConnection</tt>
340
-     * @param {SignalingLayer} signaling the signaling layer that will
341
-     * provide information about the media or participants which is not carried
342
-     * over SDP.
343
-     * @param {Object} iceConfig an object describing the ICE config like
344
-     * defined in the WebRTC specification.
345
-     * @param {boolean} isP2P indicates whether or not the new TPC will be used
346
-     * in a peer to peer type of session
347
-     * @param {Object} options the config options
348
-     * @param {boolean} options.disableSimulcast if set to 'true' will disable
349
-     * the simulcast
350
-     * @param {boolean} options.disableRtx if set to 'true' will disable the RTX
351
-     * @param {boolean} options.preferH264 if set to 'true' H264 will be
352
-     * preferred over other video codecs.
355
+     * @param {SignalingLayer} signaling The signaling layer that will
356
+     *      provide information about the media or participants which is not
357
+     *      carried over SDP.
358
+     * @param {object} iceConfig An object describing the ICE config like
359
+     *      defined in the WebRTC specification.
360
+     * @param {boolean} isP2P Indicates whether or not the new TPC will be used
361
+     *      in a peer to peer type of session.
362
+     * @param {object} options The config options.
363
+     * @param {boolean} options.disableSimulcast If set to 'true' will disable
364
+     *      the simulcast.
365
+     * @param {boolean} options.disableRtx If set to 'true' will disable the
366
+     *      RTX.
367
+     * @param {boolean} options.preferH264 If set to 'true' H264 will be
368
+     *      preferred over other video codecs.
353 369
      * @return {TraceablePeerConnection}
354 370
      */
355 371
     createPeerConnection(signaling, iceConfig, isP2P, options) {
@@ -436,7 +452,7 @@ export default class RTC extends Listenable {
436 452
     /**
437 453
      * Returns the local tracks of the given media type, or all local tracks if
438 454
      * no specific type is given.
439
-     * @param {MediaType} [mediaType] optional media type filter
455
+     * @param {MediaType} [mediaType] Optional media type filter.
440 456
      * (audio or video).
441 457
      */
442 458
     getLocalTracks(mediaType) {
@@ -452,8 +468,8 @@ export default class RTC extends Listenable {
452 468
 
453 469
     /**
454 470
      * Obtains all remote tracks currently known to this RTC module instance.
455
-     * @param {MediaType} [mediaType] the remote tracks will be filtered
456
-     * by their media type if this argument is specified.
471
+     * @param {MediaType} [mediaType] The remote tracks will be filtered
472
+     *      by their media type if this argument is specified.
457 473
      * @return {Array<JitsiRemoteTrack>}
458 474
      */
459 475
     getRemoteTracks(mediaType) {
@@ -472,7 +488,7 @@ export default class RTC extends Listenable {
472 488
 
473 489
     /**
474 490
      * Set mute for all local audio streams attached to the conference.
475
-     * @param value the mute value
491
+     * @param value The mute value.
476 492
      * @returns {Promise}
477 493
      */
478 494
     setAudioMute(value) {
@@ -506,7 +522,7 @@ export default class RTC extends Listenable {
506 522
      * Removes all JitsiRemoteTracks associated with given MUC nickname
507 523
      * (resource part of the JID). Returns array of removed tracks.
508 524
      *
509
-     * @param {string} owner - The resource part of the MUC JID.
525
+     * @param {string} Owner The resource part of the MUC JID.
510 526
      * @returns {JitsiRemoteTrack[]}
511 527
      */
512 528
     removeRemoteTracks(owner) {
@@ -560,7 +576,7 @@ export default class RTC extends Listenable {
560 576
     /**
561 577
      * Returns true if changing the input (camera / microphone) or output
562 578
      * (audio) device is supported and false if not.
563
-     * @params {string} [deviceType] - type of device to change. Default is
579
+     * @param {string} [deviceType] Type of device to change. Default is
564 580
      *      undefined or 'input', 'output' - for audio output device change.
565 581
      * @returns {boolean} true if available, false otherwise.
566 582
      */
@@ -580,7 +596,7 @@ export default class RTC extends Listenable {
580 596
     /**
581 597
      * Returns list of available media devices if its obtained, otherwise an
582 598
      * empty array is returned/
583
-     * @returns {Array} list of available media devices.
599
+     * @returns {array} list of available media devices.
584 600
      */
585 601
     static getCurrentlyAvailableMediaDevices() {
586 602
         return RTCUtils.getCurrentlyAvailableMediaDevices();
@@ -596,9 +612,9 @@ export default class RTC extends Listenable {
596 612
 
597 613
     /**
598 614
      * Sets current audio output device.
599
-     * @param {string} deviceId - id of 'audiooutput' device from
600
-     *      navigator.mediaDevices.enumerateDevices()
601
-     * @returns {Promise} - resolves when audio output is changed, is rejected
615
+     * @param {string} deviceId Id of 'audiooutput' device from
616
+     *      navigator.mediaDevices.enumerateDevices().
617
+     * @returns {Promise} resolves when audio output is changed, is rejected
602 618
      *      otherwise
603 619
      */
604 620
     static setAudioOutputDevice(deviceId) {
@@ -614,7 +630,7 @@ export default class RTC extends Listenable {
614 630
      * "streams/channels/tracks" for receiving remote stream/tracks, as opposed
615 631
      * to Plan B where there are only 3 channels: audio, video and data.
616 632
      *
617
-     * @param {MediaStream} stream the WebRTC MediaStream instance
633
+     * @param {MediaStream} stream The WebRTC MediaStream instance.
618 634
      * @returns {boolean}
619 635
      */
620 636
     static isUserStream(stream) {
@@ -630,7 +646,7 @@ export default class RTC extends Listenable {
630 646
      * "streams/channels/tracks" for receiving remote stream/tracks, as opposed
631 647
      * to Plan B where there are only 3 channels: audio, video and data.
632 648
      *
633
-     * @param {string} streamId the id of WebRTC MediaStream
649
+     * @param {string} streamId The id of WebRTC MediaStream.
634 650
      * @returns {boolean}
635 651
      */
636 652
     static isUserStreamById(streamId) {
@@ -640,7 +656,8 @@ export default class RTC extends Listenable {
640 656
 
641 657
     /**
642 658
      * Allows to receive list of available cameras/microphones.
643
-     * @param {function} callback would receive array of devices as an argument
659
+     * @param {function} callback Would receive array of devices as an
660
+     *      argument.
644 661
      */
645 662
     static enumerateDevices(callback) {
646 663
         RTCUtils.enumerateDevices(callback);
@@ -649,7 +666,7 @@ export default class RTC extends Listenable {
649 666
     /**
650 667
      * A method to handle stopping of the stream.
651 668
      * One point to handle the differences in various implementations.
652
-     * @param mediaStream MediaStream object to stop.
669
+     * @param {MediaStream} mediaStream MediaStream object to stop.
653 670
      */
654 671
     static stopMediaStream(mediaStream) {
655 672
         RTCUtils.stopMediaStream(mediaStream);
@@ -664,12 +681,12 @@ export default class RTC extends Listenable {
664 681
     }
665 682
 
666 683
     /**
667
-     * Closes all currently opened data channels.
684
+     * Closes the currently opened bridge channel.
668 685
      */
669
-    closeAllDataChannels() {
670
-        if (this.dataChannels) {
671
-            this.dataChannels.closeAllChannels();
672
-            this.dataChannelsOpen = false;
686
+    closeBridgeChannel() {
687
+        if (this._channel) {
688
+            this._channel.close();
689
+            this._channelOpen = false;
673 690
 
674 691
             this.removeListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
675 692
                 this._lastNChangeListener);
@@ -704,18 +721,18 @@ export default class RTC extends Listenable {
704 721
     /* eslint-enable max-params */
705 722
 
706 723
     /**
707
-     * Sends message via the datachannels.
708
-     * @param to {string} the id of the endpoint that should receive the
709
-     * message. If "" the message will be sent to all participants.
710
-     * @param payload {object} the payload of the message.
724
+     * Sends message via the bridge channel.
725
+     * @param {string} to The id of the endpoint that should receive the
726
+     *      message. If "" the message will be sent to all participants.
727
+     * @param {object} payload The payload of the message.
711 728
      * @throws NetworkError or InvalidStateError or Error if the operation
712
-     * fails or there is no data channel created
729
+     * fails or there is no data channel created.
713 730
      */
714
-    sendDataChannelMessage(to, payload) {
715
-        if (this.dataChannels) {
716
-            this.dataChannels.sendDataChannelMessage(to, payload);
731
+    sendChannelMessage(to, payload) {
732
+        if (this._channel) {
733
+            this._channel.sendMessage(to, payload);
717 734
         } else {
718
-            throw new Error('Data channels support is disabled!');
735
+            throw new Error('Channel support is disabled!');
719 736
         }
720 737
     }
721 738
 
@@ -723,13 +740,13 @@ export default class RTC extends Listenable {
723 740
      * Selects a new value for "lastN". The requested amount of videos are going
724 741
      * to be delivered after the value is in effect. Set to -1 for unlimited or
725 742
      * all available videos.
726
-     * @param value {number} the new value for lastN.
743
+     * @param {number} value the new value for lastN.
727 744
      */
728 745
     setLastN(value) {
729 746
         if (this._lastN !== value) {
730 747
             this._lastN = value;
731
-            if (this.dataChannels && this.dataChannelsOpen) {
732
-                this.dataChannels.sendSetLastNMessage(value);
748
+            if (this._channel && this._channelOpen) {
749
+                this._channel.sendSetLastNMessage(value);
733 750
             }
734 751
             this.eventEmitter.emit(RTCEvents.LASTN_VALUE_CHANGED, value);
735 752
         }
@@ -737,13 +754,12 @@ export default class RTC extends Listenable {
737 754
 
738 755
     /**
739 756
      * Indicates if the endpoint id is currently included in the last N.
740
-     *
741
-     * @param {string} id the endpoint id that we check for last N.
757
+     * @param {string} id The endpoint id that we check for last N.
742 758
      * @returns {boolean} true if the endpoint id is in the last N or if we
743
-     * don't have data channel support, otherwise we return false.
759
+     * don't have bridge channel support, otherwise we return false.
744 760
      */
745 761
     isInLastN(id) {
746
-        return !this._lastNEndpoints // lastNEndpoints not initialised yet
762
+        return !this._lastNEndpoints // lastNEndpoints not initialised yet.
747 763
             || this._lastNEndpoints.indexOf(id) > -1;
748 764
     }
749 765
 }

+ 9
- 0
modules/RTC/RTCBrowserType.js Vedi File

@@ -218,6 +218,15 @@ const RTCBrowserType = {
218 218
         return !RTCBrowserType.isFirefox();
219 219
     },
220 220
 
221
+    /**
222
+     * Checks if the current browser supports WebRTC datachannels.
223
+     * @return {boolean}
224
+     */
225
+    supportsDataChannels() {
226
+        // NOTE: Edge does not yet implement DataChannel.
227
+        return !RTCBrowserType.isEdge();
228
+    },
229
+
221 230
     /**
222 231
      * Checks if the current browser reports round trip time statistics for
223 232
      * the ICE candidate pair.

+ 23
- 0
modules/xmpp/JingleSessionPC.js Vedi File

@@ -150,6 +150,12 @@ export default class JingleSessionPC extends JingleSession {
150 150
          */
151 151
         this._gatheringReported = false;
152 152
 
153
+        /**
154
+         * WebSocket URL for the bridge channel with the videobridge.
155
+         * @type {string}
156
+         */
157
+        this.bridgeWebSocketUrl = null;
158
+
153 159
         this.lasticecandidate = false;
154 160
         this.closed = false;
155 161
 
@@ -643,6 +649,22 @@ export default class JingleSessionPC extends JingleSession {
643 649
         });
644 650
     }
645 651
 
652
+    /**
653
+     * Reads the "url" parameter in the <web-socket> tag of the jingle offer iq
654
+     * and stores it into this.bridgeWebSocketUrl.
655
+     * @param contets
656
+     */
657
+    readBridgeWebSocketUrl(contents) {
658
+        const webSocket
659
+            = $(contents)
660
+                .find('transport>web-socket')
661
+                .first();
662
+
663
+        if (webSocket.length === 1) {
664
+            this.bridgeWebSocketUrl = webSocket[0].getAttribute('url');
665
+        }
666
+    }
667
+
646 668
     /**
647 669
      * Makes the underlying TraceablePeerConnection generate new SSRC for
648 670
      * the recvonly video stream.
@@ -1365,6 +1387,7 @@ export default class JingleSessionPC extends JingleSession {
1365 1387
 
1366 1388
         remoteSdp.fromJingle(offerIq);
1367 1389
         this.readSsrcInfo($(offerIq).find('>content'));
1390
+        this.readBridgeWebSocketUrl($(offerIq).find('>content'));
1368 1391
 
1369 1392
         return remoteSdp;
1370 1393
     }

+ 25
- 6
modules/xmpp/moderator.js Vedi File

@@ -1,11 +1,13 @@
1 1
 /* global $, $iq, Promise, Strophe */
2 2
 
3 3
 const logger = require('jitsi-meet-logger').getLogger(__filename);
4
+
4 5
 const XMPPEvents = require('../../service/xmpp/XMPPEvents');
5 6
 const AuthenticationEvents
6 7
     = require('../../service/authentication/AuthenticationEvents');
7 8
 const GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');
8 9
 
10
+import RTCBrowserType from '../RTC/RTCBrowserType';
9 11
 import Settings from '../settings/Settings';
10 12
 
11 13
 /**
@@ -210,13 +212,30 @@ Moderator.prototype.createConferenceIq = function() {
210 212
                 value: this.options.conference.minBitrate
211 213
             }).up();
212 214
     }
213
-    if (this.options.conference.openSctp !== undefined) {
214
-        elem.c(
215
-            'property', {
216
-                name: 'openSctp',
217
-                value: this.options.conference.openSctp
218
-            }).up();
215
+
216
+    let openSctp;
217
+
218
+    switch (this.options.conference.openBridgeChannel) {
219
+    case 'datachannel':
220
+    case true:
221
+    case undefined:
222
+        openSctp = true;
223
+        break;
224
+    case 'websocket':
225
+        openSctp = false;
226
+        break;
227
+    }
228
+
229
+    if (openSctp && !RTCBrowserType.supportsDataChannels()) {
230
+        openSctp = false;
219 231
     }
232
+
233
+    elem.c(
234
+        'property', {
235
+            name: 'openSctp',
236
+            value: openSctp
237
+        }).up();
238
+
220 239
     if (this.options.conference.startAudioMuted !== undefined) {
221 240
         elem.c(
222 241
             'property', {

Loading…
Annulla
Salva