quickblox 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/quickblox.js +20704 -20873
- package/quickblox.min.js +1 -1
- package/src/modules/chat/qbChat.js +107 -46
- package/src/modules/webrtc/qbRTCPeerConnection.js +446 -306
- package/src/modules/webrtc/qbWebRTCClient.js +43 -21
- package/src/modules/webrtc/qbWebRTCHelpers.js +60 -36
- package/src/modules/webrtc/qbWebRTCSession.js +640 -408
- package/src/modules/webrtc/qbWebRTCSignalingProcessor.js +57 -52
- package/src/modules/webrtc/qbWebRTCSignalingProvider.js +24 -20
- package/src/qbConfig.js +15 -16
- package/src/qbMain.js +27 -17
- package/src/qbProxy.js +0 -3
- package/src/qbUtils.js +1 -0
|
@@ -12,14 +12,27 @@
|
|
|
12
12
|
* - onSessionConnectionStateChangedListener(session, userID, connectionState)
|
|
13
13
|
* - onSessionCloseListener(session)
|
|
14
14
|
* - onCallStatsReport(session, userId, stats, error)
|
|
15
|
+
* - onReconnectListener(session, userId, state)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} MediaParams
|
|
20
|
+
* @property {boolean | MediaTrackConstraints} [params.audio]
|
|
21
|
+
* @property {boolean | MediaTrackConstraints} [params.video]
|
|
22
|
+
* @property {string} [params.elemId] Id of HTMLVideoElement
|
|
23
|
+
* @property {Object} [params.options]
|
|
24
|
+
* @property {boolean} [params.options.muted]
|
|
25
|
+
* @property {boolean} [params.options.mirror]
|
|
15
26
|
*/
|
|
16
27
|
|
|
17
28
|
var config = require('../../qbConfig');
|
|
18
|
-
var
|
|
29
|
+
var qbRTCPeerConnection = require('./qbRTCPeerConnection');
|
|
19
30
|
var Utils = require('../../qbUtils');
|
|
20
31
|
var Helpers = require('./qbWebRTCHelpers');
|
|
21
32
|
var SignalingConstants = require('./qbWebRTCSignalingConstants');
|
|
22
33
|
|
|
34
|
+
var ICE_TIMEOUT = 5000; // 5 seconds
|
|
35
|
+
|
|
23
36
|
/**
|
|
24
37
|
* State of a session
|
|
25
38
|
*/
|
|
@@ -31,12 +44,24 @@ WebRTCSession.State = {
|
|
|
31
44
|
CLOSED: 5
|
|
32
45
|
};
|
|
33
46
|
|
|
47
|
+
var ReconnectionState = {
|
|
48
|
+
RECONNECTING: 'reconnecting',
|
|
49
|
+
RECONNECTED: 'reconnected',
|
|
50
|
+
FAILED: 'failed'
|
|
51
|
+
};
|
|
52
|
+
|
|
34
53
|
|
|
35
54
|
/**
|
|
36
|
-
*
|
|
37
|
-
* @param {
|
|
38
|
-
* @param {
|
|
39
|
-
*
|
|
55
|
+
* QuickBlox WebRTC session
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {1|2} params.callType Type of a call
|
|
58
|
+
* 1 - VIDEO
|
|
59
|
+
* 2 - AUDIO
|
|
60
|
+
* @param {Array<number>} params.opIDs An array with opponents
|
|
61
|
+
* @param {number} params.currentUserID Current user ID
|
|
62
|
+
* @param {number} params.initiatorID Call initiator ID
|
|
63
|
+
* @param {string} [params.sessionID] Session identifier (optional)
|
|
64
|
+
* @param {number} [params.bandwidth] Bandwidth limit
|
|
40
65
|
*/
|
|
41
66
|
function WebRTCSession(params) {
|
|
42
67
|
this.ID = params.sessionID ? params.sessionID : generateUUID();
|
|
@@ -45,12 +70,14 @@ function WebRTCSession(params) {
|
|
|
45
70
|
this.initiatorID = parseInt(params.initiatorID);
|
|
46
71
|
this.opponentsIDs = params.opIDs;
|
|
47
72
|
this.callType = parseInt(params.callType);
|
|
48
|
-
|
|
73
|
+
/** @type {{[userId: number]: qbRTCPeerConnection}} */
|
|
49
74
|
this.peerConnections = {};
|
|
50
|
-
|
|
51
|
-
this.localStream = null;
|
|
52
|
-
|
|
75
|
+
/** @type {MediaParams} */
|
|
53
76
|
this.mediaParams = null;
|
|
77
|
+
/** @type {{[userID: number]: number | undefined}} */
|
|
78
|
+
this.iceConnectTimers = {};
|
|
79
|
+
/** @type {{[userID: number]: number | undefined}} */
|
|
80
|
+
this.reconnectTimers = {};
|
|
54
81
|
|
|
55
82
|
this.signalingProvider = params.signalingProvider;
|
|
56
83
|
|
|
@@ -67,117 +94,110 @@ function WebRTCSession(params) {
|
|
|
67
94
|
|
|
68
95
|
this.startCallTime = 0;
|
|
69
96
|
this.acceptCallTime = 0;
|
|
97
|
+
/** @type {MediaStream | undefined} */
|
|
98
|
+
this.localStream = undefined;
|
|
70
99
|
}
|
|
71
100
|
|
|
72
101
|
/**
|
|
73
102
|
* Get the user media stream
|
|
74
|
-
* @param {
|
|
75
|
-
* @param {
|
|
103
|
+
* @param {MediaParams} params media stream constraints and additional options
|
|
104
|
+
* @param {Function} callback callback to get a result of the function
|
|
76
105
|
*/
|
|
77
|
-
WebRTCSession.prototype.getUserMedia = function(params, callback) {
|
|
78
|
-
if(!navigator.mediaDevices.getUserMedia) {
|
|
106
|
+
WebRTCSession.prototype.getUserMedia = function (params, callback) {
|
|
107
|
+
if (!navigator.mediaDevices.getUserMedia) {
|
|
79
108
|
throw new Error('getUserMedia() is not supported in your browser');
|
|
80
109
|
}
|
|
81
110
|
|
|
82
111
|
var self = this;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Additional parameters for Media Constraints
|
|
86
|
-
* http://tools.ietf.org/html/draft-alvestrand-constraints-resolution-00
|
|
87
|
-
*
|
|
88
|
-
* googEchoCancellation: true
|
|
89
|
-
* googAutoGainControl: true
|
|
90
|
-
* googNoiseSuppression: true
|
|
91
|
-
* googHighpassFilter: true
|
|
92
|
-
* minWidth: 640
|
|
93
|
-
* minHeight: 480
|
|
94
|
-
* maxWidth: 1280
|
|
95
|
-
* maxHeight: 720
|
|
96
|
-
* minFrameRate: 60
|
|
97
|
-
* maxAspectRatio: 1.333
|
|
98
|
-
*/
|
|
99
|
-
|
|
100
|
-
navigator.mediaDevices.getUserMedia({
|
|
112
|
+
var mediaConstraints = {
|
|
101
113
|
audio: params.audio || false,
|
|
102
114
|
video: params.video || false
|
|
103
|
-
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function successCallback(stream) {
|
|
104
118
|
self.localStream = stream;
|
|
105
|
-
self.mediaParams = params;
|
|
119
|
+
self.mediaParams = Object.assign({}, params);
|
|
106
120
|
|
|
107
121
|
if (params.elemId) {
|
|
108
122
|
self.attachMediaStream(params.elemId, stream, params.options);
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
callback
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
125
|
+
if (callback && typeof callback === 'function') {
|
|
126
|
+
callback(undefined, stream);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
navigator
|
|
131
|
+
.mediaDevices
|
|
132
|
+
.getUserMedia(mediaConstraints)
|
|
133
|
+
.then(successCallback)
|
|
134
|
+
.catch(callback);
|
|
115
135
|
};
|
|
116
136
|
|
|
117
137
|
/**
|
|
118
138
|
* Get the state of connection
|
|
119
139
|
* @param {number} The User Id
|
|
120
140
|
*/
|
|
121
|
-
WebRTCSession.prototype.connectionStateForUser = function(userID) {
|
|
141
|
+
WebRTCSession.prototype.connectionStateForUser = function (userID) {
|
|
122
142
|
var peerConnection = this.peerConnections[userID];
|
|
123
|
-
|
|
124
|
-
if (peerConnection) {
|
|
125
|
-
return peerConnection.state;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return null;
|
|
143
|
+
return peerConnection ? peerConnection.state : undefined;
|
|
129
144
|
};
|
|
130
145
|
|
|
131
146
|
/**
|
|
132
147
|
* Attach media stream to audio/video element
|
|
133
|
-
* @param {string} The Id of an ellement to attach a stream
|
|
134
|
-
* @param {
|
|
135
|
-
* @param {
|
|
148
|
+
* @param {string} elementId The Id of an ellement to attach a stream
|
|
149
|
+
* @param {MediaStream} stream The stream to attach
|
|
150
|
+
* @param {Object} [options] The additional options
|
|
151
|
+
* @param {boolean} [options.muted] Whether video element should be muted
|
|
152
|
+
* @param {boolean} [options.mirror] Whether video should be "mirrored"
|
|
136
153
|
*/
|
|
137
|
-
WebRTCSession.prototype.attachMediaStream = function(
|
|
138
|
-
var elem = document.getElementById(
|
|
154
|
+
WebRTCSession.prototype.attachMediaStream = function (elementId, stream, options) {
|
|
155
|
+
var elem = document.getElementById(elementId);
|
|
139
156
|
|
|
140
157
|
if (elem) {
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
158
|
+
if (elem instanceof HTMLMediaElement) {
|
|
159
|
+
if ('srcObject' in elem) {
|
|
160
|
+
elem.srcObject = stream;
|
|
161
|
+
} else {
|
|
162
|
+
elem.src = window.URL.createObjectURL(stream);
|
|
163
|
+
}
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
165
|
+
if (options && options.muted) {
|
|
166
|
+
elem.muted = true;
|
|
167
|
+
}
|
|
150
168
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
169
|
+
if (options && options.mirror) {
|
|
170
|
+
elem.style.transform = 'scaleX(-1)';
|
|
171
|
+
}
|
|
155
172
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
if (!elem.autoplay) {
|
|
174
|
+
elem.onloadedmetadata = function () {
|
|
175
|
+
elem.play();
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
throw new Error('Cannot attach media stream to element with id "' + elementId + '" because it is not of type HTMLMediaElement');
|
|
180
|
+
}
|
|
159
181
|
} else {
|
|
160
|
-
throw new Error('Unable to attach media stream, element ' +
|
|
182
|
+
throw new Error('Unable to attach media stream, cannot find element by Id "' + elementId + '"');
|
|
161
183
|
}
|
|
162
184
|
};
|
|
163
185
|
|
|
164
186
|
/**
|
|
165
187
|
* Detach media stream from audio/video element
|
|
166
|
-
* @param {string} The Id of an element to detach a stream
|
|
188
|
+
* @param {string} elementId The Id of an element to detach a stream
|
|
167
189
|
*/
|
|
168
|
-
WebRTCSession.prototype.detachMediaStream = function(
|
|
169
|
-
var elem = document.getElementById(
|
|
190
|
+
WebRTCSession.prototype.detachMediaStream = function (elementId) {
|
|
191
|
+
var elem = document.getElementById(elementId);
|
|
170
192
|
|
|
171
|
-
if (elem) {
|
|
193
|
+
if (elem && elem instanceof HTMLMediaElement) {
|
|
172
194
|
elem.pause();
|
|
173
195
|
|
|
174
196
|
if (elem.srcObject && typeof elem.srcObject === 'object') {
|
|
175
|
-
elem.srcObject.getTracks().forEach(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
);
|
|
197
|
+
elem.srcObject.getTracks().forEach(function (track) {
|
|
198
|
+
track.stop();
|
|
199
|
+
track.enabled = false;
|
|
200
|
+
});
|
|
181
201
|
elem.srcObject = null;
|
|
182
202
|
} else {
|
|
183
203
|
elem.src = '';
|
|
@@ -190,151 +210,87 @@ WebRTCSession.prototype.detachMediaStream = function(id) {
|
|
|
190
210
|
|
|
191
211
|
/**
|
|
192
212
|
* Switch media tracks in audio/video HTML's element and replace its in peers.
|
|
193
|
-
* @param {
|
|
213
|
+
* @param {Object} deviceIds - the object with deviceIds of plugged devices
|
|
194
214
|
* @param {string} [deviceIds.audio] - the deviceId, it can be gotten from QB.webrtc.getMediaDevices('audioinput')
|
|
195
215
|
* @param {string} [deviceIds.video] - the deviceId, it can be gotten from QB.webrtc.getMediaDevices('videoinput')
|
|
196
|
-
* @param {
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* var switchMediaTracksBtn = document.getElementById('confirmSwitchMediaTracks');
|
|
200
|
-
*
|
|
201
|
-
* var webRTCSession = QB.webrtc.createNewSession(params);
|
|
202
|
-
*
|
|
203
|
-
* QB.webrtc.getMediaDevices('videoinput').then(function(devices) {
|
|
204
|
-
* var selectVideoInput = document.createElement('select'),
|
|
205
|
-
* selectVideoInput.id = 'videoInput',
|
|
206
|
-
* someDocumentElement.appendChild(selectVideoInput);
|
|
207
|
-
*
|
|
208
|
-
* if (devices.length > 1) {
|
|
209
|
-
* for (var i = 0; i !== devices.length; ++i) {
|
|
210
|
-
* var device = devices[i],
|
|
211
|
-
* option = document.createElement('option');
|
|
212
|
-
*
|
|
213
|
-
* if (device.kind === 'videoinput') {
|
|
214
|
-
* option.value = device.deviceId;
|
|
215
|
-
* option.text = device.label;
|
|
216
|
-
* selectVideoInput.appendChild(option);
|
|
217
|
-
* }
|
|
218
|
-
* }
|
|
219
|
-
* }
|
|
220
|
-
* }).catch(function(error) {
|
|
221
|
-
* console.error(error);
|
|
222
|
-
* });
|
|
223
|
-
*
|
|
224
|
-
* QB.webrtc.getMediaDevices('audioinput').then(function(devices) {
|
|
225
|
-
* var selectAudioInput = document.createElement('select'),
|
|
226
|
-
* selectAudioInput.id = 'audioInput',
|
|
227
|
-
* someDocumentElement.appendChild(selectAudioInput);
|
|
228
|
-
*
|
|
229
|
-
* if (devices.length > 1) {
|
|
230
|
-
* for (var i = 0; i !== devices.length; ++i) {
|
|
231
|
-
* var device = devices[i],
|
|
232
|
-
* option = document.createElement('option');
|
|
233
|
-
*
|
|
234
|
-
* if (device.kind === 'audioinput') {
|
|
235
|
-
* option.value = device.deviceId;
|
|
236
|
-
* option.text = device.label;
|
|
237
|
-
* selectAudioInput.appendChild(option);
|
|
238
|
-
* }
|
|
239
|
-
* }
|
|
240
|
-
* }
|
|
241
|
-
* }).catch(function(error) {
|
|
242
|
-
* console.error(error);
|
|
243
|
-
* });
|
|
244
|
-
*
|
|
245
|
-
* switchMediaTracksBtn.onclick = function(event) {
|
|
246
|
-
* var audioDeviceId = document.getElementById('audioInput').value || undefined,
|
|
247
|
-
* videoDeviceId = document.getElementById('videoInput').value || undefined,
|
|
248
|
-
* deviceIds = {
|
|
249
|
-
* audio: audioDeviceId,
|
|
250
|
-
* video: videoDeviceId,
|
|
251
|
-
* };
|
|
252
|
-
*
|
|
253
|
-
* var callback = function(error, stream) {
|
|
254
|
-
* if (err) {
|
|
255
|
-
* console.error(error);
|
|
256
|
-
* } else {
|
|
257
|
-
* console.log(stream);
|
|
258
|
-
* }
|
|
259
|
-
* };
|
|
260
|
-
*
|
|
261
|
-
* // Switch media tracks in audio/video HTML's element (the local stream)
|
|
262
|
-
* // replace media tracks in peers (will change media tracks for each user in WebRTC session)
|
|
263
|
-
* webRTCSession.switchMediaTracks(deviceIds, callback);
|
|
264
|
-
* }
|
|
216
|
+
* @param {Function} callback - the callback to get a result of the function
|
|
265
217
|
*/
|
|
266
|
-
WebRTCSession.prototype.switchMediaTracks = function(deviceIds, callback) {
|
|
267
|
-
|
|
268
|
-
* Callback for webRTCSession.switchMediaTracks(deviceIds, callback)
|
|
269
|
-
* @callback switchMediaTracksCallback
|
|
270
|
-
* @param {object} error - The error object
|
|
271
|
-
* @param {object} stream - The stream from new media device
|
|
272
|
-
*/
|
|
273
|
-
|
|
274
|
-
if(!navigator.mediaDevices.getUserMedia) {
|
|
218
|
+
WebRTCSession.prototype.switchMediaTracks = function (deviceIds, callback) {
|
|
219
|
+
if (!navigator.mediaDevices.getUserMedia) {
|
|
275
220
|
throw new Error('getUserMedia() is not supported in your browser');
|
|
276
221
|
}
|
|
277
222
|
|
|
278
|
-
var self = this
|
|
279
|
-
localStream = this.localStream;
|
|
223
|
+
var self = this;
|
|
280
224
|
|
|
281
225
|
if (deviceIds && deviceIds.audio) {
|
|
282
|
-
|
|
226
|
+
this.mediaParams.audio.deviceId = deviceIds.audio;
|
|
283
227
|
}
|
|
284
228
|
|
|
285
229
|
if (deviceIds && deviceIds.video) {
|
|
286
|
-
|
|
230
|
+
this.mediaParams.video.deviceId = deviceIds.video;
|
|
287
231
|
}
|
|
288
232
|
|
|
289
|
-
localStream.getTracks().forEach(function(track) {
|
|
233
|
+
this.localStream.getTracks().forEach(function (track) {
|
|
290
234
|
track.stop();
|
|
291
235
|
});
|
|
292
236
|
|
|
293
237
|
navigator.mediaDevices.getUserMedia({
|
|
294
238
|
audio: self.mediaParams.audio || false,
|
|
295
239
|
video: self.mediaParams.video || false
|
|
296
|
-
}).then(function(stream) {
|
|
240
|
+
}).then(function (stream) {
|
|
297
241
|
self._replaceTracks(stream);
|
|
298
242
|
callback(null, stream);
|
|
299
|
-
}).catch(function(error) {
|
|
243
|
+
}).catch(function (error) {
|
|
300
244
|
callback(error, null);
|
|
301
245
|
});
|
|
302
246
|
};
|
|
303
247
|
|
|
304
|
-
WebRTCSession.prototype._replaceTracks = function(stream) {
|
|
305
|
-
var
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
248
|
+
WebRTCSession.prototype._replaceTracks = function (stream) {
|
|
249
|
+
var localStream = this.localStream;
|
|
250
|
+
var elemId = this.mediaParams.elemId;
|
|
251
|
+
var ops = this.mediaParams.options;
|
|
252
|
+
var currentStreamTracks = localStream.getTracks();
|
|
253
|
+
var newStreamTracks = stream.getTracks();
|
|
310
254
|
|
|
311
255
|
this.detachMediaStream(elemId);
|
|
312
256
|
|
|
313
|
-
newStreamTracks.forEach(function(
|
|
314
|
-
|
|
257
|
+
newStreamTracks.forEach(function (newTrack) {
|
|
258
|
+
const currentTrack = currentStreamTracks.find(function (track) {
|
|
259
|
+
return track.kind === newTrack.kind;
|
|
260
|
+
});
|
|
261
|
+
if (currentTrack) {
|
|
262
|
+
currentTrack.stop();
|
|
263
|
+
localStream.removeTrack(currentTrack);
|
|
264
|
+
localStream.addTrack(newTrack);
|
|
265
|
+
}
|
|
315
266
|
});
|
|
316
267
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
for (var userId in peers) {
|
|
320
|
-
_replaceTracksForPeer(peers[userId]);
|
|
268
|
+
if (elemId) {
|
|
269
|
+
this.attachMediaStream(elemId, stream, ops);
|
|
321
270
|
}
|
|
322
271
|
|
|
272
|
+
/** @param {RTCPeerConnection} peer */
|
|
323
273
|
function _replaceTracksForPeer(peer) {
|
|
324
|
-
peer.getSenders().map(function(sender) {
|
|
325
|
-
sender.replaceTrack(newStreamTracks.find(function(track) {
|
|
274
|
+
return Promise.all(peer.getSenders().map(function (sender) {
|
|
275
|
+
return sender.replaceTrack(newStreamTracks.find(function (track) {
|
|
326
276
|
return track.kind === sender.track.kind;
|
|
327
277
|
}));
|
|
328
|
-
});
|
|
278
|
+
}));
|
|
329
279
|
}
|
|
280
|
+
|
|
281
|
+
return Promise.all(Object
|
|
282
|
+
.values(this.peerConnections)
|
|
283
|
+
.map(function (peerConnection) { return peerConnection._pc; })
|
|
284
|
+
.map(_replaceTracksForPeer)
|
|
285
|
+
);
|
|
330
286
|
};
|
|
331
287
|
|
|
332
288
|
/**
|
|
333
|
-
*
|
|
334
|
-
* @param
|
|
335
|
-
* @param
|
|
289
|
+
* Initiate a call
|
|
290
|
+
* @param {Object} [extension] [custom parametrs]
|
|
291
|
+
* @param {Function} [callback]
|
|
336
292
|
*/
|
|
337
|
-
WebRTCSession.prototype.call = function(extension, callback) {
|
|
293
|
+
WebRTCSession.prototype.call = function (extension, callback) {
|
|
338
294
|
var self = this,
|
|
339
295
|
ext = _prepareExtension(extension);
|
|
340
296
|
|
|
@@ -342,9 +298,15 @@ WebRTCSession.prototype.call = function(extension, callback) {
|
|
|
342
298
|
|
|
343
299
|
self.state = WebRTCSession.State.ACTIVE;
|
|
344
300
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
301
|
+
// First this check if we connected to the signalling channel
|
|
302
|
+
// to make sure that opponents will receive `call` signal
|
|
303
|
+
self._reconnectToChat(function () {
|
|
304
|
+
if (self.state === WebRTCSession.State.ACTIVE) {
|
|
305
|
+
// create a peer connection for each opponent
|
|
306
|
+
self.opponentsIDs.forEach(function (userID) {
|
|
307
|
+
self._callInternal(userID, ext);
|
|
308
|
+
});
|
|
309
|
+
}
|
|
348
310
|
});
|
|
349
311
|
|
|
350
312
|
if (typeof callback === 'function') {
|
|
@@ -352,49 +314,38 @@ WebRTCSession.prototype.call = function(extension, callback) {
|
|
|
352
314
|
}
|
|
353
315
|
};
|
|
354
316
|
|
|
355
|
-
WebRTCSession.prototype._callInternal = function(userID, extension
|
|
317
|
+
WebRTCSession.prototype._callInternal = function (userID, extension) {
|
|
356
318
|
var self = this;
|
|
357
|
-
var peer =
|
|
358
|
-
|
|
359
|
-
var safariVersion = Helpers.getVersionSafari();
|
|
360
|
-
|
|
361
|
-
if (safariVersion && safariVersion >= 11) {
|
|
362
|
-
self.localStream.getTracks().forEach(function(track) {
|
|
363
|
-
peer.addTrack(track, self.localStream);
|
|
364
|
-
});
|
|
365
|
-
} else {
|
|
366
|
-
peer.addLocalStream(self.localStream);
|
|
367
|
-
}
|
|
368
|
-
|
|
319
|
+
var peer = this._createPeer(userID, this.currentUserID < userID);
|
|
369
320
|
this.peerConnections[userID] = peer;
|
|
370
|
-
|
|
371
|
-
peer.
|
|
372
|
-
if (
|
|
373
|
-
Helpers.trace("
|
|
321
|
+
peer.addLocalStream(self.localStream);
|
|
322
|
+
peer.setLocalSessionDescription({ type: 'offer' }, function (error) {
|
|
323
|
+
if (error) {
|
|
324
|
+
Helpers.trace("setLocalSessionDescription error: " + error);
|
|
374
325
|
} else {
|
|
375
|
-
Helpers.trace("
|
|
326
|
+
Helpers.trace("setLocalSessionDescription success");
|
|
376
327
|
/** let's send call requests to user */
|
|
377
|
-
peer._startDialingTimer(extension
|
|
328
|
+
peer._startDialingTimer(extension);
|
|
378
329
|
}
|
|
379
330
|
});
|
|
380
331
|
};
|
|
381
332
|
|
|
382
333
|
/**
|
|
383
334
|
* Accept a call
|
|
384
|
-
* @param {
|
|
335
|
+
* @param {Object} extension A map with custom parameters
|
|
385
336
|
*/
|
|
386
|
-
WebRTCSession.prototype.accept = function(extension) {
|
|
337
|
+
WebRTCSession.prototype.accept = function (extension) {
|
|
387
338
|
var self = this,
|
|
388
339
|
ext = _prepareExtension(extension);
|
|
389
340
|
|
|
390
341
|
Helpers.trace('Accept, extension: ' + JSON.stringify(ext.userInfo));
|
|
391
342
|
|
|
392
|
-
if(self.state === WebRTCSession.State.ACTIVE) {
|
|
343
|
+
if (self.state === WebRTCSession.State.ACTIVE) {
|
|
393
344
|
Helpers.traceError("Can't accept, the session is already active, return.");
|
|
394
345
|
return;
|
|
395
346
|
}
|
|
396
347
|
|
|
397
|
-
if(self.state === WebRTCSession.State.CLOSED) {
|
|
348
|
+
if (self.state === WebRTCSession.State.CLOSED) {
|
|
398
349
|
Helpers.traceError("Can't accept, the session is already closed, return.");
|
|
399
350
|
self.stop({});
|
|
400
351
|
return;
|
|
@@ -412,7 +363,7 @@ WebRTCSession.prototype.accept = function(extension) {
|
|
|
412
363
|
var oppIDs = self._uniqueOpponentsIDsWithoutInitiator();
|
|
413
364
|
|
|
414
365
|
/** in a case of group video chat */
|
|
415
|
-
if(oppIDs.length > 0){
|
|
366
|
+
if (oppIDs.length > 0) {
|
|
416
367
|
var offerTime = (self.acceptCallTime - self.startCallTime) / 1000;
|
|
417
368
|
self._startWaitingOfferOrAnswerTimer(offerTime);
|
|
418
369
|
|
|
@@ -420,8 +371,8 @@ WebRTCSession.prototype.accept = function(extension) {
|
|
|
420
371
|
* here we have to decide to which users the user should call.
|
|
421
372
|
* We have a rule: If a userID1 > userID2 then a userID1 should call to userID2.
|
|
422
373
|
*/
|
|
423
|
-
oppIDs.forEach(function(opID, i, arr) {
|
|
424
|
-
if(self.currentUserID > opID){
|
|
374
|
+
oppIDs.forEach(function (opID, i, arr) {
|
|
375
|
+
if (self.currentUserID > opID) {
|
|
425
376
|
/** call to the user */
|
|
426
377
|
self._callInternal(opID, {}, true);
|
|
427
378
|
}
|
|
@@ -429,58 +380,61 @@ WebRTCSession.prototype.accept = function(extension) {
|
|
|
429
380
|
}
|
|
430
381
|
};
|
|
431
382
|
|
|
432
|
-
WebRTCSession.prototype._acceptInternal = function(userID, extension) {
|
|
383
|
+
WebRTCSession.prototype._acceptInternal = function (userID, extension) {
|
|
433
384
|
var self = this;
|
|
434
385
|
|
|
435
386
|
/** create a peer connection */
|
|
436
387
|
var peerConnection = this.peerConnections[userID];
|
|
437
388
|
|
|
438
389
|
if (peerConnection) {
|
|
439
|
-
var
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
peerConnection.addTrack(track, self.localStream);
|
|
444
|
-
});
|
|
445
|
-
} else {
|
|
446
|
-
peerConnection.addLocalStream(self.localStream);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
peerConnection.setRemoteSessionDescription('offer', peerConnection.getRemoteSDP(), function(error){
|
|
450
|
-
if(error){
|
|
390
|
+
var remoteSDP = peerConnection.getRemoteSDP();
|
|
391
|
+
peerConnection.addLocalStream(self.localStream);
|
|
392
|
+
peerConnection.setRemoteSessionDescription(remoteSDP, function (error) {
|
|
393
|
+
if (error) {
|
|
451
394
|
Helpers.traceError("'setRemoteSessionDescription' error: " + error);
|
|
452
|
-
}else{
|
|
395
|
+
} else {
|
|
453
396
|
Helpers.trace("'setRemoteSessionDescription' success");
|
|
454
|
-
|
|
455
|
-
peerConnection.getAndSetLocalSessionDescription(self.callType, function(err) {
|
|
397
|
+
peerConnection.setLocalSessionDescription({ type: 'answer' }, function (err) {
|
|
456
398
|
if (err) {
|
|
457
|
-
Helpers.trace("
|
|
399
|
+
Helpers.trace("setLocalSessionDescription error: " + err);
|
|
458
400
|
} else {
|
|
459
|
-
|
|
401
|
+
Helpers.trace("'setLocalSessionDescription' success");
|
|
460
402
|
extension.sessionID = self.ID;
|
|
461
403
|
extension.callType = self.callType;
|
|
462
404
|
extension.callerID = self.initiatorID;
|
|
463
405
|
extension.opponentsIDs = self.opponentsIDs;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
406
|
+
if (peerConnection._pc.localDescription) {
|
|
407
|
+
extension.sdp = peerConnection
|
|
408
|
+
._pc
|
|
409
|
+
.localDescription
|
|
410
|
+
.toJSON()
|
|
411
|
+
.sdp;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
self.signalingProvider.sendMessage(
|
|
415
|
+
userID,
|
|
416
|
+
extension,
|
|
417
|
+
SignalingConstants.SignalingType.ACCEPT
|
|
418
|
+
);
|
|
467
419
|
}
|
|
468
420
|
});
|
|
469
421
|
}
|
|
470
422
|
});
|
|
471
|
-
}else{
|
|
472
|
-
Helpers.traceError(
|
|
423
|
+
} else {
|
|
424
|
+
Helpers.traceError(
|
|
425
|
+
"Can't accept the call, peer connection for userID " +
|
|
426
|
+
userID + " was not found"
|
|
427
|
+
);
|
|
473
428
|
}
|
|
474
429
|
};
|
|
475
430
|
|
|
476
431
|
/**
|
|
477
432
|
* Reject a call
|
|
478
|
-
* @param {
|
|
433
|
+
* @param {Object} A map with custom parameters
|
|
479
434
|
*/
|
|
480
|
-
WebRTCSession.prototype.reject = function(extension) {
|
|
435
|
+
WebRTCSession.prototype.reject = function (extension) {
|
|
481
436
|
var self = this,
|
|
482
437
|
ext = _prepareExtension(extension);
|
|
483
|
-
var peersLen = Object.keys(self.peerConnections).length;
|
|
484
438
|
|
|
485
439
|
Helpers.trace('Reject, extension: ' + JSON.stringify(ext.userInfo));
|
|
486
440
|
|
|
@@ -493,30 +447,31 @@ WebRTCSession.prototype.reject = function(extension) {
|
|
|
493
447
|
ext.callerID = self.initiatorID;
|
|
494
448
|
ext.opponentsIDs = self.opponentsIDs;
|
|
495
449
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
450
|
+
Object.keys(self.peerConnections).forEach(function (key) {
|
|
451
|
+
var peerConnection = self.peerConnections[key];
|
|
452
|
+
self.signalingProvider.sendMessage(
|
|
453
|
+
peerConnection.userID,
|
|
454
|
+
ext,
|
|
455
|
+
SignalingConstants.SignalingType.REJECT
|
|
456
|
+
);
|
|
457
|
+
});
|
|
502
458
|
|
|
503
459
|
self._close();
|
|
504
460
|
};
|
|
505
461
|
|
|
506
462
|
/**
|
|
507
463
|
* Stop a call
|
|
508
|
-
* @param {
|
|
464
|
+
* @param {Object} A map with custom parameters
|
|
509
465
|
*/
|
|
510
|
-
WebRTCSession.prototype.stop = function(extension) {
|
|
466
|
+
WebRTCSession.prototype.stop = function (extension) {
|
|
511
467
|
var self = this,
|
|
512
|
-
ext = _prepareExtension(extension)
|
|
513
|
-
peersLen = Object.keys(self.peerConnections).length;
|
|
468
|
+
ext = _prepareExtension(extension);
|
|
514
469
|
|
|
515
470
|
Helpers.trace('Stop, extension: ' + JSON.stringify(ext.userInfo));
|
|
516
471
|
|
|
517
472
|
self.state = WebRTCSession.State.HUNGUP;
|
|
518
473
|
|
|
519
|
-
if(self.answerTimer) {
|
|
474
|
+
if (self.answerTimer) {
|
|
520
475
|
self._clearAnswerTimer();
|
|
521
476
|
}
|
|
522
477
|
|
|
@@ -525,13 +480,14 @@ WebRTCSession.prototype.stop = function(extension) {
|
|
|
525
480
|
ext.callerID = self.initiatorID;
|
|
526
481
|
ext.opponentsIDs = self.opponentsIDs;
|
|
527
482
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
483
|
+
Object.keys(self.peerConnections).forEach(function (key) {
|
|
484
|
+
var peerConnection = self.peerConnections[key];
|
|
485
|
+
self.signalingProvider.sendMessage(
|
|
486
|
+
peerConnection.userID,
|
|
487
|
+
ext,
|
|
488
|
+
SignalingConstants.SignalingType.STOP
|
|
489
|
+
);
|
|
490
|
+
});
|
|
535
491
|
|
|
536
492
|
self._close();
|
|
537
493
|
};
|
|
@@ -540,11 +496,11 @@ WebRTCSession.prototype.stop = function(extension) {
|
|
|
540
496
|
* [function close connection with user]
|
|
541
497
|
* @param {Number} userId [id of user]
|
|
542
498
|
*/
|
|
543
|
-
WebRTCSession.prototype.closeConnection = function(userId) {
|
|
499
|
+
WebRTCSession.prototype.closeConnection = function (userId) {
|
|
544
500
|
var self = this,
|
|
545
501
|
peer = this.peerConnections[userId];
|
|
546
502
|
|
|
547
|
-
if(!peer) {
|
|
503
|
+
if (!peer) {
|
|
548
504
|
Helpers.traceWarn('Not found connection with user (' + userId + ')');
|
|
549
505
|
return false;
|
|
550
506
|
}
|
|
@@ -561,26 +517,40 @@ WebRTCSession.prototype.closeConnection = function(userId) {
|
|
|
561
517
|
|
|
562
518
|
/**
|
|
563
519
|
* Update a call
|
|
564
|
-
* @param {
|
|
520
|
+
* @param {Object} extension A map with custom parameters
|
|
521
|
+
* @param {number} [userID]
|
|
565
522
|
*/
|
|
566
|
-
WebRTCSession.prototype.update = function(extension) {
|
|
523
|
+
WebRTCSession.prototype.update = function (extension, userID) {
|
|
567
524
|
var self = this,
|
|
568
|
-
ext = {};
|
|
525
|
+
ext = typeof extension === 'object' ? extension : {};
|
|
569
526
|
|
|
570
527
|
Helpers.trace('Update, extension: ' + JSON.stringify(extension));
|
|
571
528
|
|
|
572
|
-
if(extension === null){
|
|
529
|
+
if (extension === null) {
|
|
573
530
|
Helpers.trace("extension is null, no parameters to update");
|
|
574
531
|
return;
|
|
575
532
|
}
|
|
576
533
|
|
|
577
|
-
ext = _prepareExtension(extension);
|
|
578
534
|
ext.sessionID = this.ID;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
535
|
+
ext.callType = this.callType;
|
|
536
|
+
ext.callerID = this.initiatorID;
|
|
537
|
+
ext.opponentsIDs = this.opponentsIDs;
|
|
538
|
+
|
|
539
|
+
if (userID) {
|
|
540
|
+
self.signalingProvider.sendMessage(
|
|
541
|
+
userID,
|
|
542
|
+
ext,
|
|
543
|
+
SignalingConstants.SignalingType.PARAMETERS_CHANGED
|
|
544
|
+
);
|
|
545
|
+
} else {
|
|
546
|
+
for (var key in self.peerConnections) {
|
|
547
|
+
var peer = self.peerConnections[key];
|
|
548
|
+
self.signalingProvider.sendMessage(
|
|
549
|
+
peer.userID,
|
|
550
|
+
ext,
|
|
551
|
+
SignalingConstants.SignalingType.PARAMETERS_CHANGED
|
|
552
|
+
);
|
|
553
|
+
}
|
|
584
554
|
}
|
|
585
555
|
};
|
|
586
556
|
|
|
@@ -588,7 +558,7 @@ WebRTCSession.prototype.update = function(extension) {
|
|
|
588
558
|
* Mutes the stream
|
|
589
559
|
* @param {string} what to mute: 'audio' or 'video'
|
|
590
560
|
*/
|
|
591
|
-
WebRTCSession.prototype.mute = function(type) {
|
|
561
|
+
WebRTCSession.prototype.mute = function (type) {
|
|
592
562
|
this._muteStream(0, type);
|
|
593
563
|
};
|
|
594
564
|
|
|
@@ -596,91 +566,105 @@ WebRTCSession.prototype.mute = function(type) {
|
|
|
596
566
|
* Unmutes the stream
|
|
597
567
|
* @param {string} what to unmute: 'audio' or 'video'
|
|
598
568
|
*/
|
|
599
|
-
WebRTCSession.prototype.unmute = function(type) {
|
|
569
|
+
WebRTCSession.prototype.unmute = function (type) {
|
|
600
570
|
this._muteStream(1, type);
|
|
601
571
|
};
|
|
602
572
|
|
|
603
573
|
/**
|
|
604
574
|
* DELEGATES (rtc client)
|
|
605
575
|
*/
|
|
606
|
-
WebRTCSession.prototype.processOnCall = function(callerID, extension) {
|
|
576
|
+
WebRTCSession.prototype.processOnCall = function (callerID, extension) {
|
|
607
577
|
var self = this,
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
oppIDs.forEach(function(opID, i, arr) {
|
|
611
|
-
var pConn = self.peerConnections[opID];
|
|
578
|
+
opponentsIds = self._uniqueOpponentsIDs();
|
|
612
579
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
pConn.updateRemoteSDP(extension.sdp);
|
|
580
|
+
opponentsIds.forEach(function (opponentID) {
|
|
581
|
+
var peer = self.peerConnections[opponentID];
|
|
616
582
|
|
|
617
|
-
|
|
618
|
-
if(callerID != self.initiatorID && self.state === WebRTCSession.State.ACTIVE){
|
|
619
|
-
self._acceptInternal(callerID, {});
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
583
|
+
if (!peer) {
|
|
623
584
|
/** create peer connections for each opponent */
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
585
|
+
peer = self._createPeer(
|
|
586
|
+
opponentID,
|
|
587
|
+
self.currentUserID < opponentID
|
|
588
|
+
);
|
|
589
|
+
self.peerConnections[opponentID] = peer;
|
|
590
|
+
if (opponentID == callerID) {
|
|
591
|
+
self._startAnswerTimer();
|
|
629
592
|
}
|
|
593
|
+
}
|
|
594
|
+
if (opponentID == callerID) {
|
|
595
|
+
peer.setRemoteSDP(new window.RTCSessionDescription({
|
|
596
|
+
sdp: extension.sdp,
|
|
597
|
+
type: 'offer'
|
|
598
|
+
}));
|
|
630
599
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
self._startAnswerTimer();
|
|
600
|
+
/** The group call logic starts here */
|
|
601
|
+
if (callerID != self.initiatorID &&
|
|
602
|
+
self.state === WebRTCSession.State.ACTIVE) {
|
|
603
|
+
self._acceptInternal(callerID, {});
|
|
636
604
|
}
|
|
637
605
|
}
|
|
638
606
|
});
|
|
639
607
|
};
|
|
640
608
|
|
|
641
|
-
WebRTCSession.prototype.processOnAccept = function(userID, extension) {
|
|
609
|
+
WebRTCSession.prototype.processOnAccept = function (userID, extension) {
|
|
610
|
+
var self = this;
|
|
642
611
|
var peerConnection = this.peerConnections[userID];
|
|
643
612
|
|
|
644
|
-
if(peerConnection){
|
|
613
|
+
if (peerConnection) {
|
|
645
614
|
peerConnection._clearDialingTimer();
|
|
646
|
-
if (peerConnection.signalingState !== 'stable') {
|
|
647
|
-
|
|
648
|
-
|
|
615
|
+
if (peerConnection._pc.signalingState !== 'stable') {
|
|
616
|
+
var remoteSessionDescription = new window.RTCSessionDescription({
|
|
617
|
+
sdp: extension.sdp,
|
|
618
|
+
type: 'answer'
|
|
619
|
+
});
|
|
620
|
+
peerConnection.setRemoteSDP(remoteSessionDescription);
|
|
621
|
+
peerConnection.setRemoteSessionDescription(remoteSessionDescription, function (error) {
|
|
622
|
+
if (error) {
|
|
649
623
|
Helpers.traceError("'setRemoteSessionDescription' error: " + error);
|
|
650
|
-
}else{
|
|
624
|
+
} else {
|
|
651
625
|
Helpers.trace("'setRemoteSessionDescription' success");
|
|
626
|
+
if (self.state !== WebRTCSession.State.ACTIVE) {
|
|
627
|
+
self.state = WebRTCSession.State.ACTIVE;
|
|
628
|
+
}
|
|
652
629
|
}
|
|
653
630
|
});
|
|
654
631
|
} else {
|
|
655
632
|
Helpers.traceError("Ignore 'onAccept', PeerConnection is already in 'stable' state");
|
|
656
633
|
}
|
|
657
|
-
}else{
|
|
658
|
-
Helpers.traceError(
|
|
634
|
+
} else {
|
|
635
|
+
Helpers.traceError(
|
|
636
|
+
"Ignore 'OnAccept': peer connection for user with Id " +
|
|
637
|
+
userID + " was not found"
|
|
638
|
+
);
|
|
659
639
|
}
|
|
660
640
|
};
|
|
661
641
|
|
|
662
|
-
WebRTCSession.prototype.processOnReject = function(userID, extension) {
|
|
642
|
+
WebRTCSession.prototype.processOnReject = function (userID, extension) {
|
|
663
643
|
var peerConnection = this.peerConnections[userID];
|
|
664
644
|
|
|
665
645
|
this._clearWaitingOfferOrAnswerTimer();
|
|
666
646
|
|
|
667
|
-
if(peerConnection){
|
|
647
|
+
if (peerConnection) {
|
|
668
648
|
peerConnection.release();
|
|
669
|
-
}else{
|
|
649
|
+
} else {
|
|
670
650
|
Helpers.traceError("Ignore 'OnReject', there is no information about peer connection by some reason.");
|
|
671
651
|
}
|
|
672
652
|
|
|
673
653
|
this._closeSessionIfAllConnectionsClosed();
|
|
674
654
|
};
|
|
675
655
|
|
|
676
|
-
WebRTCSession.prototype.processOnStop = function(userID, extension) {
|
|
656
|
+
WebRTCSession.prototype.processOnStop = function (userID, extension) {
|
|
677
657
|
var self = this;
|
|
678
658
|
|
|
679
659
|
this._clearAnswerTimer();
|
|
680
660
|
|
|
681
|
-
var
|
|
682
|
-
if (
|
|
683
|
-
|
|
661
|
+
var peerConnection = self.peerConnections[userID];
|
|
662
|
+
if (peerConnection) {
|
|
663
|
+
peerConnection.release();
|
|
664
|
+
if (peerConnection._reconnecting) {
|
|
665
|
+
peerConnection._reconnecting = false;
|
|
666
|
+
}
|
|
667
|
+
this._stopReconnectTimer(userID);
|
|
684
668
|
} else {
|
|
685
669
|
Helpers.traceError("Ignore 'OnStop', there is no information about peer connection by some reason.");
|
|
686
670
|
}
|
|
@@ -688,33 +672,125 @@ WebRTCSession.prototype.processOnStop = function(userID, extension) {
|
|
|
688
672
|
this._closeSessionIfAllConnectionsClosed();
|
|
689
673
|
};
|
|
690
674
|
|
|
691
|
-
WebRTCSession.prototype.processOnIceCandidates = function(userID, extension) {
|
|
675
|
+
WebRTCSession.prototype.processOnIceCandidates = function (userID, extension) {
|
|
692
676
|
var peerConnection = this.peerConnections[userID];
|
|
693
677
|
|
|
694
|
-
if(peerConnection){
|
|
678
|
+
if (peerConnection) {
|
|
695
679
|
peerConnection.addCandidates(extension.iceCandidates);
|
|
696
|
-
}else{
|
|
680
|
+
} else {
|
|
697
681
|
Helpers.traceError("Ignore 'OnIceCandidates', there is no information about peer connection by some reason.");
|
|
698
682
|
}
|
|
699
683
|
};
|
|
700
684
|
|
|
701
|
-
WebRTCSession.prototype.
|
|
702
|
-
var
|
|
685
|
+
WebRTCSession.prototype.processOnUpdate = function (userID, extension) {
|
|
686
|
+
var SRD = extension.sessionDescription;
|
|
687
|
+
var reason = extension.reason;
|
|
688
|
+
var sessionIsActive = this.state === WebRTCSession.State.ACTIVE;
|
|
689
|
+
if (sessionIsActive && reason && reason === 'reconnect') {
|
|
690
|
+
var peer = this.peerConnections[userID];
|
|
691
|
+
if (peer) {
|
|
692
|
+
if (SRD) {
|
|
693
|
+
if (SRD.type === 'offer') {
|
|
694
|
+
this._processReconnectOffer(userID, SRD);
|
|
695
|
+
} else {
|
|
696
|
+
this._processReconnectAnswer(userID, SRD);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
} else {
|
|
700
|
+
Helpers.traceError(
|
|
701
|
+
"Ignore 'OnUpdate': peer connection for user with Id " +
|
|
702
|
+
userID + " was not found"
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
};
|
|
703
707
|
|
|
708
|
+
/**
|
|
709
|
+
* @param {number} userID
|
|
710
|
+
* @param {RTCSessionDescriptionInit} SRD
|
|
711
|
+
*/
|
|
712
|
+
WebRTCSession.prototype._processReconnectOffer = function (userID, SRD) {
|
|
713
|
+
var self = this;
|
|
714
|
+
if (this.peerConnections[userID].polite) {
|
|
715
|
+
this._reconnect(this.peerConnections[userID]);
|
|
716
|
+
var peer = this.peerConnections[userID];
|
|
717
|
+
var offerId = SRD.offerId;
|
|
718
|
+
var remoteDescription = new window.RTCSessionDescription({
|
|
719
|
+
sdp: SRD.sdp,
|
|
720
|
+
type: SRD.type
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
peer.setRemoteSDP(remoteDescription);
|
|
724
|
+
peer.setRemoteSessionDescription(remoteDescription, function (e) {
|
|
725
|
+
if (e) {
|
|
726
|
+
Helpers.traceError('"setRemoteSessionDescription" error:' + e.message);
|
|
727
|
+
} else {
|
|
728
|
+
peer.setLocalSessionDescription({ type: 'answer' }, function () {
|
|
729
|
+
var description = peer._pc.localDescription.toJSON();
|
|
730
|
+
var ext = {
|
|
731
|
+
reason: 'reconnect',
|
|
732
|
+
sessionDescription: {
|
|
733
|
+
offerId: offerId,
|
|
734
|
+
sdp: description.sdp,
|
|
735
|
+
type: description.type
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
self.update(ext, userID);
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
} else {
|
|
743
|
+
this._reconnect(this.peerConnections[userID], true);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* @param {number} userID
|
|
749
|
+
* @param {RTCSessionDescriptionInit & { offerId: string }} SRD
|
|
750
|
+
*/
|
|
751
|
+
WebRTCSession.prototype._processReconnectAnswer = function (userID, SRD) {
|
|
752
|
+
var peer = this.peerConnections[userID];
|
|
753
|
+
var offerId = SRD.offerId;
|
|
754
|
+
if (peer && peer.offerId && offerId && peer.offerId === offerId) {
|
|
755
|
+
var remoteDescription = new window.RTCSessionDescription({
|
|
756
|
+
sdp: SRD.sdp,
|
|
757
|
+
type: SRD.type
|
|
758
|
+
});
|
|
759
|
+
peer.setRemoteSDP(remoteDescription);
|
|
760
|
+
peer.setRemoteSessionDescription(remoteDescription, function (e) {
|
|
761
|
+
if (e) {
|
|
762
|
+
Helpers.traceError('"setRemoteSessionDescription" error:' + e.message);
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Send "call" signal to the opponent
|
|
770
|
+
* @param {qbRTCPeerConnection} peerConnection
|
|
771
|
+
* @param {Object} ext
|
|
772
|
+
*/
|
|
773
|
+
WebRTCSession.prototype.processCall = function (peerConnection, ext) {
|
|
774
|
+
var extension = ext || {};
|
|
775
|
+
if (!peerConnection._pc.localDescription) return;
|
|
704
776
|
extension.sessionID = this.ID;
|
|
705
777
|
extension.callType = this.callType;
|
|
706
778
|
extension.callerID = this.initiatorID;
|
|
707
779
|
extension.opponentsIDs = this.opponentsIDs;
|
|
708
|
-
extension.sdp = peerConnection.localDescription.sdp;
|
|
780
|
+
extension.sdp = peerConnection._pc.localDescription.sdp;
|
|
709
781
|
|
|
710
782
|
//TODO: set bandwidth to the userInfo object
|
|
711
|
-
extension.userInfo = ext.userInfo
|
|
783
|
+
extension.userInfo = ext && ext.userInfo ? ext.userInfo : {};
|
|
712
784
|
extension.userInfo.bandwidth = this.bandwidth;
|
|
713
785
|
|
|
714
|
-
this.signalingProvider.sendMessage(
|
|
786
|
+
this.signalingProvider.sendMessage(
|
|
787
|
+
peerConnection.userID,
|
|
788
|
+
extension,
|
|
789
|
+
SignalingConstants.SignalingType.CALL
|
|
790
|
+
);
|
|
715
791
|
};
|
|
716
792
|
|
|
717
|
-
WebRTCSession.prototype.processIceCandidates = function(peerConnection, iceCandidates) {
|
|
793
|
+
WebRTCSession.prototype.processIceCandidates = function (peerConnection, iceCandidates) {
|
|
718
794
|
var extension = {};
|
|
719
795
|
|
|
720
796
|
extension.sessionID = this.ID;
|
|
@@ -725,7 +801,7 @@ WebRTCSession.prototype.processIceCandidates = function(peerConnection, iceCandi
|
|
|
725
801
|
this.signalingProvider.sendCandidate(peerConnection.userID, iceCandidates, extension);
|
|
726
802
|
};
|
|
727
803
|
|
|
728
|
-
WebRTCSession.prototype.processOnNotAnswer = function(peerConnection) {
|
|
804
|
+
WebRTCSession.prototype.processOnNotAnswer = function (peerConnection) {
|
|
729
805
|
Helpers.trace("Answer timeout callback for session " + this.ID + " for user " + peerConnection.userID);
|
|
730
806
|
|
|
731
807
|
this._clearWaitingOfferOrAnswerTimer();
|
|
@@ -742,7 +818,7 @@ WebRTCSession.prototype.processOnNotAnswer = function(peerConnection) {
|
|
|
742
818
|
/**
|
|
743
819
|
* DELEGATES (peer connection)
|
|
744
820
|
*/
|
|
745
|
-
WebRTCSession.prototype._onRemoteStreamListener = function(userID, stream) {
|
|
821
|
+
WebRTCSession.prototype._onRemoteStreamListener = function (userID, stream) {
|
|
746
822
|
if (typeof this.onRemoteStreamListener === 'function') {
|
|
747
823
|
Utils.safeCallbackCall(this.onRemoteStreamListener, this, userID, stream);
|
|
748
824
|
}
|
|
@@ -756,60 +832,236 @@ WebRTCSession.prototype._onRemoteStreamListener = function(userID, stream) {
|
|
|
756
832
|
* Fire onCallStatsReport callbacks with parameters(userId, stats, error).
|
|
757
833
|
* If stats will be invalid callback return null and error
|
|
758
834
|
*/
|
|
759
|
-
WebRTCSession.prototype._onCallStatsReport = function(userId, stats, error) {
|
|
835
|
+
WebRTCSession.prototype._onCallStatsReport = function (userId, stats, error) {
|
|
760
836
|
if (typeof this.onCallStatsReport === 'function') {
|
|
761
837
|
Utils.safeCallbackCall(this.onCallStatsReport, this, userId, stats, error);
|
|
762
838
|
}
|
|
763
839
|
};
|
|
764
840
|
|
|
765
|
-
WebRTCSession.prototype._onSessionConnectionStateChangedListener = function(userID, connectionState) {
|
|
841
|
+
WebRTCSession.prototype._onSessionConnectionStateChangedListener = function (userID, connectionState) {
|
|
842
|
+
var StateClosed = Helpers.SessionConnectionState.CLOSED;
|
|
843
|
+
var peer = this.peerConnections[userID];
|
|
766
844
|
if (typeof this.onSessionConnectionStateChangedListener === 'function') {
|
|
767
|
-
Utils.safeCallbackCall(
|
|
768
|
-
|
|
769
|
-
this
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
845
|
+
Utils.safeCallbackCall(
|
|
846
|
+
this.onSessionConnectionStateChangedListener,
|
|
847
|
+
this,
|
|
848
|
+
userID,
|
|
849
|
+
connectionState
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
if (connectionState === StateClosed && peer) {
|
|
853
|
+
peer._pc.onicecandidate = null;
|
|
854
|
+
peer._pc.onsignalingstatechange = null;
|
|
855
|
+
peer._pc.ontrack = null;
|
|
856
|
+
peer._pc.oniceconnectionstatechange = null;
|
|
857
|
+
delete this.peerConnections[userID];
|
|
776
858
|
}
|
|
777
859
|
};
|
|
778
860
|
|
|
779
861
|
/**
|
|
780
862
|
* Private
|
|
863
|
+
* @param {number} userId
|
|
864
|
+
* @param {boolean} polite
|
|
865
|
+
* @returns {qbRTCPeerConnection}
|
|
781
866
|
*/
|
|
782
|
-
WebRTCSession.prototype._createPeer = function(
|
|
783
|
-
if (!RTCPeerConnection)
|
|
867
|
+
WebRTCSession.prototype._createPeer = function (userId, polite) {
|
|
868
|
+
if (!window.RTCPeerConnection) {
|
|
869
|
+
throw new Error('_createPeer error: RTCPeerConnection is not supported in your browser');
|
|
870
|
+
}
|
|
784
871
|
|
|
785
872
|
this.startCallTime = new Date();
|
|
786
873
|
|
|
787
|
-
/**
|
|
788
|
-
* Additional parameters for RTCPeerConnection options
|
|
789
|
-
* new RTCPeerConnection(pcConfig, options)
|
|
790
|
-
*
|
|
791
|
-
* DtlsSrtpKeyAgreement: true
|
|
792
|
-
* RtpDataChannels: true
|
|
793
|
-
*/
|
|
794
|
-
|
|
795
874
|
var pcConfig = {
|
|
796
875
|
iceServers: config.webrtc.iceServers,
|
|
797
876
|
};
|
|
798
877
|
|
|
799
|
-
Helpers.trace("_createPeer
|
|
878
|
+
Helpers.trace("_createPeer configuration: " + JSON.stringify(pcConfig));
|
|
800
879
|
|
|
801
|
-
var peer = new
|
|
802
|
-
peer._init(this,
|
|
880
|
+
var peer = new qbRTCPeerConnection(pcConfig);
|
|
881
|
+
peer._init(this, userId, this.ID, polite);
|
|
803
882
|
|
|
804
883
|
return peer;
|
|
805
884
|
};
|
|
806
885
|
|
|
886
|
+
WebRTCSession.prototype._startReconnectTimer = function (userID) {
|
|
887
|
+
var self = this;
|
|
888
|
+
var delay = config.webrtc.disconnectTimeInterval * 1000;
|
|
889
|
+
var peer = this.peerConnections[userID];
|
|
890
|
+
|
|
891
|
+
peer._reconnecting = true;
|
|
892
|
+
|
|
893
|
+
var reconnectTimeoutCallback = function () {
|
|
894
|
+
Helpers.trace('disconnectTimeInterval reached for userID ' + userID);
|
|
895
|
+
|
|
896
|
+
self._stopReconnectTimer(userID);
|
|
897
|
+
|
|
898
|
+
self.peerConnections[userID].release();
|
|
899
|
+
self._onSessionConnectionStateChangedListener(
|
|
900
|
+
userID,
|
|
901
|
+
Helpers.SessionConnectionState.CLOSED
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
self._closeSessionIfAllConnectionsClosed();
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
if (typeof this.onReconnectListener === 'function') {
|
|
908
|
+
Utils.safeCallbackCall(
|
|
909
|
+
this.onReconnectListener,
|
|
910
|
+
this,
|
|
911
|
+
userID,
|
|
912
|
+
ReconnectionState.RECONNECTING
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
Helpers.trace(
|
|
917
|
+
'_startReconnectTimer for userID:' + userID + ', timeout: ' + delay
|
|
918
|
+
);
|
|
919
|
+
|
|
920
|
+
var iceConnectTimeoutCallback = function () {
|
|
921
|
+
Helpers.trace('iceConnectTimeout reached for user ' + userID);
|
|
922
|
+
if (self.iceConnectTimers[userID]) {
|
|
923
|
+
clearTimeout(self.iceConnectTimers[userID]);
|
|
924
|
+
self.iceConnectTimers[userID] = undefined;
|
|
925
|
+
if (!self.reconnectTimers[userID]) {
|
|
926
|
+
/** If connection won't be recovered - close session */
|
|
927
|
+
self.reconnectTimers[userID] = setTimeout(
|
|
928
|
+
reconnectTimeoutCallback,
|
|
929
|
+
delay - ICE_TIMEOUT
|
|
930
|
+
);
|
|
931
|
+
self._reconnectToChat(function () {
|
|
932
|
+
if (self.state === WebRTCSession.State.ACTIVE &&
|
|
933
|
+
self.reconnectTimers[userID]) {
|
|
934
|
+
// only if session is active
|
|
935
|
+
self._reconnect(peer, true);
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
|
|
942
|
+
if (!this.iceConnectTimers[userID]) {
|
|
943
|
+
/**
|
|
944
|
+
* Wait a bit before reconnecting. If network has not been changed -
|
|
945
|
+
* ICE candidates are still valid and connection may recover shortly
|
|
946
|
+
*/
|
|
947
|
+
this.iceConnectTimers[userID] = setTimeout(
|
|
948
|
+
iceConnectTimeoutCallback,
|
|
949
|
+
ICE_TIMEOUT
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
WebRTCSession.prototype._stopReconnectTimer = function (userID) {
|
|
956
|
+
var peer = this.peerConnections[userID];
|
|
957
|
+
if (this.iceConnectTimers[userID]) {
|
|
958
|
+
clearTimeout(this.iceConnectTimers[userID]);
|
|
959
|
+
this.iceConnectTimers[userID] = undefined;
|
|
960
|
+
}
|
|
961
|
+
if (this.reconnectTimers[userID]) {
|
|
962
|
+
Helpers.trace('_stopReconnectTimer for userID: ' + userID);
|
|
963
|
+
clearTimeout(this.reconnectTimers[userID]);
|
|
964
|
+
this.reconnectTimers[userID] = undefined;
|
|
965
|
+
}
|
|
966
|
+
if (peer && peer._reconnecting) {
|
|
967
|
+
peer._reconnecting = false;
|
|
968
|
+
if (typeof this.onReconnectListener === 'function') {
|
|
969
|
+
var state = peer._pc.iceConnectionState;
|
|
970
|
+
Utils.safeCallbackCall(
|
|
971
|
+
this.onReconnectListener,
|
|
972
|
+
this,
|
|
973
|
+
userID,
|
|
974
|
+
state === 'connected' ?
|
|
975
|
+
ReconnectionState.RECONNECTED :
|
|
976
|
+
ReconnectionState.FAILED
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Ping server until pong received, then call the `callback`
|
|
984
|
+
* @param {Function} callback
|
|
985
|
+
*/
|
|
986
|
+
WebRTCSession.prototype._reconnectToChat = function (callback) {
|
|
987
|
+
var self = this;
|
|
988
|
+
var signalingProvider = this.signalingProvider;
|
|
989
|
+
var reconnectToChat = function () {
|
|
990
|
+
var _onReconnectListener = signalingProvider.chat.onReconnectListener;
|
|
991
|
+
signalingProvider.chat.onReconnectListener = function () {
|
|
992
|
+
if (typeof _onReconnectListener === 'function') {
|
|
993
|
+
_onReconnectListener();
|
|
994
|
+
}
|
|
995
|
+
signalingProvider.chat.onReconnectListener = _onReconnectListener;
|
|
996
|
+
callback();
|
|
997
|
+
};
|
|
998
|
+
signalingProvider.chat.reconnect();
|
|
999
|
+
};
|
|
1000
|
+
if (signalingProvider && signalingProvider.chat) {
|
|
1001
|
+
try {
|
|
1002
|
+
/**
|
|
1003
|
+
* Ping chat server to make sure that chat connection
|
|
1004
|
+
* has been established
|
|
1005
|
+
*/
|
|
1006
|
+
signalingProvider.chat.ping(function (e) {
|
|
1007
|
+
if (self.state !== WebRTCSession.State.CLOSED) {
|
|
1008
|
+
if (e) {
|
|
1009
|
+
// If not - reconnect to chat
|
|
1010
|
+
reconnectToChat();
|
|
1011
|
+
} else {
|
|
1012
|
+
// If chat connected - call `callback`
|
|
1013
|
+
callback();
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
} catch (e) {
|
|
1018
|
+
if (self.state !== WebRTCSession.State.CLOSED) {
|
|
1019
|
+
/** Catch `ChatNotConnected` error and reconnect to chat */
|
|
1020
|
+
reconnectToChat();
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* @param {qbRTCPeerConnection} peerConnection
|
|
1028
|
+
* @param {boolean} [negotiate]
|
|
1029
|
+
* @returns {void}
|
|
1030
|
+
*/
|
|
1031
|
+
WebRTCSession.prototype._reconnect = function (peerConnection, negotiate) {
|
|
1032
|
+
if (!peerConnection || !peerConnection.userID) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
var userId = peerConnection.userID;
|
|
1036
|
+
var polite = peerConnection.polite;
|
|
1037
|
+
var _reconnecting = peerConnection._reconnecting;
|
|
1038
|
+
|
|
1039
|
+
peerConnection.release();
|
|
1040
|
+
|
|
1041
|
+
var pcConfig = {
|
|
1042
|
+
iceServers: config.webrtc.iceServers,
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
Helpers.trace("_reconnect peer configuration: " + JSON.stringify(pcConfig));
|
|
1046
|
+
|
|
1047
|
+
var peer = new qbRTCPeerConnection(pcConfig);
|
|
1048
|
+
this.peerConnections[userId] = peer;
|
|
1049
|
+
peer._init(this, userId, this.ID, polite);
|
|
1050
|
+
peer._reconnecting = _reconnecting;
|
|
1051
|
+
peer.addLocalStream(this.localStream);
|
|
1052
|
+
if (negotiate) {
|
|
1053
|
+
peer.offerId = generateUUID();
|
|
1054
|
+
peer.negotiate();
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
|
|
807
1058
|
/** close peer connection and local stream */
|
|
808
|
-
WebRTCSession.prototype._close = function() {
|
|
1059
|
+
WebRTCSession.prototype._close = function () {
|
|
809
1060
|
Helpers.trace('_close');
|
|
810
1061
|
|
|
811
1062
|
for (var key in this.peerConnections) {
|
|
812
1063
|
var peer = this.peerConnections[key];
|
|
1064
|
+
this._stopReconnectTimer(peer.userID);
|
|
813
1065
|
|
|
814
1066
|
try {
|
|
815
1067
|
peer.release();
|
|
@@ -819,6 +1071,14 @@ WebRTCSession.prototype._close = function() {
|
|
|
819
1071
|
}
|
|
820
1072
|
|
|
821
1073
|
this._closeLocalMediaStream();
|
|
1074
|
+
if (typeof this._detectSilentAudioTaskCleanup === 'function') {
|
|
1075
|
+
this._detectSilentAudioTaskCleanup();
|
|
1076
|
+
this._detectSilentAudioTaskCleanup = undefined;
|
|
1077
|
+
}
|
|
1078
|
+
if (typeof this._detectSilentVideoTaskCleanup === 'function') {
|
|
1079
|
+
this._detectSilentVideoTaskCleanup();
|
|
1080
|
+
this._detectSilentVideoTaskCleanup = undefined;
|
|
1081
|
+
}
|
|
822
1082
|
|
|
823
1083
|
this.state = WebRTCSession.State.CLOSED;
|
|
824
1084
|
|
|
@@ -827,31 +1087,12 @@ WebRTCSession.prototype._close = function() {
|
|
|
827
1087
|
}
|
|
828
1088
|
};
|
|
829
1089
|
|
|
830
|
-
WebRTCSession.prototype._closeSessionIfAllConnectionsClosed = function() {
|
|
831
|
-
var isAllConnectionsClosed =
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
try {
|
|
838
|
-
/*
|
|
839
|
-
TODO:
|
|
840
|
-
Uses RTCPeerConnection.signalingState instead RTCPeerConnection.iceConnectionState,
|
|
841
|
-
because state 'closed' comes after few time from Safari, but signaling state comes instantly
|
|
842
|
-
*/
|
|
843
|
-
peerState = peerCon.iceConnectionState === 'closed' ? 'closed' : peerCon.signalingState;
|
|
844
|
-
} catch(err) {
|
|
845
|
-
Helpers.traceError(err);
|
|
846
|
-
// need to set peerState to 'closed' on error. FF will crashed without this part.
|
|
847
|
-
peerState = 'closed';
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (peerState !== 'closed') {
|
|
851
|
-
isAllConnectionsClosed = false;
|
|
852
|
-
break;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
1090
|
+
WebRTCSession.prototype._closeSessionIfAllConnectionsClosed = function () {
|
|
1091
|
+
var isAllConnectionsClosed = Object
|
|
1092
|
+
.values(this.peerConnections)
|
|
1093
|
+
.every(function (peer) {
|
|
1094
|
+
return peer.state === qbRTCPeerConnection.State.CLOSED;
|
|
1095
|
+
});
|
|
855
1096
|
|
|
856
1097
|
Helpers.trace("All peer connections closed: " + isAllConnectionsClosed);
|
|
857
1098
|
|
|
@@ -866,26 +1107,17 @@ WebRTCSession.prototype._closeSessionIfAllConnectionsClosed = function() {
|
|
|
866
1107
|
}
|
|
867
1108
|
};
|
|
868
1109
|
|
|
869
|
-
WebRTCSession.prototype._closeLocalMediaStream = function(){
|
|
870
|
-
/**
|
|
871
|
-
* https://developers.google.com/web/updates/2015/07/mediastream-deprecations?hl=en
|
|
872
|
-
*/
|
|
1110
|
+
WebRTCSession.prototype._closeLocalMediaStream = function () {
|
|
873
1111
|
if (this.localStream) {
|
|
874
|
-
this.localStream.
|
|
875
|
-
|
|
876
|
-
|
|
1112
|
+
this.localStream.getTracks().forEach(function (track) {
|
|
1113
|
+
track.stop();
|
|
1114
|
+
track.enabled = false;
|
|
877
1115
|
});
|
|
878
|
-
|
|
879
|
-
this.localStream.getVideoTracks().forEach(function (videoTrack) {
|
|
880
|
-
videoTrack.stop();
|
|
881
|
-
videoTrack.enabled = false;
|
|
882
|
-
});
|
|
883
|
-
|
|
884
1116
|
this.localStream = null;
|
|
885
1117
|
}
|
|
886
1118
|
};
|
|
887
1119
|
|
|
888
|
-
WebRTCSession.prototype._muteStream = function(bool, type) {
|
|
1120
|
+
WebRTCSession.prototype._muteStream = function (bool, type) {
|
|
889
1121
|
if (type === 'audio' && this.localStream.getAudioTracks().length > 0) {
|
|
890
1122
|
this.localStream.getAudioTracks().forEach(function (track) {
|
|
891
1123
|
track.enabled = !!bool;
|
|
@@ -901,19 +1133,19 @@ WebRTCSession.prototype._muteStream = function(bool, type) {
|
|
|
901
1133
|
}
|
|
902
1134
|
};
|
|
903
1135
|
|
|
904
|
-
WebRTCSession.prototype._clearAnswerTimer = function(){
|
|
905
|
-
if(this.answerTimer){
|
|
1136
|
+
WebRTCSession.prototype._clearAnswerTimer = function () {
|
|
1137
|
+
if (this.answerTimer) {
|
|
906
1138
|
Helpers.trace("_clearAnswerTimer");
|
|
907
1139
|
clearTimeout(this.answerTimer);
|
|
908
1140
|
this.answerTimer = null;
|
|
909
1141
|
}
|
|
910
1142
|
};
|
|
911
1143
|
|
|
912
|
-
WebRTCSession.prototype._startAnswerTimer = function(){
|
|
1144
|
+
WebRTCSession.prototype._startAnswerTimer = function () {
|
|
913
1145
|
Helpers.trace("_startAnswerTimer");
|
|
914
1146
|
|
|
915
1147
|
var self = this;
|
|
916
|
-
var answerTimeoutCallback = function (){
|
|
1148
|
+
var answerTimeoutCallback = function () {
|
|
917
1149
|
Helpers.trace("_answerTimeoutCallback");
|
|
918
1150
|
|
|
919
1151
|
if (typeof self.onSessionCloseListener === 'function') {
|
|
@@ -923,28 +1155,28 @@ WebRTCSession.prototype._startAnswerTimer = function(){
|
|
|
923
1155
|
self.answerTimer = null;
|
|
924
1156
|
};
|
|
925
1157
|
|
|
926
|
-
var answerTimeInterval = config.webrtc.answerTimeInterval*1000;
|
|
1158
|
+
var answerTimeInterval = config.webrtc.answerTimeInterval * 1000;
|
|
927
1159
|
this.answerTimer = setTimeout(answerTimeoutCallback, answerTimeInterval);
|
|
928
1160
|
};
|
|
929
1161
|
|
|
930
|
-
WebRTCSession.prototype._clearWaitingOfferOrAnswerTimer = function() {
|
|
931
|
-
if(this.waitingOfferOrAnswerTimer){
|
|
1162
|
+
WebRTCSession.prototype._clearWaitingOfferOrAnswerTimer = function () {
|
|
1163
|
+
if (this.waitingOfferOrAnswerTimer) {
|
|
932
1164
|
Helpers.trace("_clearWaitingOfferOrAnswerTimer");
|
|
933
1165
|
clearTimeout(this.waitingOfferOrAnswerTimer);
|
|
934
1166
|
this.waitingOfferOrAnswerTimer = null;
|
|
935
1167
|
}
|
|
936
1168
|
};
|
|
937
1169
|
|
|
938
|
-
WebRTCSession.prototype._startWaitingOfferOrAnswerTimer = function(time) {
|
|
1170
|
+
WebRTCSession.prototype._startWaitingOfferOrAnswerTimer = function (time) {
|
|
939
1171
|
var self = this,
|
|
940
1172
|
timeout = (config.webrtc.answerTimeInterval - time) < 0 ? 1 : config.webrtc.answerTimeInterval - time,
|
|
941
|
-
waitingOfferOrAnswerTimeoutCallback = function() {
|
|
1173
|
+
waitingOfferOrAnswerTimeoutCallback = function () {
|
|
942
1174
|
Helpers.trace("waitingOfferOrAnswerTimeoutCallback");
|
|
943
1175
|
|
|
944
|
-
if(Object.keys(self.peerConnections).length > 0) {
|
|
945
|
-
Object.keys(self.peerConnections).forEach(function(key) {
|
|
1176
|
+
if (Object.keys(self.peerConnections).length > 0) {
|
|
1177
|
+
Object.keys(self.peerConnections).forEach(function (key) {
|
|
946
1178
|
var peerConnection = self.peerConnections[key];
|
|
947
|
-
if (peerConnection.state ===
|
|
1179
|
+
if (peerConnection.state === qbRTCPeerConnection.State.CONNECTING || peerConnection.state === qbRTCPeerConnection.State.NEW) {
|
|
948
1180
|
self.processOnNotAnswer(peerConnection);
|
|
949
1181
|
}
|
|
950
1182
|
});
|
|
@@ -955,19 +1187,19 @@ WebRTCSession.prototype._startWaitingOfferOrAnswerTimer = function(time) {
|
|
|
955
1187
|
|
|
956
1188
|
Helpers.trace("_startWaitingOfferOrAnswerTimer, timeout: " + timeout);
|
|
957
1189
|
|
|
958
|
-
this.waitingOfferOrAnswerTimer = setTimeout(waitingOfferOrAnswerTimeoutCallback, timeout*1000);
|
|
1190
|
+
this.waitingOfferOrAnswerTimer = setTimeout(waitingOfferOrAnswerTimeoutCallback, timeout * 1000);
|
|
959
1191
|
};
|
|
960
1192
|
|
|
961
|
-
WebRTCSession.prototype._uniqueOpponentsIDs = function(){
|
|
1193
|
+
WebRTCSession.prototype._uniqueOpponentsIDs = function () {
|
|
962
1194
|
var self = this;
|
|
963
1195
|
var opponents = [];
|
|
964
1196
|
|
|
965
|
-
if(this.initiatorID !== this.currentUserID){
|
|
1197
|
+
if (this.initiatorID !== this.currentUserID) {
|
|
966
1198
|
opponents.push(this.initiatorID);
|
|
967
1199
|
}
|
|
968
1200
|
|
|
969
|
-
this.opponentsIDs.forEach(function(userID, i, arr) {
|
|
970
|
-
if(userID != self.currentUserID){
|
|
1201
|
+
this.opponentsIDs.forEach(function (userID, i, arr) {
|
|
1202
|
+
if (userID != self.currentUserID) {
|
|
971
1203
|
opponents.push(parseInt(userID));
|
|
972
1204
|
}
|
|
973
1205
|
});
|
|
@@ -975,12 +1207,12 @@ WebRTCSession.prototype._uniqueOpponentsIDs = function(){
|
|
|
975
1207
|
return opponents;
|
|
976
1208
|
};
|
|
977
1209
|
|
|
978
|
-
WebRTCSession.prototype._uniqueOpponentsIDsWithoutInitiator = function(){
|
|
1210
|
+
WebRTCSession.prototype._uniqueOpponentsIDsWithoutInitiator = function () {
|
|
979
1211
|
var self = this;
|
|
980
1212
|
var opponents = [];
|
|
981
1213
|
|
|
982
|
-
this.opponentsIDs.forEach(function(userID, i, arr) {
|
|
983
|
-
if(userID != self.currentUserID){
|
|
1214
|
+
this.opponentsIDs.forEach(function (userID, i, arr) {
|
|
1215
|
+
if (userID != self.currentUserID) {
|
|
984
1216
|
opponents.push(parseInt(userID));
|
|
985
1217
|
}
|
|
986
1218
|
});
|
|
@@ -992,12 +1224,12 @@ WebRTCSession.prototype.toString = function sessionToString() {
|
|
|
992
1224
|
return 'ID: ' + this.ID + ', initiatorID: ' + this.initiatorID + ', opponentsIDs: ' + this.opponentsIDs + ', state: ' + this.state + ', callType: ' + this.callType;
|
|
993
1225
|
};
|
|
994
1226
|
|
|
995
|
-
function generateUUID(){
|
|
1227
|
+
function generateUUID() {
|
|
996
1228
|
var d = new Date().getTime();
|
|
997
|
-
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
998
|
-
var r = (d + Math.random()*16)%16 | 0;
|
|
999
|
-
d = Math.floor(d/16);
|
|
1000
|
-
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
|
|
1229
|
+
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
1230
|
+
var r = (d + Math.random() * 16) % 16 | 0;
|
|
1231
|
+
d = Math.floor(d / 16);
|
|
1232
|
+
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
|
|
1001
1233
|
});
|
|
1002
1234
|
return uuid;
|
|
1003
1235
|
}
|
|
@@ -1010,9 +1242,9 @@ function _prepareExtension(extension) {
|
|
|
1010
1242
|
var ext = {};
|
|
1011
1243
|
|
|
1012
1244
|
try {
|
|
1013
|
-
if (
|
|
1245
|
+
if (({}).toString.call(extension) === '[object Object]') {
|
|
1014
1246
|
ext.userInfo = extension;
|
|
1015
|
-
ext = JSON.parse(
|
|
1247
|
+
ext = JSON.parse(JSON.stringify(ext).replace(/null/g, "\"\""));
|
|
1016
1248
|
} else {
|
|
1017
1249
|
throw new Error('Invalid type of "extension" object.');
|
|
1018
1250
|
}
|