@techsee/techsee-media-service 0.1.3
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 -0
- package/lib/constants.js +20 -0
- package/lib/constants.js.map +1 -0
- package/lib/event-emitter.js +39 -0
- package/lib/event-emitter.js.map +1 -0
- package/lib/index.js +64 -0
- package/lib/index.js.map +1 -0
- package/lib/opentok/session.js +304 -0
- package/lib/opentok/session.js.map +1 -0
- package/lib/opentok/stream.js +310 -0
- package/lib/opentok/stream.js.map +1 -0
- package/lib/publisher.js +143 -0
- package/lib/publisher.js.map +1 -0
- package/lib/qos/raw-qos.js +319 -0
- package/lib/qos/raw-qos.js.map +1 -0
- package/lib/service.js +316 -0
- package/lib/service.js.map +1 -0
- package/lib/session.js +104 -0
- package/lib/session.js.map +1 -0
- package/lib/stream.js +195 -0
- package/lib/stream.js.map +1 -0
- package/lib/subscriber.js +90 -0
- package/lib/subscriber.js.map +1 -0
- package/lib/webrtc/constants.js +112 -0
- package/lib/webrtc/constants.js.map +1 -0
- package/lib/webrtc/helper.js +169 -0
- package/lib/webrtc/helper.js.map +1 -0
- package/lib/webrtc/session-kms.js +558 -0
- package/lib/webrtc/session-kms.js.map +1 -0
- package/lib/webrtc/session-loopback.js +340 -0
- package/lib/webrtc/session-loopback.js.map +1 -0
- package/lib/webrtc/session-turn.js +807 -0
- package/lib/webrtc/session-turn.js.map +1 -0
- package/lib/webrtc/session-turn.v2.js +2 -0
- package/lib/webrtc/session-turn.v2.js.map +1 -0
- package/lib/webrtc/session.js +40 -0
- package/lib/webrtc/session.js.map +1 -0
- package/lib/webrtc/stream.js +631 -0
- package/lib/webrtc/stream.js.map +1 -0
- package/lib/webrtc/temasys/adapter-loader.js +12 -0
- package/lib/webrtc/temasys/adapter-loader.js.map +1 -0
- package/lib/webrtc/temasys/adapter.js +5861 -0
- package/lib/webrtc/temasys/adapter.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.TurnWebRTCSession = undefined;
|
|
5
|
+
|
|
6
|
+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
7
|
+
|
|
8
|
+
var _session = require('../session');
|
|
9
|
+
|
|
10
|
+
var _publisher = require('../publisher');
|
|
11
|
+
|
|
12
|
+
var _stream = require('./stream');
|
|
13
|
+
|
|
14
|
+
var _socket = require('socket.io-client');
|
|
15
|
+
|
|
16
|
+
var io = _interopRequireWildcard(_socket);
|
|
17
|
+
|
|
18
|
+
var _tsEnvironmentDetect = require('techsee-common/lib/helpers/ts-environment-detect');
|
|
19
|
+
|
|
20
|
+
var _constants = require('./constants');
|
|
21
|
+
|
|
22
|
+
var _helper = require('./helper');
|
|
23
|
+
|
|
24
|
+
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
25
|
+
|
|
26
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
27
|
+
|
|
28
|
+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
29
|
+
|
|
30
|
+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
31
|
+
|
|
32
|
+
var isIE11 = new _tsEnvironmentDetect.TsEnvironmentDetect().isIE11();
|
|
33
|
+
|
|
34
|
+
var _ref = isIE11 ? require('kurento-utils-temasys-mod') : require('kurento-utils'),
|
|
35
|
+
WebRtcPeer = _ref.WebRtcPeer; // eslint-disable-line global-require
|
|
36
|
+
|
|
37
|
+
var AdapterJS = void 0;
|
|
38
|
+
|
|
39
|
+
if (isIE11) {
|
|
40
|
+
AdapterJS = require('./temasys/adapter-loader')(); // eslint-disable-line global-require
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* WebRTC-specific implementation of TsMediaSession.
|
|
45
|
+
*
|
|
46
|
+
* Instance of this class is returned by TsMediaService.initSession call
|
|
47
|
+
* with 'TURN_SERVER' as desired mediaType.
|
|
48
|
+
*
|
|
49
|
+
* @class
|
|
50
|
+
* @extends TsMediaSession
|
|
51
|
+
*
|
|
52
|
+
* @see TsMediaSession
|
|
53
|
+
*/
|
|
54
|
+
var TurnWebRTCSession = exports.TurnWebRTCSession = function (_TsMediaSession) {
|
|
55
|
+
_inherits(TurnWebRTCSession, _TsMediaSession);
|
|
56
|
+
|
|
57
|
+
function TurnWebRTCSession(_ref2, localStreamFactory) {
|
|
58
|
+
var credentials = _ref2.credentials,
|
|
59
|
+
isMobile = _ref2.isMobile,
|
|
60
|
+
resolution = _ref2.resolution,
|
|
61
|
+
deviceOpts = _ref2.deviceOpts;
|
|
62
|
+
|
|
63
|
+
_classCallCheck(this, TurnWebRTCSession);
|
|
64
|
+
|
|
65
|
+
var _this = _possibleConstructorReturn(this, _TsMediaSession.call(this));
|
|
66
|
+
|
|
67
|
+
_this._credentials = credentials;
|
|
68
|
+
_this._isMobile = isMobile;
|
|
69
|
+
_this._resolution = resolution;
|
|
70
|
+
_this._deviceOpts = deviceOpts;
|
|
71
|
+
_this._incomingIce = [];
|
|
72
|
+
_this._localStreamFactory = localStreamFactory;
|
|
73
|
+
|
|
74
|
+
var iceServers = credentials.ice.map(function (item) {
|
|
75
|
+
return {
|
|
76
|
+
urls: item.url,
|
|
77
|
+
username: item.login,
|
|
78
|
+
credential: item.password
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
_this._configuration = {
|
|
83
|
+
iceServers: iceServers,
|
|
84
|
+
|
|
85
|
+
iceTransportPolicy: 'relay'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Socket.io connection to the signaling server
|
|
90
|
+
*
|
|
91
|
+
* @private
|
|
92
|
+
* @member { Object }
|
|
93
|
+
*/
|
|
94
|
+
_this._socket = null;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* RTCPeerConnection to the media server.
|
|
98
|
+
*
|
|
99
|
+
* For now kurento-utils is used for connection establishment.
|
|
100
|
+
*
|
|
101
|
+
* @private
|
|
102
|
+
* @member { Object }
|
|
103
|
+
*/
|
|
104
|
+
_this._pcs = {};
|
|
105
|
+
return _this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Id of the session.
|
|
110
|
+
*
|
|
111
|
+
* @member { String }
|
|
112
|
+
*
|
|
113
|
+
* @type { String }
|
|
114
|
+
*/
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
*
|
|
119
|
+
* @member { String }
|
|
120
|
+
*
|
|
121
|
+
* @type { String }
|
|
122
|
+
*/
|
|
123
|
+
TurnWebRTCSession.prototype._connectSocket = function _connectSocket(url) {
|
|
124
|
+
var _this2 = this;
|
|
125
|
+
|
|
126
|
+
return new Promise(function (resolve, reject) {
|
|
127
|
+
var socket = io.connect(url, {
|
|
128
|
+
'forceNew': true,
|
|
129
|
+
'reconnectionDelay': _constants.SOCKET_RECONNECTION_DELAY,
|
|
130
|
+
'reconnectionDelayMax': _constants.SOCKET_RECONNECTION_DELAY_MAX,
|
|
131
|
+
'reconnectionAttempts': _constants.SOCKET_RECONNECTION_ATTEMPTS,
|
|
132
|
+
'path': _constants.SOCKET_PATH
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
var done = _.once(function (err) {
|
|
136
|
+
err ? reject(err) : resolve();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
//
|
|
140
|
+
// When connection is established
|
|
141
|
+
//
|
|
142
|
+
socket.once('connect', function () {
|
|
143
|
+
return done();
|
|
144
|
+
});
|
|
145
|
+
//
|
|
146
|
+
// errorOccured is ts-media-client signal
|
|
147
|
+
//
|
|
148
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.ERROR, done);
|
|
149
|
+
//
|
|
150
|
+
// And these 2 are socket.io connection failed events
|
|
151
|
+
//
|
|
152
|
+
socket.on('connect_error', done);
|
|
153
|
+
socket.on('connect_timeout', done);
|
|
154
|
+
|
|
155
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.JOIN_RESPONSE, function (_ref3) {
|
|
156
|
+
var sdpAnswer = _ref3.sdpAnswer,
|
|
157
|
+
uid = _ref3.uid,
|
|
158
|
+
peerId = _ref3.peerId;
|
|
159
|
+
|
|
160
|
+
var pc = _this2._pcs[peerId];
|
|
161
|
+
|
|
162
|
+
if (!pc) {
|
|
163
|
+
var error = 'pc not found. peer=' + peerId + ' uid=' + uid;
|
|
164
|
+
|
|
165
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'JOIN_RESPONSE', error: error });
|
|
166
|
+
|
|
167
|
+
return done(error);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
pc.processAnswer(sdpAnswer, function (error) {
|
|
171
|
+
if (error) {
|
|
172
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'processAnswer', error: error });
|
|
173
|
+
|
|
174
|
+
return done(error);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// FIXME why don't we have _incomingIce per PC? current assumes we don't have parallel connections, not good
|
|
178
|
+
_.forEach(_this2._incomingIce, function (candidate) {
|
|
179
|
+
pc.addIceCandidate(candidate, function (error) {
|
|
180
|
+
if (error) {
|
|
181
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'JOIN_RESPONSE addIceCandidate', error: error });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
_this2._incomingIce.length = 0;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
_this2.emit(_session.EVENTS.CONNECTED);
|
|
190
|
+
|
|
191
|
+
_this2.publish(_this2.publisher).catch(function (error) {
|
|
192
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'publish', error: error });
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.ICE_CANDIDATE, function (_ref4) {
|
|
197
|
+
var candidate = _ref4.candidate,
|
|
198
|
+
peerId = _ref4.peerId;
|
|
199
|
+
|
|
200
|
+
var pc = _this2._pcs[peerId];
|
|
201
|
+
|
|
202
|
+
if (!pc) {
|
|
203
|
+
_this2._incomingIce.push(candidate);
|
|
204
|
+
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
pc.addIceCandidate(candidate, function (error) {
|
|
209
|
+
if (error) {
|
|
210
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'ICE_CANDIDATE addIceCandidate', error: error });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Received by publisher, causing him to emit SDP_OFFER
|
|
216
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.SEND_OFFER, function (_ref5) {
|
|
217
|
+
var uid = _ref5.uid,
|
|
218
|
+
peerId = _ref5.peerId;
|
|
219
|
+
|
|
220
|
+
_this2._createRTCPeerConnection(peerId).then(function (pc) {
|
|
221
|
+
pc.generateOffer(function (error, offer) {
|
|
222
|
+
if (error) {
|
|
223
|
+
return done(error);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
socket.emit(_constants.SOCKET_EVENTS.OUTGOING.SDP_OFFER, {
|
|
227
|
+
uid: uid,
|
|
228
|
+
peerId: peerId,
|
|
229
|
+
sessionId: _this2._credentials.sessionId,
|
|
230
|
+
sdpOffer: offer
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
}).catch(function (error) {
|
|
234
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: '_createRTCPeerConnection', error: error });
|
|
235
|
+
if (error && error.name) {
|
|
236
|
+
_this2.lastError = error.name;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Received by subscriber, causing him to emit SDP_ANSWER
|
|
242
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.SDP_OFFER, function (_ref6) {
|
|
243
|
+
var sdpOffer = _ref6.sdpOffer,
|
|
244
|
+
peerId = _ref6.peerId,
|
|
245
|
+
uid = _ref6.uid;
|
|
246
|
+
|
|
247
|
+
Promise.resolve().then(function () {
|
|
248
|
+
var pc = _this2._pcs[peerId];
|
|
249
|
+
|
|
250
|
+
if (pc) {
|
|
251
|
+
return pc;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return _this2._createRTCPeerConnection(peerId);
|
|
255
|
+
}).then(function (pc) {
|
|
256
|
+
pc.processOffer(sdpOffer, function (error, sdpAnswer) {
|
|
257
|
+
if (error) {
|
|
258
|
+
return done(error);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
socket.emit(_constants.SOCKET_EVENTS.OUTGOING.SDP_ANSWER, {
|
|
262
|
+
uid: uid,
|
|
263
|
+
peerId: peerId,
|
|
264
|
+
sessionId: _this2._credentials.sessionId,
|
|
265
|
+
sdpAnswer: sdpAnswer
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}).catch(function (error) {
|
|
269
|
+
_this2.emit(_session.EVENTS.EXCEPTION, { source: 'response on INCOMING.SDP_OFFER', error: error });
|
|
270
|
+
if (error && error.name) {
|
|
271
|
+
_this2.lastError = error.name;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Received by subscriber after a publisher reconnects to room
|
|
277
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.PUBLISHER_RECONNECT, function () {
|
|
278
|
+
// Clean up and reconnect with a new peer-connection,
|
|
279
|
+
// to prevent reconnection issues
|
|
280
|
+
|
|
281
|
+
_this2._streams.clear();
|
|
282
|
+
socket.off();
|
|
283
|
+
_.forEach(_this2._pcs, function (_ref7) {
|
|
284
|
+
var peerConnection = _ref7.peerConnection;
|
|
285
|
+
return peerConnection.close();
|
|
286
|
+
});
|
|
287
|
+
_this2._pcs = {};
|
|
288
|
+
|
|
289
|
+
_this2.emit(_session.EVENTS.RECONNECT_TURN_SERVER);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Received by publisher after a subscriber's socket has been disconnected
|
|
293
|
+
socket.on(_constants.SOCKET_EVENTS.INCOMING.PEER_DISCONNECTED, function () {
|
|
294
|
+
var _ref8 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
295
|
+
peerId = _ref8.peerId;
|
|
296
|
+
|
|
297
|
+
if (peerId && _this2._pcs[peerId]) {
|
|
298
|
+
if (_this2._lastPc === _this2._pcs[peerId]) {
|
|
299
|
+
_this2._lastPc = null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (Object.getOwnPropertyNames(_this2._pcs).length === 1) {
|
|
303
|
+
_this2._toggleAllStreams(false);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
_this2._pcs[peerId].peerConnection.close();
|
|
307
|
+
|
|
308
|
+
delete _this2._pcs[peerId];
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
_this2._socket = socket;
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
TurnWebRTCSession.prototype._setupConnection = function _setupConnection(pc) {
|
|
317
|
+
var _this3 = this;
|
|
318
|
+
|
|
319
|
+
return new Promise(function (resolve) {
|
|
320
|
+
pc.peerConnection.oniceconnectionstatechange = function (e) {
|
|
321
|
+
if (_this3.iceCheckingTimeout) {
|
|
322
|
+
clearTimeout(_this3.iceCheckingTimeout);
|
|
323
|
+
_this3.iceCheckingTimeout = null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (e.target.iceConnectionState === 'failed') {
|
|
327
|
+
// if this is one of many in the publisher, skip
|
|
328
|
+
|
|
329
|
+
// already attempting a reconnect
|
|
330
|
+
if (_this3._turnServerReconnectInterval) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
_this3._turnServerReconnectInterval = setInterval(function () {
|
|
335
|
+
if (!navigator.onLine) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
clearInterval(_this3._turnServerReconnectInterval);
|
|
340
|
+
_this3._turnServerReconnectInterval = null;
|
|
341
|
+
|
|
342
|
+
if (!_this3._socket.connected) {
|
|
343
|
+
_this3.disconnected = false;
|
|
344
|
+
_this3._socket.connect();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
_this3.emit(_session.EVENTS.RECONNECT_TURN_SERVER);
|
|
348
|
+
}, _constants.RECONNECT_TURN_SERVER_INTERVAL);
|
|
349
|
+
} else if (e.target.iceConnectionState === 'checking') {
|
|
350
|
+
_this3.iceCheckingTimeout = setTimeout(function () {
|
|
351
|
+
_this3.emit(_session.EVENTS.TURN_SERVER_CONNECTION_FAILED);
|
|
352
|
+
}, _constants.FAILED_CONNECTING_TURN_SERVER_INTERVAL);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
pc.peerConnection.onremovestream = function (_ref9) {
|
|
357
|
+
var stream = _ref9.stream;
|
|
358
|
+
|
|
359
|
+
_this3._streams.delete(stream.id);
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
resolve();
|
|
363
|
+
});
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
TurnWebRTCSession.prototype._getPeerType = function _getPeerType(constraints, isMobile) {
|
|
367
|
+
if (isMobile || constraints.audio) {
|
|
368
|
+
return _constants.PEER_CONNECTION_TYPES.SEND_RECV;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return _constants.PEER_CONNECTION_TYPES.RECV_ONLY;
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
TurnWebRTCSession.prototype._connectRTCPeer = function _connectRTCPeer(_ref10) {
|
|
375
|
+
var _this4 = this;
|
|
376
|
+
|
|
377
|
+
var constraints = _ref10.constraints,
|
|
378
|
+
configuration = _ref10.configuration,
|
|
379
|
+
sessionId = _ref10.sessionId,
|
|
380
|
+
isMobile = _ref10.isMobile,
|
|
381
|
+
peerId = _ref10.peerId;
|
|
382
|
+
|
|
383
|
+
return new Promise(function (resolve, reject) {
|
|
384
|
+
var done = _.once(function (err, pc) {
|
|
385
|
+
err ? reject(err) : resolve(pc);
|
|
386
|
+
});
|
|
387
|
+
var setupWebRtcPeer = function setupWebRtcPeer(stream) {
|
|
388
|
+
var peerConnectionType = _this4._getPeerType(constraints, isMobile);
|
|
389
|
+
var peerConfig = {
|
|
390
|
+
configuration: configuration,
|
|
391
|
+
videoStream: stream,
|
|
392
|
+
mediaConstraints: constraints,
|
|
393
|
+
onicecandidate: function onicecandidate(candidate) {
|
|
394
|
+
_this4._socket.emit(_constants.SOCKET_EVENTS.OUTGOING.ICE_CANDIDATE, {
|
|
395
|
+
sessionId: sessionId,
|
|
396
|
+
peerId: peerId,
|
|
397
|
+
candidate: candidate,
|
|
398
|
+
clientType: isMobile ? _constants.CLIENT_TYPES.PUBLISHER : _constants.CLIENT_TYPES.SUBSCRIBER
|
|
399
|
+
});
|
|
400
|
+
},
|
|
401
|
+
AdapterJS: AdapterJS
|
|
402
|
+
};
|
|
403
|
+
var pc = WebRtcPeer[peerConnectionType](peerConfig, function (error) {
|
|
404
|
+
if (error) {
|
|
405
|
+
return done(error);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
_this4._setupConnection(pc).then(function () {
|
|
409
|
+
return done(null, pc);
|
|
410
|
+
}).catch(done);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
_this4._pcs[peerId] = pc;
|
|
414
|
+
_this4._lastPc = pc;
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
_this4._localStreamFactory(constraints).then(function (localStream) {
|
|
418
|
+
setupWebRtcPeer(localStream);
|
|
419
|
+
}).catch(function () {
|
|
420
|
+
setupWebRtcPeer(undefined);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
TurnWebRTCSession.prototype._setTrackStateTo = function _setTrackStateTo(state) {
|
|
426
|
+
return function (track) {
|
|
427
|
+
if (track.setEnabled) {
|
|
428
|
+
track.setEnabled(state);
|
|
429
|
+
} else {
|
|
430
|
+
track.enabled = state;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Establish socket and peer connection to the media server.
|
|
437
|
+
*
|
|
438
|
+
* @method
|
|
439
|
+
* @returns { Promise }
|
|
440
|
+
*
|
|
441
|
+
* @fires SESSION_EVENTS#CONNECTED
|
|
442
|
+
*/
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
TurnWebRTCSession.prototype.connect = function connect() {
|
|
446
|
+
var _this5 = this;
|
|
447
|
+
|
|
448
|
+
return this._connectSocket(this._credentials.url).then(function () {
|
|
449
|
+
return (0, _helper.getMediaConstraints)({
|
|
450
|
+
resolution: _this5._resolution,
|
|
451
|
+
deviceOpts: _this5._deviceOpts
|
|
452
|
+
});
|
|
453
|
+
}).then(function (constraints) {
|
|
454
|
+
// streaming from dashboard is not supported and we
|
|
455
|
+
// don't want to access the camera without a reason
|
|
456
|
+
if (!_this5._isMobile && constraints.video) {
|
|
457
|
+
delete constraints.video;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
_this5._constraints = constraints;
|
|
461
|
+
|
|
462
|
+
var clientType = _this5._isMobile ? _constants.CLIENT_TYPES.PUBLISHER : _constants.CLIENT_TYPES.SUBSCRIBER;
|
|
463
|
+
|
|
464
|
+
_this5._socket.emit(_constants.SOCKET_EVENTS.OUTGOING.JOIN_ROOM, {
|
|
465
|
+
clientType: clientType,
|
|
466
|
+
sessionId: _this5._credentials.sessionId
|
|
467
|
+
});
|
|
468
|
+
}).catch(function (error) {
|
|
469
|
+
_this5.emit(_session.EVENTS.EXCEPTION, { source: 'connect', error: error });
|
|
470
|
+
throw error;
|
|
471
|
+
});
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
TurnWebRTCSession.prototype._createRTCPeerConnection = function _createRTCPeerConnection(peerId) {
|
|
475
|
+
var _this6 = this;
|
|
476
|
+
|
|
477
|
+
return this._connectRTCPeer({
|
|
478
|
+
constraints: this._constraints,
|
|
479
|
+
configuration: this._configuration,
|
|
480
|
+
sessionId: this._credentials.sessionId,
|
|
481
|
+
isMobile: this._isMobile,
|
|
482
|
+
peerId: peerId
|
|
483
|
+
}).then(function (pc) {
|
|
484
|
+
pc.peerConnection.getLocalStreams().forEach(function (stream) {
|
|
485
|
+
if (!_this6.publisher) {
|
|
486
|
+
stream.getTracks().forEach(_this6._setTrackStateTo(false));
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
pc.peerConnection.onaddstream = function (_ref11) {
|
|
491
|
+
var stream = _ref11.stream;
|
|
492
|
+
|
|
493
|
+
var tsStream = new _stream.TsWebRTCStream(_this6, stream);
|
|
494
|
+
|
|
495
|
+
_this6.emit(_session.EVENTS.STREAM_CREATED, tsStream);
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
return pc;
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
TurnWebRTCSession.prototype._toggleAllStreams = function _toggleAllStreams(state) {
|
|
503
|
+
var _this7 = this;
|
|
504
|
+
|
|
505
|
+
_.forEach(this._pcs, function (pc) {
|
|
506
|
+
pc.peerConnection.getLocalStreams().forEach(function (stream) {
|
|
507
|
+
stream.getTracks().forEach(_this7._setTrackStateTo(state));
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
TurnWebRTCSession.prototype._stopStream = function _stopStream(pc) {
|
|
513
|
+
pc.peerConnection.getLocalStreams().forEach(function (stream) {
|
|
514
|
+
stream.getTracks().forEach(function (track) {
|
|
515
|
+
return track.stop();
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
TurnWebRTCSession.prototype._stopAllStreams = function _stopAllStreams() {
|
|
521
|
+
var _this8 = this;
|
|
522
|
+
|
|
523
|
+
_.forEach(this._pcs, function (pc) {
|
|
524
|
+
return _this8._stopStream(pc);
|
|
525
|
+
});
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Close all media streams, disconnect RTCPeerConnection and socket.io
|
|
530
|
+
*
|
|
531
|
+
* @method
|
|
532
|
+
* @returns { Promise }
|
|
533
|
+
*
|
|
534
|
+
* @fires SESSION_EVENTS#DISCONNECTED
|
|
535
|
+
*/
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
TurnWebRTCSession.prototype.disconnect = function disconnect() {
|
|
539
|
+
var _this9 = this;
|
|
540
|
+
|
|
541
|
+
return Promise.resolve().then(function () {
|
|
542
|
+
if (_this9.destroyed) {
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
_this9._stopAllStreams();
|
|
547
|
+
|
|
548
|
+
_.forEach(_this9._pcs, function (_ref12) {
|
|
549
|
+
var peerConnection = _ref12.peerConnection;
|
|
550
|
+
return peerConnection.close();
|
|
551
|
+
});
|
|
552
|
+
_this9._pcs = {};
|
|
553
|
+
_this9._socket.disconnect();
|
|
554
|
+
|
|
555
|
+
var event = { reason: 'disconnected' };
|
|
556
|
+
|
|
557
|
+
_this9.emit(_session.EVENTS.DISCONNECTED, event);
|
|
558
|
+
|
|
559
|
+
return event;
|
|
560
|
+
});
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
TurnWebRTCSession.prototype._waitForPublishingApproval = function _waitForPublishingApproval(caller, cb) {
|
|
564
|
+
try {
|
|
565
|
+
var pc = _.get(caller, '_lastPc.peerConnection');
|
|
566
|
+
|
|
567
|
+
if (!pc) {
|
|
568
|
+
throw new Error('No peer connection for publishing');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
//
|
|
572
|
+
// Get media streams that were created on session initialization
|
|
573
|
+
//
|
|
574
|
+
var mediaStream = pc.getLocalStreams()[0];
|
|
575
|
+
|
|
576
|
+
if (mediaStream) {
|
|
577
|
+
return cb(null, mediaStream);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (caller.lastError === 'NotAllowedError') {
|
|
581
|
+
return cb({ code: 1500, name: 'NotAllowedError' });
|
|
582
|
+
} else if (caller.lastError === 'PermissionDismissedError') {
|
|
583
|
+
return cb({ code: 8, name: 'PermissionDismissedError' });
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
setTimeout(caller._waitForPublishingApproval, _constants.PUBLISH_APPROVAL_CHECK_INTERVAL, caller, cb);
|
|
587
|
+
} catch (err) {
|
|
588
|
+
return cb(err);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Create new TsMediaPublisher instance
|
|
594
|
+
*
|
|
595
|
+
* @method
|
|
596
|
+
*
|
|
597
|
+
* @param { DOMElement } [target] Video element
|
|
598
|
+
* that will be used to render publishing video stream
|
|
599
|
+
* @param { Object } params
|
|
600
|
+
* @param { String } [params.videoDevice] Video device id
|
|
601
|
+
* @param { String } [params.videoParams] Video device id
|
|
602
|
+
* @param { String } [params.videoParams.fitMode] Video fit mode
|
|
603
|
+
* @param { Number|String } [params.videoParams.width] Video element width
|
|
604
|
+
* @param { Number|String } [params.videoParams.height] Video element height
|
|
605
|
+
* @param { String } [params.audioDevice] Video device id
|
|
606
|
+
*
|
|
607
|
+
* @returns { Promise<TsMediaPublisher> } publisher
|
|
608
|
+
*
|
|
609
|
+
* @see TsMediaPublisher
|
|
610
|
+
*/
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
TurnWebRTCSession.prototype.createPublisher = function createPublisher(target, params) {
|
|
614
|
+
var _this10 = this;
|
|
615
|
+
|
|
616
|
+
if (!params) {
|
|
617
|
+
params = target;
|
|
618
|
+
target = null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return (this._lastPc ? Promise.resolve() : Promise.delay(_constants.WAIT_FOR_PC_INTERVAL)).then(function () {
|
|
622
|
+
return new Promise(function (resolve, reject) {
|
|
623
|
+
_this10._waitForPublishingApproval(_this10, function (err, mediaStream) {
|
|
624
|
+
if (err) {
|
|
625
|
+
return reject(err);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return resolve(mediaStream);
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
}).then(function (mediaStream) {
|
|
632
|
+
//
|
|
633
|
+
// Get tracks that are related to the publisher (audio or video)
|
|
634
|
+
//
|
|
635
|
+
var tracks = _this10._lastPc.peerConnection.getLocalStreams().reduce(function (result, mediaStream) {
|
|
636
|
+
mediaStream.getTracks().filter(function (track) {
|
|
637
|
+
return track.kind === 'video' && params.videoDevice || track.kind === 'audio' && params.audioDevice;
|
|
638
|
+
}).forEach(function (track) {
|
|
639
|
+
return result.push(track);
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
return result;
|
|
643
|
+
}, []);
|
|
644
|
+
|
|
645
|
+
var tsstream = new _stream.TsWebRTCStream(_this10, mediaStream, params.name);
|
|
646
|
+
|
|
647
|
+
tsstream.remote = false;
|
|
648
|
+
|
|
649
|
+
var publisher = new _publisher.TsMediaPublisher(_this10, {
|
|
650
|
+
stream: tsstream,
|
|
651
|
+
tracks: tracks,
|
|
652
|
+
videoParams: params.videoParams,
|
|
653
|
+
|
|
654
|
+
videoHeight: tsstream.videoDimensions.height,
|
|
655
|
+
videoWidth: tsstream.videoDimensions.width,
|
|
656
|
+
|
|
657
|
+
_target: target
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
tsstream.publisher = publisher;
|
|
661
|
+
_this10.publisher = publisher;
|
|
662
|
+
|
|
663
|
+
return publisher;
|
|
664
|
+
});
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Publish stream from previously create TsMediaPublisher instance.
|
|
669
|
+
*
|
|
670
|
+
* Used internally by TsMediaPublisher.publish
|
|
671
|
+
*
|
|
672
|
+
* @method
|
|
673
|
+
*
|
|
674
|
+
* @param { TsMediaPublisher } publisher
|
|
675
|
+
*
|
|
676
|
+
* @returns { Promise }
|
|
677
|
+
*
|
|
678
|
+
* @fires PUBLISHER_EVENTS#STREAM_CREATED
|
|
679
|
+
* @fires SESSION_EVENTS#STREAM_PROPERTY_CHANGED
|
|
680
|
+
* @fires STREAM_EVENTS#PROPERTY_CHANGED
|
|
681
|
+
*
|
|
682
|
+
* @see TsMediaPublisher#publish
|
|
683
|
+
*/
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
TurnWebRTCSession.prototype.publish = function publish(publisher) {
|
|
687
|
+
var _this11 = this;
|
|
688
|
+
|
|
689
|
+
return Promise.resolve().then(function () {
|
|
690
|
+
if (!publisher) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
publisher.emit(_publisher.EVENTS.STREAM_CREATED, publisher.stream);
|
|
695
|
+
_this11.emit(_session.EVENTS.STREAM_PROPERTY_CHANGED, {
|
|
696
|
+
stream: publisher.stream,
|
|
697
|
+
property: 'hasVideo',
|
|
698
|
+
value: publisher.stream.hasVideo
|
|
699
|
+
});
|
|
700
|
+
_this11.emit(_session.EVENTS.STREAM_PROPERTY_CHANGED, {
|
|
701
|
+
stream: publisher.stream,
|
|
702
|
+
property: 'hasAudio',
|
|
703
|
+
value: publisher.stream.hasAudio
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
if (_this11.publisher._element) {
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (publisher._target) {
|
|
711
|
+
//
|
|
712
|
+
// New video element where publishing video stream will be rendered
|
|
713
|
+
//
|
|
714
|
+
var video = document.createElement('video');
|
|
715
|
+
|
|
716
|
+
if (!isIE11) {
|
|
717
|
+
video.src = URL.createObjectURL(publisher.stream._mediaStream);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
video.playsInline = true;
|
|
721
|
+
video.autoplay = true;
|
|
722
|
+
|
|
723
|
+
if (isIE11) {
|
|
724
|
+
publisher._target.appendChild(video);
|
|
725
|
+
publisher._element = attachMediaStream(video, publisher.stream._mediaStream);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
var params = _.assign({}, {
|
|
729
|
+
fitMode: 'contain',
|
|
730
|
+
insertMode: 'append',
|
|
731
|
+
mirror: false,
|
|
732
|
+
resolution: '480x640',
|
|
733
|
+
width: '100%',
|
|
734
|
+
height: '100%'
|
|
735
|
+
}, publisher.videoParams);
|
|
736
|
+
|
|
737
|
+
var elementHeight = typeof params.height === 'string' ? params.height : params.height + 'px';
|
|
738
|
+
var elementWidth = typeof params.width === 'string' ? params.width : params.width + 'px';
|
|
739
|
+
|
|
740
|
+
_.assign((isIE11 ? publisher._element : video).style, {
|
|
741
|
+
height: elementHeight,
|
|
742
|
+
width: elementWidth,
|
|
743
|
+
|
|
744
|
+
position: 'relative',
|
|
745
|
+
'object-fit': params.fitMode
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
if (!isIE11) {
|
|
749
|
+
publisher._target.appendChild(video);
|
|
750
|
+
publisher._element = video;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
publisher.stream.setupDimensionsListener(video);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
//
|
|
757
|
+
// Enable (unpause) all media tracks related to publisher.
|
|
758
|
+
// They are sending data to the server from this moment.
|
|
759
|
+
//
|
|
760
|
+
_this11._toggleAllStreams(true);
|
|
761
|
+
});
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Unpublish stream and destroy publisher's view.
|
|
766
|
+
*
|
|
767
|
+
* Used internally by TsMediaPublisher.destroy
|
|
768
|
+
*
|
|
769
|
+
* @method
|
|
770
|
+
*
|
|
771
|
+
* @param { TsMediaPublisher } publisher
|
|
772
|
+
*
|
|
773
|
+
* @returns { Promise }
|
|
774
|
+
*
|
|
775
|
+
* @fires PUBLISHER_EVENTS#DESTROYED
|
|
776
|
+
*
|
|
777
|
+
* @see TsMediaPublisher#destroy
|
|
778
|
+
*/
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
TurnWebRTCSession.prototype.destroyPublisher = function destroyPublisher(publisher) {
|
|
782
|
+
var _this12 = this;
|
|
783
|
+
|
|
784
|
+
return new Promise(function (resolve) {
|
|
785
|
+
_this12._toggleAllStreams(false);
|
|
786
|
+
|
|
787
|
+
if (publisher._element) {
|
|
788
|
+
publisher._element.remove();
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
_this12.publisher = null;
|
|
792
|
+
publisher.emit(_publisher.EVENTS.DESTROYED);
|
|
793
|
+
|
|
794
|
+
return resolve();
|
|
795
|
+
});
|
|
796
|
+
};
|
|
797
|
+
|
|
798
|
+
_createClass(TurnWebRTCSession, [{
|
|
799
|
+
key: 'id',
|
|
800
|
+
get: function get() {
|
|
801
|
+
return this._credentials.sessionId;
|
|
802
|
+
}
|
|
803
|
+
}]);
|
|
804
|
+
|
|
805
|
+
return TurnWebRTCSession;
|
|
806
|
+
}(_session.TsMediaSession);
|
|
807
|
+
//# sourceMappingURL=session-turn.js.map
|