Explorar el Código

feat(screenshare): support remote wireless screenshare (#857)

* feat(screenshare): support remote wireless screenshare

- ProxyConnectionService is exposed and meant to be the
  facade for creating and updating a peer connection with
  another peer, outside of the muc.
- ProxyConnectionPC wraps JingleSessionPC so the peer
  connection handling can be reused.

* attempt to make more configurable
dev1
virtuacoplenny hace 6 años
padre
commit
dbf2275852
No account linked to committer's email address

+ 12
- 0
JitsiMeetJS.js Ver fichero

27
 import browser from './modules/browser';
27
 import browser from './modules/browser';
28
 import ScriptUtil from './modules/util/ScriptUtil';
28
 import ScriptUtil from './modules/util/ScriptUtil';
29
 import recordingConstants from './modules/recording/recordingConstants';
29
 import recordingConstants from './modules/recording/recordingConstants';
30
+import ProxyConnectionService
31
+    from './modules/proxyconnection/ProxyConnectionService';
30
 import Statistics from './modules/statistics/statistics';
32
 import Statistics from './modules/statistics/statistics';
31
 import * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';
33
 import * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';
32
 
34
 
129
     version: '{#COMMIT_HASH#}',
131
     version: '{#COMMIT_HASH#}',
130
 
132
 
131
     JitsiConnection,
133
     JitsiConnection,
134
+
135
+    /**
136
+     * {@code ProxyConnectionService} is used to connect a remote peer to a
137
+     * local Jitsi participant without going through a Jitsi conference. It is
138
+     * currently used for room integration development, specifically wireless
139
+     * screensharing. Its API is experimental and will likely change; usage of
140
+     * it is advised against.
141
+     */
142
+    ProxyConnectionService,
143
+
132
     constants: {
144
     constants: {
133
         participantConnectionStatus: ParticipantConnectionStatus,
145
         participantConnectionStatus: ParticipantConnectionStatus,
134
         recording: recordingConstants,
146
         recording: recordingConstants,

+ 11
- 0
modules/RTC/RTC.js Ver fichero

202
         }
202
         }
203
     }
203
     }
204
 
204
 
205
+    /**
206
+     * Exposes the private helper for converting a WebRTC MediaStream to a
207
+     * JitsiLocalTrack.
208
+     *
209
+     * @param {Array<Object>} tracksInfo
210
+     * @returns {Array<JitsiLocalTrack>}
211
+     */
212
+    static newCreateLocalTracks(tracksInfo) {
213
+        return _newCreateLocalTracks(tracksInfo);
214
+    }
215
+
205
     /**
216
     /**
206
      * Creates the local MediaStreams.
217
      * Creates the local MediaStreams.
207
      * @param {object} [options] Optional parameters.
218
      * @param {object} [options] Optional parameters.

+ 388
- 0
modules/proxyconnection/ProxyConnectionPC.js Ver fichero

1
+import { getLogger } from 'jitsi-meet-logger';
2
+
3
+import RTC from '../RTC/RTC';
4
+import RTCEvents from '../../service/RTC/RTCEvents';
5
+import XMPPEvents from '../../service/xmpp/XMPPEvents';
6
+
7
+import JingleSessionPC from '../xmpp/JingleSessionPC';
8
+import { DEFAULT_STUN_SERVERS } from '../xmpp/xmpp';
9
+
10
+import { ACTIONS } from './constants';
11
+
12
+const logger = getLogger(__filename);
13
+
14
+/**
15
+ * An adapter around {@code JingleSessionPC} so its logic can be re-used without
16
+ * an XMPP connection. It is being re-used for consistency with the rest of the
17
+ * codebase and to leverage existing peer connection event handling. Also
18
+ * this class provides a facade to hide most of the API for
19
+ * {@code JingleSessionPC}.
20
+ */
21
+export default class ProxyConnectionPC {
22
+    /**
23
+     * Initializes a new {@code ProxyConnectionPC} instance.
24
+     *
25
+     * @param {Object} options - Values to initialize the instance with.
26
+     * @param {Object} [options.iceConfig] - The {@code RTCConfiguration} to use
27
+     * for the peer connection.
28
+     * @param {boolean} [options.isInitiator] - If true, the local client should
29
+     * send offers. If false, the local client should send answers. Defaults to
30
+     * false.
31
+     * @param {Function} options.onRemoteStream - Callback to invoke when a
32
+     * remote media stream has been received through the peer connection.
33
+     * @param {string} options.peerJid - The jid of the remote client with which
34
+     * the peer connection is being establish and which should receive direct
35
+     * messages regarding peer connection updates.
36
+     * @param {boolean} [options.receiveVideo] - Whether or not the peer
37
+     * connection should accept incoming video streams. Defaults to false.
38
+     * @param {Function} options.onSendMessage - Callback to invoke when a
39
+     * message has to be sent (signaled) out.
40
+     */
41
+    constructor(options = {}) {
42
+        this._options = {
43
+            iceConfig: {},
44
+            isInitiator: false,
45
+            receiveAudio: false,
46
+            receiveVideo: false,
47
+            ...options
48
+        };
49
+
50
+        /**
51
+         * Instances of {@code JitsiTrack} associated with this instance of
52
+         * {@code ProxyConnectionPC}.
53
+         *
54
+         * @type {Array<JitsiTrack>}
55
+         */
56
+        this._tracks = [];
57
+
58
+        /**
59
+         * The active instance of {@code JingleSessionPC}.
60
+         *
61
+         * @type {JingleSessionPC|null}
62
+         */
63
+        this._peerConnection = null;
64
+
65
+        // Bind event handlers so they are only bound once for every instance.
66
+        this._onError = this._onError.bind(this);
67
+        this._onRemoteStream = this._onRemoteStream.bind(this);
68
+        this._onSendMessage = this._onSendMessage.bind(this);
69
+    }
70
+
71
+    /**
72
+     * Returns the jid of the remote peer with which this peer connection should
73
+     * be established with.
74
+     *
75
+     * @returns {string}
76
+     */
77
+    getPeerJid() {
78
+        return this._options.peerJid;
79
+    }
80
+
81
+    /**
82
+     * Updates the peer connection based on the passed in jingle.
83
+     *
84
+     * @param {Object} $jingle - An XML jingle element, wrapped in query,
85
+     * describing how the peer connection should be updated.
86
+     * @returns {void}
87
+     */
88
+    processMessage($jingle) {
89
+        switch ($jingle.attr('action')) {
90
+        case ACTIONS.ACCEPT:
91
+            this._onSessionAccept($jingle);
92
+            break;
93
+
94
+        case ACTIONS.INITIATE:
95
+            this._onSessionInitiate($jingle);
96
+            break;
97
+
98
+        case ACTIONS.TERMINATE:
99
+            this._onSessionTerminate($jingle);
100
+            break;
101
+
102
+        case ACTIONS.TRANSPORT_INFO:
103
+            this._onTransportInfo($jingle);
104
+            break;
105
+        }
106
+    }
107
+
108
+    /**
109
+     * Instantiates a peer connection and starts the offer/answer cycle to
110
+     * establish a connection with a remote peer.
111
+     *
112
+     * @param {Array<JitsiLocalTrack>} localTracks - Initial local tracks to add
113
+     * to add to the peer connection.
114
+     * @returns {void}
115
+     */
116
+    start(localTracks = []) {
117
+        if (this._peerConnection) {
118
+            return;
119
+        }
120
+
121
+        this._tracks = this._tracks.concat(localTracks);
122
+
123
+        this._peerConnection = this._createPeerConnection();
124
+
125
+        this._peerConnection.invite(localTracks);
126
+    }
127
+
128
+    /**
129
+     * Begins the process of disconnecting from a remote peer and cleaning up
130
+     * the peer connection.
131
+     *
132
+     * @returns {void}
133
+     */
134
+    stop() {
135
+        if (this._peerConnection) {
136
+            this._peerConnection.terminate();
137
+        }
138
+
139
+        this._onSessionTerminate();
140
+    }
141
+
142
+    /**
143
+     * Instantiates a new {@code JingleSessionPC} by stubbing out the various
144
+     * dependencies of {@code JingleSessionPC}.
145
+     *
146
+     * @private
147
+     * @returns {JingleSessionPC}
148
+     */
149
+    _createPeerConnection() {
150
+        /**
151
+         * {@code JingleSessionPC} takes in the entire jitsi-meet config.js
152
+         * object, which may not be accessible from the caller.
153
+         *
154
+         * @type {Object}
155
+         */
156
+        const configStub = {};
157
+
158
+        /**
159
+         * {@code JingleSessionPC} assumes an XMPP/Strophe connection object is
160
+         * passed through, which also has the jingle plugin initialized on it.
161
+         * This connection object is used to signal out peer connection updates
162
+         * via iqs, and those updates need to be piped back out to the remote
163
+         * peer.
164
+         *
165
+         * @type {Object}
166
+         */
167
+        const connectionStub = {
168
+            jingle: {
169
+                terminate: () => { /** no-op */ }
170
+            },
171
+            sendIQ: this._onSendMessage
172
+        };
173
+
174
+        /**
175
+         * {@code JingleSessionPC} can take in a custom ice configuration,
176
+         * depending on the peer connection type, peer-to-peer or other.
177
+         * However, {@code ProxyConnectionPC} always assume a peer-to-peer
178
+         * connection so the ice configuration is hard-coded with defaults.
179
+         *
180
+         * @type {Object}
181
+         */
182
+        const iceConfigStub = {
183
+            jvb: { iceServers: [] },
184
+            p2p: {
185
+                iceServers: DEFAULT_STUN_SERVERS,
186
+                ...this._options.iceConfig
187
+            }
188
+        };
189
+
190
+        /**
191
+         * {@code JingleSessionPC} expects an instance of
192
+         * {@code JitsiConference}, which has an event emitter that is used
193
+         * to signal various connection updates that the local client should
194
+         * act upon. The conference instance is not a dependency of a proxy
195
+         * connection, but the emitted events can be relevant to the proxy
196
+         * connection so the event emitter is stubbed.
197
+         *
198
+         * @param {string} event - The constant for the event type.
199
+         * @type {Function}
200
+         * @returns {void}
201
+         */
202
+        const emitter = event => {
203
+            switch (event) {
204
+            case XMPPEvents.CONNECTION_ICE_FAILED:
205
+            case XMPPEvents.CONNECTION_FAILED:
206
+                this._onError(ACTIONS.CONNECTION_ERROR, event);
207
+                break;
208
+            }
209
+        };
210
+
211
+        /**
212
+         * {@code JingleSessionPC} expects an instance of
213
+         * {@code JitsiConference} to be passed in. {@code ProxyConnectionPC}
214
+         * is instantiated outside of the {@code JitsiConference}, so it must be
215
+         * stubbed to prevent errors.
216
+         *
217
+         * @type {Object}
218
+         */
219
+        const roomStub = {
220
+            addPresenceListener: () => { /** no-op */ },
221
+            connectionTimes: [],
222
+            eventEmitter: { emit: emitter },
223
+            getMediaPresenceInfo: () => {
224
+                // Errors occur if this function does not return an object
225
+
226
+                return {};
227
+            },
228
+            removePresenceListener: () => { /** no-op */ }
229
+        };
230
+
231
+        /**
232
+         * Create an instance of {@code RTC} as it is required for peer
233
+         * connection creation by {@code JingleSessionPC}. An existing instance
234
+         * of {@code RTC} from elsewhere should not be re-used because it is
235
+         * a stateful grouping of utilities.
236
+         */
237
+        const rtc = new RTC(this, {});
238
+
239
+        /**
240
+         * Add the remote track listener here as {@code JingleSessionPC} has
241
+         * {@code TraceablePeerConnection} which uses {@code RTC}'s event
242
+         * emitter.
243
+         */
244
+        rtc.addListener(
245
+            RTCEvents.REMOTE_TRACK_ADDED,
246
+            this._onRemoteStream
247
+        );
248
+
249
+        const peerConnection = new JingleSessionPC(
250
+            undefined, // sid
251
+            undefined, // localJid
252
+            this._options.peerJid, // remoteJid
253
+            connectionStub, // connection
254
+            {
255
+                offerToReceiveAudio: this._options.receiveAudio,
256
+                offerToReceiveVideo: this._options.receiveVideo
257
+            }, // mediaConstraints
258
+            iceConfigStub, // iceConfig
259
+            true, // isP2P
260
+            this._options.isInitiator // isInitiator
261
+        );
262
+
263
+        /**
264
+         * An additional initialize call is necessary to properly set instance
265
+         * variable for calling.
266
+         */
267
+        peerConnection.initialize(roomStub, rtc, configStub);
268
+
269
+        return peerConnection;
270
+    }
271
+
272
+    /**
273
+     * Invoked when a connection related issue has been encountered.
274
+     *
275
+     * @param {string} errorType - The constant indicating the type of the error
276
+     * that occured.
277
+     * @param {string} details - Optional additional data about the error.
278
+     * @private
279
+     * @returns {void}
280
+     */
281
+    _onError(errorType, details = '') {
282
+        this._options.onError(this._options.peerJid, errorType, details);
283
+    }
284
+
285
+    /**
286
+     * Callback invoked when the peer connection has received a remote media
287
+     * stream.
288
+     *
289
+     * @param {JitsiRemoteTrack} jitsiRemoteTrack - The remote media stream
290
+     * wrapped in {@code JitsiRemoteTrack}.
291
+     * @private
292
+     * @returns {void}
293
+     */
294
+    _onRemoteStream(jitsiRemoteTrack) {
295
+        this._tracks.push(jitsiRemoteTrack);
296
+
297
+        this._options.onRemoteStream(jitsiRemoteTrack);
298
+    }
299
+
300
+    /**
301
+     * Callback invoked when {@code JingleSessionPC} needs to signal a message
302
+     * out to the remote peer.
303
+     *
304
+     * @param {XML} iq - The message to signal out.
305
+     * @private
306
+     * @returns {void}
307
+     */
308
+    _onSendMessage(iq) {
309
+        this._options.onSendMessage(this._options.peerJid, iq);
310
+    }
311
+
312
+    /**
313
+     * Callback invoked in response to an agreement to start a proxy connection.
314
+     * The passed in jingle element should contain an SDP answer to a previously
315
+     * sent SDP offer.
316
+     *
317
+     * @param {Object} $jingle - The jingle element wrapped in jQuery.
318
+     * @private
319
+     * @returns {void}
320
+     */
321
+    _onSessionAccept($jingle) {
322
+        if (!this._peerConnection) {
323
+            logger.error('Received an answer when no peer connection exists.');
324
+
325
+            return;
326
+        }
327
+
328
+        this._peerConnection.setAnswer($jingle);
329
+    }
330
+
331
+    /**
332
+     * Callback invoked in response to a request to start a proxy connection.
333
+     * The passed in jingle element should contain an SDP offer.
334
+     *
335
+     * @param {Object} $jingle - The jingle element wrapped in jQuery.
336
+     * @private
337
+     * @returns {void}
338
+     */
339
+    _onSessionInitiate($jingle) {
340
+        if (this._peerConnection) {
341
+            logger.error('Received an offer when an offer was already sent.');
342
+
343
+            return;
344
+        }
345
+
346
+        this._peerConnection = this._createPeerConnection();
347
+
348
+        this._peerConnection.acceptOffer(
349
+            $jingle,
350
+            () => { /** no-op */ },
351
+            () => this._onError(
352
+                this._options.peerJid,
353
+                ACTIONS.CONNECTION_ERROR,
354
+                'session initiate error'
355
+            )
356
+        );
357
+    }
358
+
359
+    /**
360
+     * Callback invoked in response to a request to disconnect an active proxy
361
+     * connection. Cleans up tracks and the peer connection.
362
+     *
363
+     * @private
364
+     * @returns {void}
365
+     */
366
+    _onSessionTerminate() {
367
+        this._tracks.forEach(track => track.dispose());
368
+        this._tracks = [];
369
+
370
+        if (!this._peerConnection) {
371
+            return;
372
+        }
373
+
374
+        this._peerConnection.onTerminated();
375
+    }
376
+
377
+    /**
378
+     * Callback invoked in response to ICE candidates from the remote peer.
379
+     * The passed in jingle element should contain an ICE candidate.
380
+     *
381
+     * @param {Object} $jingle - The jingle element wrapped in jQuery.
382
+     * @private
383
+     * @returns {void}
384
+     */
385
+    _onTransportInfo($jingle) {
386
+        this._peerConnection.addIceCandidates($jingle);
387
+    }
388
+}

+ 317
- 0
modules/proxyconnection/ProxyConnectionService.js Ver fichero

1
+/* globals $ */
2
+
3
+import { getLogger } from 'jitsi-meet-logger';
4
+import { $iq } from 'strophe.js';
5
+
6
+import * as MediaType from '../../service/RTC/MediaType';
7
+import VideoType from '../../service/RTC/VideoType';
8
+import RTC from '../RTC/RTC';
9
+
10
+import ProxyConnectionPC from './ProxyConnectionPC';
11
+import { ACTIONS } from './constants';
12
+
13
+const logger = getLogger(__filename);
14
+
15
+/**
16
+ * Instantiates a new ProxyConnectionPC and ensures only one exists at a given
17
+ * time. Currently it assumes ProxyConnectionPC is used only for screensharing
18
+ * and assumes IQs to be used for communication.
19
+ */
20
+export default class ProxyConnectionService {
21
+    /**
22
+     * Initializes a new {@code ProxyConnectionService} instance.
23
+     *
24
+     * @param {Object} options - Values to initialize the instance with.
25
+     * @param {boolean} [options.convertVideoToDesktop] - Whether or not proxied
26
+     * video should be returned as a desktop stream. Defaults to false.
27
+     * @param {Object} [options.iceConfig] - The {@code RTCConfiguration} to use
28
+     * for the peer connection.
29
+     * @param {Function} options.onRemoteStream - Callback to invoke when a
30
+     * remote video stream has been received and converted to a
31
+     * {@code JitsiLocakTrack}. The {@code JitsiLocakTrack} will be passed in.
32
+     * @param {Function} options.onSendMessage - Callback to invoke when a
33
+     * message has to be sent (signaled) out. The arguments passed in are the
34
+     * jid to send the message to and the message
35
+     */
36
+    constructor(options = {}) {
37
+        /**
38
+         * Holds a reference to the collection of all callbacks.
39
+         *
40
+         * @type {Object}
41
+         */
42
+        this._options = options;
43
+
44
+        /**
45
+         * The active instance of {@code ProxyConnectionService}.
46
+         *
47
+         * @type {ProxyConnectionPC|null}
48
+         */
49
+        this._peerConnection = null;
50
+
51
+        // Bind event handlers so they are only bound once for every instance.
52
+        this._onFatalError = this._onFatalError.bind(this);
53
+        this._onSendMessage = this._onSendMessage.bind(this);
54
+        this._onRemoteStream = this._onRemoteStream.bind(this);
55
+    }
56
+
57
+    /**
58
+     * Parses a message object regarding a proxy connection to create a new
59
+     * proxy connection or update and existing connection.
60
+     *
61
+     * @param {Object} message - A message object regarding establishing or
62
+     * updating a proxy connection.
63
+     * @param {Object} message.data - An object containing additional message
64
+     * details.
65
+     * @param {string} message.data.iq - The stringified iq which explains how
66
+     * and what to update regarding the proxy connection.
67
+     * @param {string} message.from - The message sender's full jid. Used for
68
+     * sending replies.
69
+     * @returns {void}
70
+     */
71
+    processMessage(message) {
72
+        const peerJid = message.from;
73
+
74
+        if (!peerJid) {
75
+            return;
76
+        }
77
+
78
+        // If a proxy connection has already been established and messages come
79
+        // from another peer jid then those messages should be replied to with
80
+        // a rejection.
81
+        if (this._peerConnection
82
+            && this._peerConnection.getPeerJid() !== peerJid) {
83
+            this._onFatalError(
84
+                peerJid,
85
+                ACTIONS.CONNECTION_ERROR,
86
+                'rejected'
87
+            );
88
+
89
+            return;
90
+        }
91
+
92
+        const iq = this._convertStringToXML(message.data.iq);
93
+        const $jingle = iq && iq.find('jingle');
94
+        const action = $jingle && $jingle.attr('action');
95
+
96
+        if (action === ACTIONS.INITIATE) {
97
+            this._peerConnection = this._createPeerConnection(peerJid, {
98
+                isInitiator: false,
99
+                receiveVideo: true
100
+            });
101
+        }
102
+
103
+        // Truthy check for peer connection added to protect against possibly
104
+        // receiving actions before an ACTIONS.INITIATE.
105
+        if (this._peerConnection) {
106
+            this._peerConnection.processMessage($jingle);
107
+        }
108
+
109
+        // Take additional steps to ensure the peer connection is cleaned up
110
+        // if it is to be closed.
111
+        if (action === ACTIONS.CONNECTION_ERROR
112
+            || action === ACTIONS.UNAVAILABLE
113
+            || action === ACTIONS.TERMINATE) {
114
+            this._selfCloseConnection();
115
+        }
116
+
117
+        return;
118
+    }
119
+
120
+    /**
121
+     * Instantiates and initiates a proxy peer connection.
122
+     *
123
+     * @param {string} peerJid - The jid of the remote client that should
124
+     * receive messages.
125
+     * @param {Array<JitsiLocalTrack>} localTracks - Initial media tracks to
126
+     * send through to the peer.
127
+     * @returns {void}
128
+     */
129
+    start(peerJid, localTracks = []) {
130
+        this._peerConnection = this._createPeerConnection(peerJid, {
131
+            isInitiator: true,
132
+            receiveVideo: false
133
+        });
134
+
135
+        this._peerConnection.start(localTracks);
136
+    }
137
+
138
+    /**
139
+     * Terminates any active proxy peer connection.
140
+     *
141
+     * @returns {void}
142
+     */
143
+    stop() {
144
+        if (this._peerConnection) {
145
+            this._peerConnection.stop();
146
+        }
147
+
148
+        this._peerConnection = null;
149
+    }
150
+
151
+    /**
152
+     * Transforms a stringified xML into a XML wrapped in jQuery.
153
+     *
154
+     * @param {string} xml - The XML in string form.
155
+     * @private
156
+     * @returns {Object|null} A jQuery version of the xml. Null will be returned
157
+     * if an error is encountered during transformation.
158
+     */
159
+    _convertStringToXML(xml) {
160
+        try {
161
+            const xmlDom = new DOMParser().parseFromString(xml, 'text/xml');
162
+
163
+            return $(xmlDom);
164
+        } catch (e) {
165
+            logger.error('Attempted to convert incorrectly formatted xml');
166
+
167
+            return null;
168
+        }
169
+    }
170
+
171
+    /**
172
+     * Helper for creating an instance of {@code ProxyConnectionPC}.
173
+     *
174
+     * @param {string} peerJid - The jid of the remote peer with which the
175
+     * {@code ProxyConnectionPC} will be established with.
176
+     * @param {Object} options - Additional defaults to instantiate the
177
+     * {@code ProxyConnectionPC} with. See the constructor of ProxyConnectionPC
178
+     * for more details.
179
+     * @private
180
+     * @returns {ProxyConnectionPC}
181
+     */
182
+    _createPeerConnection(peerJid, options = {}) {
183
+        if (!peerJid) {
184
+            throw new Error('Cannot create ProxyConnectionPC without a peer.');
185
+        }
186
+
187
+        const pcOptions = {
188
+            iceConfig: this._options.iceConfig,
189
+            onError: this._onFatalError,
190
+            onRemoteStream: this._onRemoteStream,
191
+            onSendMessage: this._onSendMessage,
192
+            peerJid,
193
+            ...options
194
+        };
195
+
196
+        return new ProxyConnectionPC(pcOptions);
197
+    }
198
+
199
+    /**
200
+     * Callback invoked when an error occurs that should cause
201
+     * {@code ProxyConnectionPC} to be closed if the peer is currently
202
+     * connected. Sends an error message/reply back to the peer.
203
+     *
204
+     * @param {string} peerJid - The peer jid with which the connection was
205
+     * attempted or started, and to which an iq with error details should be
206
+     * sent.
207
+     * @param {string} errorType - The constant indicating the type of the error
208
+     * that occured.
209
+     * @param {string} details - Optional additional data about the error.
210
+     * @private
211
+     * @returns {void}
212
+     */
213
+    _onFatalError(peerJid, errorType, details = '') {
214
+        logger.error(
215
+            'Received a proxy connection error', peerJid, errorType, details);
216
+
217
+        const iq = $iq({
218
+            to: peerJid,
219
+            type: 'set'
220
+        })
221
+            .c('jingle', {
222
+                xmlns: 'urn:xmpp:jingle:1',
223
+                action: errorType
224
+            })
225
+            .c('details')
226
+            .t(details)
227
+            .up();
228
+
229
+        this._onSendMessage(peerJid, iq);
230
+
231
+        if (this._peerConnection
232
+            && this._peerConnection.getPeerJid() === peerJid) {
233
+            this._selfCloseConnection();
234
+        }
235
+    }
236
+
237
+    /**
238
+     * Callback invoked when the remote peer of the {@code ProxyConnectionPC}
239
+     * has offered a media stream. The stream is converted into a
240
+     * {@code JitsiLocalTrack} for local usage if the {@code onRemoteStream}
241
+     * callback is defined.
242
+     *
243
+     * @param {JitsiRemoteTrack} jitsiRemoteTrack - The {@code JitsiRemoteTrack}
244
+     * for the peer's media stream.
245
+     * @private
246
+     * @returns {void}
247
+     */
248
+    _onRemoteStream(jitsiRemoteTrack) {
249
+        if (!this._options.onRemoteStream) {
250
+            logger.error('Remote track received without callback.');
251
+            jitsiRemoteTrack.dispose();
252
+
253
+            return;
254
+        }
255
+
256
+        const isVideo = jitsiRemoteTrack.isVideoTrack();
257
+        let videoType;
258
+
259
+        if (isVideo) {
260
+            videoType = this._options.convertVideoToDesktop
261
+                ? VideoType.DESKTOP : VideoType.CAMERA;
262
+        }
263
+
264
+        // Grab the webrtc media stream and pipe it through the same processing
265
+        // that would occur for a locally obtained media stream.
266
+        const mediaStream = jitsiRemoteTrack.getOriginalStream();
267
+        const jitsiLocalTracks = RTC.newCreateLocalTracks(
268
+            [
269
+                {
270
+                    deviceId:
271
+                        `proxy:${this._peerConnection.getPeerJid()}`,
272
+                    mediaType: isVideo ? MediaType.VIDEO : MediaType.AUDIO,
273
+                    stream: mediaStream,
274
+                    track: mediaStream.getVideoTracks()[0],
275
+                    videoType
276
+                }
277
+            ]);
278
+
279
+        this._options.onRemoteStream(jitsiLocalTracks[0]);
280
+    }
281
+
282
+    /**
283
+     * Formats and forwards a message an iq to be sent to a peer jid.
284
+     *
285
+     * @param {string} peerJid - The jid the iq should be sent to.
286
+     * @param {Object} iq - The iq which would be sent to the peer jid.
287
+     * @private
288
+     * @returns {void}
289
+     */
290
+    _onSendMessage(peerJid, iq) {
291
+        if (!this._options.onSendMessage) {
292
+            return;
293
+        }
294
+
295
+        try {
296
+            const stringifiedIq
297
+                = new XMLSerializer().serializeToString(iq.nodeTree || iq);
298
+
299
+            this._options.onSendMessage(peerJid, { iq: stringifiedIq });
300
+        } catch (e) {
301
+            logger.error('Attempted to send an incorrectly formatted iq.');
302
+        }
303
+    }
304
+
305
+    /**
306
+     * Invoked when preemptively closing the {@code ProxyConnectionPC}.
307
+     *
308
+     * @private
309
+     * @returns {void}
310
+     */
311
+    _selfCloseConnection() {
312
+        this.stop();
313
+
314
+        this._options.onConnectionClosed
315
+            && this._options.onConnectionClosed();
316
+    }
317
+}

+ 12
- 0
modules/proxyconnection/constants.js Ver fichero

1
+/**
2
+ * The know jingle actions that can be sent and should be acted upon by
3
+ * {@code ProxyConnectionService} and {@code ProxyConnectionPC}.
4
+ */
5
+export const ACTIONS = {
6
+    ACCEPT: 'session-accept',
7
+    CONNECTION_ERROR: 'connection-error-encountered',
8
+    INITIATE: 'session-initiate',
9
+    TERMINATE: 'session-terminate',
10
+    TRANSPORT_INFO: 'transport-info',
11
+    UNAVAILABLE: 'unavailable'
12
+};

+ 11
- 8
modules/xmpp/xmpp.js Ver fichero

41
     return conn;
41
     return conn;
42
 }
42
 }
43
 
43
 
44
+// FIXME: remove once we have a default config template. -saghul
45
+/**
46
+ * A list of ice servers to use by default for P2P.
47
+ */
48
+export const DEFAULT_STUN_SERVERS = [
49
+    { urls: 'stun:stun.l.google.com:19302' },
50
+    { urls: 'stun:stun1.l.google.com:19302' },
51
+    { urls: 'stun:stun2.l.google.com:19302' }
52
+];
53
+
44
 /**
54
 /**
45
  * The name of the field used to recognize a chat message as carrying a JSON
55
  * The name of the field used to recognize a chat message as carrying a JSON
46
  * payload from another endpoint.
56
  * payload from another endpoint.
507
             p2p: { iceServers: [ ] }
517
             p2p: { iceServers: [ ] }
508
         };
518
         };
509
 
519
 
510
-        // FIXME: remove once we have a default config template. -saghul
511
-        const defaultStunServers = [
512
-            { urls: 'stun:stun.l.google.com:19302' },
513
-            { urls: 'stun:stun1.l.google.com:19302' },
514
-            { urls: 'stun:stun2.l.google.com:19302' }
515
-        ];
516
-
517
         const p2pStunServers = (this.options.p2p
520
         const p2pStunServers = (this.options.p2p
518
-            && this.options.p2p.stunServers) || defaultStunServers;
521
+            && this.options.p2p.stunServers) || DEFAULT_STUN_SERVERS;
519
 
522
 
520
         if (Array.isArray(p2pStunServers)) {
523
         if (Array.isArray(p2pStunServers)) {
521
             logger.info('P2P STUN servers: ', p2pStunServers);
524
             logger.info('P2P STUN servers: ', p2pStunServers);

Loading…
Cancelar
Guardar