@voicenter-team/opensips-js 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/README.md +75 -0
  2. package/build/enum/call.event.listener.type.d.ts +7 -0
  3. package/build/enum/call.event.listener.type.js +10 -0
  4. package/build/enum/metric.keys.to.include.d.ts +2 -0
  5. package/build/enum/metric.keys.to.include.js +4 -0
  6. package/build/helpers/UA/index.d.ts +6 -0
  7. package/build/helpers/UA/index.js +9 -0
  8. package/build/helpers/audio.helper.d.ts +9 -0
  9. package/build/helpers/audio.helper.js +60 -0
  10. package/build/helpers/filter.helper.d.ts +2 -0
  11. package/build/helpers/filter.helper.js +14 -0
  12. package/build/helpers/time.helper.d.ts +16 -0
  13. package/build/helpers/time.helper.js +28 -0
  14. package/build/helpers/volume.helper.d.ts +2 -0
  15. package/build/helpers/volume.helper.js +76 -0
  16. package/build/helpers/webrtcmetrics/collector.d.ts +32 -0
  17. package/build/helpers/webrtcmetrics/collector.js +282 -0
  18. package/build/helpers/webrtcmetrics/engine.d.ts +20 -0
  19. package/build/helpers/webrtcmetrics/engine.js +164 -0
  20. package/build/helpers/webrtcmetrics/exporter.d.ts +116 -0
  21. package/build/helpers/webrtcmetrics/exporter.js +528 -0
  22. package/build/helpers/webrtcmetrics/extractor.d.ts +1 -0
  23. package/build/helpers/webrtcmetrics/extractor.js +976 -0
  24. package/build/helpers/webrtcmetrics/index.d.ts +63 -0
  25. package/build/helpers/webrtcmetrics/index.js +93 -0
  26. package/build/helpers/webrtcmetrics/metrics.d.ts +2 -0
  27. package/build/helpers/webrtcmetrics/metrics.js +8 -0
  28. package/build/helpers/webrtcmetrics/probe.d.ts +76 -0
  29. package/build/helpers/webrtcmetrics/probe.js +153 -0
  30. package/build/helpers/webrtcmetrics/utils/config.d.ts +12 -0
  31. package/build/helpers/webrtcmetrics/utils/config.js +28 -0
  32. package/build/helpers/webrtcmetrics/utils/helper.d.ts +13 -0
  33. package/build/helpers/webrtcmetrics/utils/helper.js +134 -0
  34. package/build/helpers/webrtcmetrics/utils/log.d.ts +7 -0
  35. package/build/helpers/webrtcmetrics/utils/log.js +71 -0
  36. package/build/helpers/webrtcmetrics/utils/models.d.ts +309 -0
  37. package/build/helpers/webrtcmetrics/utils/models.js +298 -0
  38. package/build/helpers/webrtcmetrics/utils/score.d.ts +4 -0
  39. package/build/helpers/webrtcmetrics/utils/score.js +235 -0
  40. package/build/helpers/webrtcmetrics/utils/shortUUId.d.ts +1 -0
  41. package/build/helpers/webrtcmetrics/utils/shortUUId.js +7 -0
  42. package/build/index.d.ts +170 -0
  43. package/build/index.js +849 -0
  44. package/package.json +61 -0
  45. package/src/types/declarations.d.ts +6 -0
  46. package/src/types/generic.d.ts +1 -0
  47. package/src/types/listeners.d.ts +42 -0
  48. package/src/types/rtc.d.ts +133 -0
  49. package/src/types/webrtcmetrics.d.ts +64 -0
package/build/index.js ADDED
@@ -0,0 +1,849 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const jssip_1 = __importDefault(require("jssip"));
16
+ const UA_1 = __importDefault(require("./helpers/UA"));
17
+ const p_iteration_1 = require("p-iteration");
18
+ const time_helper_1 = require("./helpers/time.helper");
19
+ const filter_helper_1 = require("./helpers/filter.helper");
20
+ const audio_helper_1 = require("./helpers/audio.helper");
21
+ const metrics_1 = __importDefault(require("./helpers/webrtcmetrics/metrics"));
22
+ const metric_keys_to_include_1 = require("./enum/metric.keys.to.include");
23
+ const call_event_listener_type_1 = require("./enum/call.event.listener.type");
24
+ const CALL_STATUS_UNANSWERED = 0;
25
+ const STORAGE_KEYS = {
26
+ SELECTED_INPUT_DEVICE: 'selectedInputDevice',
27
+ SELECTED_OUTPUT_DEVICE: 'selectedOutputDevice'
28
+ };
29
+ const activeCalls = {};
30
+ class OpenSIPSJS extends UA_1.default {
31
+ constructor(options) {
32
+ const configuration = Object.assign(Object.assign({}, options.configuration), { sockets: options.socketInterfaces.map(sock => new jssip_1.default.WebSocketInterface(sock)) });
33
+ super(configuration);
34
+ this.initialized = false;
35
+ this.newRTCSessionEventName = 'newRTCSession';
36
+ this.activeCalls = {};
37
+ this.state = {
38
+ isMuted: false,
39
+ activeCalls: {},
40
+ availableMediaDevices: [],
41
+ selectedMediaDevices: {
42
+ input: 'default',
43
+ output: 'default' //localStorage.getItem(STORAGE_KEYS.SELECTED_OUTPUT_DEVICE) || 'default'
44
+ },
45
+ microphoneInputLevel: 2,
46
+ speakerVolume: 1,
47
+ muteWhenJoin: false,
48
+ originalStream: null,
49
+ isDND: false,
50
+ listeners: {},
51
+ activeRooms: {},
52
+ callStatus: {},
53
+ callTime: {},
54
+ timeIntervals: {},
55
+ callMetrics: {},
56
+ metricConfig: {
57
+ refreshEvery: 1000,
58
+ }
59
+ };
60
+ this.options = options;
61
+ }
62
+ on(type, listener) {
63
+ return super.on(type, listener);
64
+ }
65
+ off(type, listener) {
66
+ return super.off(type, listener);
67
+ }
68
+ emit(type, args) {
69
+ return super.emit(type, args);
70
+ }
71
+ get sipDomain() {
72
+ return this.options.sipDomain;
73
+ }
74
+ get sipOptions() {
75
+ const options = Object.assign(Object.assign({}, this.options.sipOptions), { mediaConstraints: this.getUserMediaConstraints });
76
+ return options;
77
+ }
78
+ get currentActiveRoomId() {
79
+ return this._currentActiveRoomId;
80
+ }
81
+ set currentActiveRoomId(roomId) {
82
+ this._currentActiveRoomId = roomId;
83
+ this.emit('currentActiveRoomChanged', roomId);
84
+ }
85
+ get callAddingInProgress() {
86
+ return this._callAddingInProgress;
87
+ }
88
+ set callAddingInProgress(value) {
89
+ this._callAddingInProgress = value;
90
+ this.emit('callAddingInProgressChanged', value);
91
+ }
92
+ get muteWhenJoin() {
93
+ return this.state.muteWhenJoin;
94
+ }
95
+ set muteWhenJoin(value) {
96
+ this.state.muteWhenJoin = value;
97
+ this.emit('changeMuteWhenJoin', value);
98
+ }
99
+ get isDND() {
100
+ return this.state.isDND;
101
+ }
102
+ set isDND(value) {
103
+ this.state.isDND = value;
104
+ this.emit('changeIsDND', value);
105
+ }
106
+ get speakerVolume() {
107
+ return this.state.speakerVolume;
108
+ }
109
+ set speakerVolume(value) {
110
+ this.state.speakerVolume = value;
111
+ Object.values(activeCalls).forEach((call) => {
112
+ if (call.audioTag) {
113
+ call.audioTag.volume = value;
114
+ }
115
+ });
116
+ }
117
+ get microphoneInputLevel() {
118
+ return this.state.microphoneInputLevel;
119
+ }
120
+ set microphoneInputLevel(value) {
121
+ this.state.microphoneInputLevel = value;
122
+ this.roomReconfigure(this.currentActiveRoomId);
123
+ }
124
+ get getActiveCalls() {
125
+ return this.state.activeCalls;
126
+ }
127
+ get getActiveRooms() {
128
+ return this.state.activeRooms;
129
+ }
130
+ get isMuted() {
131
+ return this.state.isMuted;
132
+ }
133
+ set isMuted(value) {
134
+ this.state.isMuted = value;
135
+ this.emit('changeIsMuted', value);
136
+ }
137
+ get getInputDeviceList() {
138
+ return this.state.availableMediaDevices.filter(device => device.kind === 'audioinput');
139
+ }
140
+ get getOutputDeviceList() {
141
+ return this.state.availableMediaDevices.filter(device => device.kind === 'audiooutput');
142
+ }
143
+ /*getInputDeviceList: (state) => {
144
+ return state.availableMediaDevices.filter(device => device.kind === 'audioinput');
145
+ },
146
+ getOutputDeviceList: (state) => {
147
+ return state.availableMediaDevices.filter(device => device.kind === 'audiooutput');
148
+ }*/
149
+ get getUserMediaConstraints() {
150
+ return {
151
+ audio: {
152
+ deviceId: {
153
+ exact: this.state.selectedMediaDevices.input
154
+ }
155
+ },
156
+ video: false
157
+ };
158
+ }
159
+ get getInputDefaultDevice() {
160
+ return this.getInputDeviceList.find(device => device.deviceId === 'default');
161
+ }
162
+ get getOutputDefaultDevice() {
163
+ return this.getOutputDeviceList.find(device => device.deviceId === 'default');
164
+ }
165
+ get selectedInputDevice() {
166
+ return this.state.selectedMediaDevices.input;
167
+ }
168
+ set selectedInputDevice(deviceId) {
169
+ localStorage.setItem(STORAGE_KEYS.SELECTED_INPUT_DEVICE, deviceId);
170
+ this.state.selectedMediaDevices.input = deviceId;
171
+ this.emit('changeActiveInputMediaDevice', deviceId);
172
+ }
173
+ get selectedOutputDevice() {
174
+ return this.state.selectedMediaDevices.output;
175
+ }
176
+ set selectedOutputDevice(deviceId) {
177
+ localStorage.setItem(STORAGE_KEYS.SELECTED_OUTPUT_DEVICE, deviceId);
178
+ this.state.selectedMediaDevices.output = deviceId;
179
+ this.emit('changeActiveOutputMediaDevice', deviceId);
180
+ }
181
+ get originalStream() {
182
+ return this.state.originalStream;
183
+ }
184
+ setAvailableMediaDevices(devices) {
185
+ this.state.availableMediaDevices = devices;
186
+ this.emit('changeAvailableDeviceList', devices);
187
+ }
188
+ updateDeviceList() {
189
+ return __awaiter(this, void 0, void 0, function* () {
190
+ yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
191
+ const devices = yield navigator.mediaDevices.enumerateDevices();
192
+ this.setAvailableMediaDevices(devices);
193
+ });
194
+ }
195
+ setMediaDevices(setDefaults = false) {
196
+ var _a, _b;
197
+ return __awaiter(this, void 0, void 0, function* () {
198
+ this.state.selectedMediaDevices.input = localStorage.getItem(STORAGE_KEYS.SELECTED_INPUT_DEVICE) || 'default';
199
+ this.state.selectedMediaDevices.output = localStorage.getItem(STORAGE_KEYS.SELECTED_OUTPUT_DEVICE) || 'default';
200
+ yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
201
+ const devices = yield navigator.mediaDevices.enumerateDevices();
202
+ this.setAvailableMediaDevices(devices);
203
+ const defaultMicrophone = setDefaults
204
+ ? ((_a = this.getInputDefaultDevice) === null || _a === void 0 ? void 0 : _a.deviceId) || ''
205
+ : '';
206
+ const defaultSpeaker = setDefaults
207
+ ? ((_b = this.getOutputDefaultDevice) === null || _b === void 0 ? void 0 : _b.deviceId) || ''
208
+ : '';
209
+ yield this.setMicrophone(defaultMicrophone);
210
+ yield this.setSpeaker(defaultSpeaker);
211
+ });
212
+ }
213
+ setCallTime(value) {
214
+ const time = Object.assign({}, value);
215
+ delete time.callId;
216
+ this.state.callTime = Object.assign(Object.assign({}, this.state.callTime), { [value.callId]: time });
217
+ }
218
+ removeCallTime(callId) {
219
+ const callTimeCopy = Object.assign({}, this.state.callTime);
220
+ delete callTimeCopy[callId];
221
+ this.state.callTime = Object.assign({}, callTimeCopy);
222
+ }
223
+ setTimeInterval(callId, interval) {
224
+ this.state.timeIntervals = Object.assign(Object.assign({}, this.state.timeIntervals), { [callId]: interval });
225
+ }
226
+ removeTimeInterval(callId) {
227
+ const timeIntervalsCopy = Object.assign({}, this.state.timeIntervals);
228
+ clearInterval(timeIntervalsCopy[callId]);
229
+ delete timeIntervalsCopy[callId];
230
+ this.state.timeIntervals = Object.assign({}, timeIntervalsCopy);
231
+ }
232
+ _stopCallTimer(callId) {
233
+ this.removeTimeInterval(callId);
234
+ this.removeCallTime(callId);
235
+ }
236
+ setMetricsConfig(config) {
237
+ this.state.metricConfig = Object.assign(Object.assign({}, this.state.metricConfig), config);
238
+ }
239
+ sendDTMF(callId, value) {
240
+ const validation_regex = /^[A-D0-9]+$/g;
241
+ if (!validation_regex.test(value)) {
242
+ throw new Error('Not allowed character in DTMF input');
243
+ }
244
+ const call = activeCalls[callId];
245
+ call.sendDTMF(value);
246
+ }
247
+ doMute(value) {
248
+ const activeRoomId = this.currentActiveRoomId;
249
+ this.isMuted = value;
250
+ this.roomReconfigure(activeRoomId);
251
+ }
252
+ sendMessage(target, body, options) {
253
+ return super.sendMessage(`sip:${target}@${this.sipDomain}`, body, options);
254
+ }
255
+ doCallHold({ callId, toHold, automatic }) {
256
+ const call = activeCalls[callId];
257
+ call._automaticHold = automatic !== null && automatic !== void 0 ? automatic : false;
258
+ if (toHold) {
259
+ call.hold();
260
+ }
261
+ else {
262
+ call.unhold();
263
+ }
264
+ }
265
+ _cancelAllOutgoingUnanswered() {
266
+ Object.values(this.getActiveCalls).filter(call => {
267
+ return call.direction === 'outgoing'
268
+ && call.status === CALL_STATUS_UNANSWERED;
269
+ }).forEach(call => this.callTerminate(call._id));
270
+ }
271
+ callAnswer(callId) {
272
+ const call = activeCalls[callId];
273
+ this._cancelAllOutgoingUnanswered();
274
+ call.answer(this.sipOptions);
275
+ this.updateCall(call);
276
+ // TODO: maybe would be better to move to the top
277
+ this.setCurrentActiveRoomId(call.roomId);
278
+ call.connection.addEventListener('addstream', (event) => __awaiter(this, void 0, void 0, function* () {
279
+ this._triggerAddStream(event, call);
280
+ }));
281
+ }
282
+ callMove(callId, roomId) {
283
+ return __awaiter(this, void 0, void 0, function* () {
284
+ this._updateCallStatus({ callId, isMoving: true });
285
+ yield this.callChangeRoom({ callId, roomId });
286
+ this._updateCallStatus({ callId, isMoving: false });
287
+ });
288
+ }
289
+ updateCall(value) {
290
+ this.state.activeCalls[value._id] = (0, audio_helper_1.simplifyCallObject)(value);
291
+ this.emit('changeActiveCalls', this.state.activeCalls);
292
+ }
293
+ updateRoom(value) {
294
+ const room = this.state.activeRooms[value.roomId];
295
+ const newRoomData = Object.assign(Object.assign({}, room), value);
296
+ this.state.activeRooms = Object.assign(Object.assign({}, this.state.activeRooms), { [value.roomId]: Object.assign({}, newRoomData) });
297
+ this.emit('updateRoom', { room: newRoomData, roomList: this.state.activeRooms });
298
+ }
299
+ _addCall(value) {
300
+ this.state.activeCalls = Object.assign(Object.assign({}, this.state.activeCalls), { [value._id]: (0, audio_helper_1.simplifyCallObject)(value) });
301
+ activeCalls[value._id] = value;
302
+ this.emit('changeActiveCalls', this.state.activeCalls);
303
+ }
304
+ _addCallStatus(callId) {
305
+ this.state.callStatus = Object.assign(Object.assign({}, this.state.callStatus), { [callId]: {
306
+ isMoving: false,
307
+ isTransferring: false,
308
+ isMerging: false
309
+ } });
310
+ }
311
+ _updateCallStatus(value) {
312
+ const prevStatus = Object.assign({}, this.state.callStatus[value.callId]);
313
+ const newStatus = Object.assign({}, prevStatus);
314
+ if (value.isMoving !== undefined) {
315
+ newStatus.isMoving = value.isMoving;
316
+ }
317
+ if (value.isTransferring !== undefined) {
318
+ newStatus.isTransferring = value.isTransferring;
319
+ }
320
+ if (value.isMerging !== undefined) {
321
+ newStatus.isMerging = value.isMerging;
322
+ }
323
+ this.state.callStatus = Object.assign(Object.assign({}, this.state.callStatus), { [value.callId]: Object.assign({}, newStatus) });
324
+ }
325
+ _removeCallStatus(callId) {
326
+ const callStatusCopy = Object.assign({}, this.state.callStatus);
327
+ delete callStatusCopy[callId];
328
+ this.state.callStatus = Object.assign({}, callStatusCopy);
329
+ }
330
+ _addRoom(value) {
331
+ this.state.activeRooms = Object.assign(Object.assign({}, this.state.activeRooms), { [value.roomId]: value });
332
+ this.emit('addRoom', { room: value, roomList: this.state.activeRooms });
333
+ }
334
+ setMicrophone(dId) {
335
+ return __awaiter(this, void 0, void 0, function* () {
336
+ if (!this.getInputDeviceList.find(({ deviceId }) => deviceId === dId)) {
337
+ return;
338
+ }
339
+ this.selectedInputDevice = dId;
340
+ let stream; // = null
341
+ try {
342
+ stream = yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
343
+ }
344
+ catch (err) {
345
+ console.error(err);
346
+ }
347
+ if (Object.keys(this.getActiveCalls).length === 0) {
348
+ return;
349
+ }
350
+ const callsInCurrentRoom = Object.values(activeCalls).filter(call => call.roomId === this.currentActiveRoomId);
351
+ if (callsInCurrentRoom.length === 1) {
352
+ Object.values(callsInCurrentRoom).forEach(call => {
353
+ const processedStream = (0, audio_helper_1.processAudioVolume)(stream, this.microphoneInputLevel);
354
+ processedStream.getTracks().forEach(track => track.enabled = !this.isMuted);
355
+ this._setOriginalStream(processedStream);
356
+ call.connection.getSenders()[0].replaceTrack(processedStream.getTracks()[0]);
357
+ this.updateCall(call);
358
+ });
359
+ }
360
+ else {
361
+ yield this._doConference(callsInCurrentRoom);
362
+ }
363
+ });
364
+ }
365
+ _setOriginalStream(value) {
366
+ this.state.originalStream = value;
367
+ this.emit('changeOriginalStream', value);
368
+ }
369
+ setSpeaker(dId) {
370
+ return __awaiter(this, void 0, void 0, function* () {
371
+ if (!this.getOutputDeviceList.find(({ deviceId }) => deviceId === dId)) {
372
+ return;
373
+ }
374
+ this.selectedOutputDevice = dId;
375
+ const activeCallList = Object.values(activeCalls);
376
+ if (activeCallList.length === 0) {
377
+ return;
378
+ }
379
+ const callsInCurrentRoom = activeCallList.filter(call => call.roomId === this.currentActiveRoomId);
380
+ if (callsInCurrentRoom.length === 1) {
381
+ activeCallList.forEach(call => {
382
+ var _a;
383
+ (_a = call.audioTag) === null || _a === void 0 ? void 0 : _a.setSinkId(dId);
384
+ this.updateCall(call);
385
+ });
386
+ }
387
+ else {
388
+ yield this._doConference(callsInCurrentRoom);
389
+ }
390
+ });
391
+ }
392
+ removeRoom(roomId) {
393
+ const activeRoomsCopy = Object.assign({}, this.state.activeRooms);
394
+ const roomToRemove = Object.assign({}, activeRoomsCopy[roomId]);
395
+ delete activeRoomsCopy[roomId];
396
+ this.state.activeRooms = Object.assign({}, activeRoomsCopy);
397
+ this.emit('removeRoom', { room: roomToRemove, roomList: this.state.activeRooms });
398
+ }
399
+ deleteRoomIfEmpty(roomId) {
400
+ if (roomId === undefined) {
401
+ return;
402
+ }
403
+ if (Object.values(activeCalls).filter(call => call.roomId === roomId).length === 0) {
404
+ this.removeRoom(roomId);
405
+ if (this.currentActiveRoomId === roomId) {
406
+ this.currentActiveRoomId = roomId;
407
+ }
408
+ }
409
+ }
410
+ checkInitialized() {
411
+ if (!this.initialized) {
412
+ throw new Error('[OpenSIPSJS] You must call `start` method first!');
413
+ }
414
+ }
415
+ muteReconfigure(call) {
416
+ if (this.state.isMuted) {
417
+ call.mute({ audio: true });
418
+ }
419
+ else {
420
+ call.unmute({ audio: true });
421
+ }
422
+ }
423
+ roomReconfigure(roomId) {
424
+ return __awaiter(this, void 0, void 0, function* () {
425
+ if (roomId === undefined) {
426
+ return;
427
+ }
428
+ const callsInRoom = Object.values(activeCalls).filter(call => call.roomId === roomId);
429
+ // Let`s take care on the audio output first and check if passed room is our selected room
430
+ if (this.currentActiveRoomId === roomId) {
431
+ callsInRoom.forEach(call => {
432
+ if (call.audioTag) {
433
+ this.muteReconfigure(call);
434
+ call.audioTag.muted = false;
435
+ this.updateCall(call);
436
+ }
437
+ });
438
+ }
439
+ else {
440
+ callsInRoom.forEach(call => {
441
+ if (call.audioTag) {
442
+ call.audioTag.muted = true;
443
+ this.updateCall(call);
444
+ }
445
+ });
446
+ }
447
+ // Now let`s configure the sound we are sending for each active call on this room
448
+ if (callsInRoom.length === 0) {
449
+ this.deleteRoomIfEmpty(roomId);
450
+ }
451
+ else if (callsInRoom.length === 1 && this.currentActiveRoomId !== roomId) {
452
+ if (!callsInRoom[0].isOnHold().local) {
453
+ this.doCallHold({ callId: callsInRoom[0].id, toHold: true, automatic: true });
454
+ }
455
+ }
456
+ else if (callsInRoom.length === 1 && this.currentActiveRoomId === roomId) {
457
+ if (callsInRoom[0].isOnHold().local && callsInRoom[0]._automaticHold) {
458
+ this.doCallHold({ callId: callsInRoom[0].id, toHold: false });
459
+ }
460
+ let stream;
461
+ try {
462
+ stream = yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
463
+ }
464
+ catch (err) {
465
+ console.error(err);
466
+ }
467
+ if (stream && callsInRoom[0].connection && callsInRoom[0].connection.getSenders()[0]) {
468
+ const processedStream = (0, audio_helper_1.processAudioVolume)(stream, this.microphoneInputLevel);
469
+ processedStream.getTracks().forEach(track => track.enabled = !this.state.isMuted);
470
+ this._setOriginalStream(processedStream);
471
+ yield callsInRoom[0].connection.getSenders()[0].replaceTrack(processedStream.getTracks()[0]);
472
+ this.muteReconfigure(callsInRoom[0]);
473
+ }
474
+ }
475
+ else if (callsInRoom.length > 1) {
476
+ yield this._doConference(callsInRoom);
477
+ }
478
+ });
479
+ }
480
+ _doConference(sessions) {
481
+ return __awaiter(this, void 0, void 0, function* () {
482
+ sessions.forEach(call => {
483
+ if (call._localHold) {
484
+ this.doCallHold({ callId: call._id, toHold: false });
485
+ }
486
+ });
487
+ // Take all received tracks from the sessions you want to merge
488
+ const receivedTracks = [];
489
+ sessions.forEach(session => {
490
+ if (session !== null && session !== undefined) {
491
+ session.connection.getReceivers().forEach((receiver) => {
492
+ receivedTracks.push(receiver.track);
493
+ });
494
+ }
495
+ });
496
+ // Use the Web Audio API to mix the received tracks
497
+ const audioContext = new AudioContext();
498
+ const allReceivedMediaStreams = new MediaStream();
499
+ // For each call we will build dedicated mix for all other calls
500
+ yield (0, p_iteration_1.forEach)(sessions, (session) => __awaiter(this, void 0, void 0, function* () {
501
+ if (session === null || session === undefined) {
502
+ return;
503
+ }
504
+ const mixedOutput = audioContext.createMediaStreamDestination();
505
+ session.connection.getReceivers().forEach((receiver) => {
506
+ receivedTracks.forEach(track => {
507
+ allReceivedMediaStreams.addTrack(receiver.track);
508
+ if (receiver.track.id !== track.id) {
509
+ const sourceStream = audioContext.createMediaStreamSource(new MediaStream([track]));
510
+ sourceStream.connect(mixedOutput);
511
+ }
512
+ });
513
+ });
514
+ if (sessions[0].roomId === this.currentActiveRoomId) {
515
+ // Mixing your voice with all the received audio
516
+ const stream = yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
517
+ const processedStream = (0, audio_helper_1.processAudioVolume)(stream, this.microphoneInputLevel);
518
+ processedStream.getTracks().forEach(track => track.enabled = !this.isMuted);
519
+ this._setOriginalStream(processedStream);
520
+ const sourceStream = audioContext.createMediaStreamSource(processedStream);
521
+ // stream.getTracks().forEach(track => track.enabled = !getters.isMuted) // TODO: Fix this
522
+ sourceStream.connect(mixedOutput);
523
+ }
524
+ if (session.connection.getSenders()[0]) {
525
+ //mixedOutput.stream.getTracks().forEach(track => track.enabled = !getters.isMuted) // Uncomment to mute all callers on mute
526
+ yield session.connection.getSenders()[0].replaceTrack(mixedOutput.stream.getTracks()[0]);
527
+ this._muteReconfigure(session);
528
+ }
529
+ }));
530
+ });
531
+ }
532
+ _muteReconfigure(call) {
533
+ if (this.isMuted) {
534
+ call.mute({ audio: true });
535
+ }
536
+ else {
537
+ call.unmute({ audio: true });
538
+ }
539
+ }
540
+ muteCaller(callId, value) {
541
+ const call = activeCalls[callId];
542
+ if (call && call.connection.getReceivers().length) {
543
+ call.localMuted = value;
544
+ call.connection.getReceivers().forEach((receiver) => {
545
+ receiver.track.enabled = !value;
546
+ });
547
+ this.updateCall(call);
548
+ this.roomReconfigure(call.roomId);
549
+ }
550
+ }
551
+ callTerminate(callId) {
552
+ const call = activeCalls[callId];
553
+ if (call._status !== 8) {
554
+ call.terminate();
555
+ }
556
+ }
557
+ callTransfer(callId, target) {
558
+ if (target.toString().length === 0) {
559
+ return console.error('Target must be passed');
560
+ }
561
+ this._updateCallStatus({ callId, isTransferring: true });
562
+ const call = activeCalls[callId];
563
+ call.refer(`sip:${target}@${this.sipDomain}`);
564
+ this.updateCall(call);
565
+ }
566
+ callMerge(roomId) {
567
+ const callsInRoom = Object.values(activeCalls).filter((call) => call.roomId === roomId);
568
+ if (callsInRoom.length !== 2)
569
+ return;
570
+ const firstCall = callsInRoom[0];
571
+ const secondCall = callsInRoom[1];
572
+ if (!firstCall || !secondCall) {
573
+ return;
574
+ }
575
+ // TODO: Check all call.id for working in the same way as call._id
576
+ this._updateCallStatus({ callId: firstCall._id, isMerging: true });
577
+ this._updateCallStatus({ callId: secondCall._id, isMerging: true });
578
+ firstCall.refer(secondCall.remote_identity.uri.toString(), { 'replaces': secondCall });
579
+ this.updateCall(firstCall);
580
+ }
581
+ // TODO: Use this method in demo
582
+ setDND(value) {
583
+ this.isDND = value;
584
+ }
585
+ _startCallTimer(callId) {
586
+ const timeData = {
587
+ callId,
588
+ hours: 0,
589
+ minutes: 0,
590
+ seconds: 0,
591
+ formatted: ''
592
+ };
593
+ this.setCallTime(timeData);
594
+ const interval = setInterval(() => {
595
+ const callTime = Object.assign({}, this.state.callTime[callId]);
596
+ const updatedTime = (0, time_helper_1.setupTime)(callTime);
597
+ this.setCallTime(Object.assign({ callId }, updatedTime));
598
+ }, 1000);
599
+ this.setTimeInterval(callId, interval);
600
+ }
601
+ setCurrentActiveRoomId(roomId) {
602
+ return __awaiter(this, void 0, void 0, function* () {
603
+ const oldRoomId = this.currentActiveRoomId;
604
+ if (roomId === oldRoomId) {
605
+ return;
606
+ }
607
+ this.currentActiveRoomId = roomId;
608
+ yield this.roomReconfigure(oldRoomId);
609
+ yield this.roomReconfigure(roomId);
610
+ });
611
+ }
612
+ getNewRoomId() {
613
+ const roomIdList = Object.keys(this.state.activeRooms);
614
+ if (roomIdList.length === 0) {
615
+ return 1;
616
+ }
617
+ return (parseInt(roomIdList.sort()[roomIdList.length - 1]) + 1);
618
+ }
619
+ subscribe(type, listener) {
620
+ const isListenerEmpty = !this.state.listeners[type] || !this.state.listeners[type].length;
621
+ const newListeners = isListenerEmpty ? [listener] : [...this.state.listeners[type], listener];
622
+ this.state.listeners = Object.assign(Object.assign({}, this.state.listeners), { [type]: newListeners });
623
+ }
624
+ removeIListener(value) {
625
+ const listenersCopy = Object.assign({}, this.state.listeners);
626
+ delete listenersCopy[value];
627
+ this.state.listeners = Object.assign({}, listenersCopy);
628
+ }
629
+ addCall(session) {
630
+ return __awaiter(this, void 0, void 0, function* () {
631
+ const sessionAlreadyInActiveCalls = this.getActiveCalls[session.id];
632
+ if (sessionAlreadyInActiveCalls !== undefined) {
633
+ return;
634
+ }
635
+ const roomId = this.getNewRoomId();
636
+ const newRoomInfo = {
637
+ started: new Date(),
638
+ incomingInProgress: false,
639
+ roomId
640
+ };
641
+ if (session.direction === 'incoming') {
642
+ newRoomInfo.incomingInProgress = true;
643
+ this.subscribe(call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_CONFIRMED, (call) => {
644
+ if (session.id === call.id) {
645
+ this.updateRoom({
646
+ incomingInProgress: false,
647
+ roomId
648
+ });
649
+ this._startCallTimer(session.id);
650
+ }
651
+ });
652
+ this.subscribe(call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_FAILED, (call) => {
653
+ if (session.id === call.id) {
654
+ this.updateRoom({
655
+ incomingInProgress: false,
656
+ roomId
657
+ });
658
+ }
659
+ });
660
+ }
661
+ else if (session.direction === 'outgoing') {
662
+ this._startCallTimer(session.id);
663
+ }
664
+ const call = session;
665
+ call.roomId = roomId;
666
+ call.localMuted = false;
667
+ this._addCall(call);
668
+ this._addCallStatus(session.id);
669
+ this._addRoom(newRoomInfo);
670
+ });
671
+ }
672
+ _triggerListener({ listenerType, session, event }) {
673
+ const listeners = this.state.listeners[listenerType];
674
+ if (!listeners || !listeners.length) {
675
+ return;
676
+ }
677
+ listeners.forEach((listener) => {
678
+ listener(session, event);
679
+ });
680
+ }
681
+ _removeCall(value) {
682
+ const stateActiveCallsCopy = Object.assign({}, this.state.activeCalls);
683
+ delete stateActiveCallsCopy[value];
684
+ delete activeCalls[value];
685
+ this.state.activeCalls = Object.assign({}, stateActiveCallsCopy);
686
+ this.emit('changeActiveCalls', this.state.activeCalls);
687
+ }
688
+ _activeCallListRemove(call) {
689
+ const callRoomIdToConfigure = activeCalls[call._id].roomId;
690
+ this._removeCall(call._id);
691
+ this.roomReconfigure(callRoomIdToConfigure);
692
+ }
693
+ newRTCSessionCallback(event) {
694
+ const session = event.session;
695
+ if (this.isDND) {
696
+ session.terminate({ status_code: 486, reason_phrase: 'Do Not Disturb' });
697
+ return;
698
+ }
699
+ // stop timers on ended and failed
700
+ session.on('ended', (event) => {
701
+ this._triggerListener({ listenerType: call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_ENDED, session, event });
702
+ const s = this.getActiveCalls[session.id];
703
+ this._activeCallListRemove(s);
704
+ this._stopCallTimer(session.id);
705
+ this._removeCallStatus(session.id);
706
+ this._removeCallMetrics(session.id);
707
+ if (!Object.keys(activeCalls).length) {
708
+ this.isMuted = false;
709
+ }
710
+ });
711
+ session.on('progress', (event) => {
712
+ this._triggerListener({ listenerType: call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_PROGRESS, session, event });
713
+ });
714
+ session.on('failed', (event) => {
715
+ this._triggerListener({ listenerType: call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_FAILED, session, event });
716
+ if (session.id === this.callAddingInProgress) {
717
+ this.callAddingInProgress = undefined;
718
+ }
719
+ const s = this.getActiveCalls[session.id];
720
+ this._activeCallListRemove(s);
721
+ this._stopCallTimer(session.id);
722
+ this._removeCallStatus(session.id);
723
+ this._removeCallMetrics(session.id);
724
+ if (!Object.keys(activeCalls).length) {
725
+ this.isMuted = false;
726
+ }
727
+ });
728
+ session.on('confirmed', (event) => {
729
+ this._triggerListener({ listenerType: call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_CONFIRMED, session, event });
730
+ this.updateCall(session);
731
+ if (session.id === this.callAddingInProgress) {
732
+ this.callAddingInProgress = undefined;
733
+ }
734
+ });
735
+ this.addCall(session);
736
+ if (session.direction === 'outgoing') {
737
+ const roomId = this.getActiveCalls[session.id].roomId;
738
+ this.setCurrentActiveRoomId(roomId);
739
+ }
740
+ }
741
+ setInitialized() {
742
+ this.initialized = true;
743
+ this.emit('ready', true);
744
+ }
745
+ start() {
746
+ this.on(this.newRTCSessionEventName, this.newRTCSessionCallback.bind(this));
747
+ super.start();
748
+ this.setInitialized();
749
+ //this.setDefaultMediaDevices()
750
+ this.setMediaDevices(true);
751
+ return this;
752
+ }
753
+ setMuteWhenJoin(value) {
754
+ this.muteWhenJoin = value;
755
+ }
756
+ _setCallMetrics(value) {
757
+ const metrics = Object.assign({}, value);
758
+ delete metrics['callId'];
759
+ this.state.callMetrics = Object.assign(Object.assign({}, this.state.callMetrics), { [value.callId]: metrics });
760
+ }
761
+ _removeCallMetrics(callId) {
762
+ const callMetricsCopy = Object.assign({}, this.state.callMetrics);
763
+ delete callMetricsCopy[callId];
764
+ this.state.callMetrics = Object.assign({}, callMetricsCopy);
765
+ }
766
+ _getCallQuality(call) {
767
+ const metrics = new metrics_1.default(this.state.metricConfig);
768
+ const probe = metrics.createProbe(call.connection, {
769
+ cid: call._id
770
+ });
771
+ const inboundKeys = [];
772
+ let inboundAudio;
773
+ probe.onreport = (probe) => {
774
+ //console.log('probe', probe)
775
+ /*const inboundMetrics = Object.entries(probe.audio).filter(([ key, value ]) => {
776
+ return value.direction === 'inbound'
777
+ })*/
778
+ //const ioo = Object.entries(probe.audio).
779
+ Object.entries(probe.audio).forEach(([key, value]) => {
780
+ if (value.direction === 'inbound' && !inboundKeys.includes(key)) {
781
+ inboundKeys.push(key);
782
+ inboundAudio = key;
783
+ }
784
+ });
785
+ /*inboundMetrics.forEach(([ key, value ]) => {
786
+ if (!inboundKeys.includes(key)) {
787
+ inboundKeys.push(key)
788
+ inboundAudio = key
789
+ }
790
+ })*/
791
+ const inboundAudioMetric = probe.audio[inboundAudio];
792
+ const metric = (0, filter_helper_1.filterObjectKeys)(inboundAudioMetric, metric_keys_to_include_1.METRIC_KEYS_TO_INCLUDE);
793
+ metric.callId = call._id;
794
+ this._setCallMetrics(metrics);
795
+ };
796
+ this.subscribe(call_event_listener_type_1.CALL_EVENT_LISTENER_TYPE.CALL_ENDED, (session) => {
797
+ if (session._id === call._id) {
798
+ metrics.stopAllProbes();
799
+ }
800
+ });
801
+ metrics.startAllProbes();
802
+ }
803
+ _triggerAddStream(event, call) {
804
+ return __awaiter(this, void 0, void 0, function* () {
805
+ this.isMuted = this.muteWhenJoin;
806
+ const stream = yield navigator.mediaDevices.getUserMedia(this.getUserMediaConstraints);
807
+ const processedStream = (0, audio_helper_1.processAudioVolume)(stream, this.microphoneInputLevel);
808
+ const muteMicro = this.isMuted || this.muteWhenJoin;
809
+ processedStream.getTracks().forEach(track => track.enabled = !muteMicro);
810
+ this._setOriginalStream(processedStream);
811
+ yield call.connection.getSenders()[0].replaceTrack(processedStream.getTracks()[0]);
812
+ (0, audio_helper_1.syncStream)(event, call, this.selectedOutputDevice, this.speakerVolume);
813
+ this._getCallQuality(call);
814
+ this.updateCall(call);
815
+ });
816
+ }
817
+ doCall({ target, addToCurrentRoom }) {
818
+ this.checkInitialized();
819
+ if (target.length === 0) {
820
+ return console.error('Target must be a valid string');
821
+ }
822
+ const call = this.call(`sip:${target}@${this.sipDomain}`, this.sipOptions);
823
+ this.callAddingInProgress = call.id;
824
+ if (addToCurrentRoom && this.currentActiveRoomId !== undefined) {
825
+ this.callChangeRoom({
826
+ callId: call.id,
827
+ roomId: this.currentActiveRoomId
828
+ });
829
+ }
830
+ call.connection.addEventListener('addstream', (event) => {
831
+ this._triggerAddStream(event, call);
832
+ });
833
+ }
834
+ callChangeRoom({ callId, roomId }) {
835
+ return __awaiter(this, void 0, void 0, function* () {
836
+ const oldRoomId = activeCalls[callId].roomId;
837
+ activeCalls[callId].roomId = roomId;
838
+ yield this.setCurrentActiveRoomId(roomId);
839
+ return Promise.all([
840
+ this.roomReconfigure(oldRoomId),
841
+ this.roomReconfigure(roomId)
842
+ ]).then(() => {
843
+ this.deleteRoomIfEmpty(oldRoomId);
844
+ this.deleteRoomIfEmpty(roomId);
845
+ });
846
+ });
847
+ }
848
+ }
849
+ exports.default = OpenSIPSJS;