@signalwire/js 1.3.0-dev.1 → 1.4.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 +4 -2
- package/dist/esm/common/src/BaseSession.d.ts +8 -13
- package/dist/esm/common/src/BaseSession.js +33 -46
- package/dist/esm/common/src/BrowserSession.d.ts +20 -57
- package/dist/esm/common/src/BrowserSession.js +91 -359
- package/dist/esm/common/src/messages/Verto.d.ts +1 -10
- package/dist/esm/common/src/messages/Verto.js +1 -16
- package/dist/esm/common/src/messages/verto/Login.d.ts +1 -13
- package/dist/esm/common/src/messages/verto/Login.js +2 -5
- package/dist/esm/common/src/services/BroadcastHandler.js +3 -2
- package/dist/esm/common/src/services/Connection.d.ts +2 -2
- package/dist/esm/common/src/services/Handler.d.ts +8 -9
- package/dist/esm/common/src/services/Handler.js +38 -41
- package/dist/esm/common/src/util/constants/index.d.ts +0 -1
- package/dist/esm/common/src/util/constants/index.js +0 -1
- package/dist/esm/common/src/util/helpers.d.ts +10 -3
- package/dist/esm/common/src/util/helpers.js +11 -6
- package/dist/esm/common/src/util/interfaces.d.ts +56 -21
- package/dist/esm/common/src/util/interfaces.js +1 -0
- package/dist/esm/common/src/util/logger.js +1 -4
- package/dist/esm/common/src/util/webrtc/index.d.ts +2 -7
- package/dist/esm/common/src/util/webrtc/index.js +2 -63
- package/dist/esm/common/src/webrtc/BaseCall.d.ts +79 -0
- package/dist/esm/common/src/webrtc/BaseCall.js +711 -0
- package/dist/esm/common/src/webrtc/Call.d.ts +5 -5
- package/dist/esm/common/src/webrtc/Call.js +15 -34
- package/dist/esm/common/src/webrtc/CantinaAuth.d.ts +14 -9
- package/dist/esm/common/src/webrtc/CantinaAuth.js +18 -24
- package/dist/esm/common/src/webrtc/LayoutHandler.d.ts +3 -0
- package/dist/esm/common/src/webrtc/LayoutHandler.js +36 -0
- package/dist/esm/common/src/webrtc/Peer.d.ts +23 -0
- package/dist/esm/common/src/webrtc/Peer.js +169 -0
- package/dist/esm/common/src/webrtc/VertoHandler.d.ts +11 -2
- package/dist/esm/common/src/webrtc/VertoHandler.js +187 -147
- package/dist/esm/common/src/webrtc/constants.d.ts +12 -27
- package/dist/esm/common/src/webrtc/constants.js +11 -33
- package/dist/esm/common/src/webrtc/helpers.d.ts +25 -19
- package/dist/esm/common/src/webrtc/helpers.js +190 -162
- package/dist/esm/common/src/webrtc/interfaces.d.ts +52 -209
- package/dist/esm/common/src/webrtc/interfaces.js +1 -0
- package/dist/esm/js/index.d.ts +1 -3
- package/dist/esm/js/index.js +2 -2
- package/dist/esm/js/src/SignalWire.d.ts +2 -0
- package/dist/esm/js/src/SignalWire.js +13 -0
- package/dist/esm/js/src/Verto.d.ts +2 -8
- package/dist/esm/js/src/Verto.js +14 -50
- package/dist/index.min.js +2 -6
- package/dist/index.min.js.LICENSE.txt +5 -0
- package/package.json +24 -19
- package/dist/esm/common/src/webrtc/ChatChannelHandler.d.ts +0 -5
- package/dist/esm/common/src/webrtc/ChatChannelHandler.js +0 -22
- package/dist/esm/common/src/webrtc/Conference.d.ts +0 -15
- package/dist/esm/common/src/webrtc/Conference.js +0 -68
- package/dist/esm/common/src/webrtc/ConferenceListChannelHandler.d.ts +0 -5
- package/dist/esm/common/src/webrtc/ConferenceListChannelHandler.js +0 -77
- package/dist/esm/common/src/webrtc/InfoChannelHandler.d.ts +0 -7
- package/dist/esm/common/src/webrtc/InfoChannelHandler.js +0 -85
- package/dist/esm/common/src/webrtc/LaChannelHandler.d.ts +0 -5
- package/dist/esm/common/src/webrtc/LaChannelHandler.js +0 -58
- package/dist/esm/common/src/webrtc/ModChannelHandler.d.ts +0 -71
- package/dist/esm/common/src/webrtc/ModChannelHandler.js +0 -267
- package/dist/esm/common/src/webrtc/RTCPeer.d.ts +0 -51
- package/dist/esm/common/src/webrtc/RTCPeer.js +0 -556
- package/dist/esm/common/src/webrtc/WebRTCCall.d.ts +0 -182
- package/dist/esm/common/src/webrtc/WebRTCCall.js +0 -784
- package/dist/esm/common/src/webrtc/deviceHelpers.d.ts +0 -18
- package/dist/esm/common/src/webrtc/deviceHelpers.js +0 -113
- package/dist/esm/common/src/webrtc/sdpHelpers.d.ts +0 -3
- package/dist/esm/common/src/webrtc/sdpHelpers.js +0 -56
|
@@ -1,556 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import logger from '../util/logger';
|
|
11
|
-
import SDPUtils from 'sdp';
|
|
12
|
-
import { getUserMedia, getMediaConstraints } from './helpers';
|
|
13
|
-
import { sdpStereoHack, sdpBitrateHack, sdpMediaOrderHack } from './sdpHelpers';
|
|
14
|
-
import { SwEvent } from '../util/constants';
|
|
15
|
-
import { PeerType, State } from './constants';
|
|
16
|
-
import { attachMediaStream, muteMediaElement, sdpToJsonHack, RTCPeerConnection, streamIsValid, buildAudioElementByTrack, buildVideoElementByTrack, stopTrack } from '../util/webrtc';
|
|
17
|
-
import { trigger } from '../services/Handler';
|
|
18
|
-
import { Invite, Attach, Answer, Modify } from '../messages/Verto';
|
|
19
|
-
export default class RTCPeer {
|
|
20
|
-
constructor(call, type, options) {
|
|
21
|
-
this.call = call;
|
|
22
|
-
this.type = type;
|
|
23
|
-
this.options = options;
|
|
24
|
-
this._iceTimeout = null;
|
|
25
|
-
this._negotiating = false;
|
|
26
|
-
logger.info('New Peer with type:', this.type, 'Options:', this.options);
|
|
27
|
-
this._onIce = this._onIce.bind(this);
|
|
28
|
-
this._init();
|
|
29
|
-
}
|
|
30
|
-
get isOffer() {
|
|
31
|
-
return this.type === PeerType.Offer;
|
|
32
|
-
}
|
|
33
|
-
get isAnswer() {
|
|
34
|
-
return this.type === PeerType.Answer;
|
|
35
|
-
}
|
|
36
|
-
get isSimulcast() {
|
|
37
|
-
return this.options.simulcast === true;
|
|
38
|
-
}
|
|
39
|
-
get isSfu() {
|
|
40
|
-
return this.options.sfu === true;
|
|
41
|
-
}
|
|
42
|
-
get hasExperimentalFlag() {
|
|
43
|
-
return this.options.experimental === true;
|
|
44
|
-
}
|
|
45
|
-
get hasAudioSender() {
|
|
46
|
-
return this._getSenderByKind('audio') ? true : false;
|
|
47
|
-
}
|
|
48
|
-
get hasVideoSender() {
|
|
49
|
-
return this._getSenderByKind('video') ? true : false;
|
|
50
|
-
}
|
|
51
|
-
get hasAudioReceiver() {
|
|
52
|
-
return this._getReceiverByKind('audio') ? true : false;
|
|
53
|
-
}
|
|
54
|
-
get hasVideoReceiver() {
|
|
55
|
-
return this._getReceiverByKind('video') ? true : false;
|
|
56
|
-
}
|
|
57
|
-
get config() {
|
|
58
|
-
const { iceServers = [], rtcPeerConfig = {} } = this.options;
|
|
59
|
-
const config = Object.assign({ bundlePolicy: 'max-compat', iceServers, sdpSemantics: 'unified-plan' }, rtcPeerConfig);
|
|
60
|
-
logger.info('RTC config', config);
|
|
61
|
-
return config;
|
|
62
|
-
}
|
|
63
|
-
get localSdp() {
|
|
64
|
-
return this.instance.localDescription.sdp;
|
|
65
|
-
}
|
|
66
|
-
get hasIceServers() {
|
|
67
|
-
if (this.instance) {
|
|
68
|
-
const { iceServers = [] } = this.instance.getConfiguration();
|
|
69
|
-
return Boolean(iceServers === null || iceServers === void 0 ? void 0 : iceServers.length);
|
|
70
|
-
}
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
stopTrackSender(kind) {
|
|
74
|
-
try {
|
|
75
|
-
const sender = this._getSenderByKind(kind);
|
|
76
|
-
if (!sender) {
|
|
77
|
-
return logger.info(`There is not a '${kind}' sender to stop.`);
|
|
78
|
-
}
|
|
79
|
-
if (sender.track) {
|
|
80
|
-
stopTrack(sender.track);
|
|
81
|
-
this.options.localStream.removeTrack(sender.track);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
logger.error('RTCPeer stopTrackSender error', kind, error);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
restoreTrackSender(kind) {
|
|
89
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
-
try {
|
|
91
|
-
const sender = this._getSenderByKind(kind);
|
|
92
|
-
if (!sender) {
|
|
93
|
-
return logger.info(`There is not a '${kind}' sender to restore.`);
|
|
94
|
-
}
|
|
95
|
-
if (sender.track && sender.track.readyState !== 'ended') {
|
|
96
|
-
return logger.info(`There is already an active ${kind} track.`);
|
|
97
|
-
}
|
|
98
|
-
const constraints = yield getMediaConstraints(this.options);
|
|
99
|
-
const stream = yield getUserMedia({ [kind]: constraints[kind] });
|
|
100
|
-
if (streamIsValid(stream)) {
|
|
101
|
-
const newTrack = stream.getTracks().find(t => t.kind === kind);
|
|
102
|
-
yield sender.replaceTrack(newTrack);
|
|
103
|
-
this.options.localStream.addTrack(newTrack);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
logger.error('RTCPeer restoreTrackSender error', kind, error);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
getDeviceId(kind) {
|
|
112
|
-
try {
|
|
113
|
-
const sender = this._getSenderByKind(kind);
|
|
114
|
-
if (!sender || !sender.track) {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
const { deviceId = null } = sender.track.getSettings();
|
|
118
|
-
return deviceId;
|
|
119
|
-
}
|
|
120
|
-
catch (error) {
|
|
121
|
-
logger.error('RTCPeer getDeviceId error', kind, error);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
getTrackSettings(kind) {
|
|
125
|
-
try {
|
|
126
|
-
const sender = this._getSenderByKind(kind);
|
|
127
|
-
if (!sender || !sender.track) {
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
return sender.track.getSettings();
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
logger.error('RTCPeer getTrackSettings error', kind, error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
getDeviceLabel(kind) {
|
|
137
|
-
try {
|
|
138
|
-
const sender = this._getSenderByKind(kind);
|
|
139
|
-
if (!sender || !sender.track) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
return sender.track.label;
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
logger.error('RTCPeer getDeviceLabel error', kind, error);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
restartIceWithRelayOnly() {
|
|
149
|
-
try {
|
|
150
|
-
const config = this.instance.getConfiguration();
|
|
151
|
-
if (config.iceTransportPolicy === 'relay') {
|
|
152
|
-
return console.warn('RTCPeer already with iceTransportPolicy relay only');
|
|
153
|
-
}
|
|
154
|
-
const newConfig = Object.assign(Object.assign({}, config), { iceTransportPolicy: 'relay' });
|
|
155
|
-
this.instance.setConfiguration(newConfig);
|
|
156
|
-
this.instance.restartIce();
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
logger.error('RTCPeer restartIce error', error);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
applyMediaConstraints(kind, constraints) {
|
|
163
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
-
try {
|
|
165
|
-
const sender = this._getSenderByKind(kind);
|
|
166
|
-
if (!sender || !sender.track) {
|
|
167
|
-
return logger.info('No sender to apply constraints', kind, constraints);
|
|
168
|
-
}
|
|
169
|
-
if (sender.track.readyState === 'live') {
|
|
170
|
-
logger.info(`Apply ${kind} constraints`, this.options.id, constraints);
|
|
171
|
-
yield sender.track.applyConstraints(constraints);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
logger.error('Error applying constraints', kind, constraints);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
_getSenderByKind(kind) {
|
|
180
|
-
return this.instance.getSenders().find(({ track }) => (track && track.kind === kind));
|
|
181
|
-
}
|
|
182
|
-
_getReceiverByKind(kind) {
|
|
183
|
-
return this.instance.getReceivers().find(({ track }) => (track && track.kind === kind));
|
|
184
|
-
}
|
|
185
|
-
startNegotiation(force = false) {
|
|
186
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
187
|
-
if (this._negotiating) {
|
|
188
|
-
return logger.warn('Skip twice onnegotiationneeded!');
|
|
189
|
-
}
|
|
190
|
-
this._negotiating = true;
|
|
191
|
-
try {
|
|
192
|
-
if (this.options.screenShare === true || this.options.secondSource === true) {
|
|
193
|
-
this.instance.getTransceivers().forEach(tr => {
|
|
194
|
-
tr.direction = 'sendonly';
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
this.instance.removeEventListener('icecandidate', this._onIce);
|
|
198
|
-
this.instance.addEventListener('icecandidate', this._onIce);
|
|
199
|
-
if (this.isOffer) {
|
|
200
|
-
logger.info('Trying to generate offer');
|
|
201
|
-
const offer = yield this.instance.createOffer({ voiceActivityDetection: false });
|
|
202
|
-
yield this._setLocalDescription(offer);
|
|
203
|
-
}
|
|
204
|
-
if (this.isAnswer) {
|
|
205
|
-
logger.info('Trying to generate answer');
|
|
206
|
-
yield this._setRemoteDescription({ sdp: this.options.remoteSdp, type: PeerType.Offer });
|
|
207
|
-
this._logTransceivers();
|
|
208
|
-
const answer = yield this.instance.createAnswer({ voiceActivityDetection: false });
|
|
209
|
-
yield this._setLocalDescription(answer);
|
|
210
|
-
}
|
|
211
|
-
if (force) {
|
|
212
|
-
this._sdpReady();
|
|
213
|
-
}
|
|
214
|
-
logger.info('iceGatheringState', this.instance.iceGatheringState);
|
|
215
|
-
if (this.instance.iceGatheringState === 'gathering') {
|
|
216
|
-
this._iceTimeout = setTimeout(() => {
|
|
217
|
-
this._onIceTimeout();
|
|
218
|
-
}, this.options.maxIceGatheringTimeout);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
catch (error) {
|
|
222
|
-
logger.error(`Error creating ${this.type}:`, error);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
onRemoteSdp(sdp) {
|
|
227
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
228
|
-
try {
|
|
229
|
-
const type = this.isOffer ? PeerType.Answer : PeerType.Offer;
|
|
230
|
-
yield this._setRemoteDescription({ sdp, type });
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
logger.error(`Error handling remote SDP on call ${this.options.id}:`, error);
|
|
234
|
-
this.call.hangup();
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
_logTransceivers() {
|
|
239
|
-
logger.info('Number of transceivers:', this.instance.getTransceivers().length);
|
|
240
|
-
this.instance.getTransceivers().forEach((tr, index) => {
|
|
241
|
-
logger.info(`>> Transceiver [${index}]:`, tr.mid, tr.direction, tr.stopped);
|
|
242
|
-
logger.info(`>> Sender Params [${index}]:`, JSON.stringify(tr.sender.getParameters(), null, 2));
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
_init() {
|
|
246
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
247
|
-
this.instance = RTCPeerConnection(this.config);
|
|
248
|
-
this.instance.onsignalingstatechange = event => {
|
|
249
|
-
logger.info('signalingState:', this.instance.signalingState);
|
|
250
|
-
switch (this.instance.signalingState) {
|
|
251
|
-
case 'stable':
|
|
252
|
-
this._negotiating = false;
|
|
253
|
-
break;
|
|
254
|
-
case 'closed':
|
|
255
|
-
this.instance = null;
|
|
256
|
-
break;
|
|
257
|
-
default:
|
|
258
|
-
this._negotiating = true;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
this.instance.addEventListener('connectionstatechange', (event) => {
|
|
262
|
-
logger.info('connectionState:', this.instance.connectionState);
|
|
263
|
-
}, false);
|
|
264
|
-
this.instance.onnegotiationneeded = event => {
|
|
265
|
-
logger.info('Negotiation needed event');
|
|
266
|
-
this.startNegotiation();
|
|
267
|
-
};
|
|
268
|
-
this.instance.addEventListener('track', (event) => {
|
|
269
|
-
if (this.hasExperimentalFlag) {
|
|
270
|
-
this._buildMediaElementByTrack(event);
|
|
271
|
-
const notification = { type: 'trackAdd', event };
|
|
272
|
-
this.call._dispatchNotification(notification);
|
|
273
|
-
}
|
|
274
|
-
if (this.isSfu) {
|
|
275
|
-
const notification = { type: 'trackAdd', event };
|
|
276
|
-
this.call._dispatchNotification(notification);
|
|
277
|
-
}
|
|
278
|
-
this.options.remoteStream = event.streams[0];
|
|
279
|
-
const { remoteElement, remoteStream, screenShare } = this.options;
|
|
280
|
-
if (screenShare === false) {
|
|
281
|
-
attachMediaStream(remoteElement, remoteStream);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
this.instance.addEventListener('addstream', (event) => {
|
|
285
|
-
this.options.remoteStream = event.stream;
|
|
286
|
-
});
|
|
287
|
-
this.options.localStream = yield this._retrieveLocalStream().catch(error => {
|
|
288
|
-
trigger(this.options.id, error, SwEvent.MediaError);
|
|
289
|
-
return null;
|
|
290
|
-
});
|
|
291
|
-
const { localElement, localStream = null, screenShare } = this.options;
|
|
292
|
-
if (streamIsValid(localStream)) {
|
|
293
|
-
const audioTracks = localStream.getAudioTracks();
|
|
294
|
-
logger.info('Local audio tracks: ', audioTracks);
|
|
295
|
-
const videoTracks = localStream.getVideoTracks();
|
|
296
|
-
logger.info('Local video tracks: ', videoTracks);
|
|
297
|
-
if (this.isOffer && typeof this.instance.addTransceiver === 'function') {
|
|
298
|
-
audioTracks.forEach(track => {
|
|
299
|
-
this.options.userVariables.microphoneLabel = track.label;
|
|
300
|
-
this.instance.addTransceiver(track, {
|
|
301
|
-
direction: 'sendrecv',
|
|
302
|
-
streams: [localStream],
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
const transceiverParams = {
|
|
306
|
-
direction: 'sendrecv',
|
|
307
|
-
streams: [localStream],
|
|
308
|
-
};
|
|
309
|
-
if (this.isSimulcast) {
|
|
310
|
-
const rids = ['0', '1', '2'];
|
|
311
|
-
transceiverParams.sendEncodings = rids.map(rid => ({
|
|
312
|
-
active: true,
|
|
313
|
-
rid: rid,
|
|
314
|
-
scaleResolutionDownBy: (Number(rid) * 6 || 1.0),
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
console.debug('Applying video transceiverParams', transceiverParams);
|
|
318
|
-
videoTracks.forEach(track => {
|
|
319
|
-
this.options.userVariables.cameraLabel = track.label;
|
|
320
|
-
this.instance.addTransceiver(track, transceiverParams);
|
|
321
|
-
});
|
|
322
|
-
if (this.isSfu) {
|
|
323
|
-
const { msStreamsNumber = 5 } = this.options;
|
|
324
|
-
console.debug('Add ', msStreamsNumber, 'recvonly MS Streams');
|
|
325
|
-
transceiverParams.direction = 'recvonly';
|
|
326
|
-
for (let i = 0; i < Number(msStreamsNumber); i++) {
|
|
327
|
-
this.instance.addTransceiver('video', transceiverParams);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
else if (typeof this.instance.addTrack === 'function') {
|
|
332
|
-
audioTracks.forEach(track => {
|
|
333
|
-
this.options.userVariables.microphoneLabel = track.label;
|
|
334
|
-
this.instance.addTrack(track, localStream);
|
|
335
|
-
});
|
|
336
|
-
videoTracks.forEach(track => {
|
|
337
|
-
this.options.userVariables.cameraLabel = track.label;
|
|
338
|
-
this.instance.addTrack(track, localStream);
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
this.instance.addStream(localStream);
|
|
343
|
-
}
|
|
344
|
-
if (screenShare === false) {
|
|
345
|
-
muteMediaElement(localElement);
|
|
346
|
-
attachMediaStream(localElement, localStream);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
if (this.isOffer) {
|
|
350
|
-
if (this.options.negotiateAudio) {
|
|
351
|
-
this._checkMediaToNegotiate('audio');
|
|
352
|
-
}
|
|
353
|
-
if (this.options.negotiateVideo) {
|
|
354
|
-
this._checkMediaToNegotiate('video');
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
else {
|
|
358
|
-
this.startNegotiation();
|
|
359
|
-
}
|
|
360
|
-
this._logTransceivers();
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
_checkMediaToNegotiate(kind) {
|
|
364
|
-
const sender = this._getSenderByKind(kind);
|
|
365
|
-
if (!sender) {
|
|
366
|
-
const transceiver = this.instance.addTransceiver(kind);
|
|
367
|
-
console.debug('Add transceiver', kind, transceiver);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
_sdpIsValid() {
|
|
371
|
-
try {
|
|
372
|
-
if (this.hasIceServers) {
|
|
373
|
-
const regex = /typ (?:srflx|prflx|relay)/;
|
|
374
|
-
const sections = SDPUtils.getMediaSections(this.localSdp);
|
|
375
|
-
for (const section of sections) {
|
|
376
|
-
const lines = SDPUtils.splitLines(section);
|
|
377
|
-
const valid = lines.some(line => {
|
|
378
|
-
return line.indexOf('a=candidate') === 0 && regex.test(line);
|
|
379
|
-
});
|
|
380
|
-
if (!valid) {
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return true;
|
|
386
|
-
}
|
|
387
|
-
catch (error) {
|
|
388
|
-
logger.error('Error checking SDP', error);
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
_forceNegotiation() {
|
|
393
|
-
logger.info('Force negotiation again');
|
|
394
|
-
this._negotiating = false;
|
|
395
|
-
this.startNegotiation();
|
|
396
|
-
}
|
|
397
|
-
_sdpReady() {
|
|
398
|
-
clearTimeout(this._iceTimeout);
|
|
399
|
-
this._iceTimeout = null;
|
|
400
|
-
if (!this._sdpIsValid()) {
|
|
401
|
-
logger.info('SDP ready but not valid');
|
|
402
|
-
this._forceNegotiation();
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
const { sdp, type } = this.instance.localDescription;
|
|
406
|
-
if (sdp.indexOf('candidate') === -1) {
|
|
407
|
-
logger.info('No candidate - retry \n');
|
|
408
|
-
this.startNegotiation(true);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
logger.info('LOCAL SDP \n', `Type: ${type}`, '\n\n', sdp);
|
|
412
|
-
switch (type) {
|
|
413
|
-
case PeerType.Offer:
|
|
414
|
-
if (this.call.active) {
|
|
415
|
-
this.executeUpdateMedia();
|
|
416
|
-
}
|
|
417
|
-
else {
|
|
418
|
-
this.executeInvite();
|
|
419
|
-
}
|
|
420
|
-
break;
|
|
421
|
-
case PeerType.Answer:
|
|
422
|
-
this.executeAnswer();
|
|
423
|
-
break;
|
|
424
|
-
default:
|
|
425
|
-
return logger.error(`Unknown SDP type: '${type}' on call ${this.options.id}`);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
executeInvite() {
|
|
429
|
-
this.call.setState(State.Requesting);
|
|
430
|
-
const msg = new Invite(Object.assign(Object.assign({}, this.call.messagePayload), { sdp: this.localSdp }));
|
|
431
|
-
return this._execute(msg);
|
|
432
|
-
}
|
|
433
|
-
executeUpdateMedia() {
|
|
434
|
-
const msg = new Modify(Object.assign(Object.assign({}, this.call.messagePayload), { sdp: this.localSdp, action: 'updateMedia' }));
|
|
435
|
-
return this._execute(msg);
|
|
436
|
-
}
|
|
437
|
-
executeAnswer() {
|
|
438
|
-
this.call.setState(State.Answering);
|
|
439
|
-
const params = Object.assign(Object.assign({}, this.call.messagePayload), { sdp: this.localSdp });
|
|
440
|
-
const msg = this.options.attach === true ? new Attach(params) : new Answer(params);
|
|
441
|
-
return this._execute(msg);
|
|
442
|
-
}
|
|
443
|
-
_execute(msg) {
|
|
444
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
445
|
-
try {
|
|
446
|
-
const { node_id = null, sdp = null } = yield this.call._execute(msg);
|
|
447
|
-
if (node_id) {
|
|
448
|
-
this.call.nodeId = node_id;
|
|
449
|
-
}
|
|
450
|
-
if (sdp !== null) {
|
|
451
|
-
yield this._setRemoteDescription({ sdp, type: PeerType.Answer });
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
const state = this.isOffer ? State.Trying : State.Active;
|
|
455
|
-
this.call.setState(state);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
catch (error) {
|
|
459
|
-
logger.error(`Error sending ${this.type} on call ${this.options.id}:`, error);
|
|
460
|
-
this.call.hangup();
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
_onIceTimeout() {
|
|
465
|
-
if (this._sdpIsValid()) {
|
|
466
|
-
return this._sdpReady();
|
|
467
|
-
}
|
|
468
|
-
const config = this.instance.getConfiguration();
|
|
469
|
-
if (config.iceTransportPolicy === 'relay') {
|
|
470
|
-
logger.info('RTCPeer already with `iceTransportPolicy: relay`');
|
|
471
|
-
this.call.setState(State.Destroy);
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
this.instance.setConfiguration(Object.assign(Object.assign({}, config), { iceTransportPolicy: 'relay' }));
|
|
475
|
-
this._forceNegotiation();
|
|
476
|
-
}
|
|
477
|
-
_onIce(event) {
|
|
478
|
-
if (this._iceTimeout) {
|
|
479
|
-
clearTimeout(this._iceTimeout);
|
|
480
|
-
this._iceTimeout = null;
|
|
481
|
-
}
|
|
482
|
-
if (!event.candidate) {
|
|
483
|
-
this.instance.removeEventListener('icecandidate', this._onIce);
|
|
484
|
-
this._sdpReady();
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
logger.info('RTCPeer Candidate:', event.candidate);
|
|
488
|
-
if (event.candidate.type === 'host') {
|
|
489
|
-
this._iceTimeout = setTimeout(() => {
|
|
490
|
-
this.instance.removeEventListener('icecandidate', this._onIce);
|
|
491
|
-
this._onIceTimeout();
|
|
492
|
-
}, this.options.maxIceGatheringTimeout);
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
this._iceTimeout = setTimeout(() => {
|
|
496
|
-
this.instance.removeEventListener('icecandidate', this._onIce);
|
|
497
|
-
this._sdpReady();
|
|
498
|
-
}, this.options.iceGatheringTimeout);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
_setLocalDescription(localDescription) {
|
|
502
|
-
const { useStereo, googleMaxBitrate, googleMinBitrate, googleStartBitrate } = this.options;
|
|
503
|
-
if (useStereo) {
|
|
504
|
-
localDescription.sdp = sdpStereoHack(localDescription.sdp);
|
|
505
|
-
}
|
|
506
|
-
if (googleMaxBitrate && googleMinBitrate && googleStartBitrate) {
|
|
507
|
-
localDescription.sdp = sdpBitrateHack(localDescription.sdp, googleMaxBitrate, googleMinBitrate, googleStartBitrate);
|
|
508
|
-
}
|
|
509
|
-
if (this.isAnswer) {
|
|
510
|
-
logger.info('LOCAL SDP \n', `Type: ${localDescription.type}`, '\n\n', localDescription.sdp);
|
|
511
|
-
}
|
|
512
|
-
return this.instance.setLocalDescription(localDescription);
|
|
513
|
-
}
|
|
514
|
-
_setRemoteDescription(remoteDescription) {
|
|
515
|
-
if (this.options.useStereo) {
|
|
516
|
-
remoteDescription.sdp = sdpStereoHack(remoteDescription.sdp);
|
|
517
|
-
}
|
|
518
|
-
if (this.instance.localDescription) {
|
|
519
|
-
remoteDescription.sdp = sdpMediaOrderHack(remoteDescription.sdp, this.instance.localDescription.sdp);
|
|
520
|
-
}
|
|
521
|
-
const sessionDescr = sdpToJsonHack(remoteDescription);
|
|
522
|
-
logger.info('REMOTE SDP \n', `Type: ${remoteDescription.type}`, '\n\n', remoteDescription.sdp);
|
|
523
|
-
return this.instance.setRemoteDescription(sessionDescr);
|
|
524
|
-
}
|
|
525
|
-
_retrieveLocalStream() {
|
|
526
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
527
|
-
if (streamIsValid(this.options.localStream)) {
|
|
528
|
-
return this.options.localStream;
|
|
529
|
-
}
|
|
530
|
-
const constraints = yield getMediaConstraints(this.options);
|
|
531
|
-
return getUserMedia(constraints);
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
_buildMediaElementByTrack(event) {
|
|
535
|
-
console.debug('_buildMediaElementByTrack', event.track.kind, event.track.id, event.streams, event);
|
|
536
|
-
const streamIds = event.streams.map(stream => stream.id);
|
|
537
|
-
switch (event.track.kind) {
|
|
538
|
-
case 'audio': {
|
|
539
|
-
const audio = buildAudioElementByTrack(event.track, streamIds);
|
|
540
|
-
if (this.options.speakerId) {
|
|
541
|
-
try {
|
|
542
|
-
audio.setSinkId(this.options.speakerId);
|
|
543
|
-
}
|
|
544
|
-
catch (error) {
|
|
545
|
-
console.debug('setSinkId not supported', this.options.speakerId);
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
this.call.audioElements.push(audio);
|
|
549
|
-
break;
|
|
550
|
-
}
|
|
551
|
-
case 'video':
|
|
552
|
-
this.call.videoElements.push(buildVideoElementByTrack(event.track, streamIds));
|
|
553
|
-
break;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|