@wazo/sdk 0.43.6 → 1.0.0-alpha.1

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.
Files changed (141) hide show
  1. package/dist/wazo-sdk.js +2 -2
  2. package/esm/__tests__/__voice__/call.test.js +72 -0
  3. package/esm/__tests__/__voice__/softphone.test.js +66 -0
  4. package/esm/checker/checks/webrtc.js +2 -1
  5. package/esm/domain/InvalidState.js +8 -0
  6. package/esm/domain/InvalidStateTransition.js +10 -0
  7. package/esm/domain/Phone/WebRTCPhone.js +33 -35
  8. package/esm/index.js +2 -0
  9. package/esm/simple/Phone.js +2 -1
  10. package/esm/simple/index.js +8 -2
  11. package/esm/simple/room/Room.js +2 -1
  12. package/esm/simple/room/SipRoom.js +2 -1
  13. package/esm/state-machine/call-state-machine.js +114 -0
  14. package/esm/state-machine/softphone-state-machine.js +39 -0
  15. package/esm/state-machine/utils.js +40 -0
  16. package/esm/types/__tests__/__voice__/call.test.d.ts +2 -0
  17. package/esm/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  18. package/esm/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  19. package/esm/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  20. package/esm/types/checker/checks/webrtc.d.ts.map +1 -1
  21. package/esm/types/domain/InvalidState.d.ts +6 -0
  22. package/esm/types/domain/InvalidState.d.ts.map +1 -0
  23. package/esm/types/domain/InvalidStateTransition.d.ts +7 -0
  24. package/esm/types/domain/InvalidStateTransition.d.ts.map +1 -0
  25. package/esm/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  26. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  27. package/esm/types/domain/types.d.ts +4 -0
  28. package/esm/types/domain/types.d.ts.map +1 -1
  29. package/esm/types/index.d.ts +2 -0
  30. package/esm/types/index.d.ts.map +1 -1
  31. package/esm/types/simple/Phone.d.ts.map +1 -1
  32. package/esm/types/simple/index.d.ts +7 -2
  33. package/esm/types/simple/index.d.ts.map +1 -1
  34. package/esm/types/simple/room/Room.d.ts.map +1 -1
  35. package/esm/types/simple/room/SipRoom.d.ts +1 -1
  36. package/esm/types/simple/room/SipRoom.d.ts.map +1 -1
  37. package/esm/types/state-machine/call-state-machine.d.ts +43 -0
  38. package/esm/types/state-machine/call-state-machine.d.ts.map +1 -0
  39. package/esm/types/state-machine/softphone-state-machine.d.ts +19 -0
  40. package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  41. package/esm/types/state-machine/utils.d.ts +9 -0
  42. package/esm/types/state-machine/utils.d.ts.map +1 -0
  43. package/esm/types/utils/sdp.d.ts +6 -0
  44. package/esm/types/utils/sdp.d.ts.map +1 -1
  45. package/esm/types/utils/sip-logger.d.ts +4 -0
  46. package/esm/types/utils/sip-logger.d.ts.map +1 -0
  47. package/esm/types/utils/webrtc.d.ts +40 -0
  48. package/esm/types/utils/webrtc.d.ts.map +1 -1
  49. package/esm/types/voice/call.d.ts +110 -0
  50. package/esm/types/voice/call.d.ts.map +1 -0
  51. package/esm/types/voice/softphone.d.ts +77 -0
  52. package/esm/types/voice/softphone.d.ts.map +1 -0
  53. package/esm/types/web-rtc-client.d.ts +1 -36
  54. package/esm/types/web-rtc-client.d.ts.map +1 -1
  55. package/esm/utils/sdp.js +26 -0
  56. package/esm/utils/sip-logger.js +32 -0
  57. package/esm/utils/webrtc.js +465 -0
  58. package/esm/voice/call.js +527 -0
  59. package/esm/voice/softphone.js +347 -0
  60. package/esm/web-rtc-client.js +86 -584
  61. package/lib/__tests__/__voice__/call.test.js +86 -0
  62. package/lib/__tests__/__voice__/softphone.test.js +80 -0
  63. package/lib/checker/checks/webrtc.js +2 -1
  64. package/lib/domain/InvalidState.js +9 -0
  65. package/lib/domain/InvalidStateTransition.js +10 -0
  66. package/lib/domain/Phone/WebRTCPhone.js +33 -35
  67. package/lib/index.js +6 -2
  68. package/lib/simple/Phone.js +2 -1
  69. package/lib/simple/index.js +6 -0
  70. package/lib/simple/room/Room.js +4 -3
  71. package/lib/simple/room/SipRoom.js +2 -1
  72. package/lib/state-machine/call-state-machine.js +120 -0
  73. package/lib/state-machine/softphone-state-machine.js +42 -0
  74. package/lib/state-machine/utils.js +52 -0
  75. package/lib/types/__tests__/__voice__/call.test.d.ts +2 -0
  76. package/lib/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  77. package/lib/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  78. package/lib/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  79. package/lib/types/checker/checks/webrtc.d.ts.map +1 -1
  80. package/lib/types/domain/InvalidState.d.ts +6 -0
  81. package/lib/types/domain/InvalidState.d.ts.map +1 -0
  82. package/lib/types/domain/InvalidStateTransition.d.ts +7 -0
  83. package/lib/types/domain/InvalidStateTransition.d.ts.map +1 -0
  84. package/lib/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  85. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  86. package/lib/types/domain/types.d.ts +4 -0
  87. package/lib/types/domain/types.d.ts.map +1 -1
  88. package/lib/types/index.d.ts +2 -0
  89. package/lib/types/index.d.ts.map +1 -1
  90. package/lib/types/simple/Phone.d.ts.map +1 -1
  91. package/lib/types/simple/index.d.ts +7 -2
  92. package/lib/types/simple/index.d.ts.map +1 -1
  93. package/lib/types/simple/room/Room.d.ts.map +1 -1
  94. package/lib/types/simple/room/SipRoom.d.ts +1 -1
  95. package/lib/types/simple/room/SipRoom.d.ts.map +1 -1
  96. package/lib/types/state-machine/call-state-machine.d.ts +43 -0
  97. package/lib/types/state-machine/call-state-machine.d.ts.map +1 -0
  98. package/lib/types/state-machine/softphone-state-machine.d.ts +19 -0
  99. package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  100. package/lib/types/state-machine/utils.d.ts +9 -0
  101. package/lib/types/state-machine/utils.d.ts.map +1 -0
  102. package/lib/types/utils/sdp.d.ts +6 -0
  103. package/lib/types/utils/sdp.d.ts.map +1 -1
  104. package/lib/types/utils/sip-logger.d.ts +4 -0
  105. package/lib/types/utils/sip-logger.d.ts.map +1 -0
  106. package/lib/types/utils/webrtc.d.ts +40 -0
  107. package/lib/types/utils/webrtc.d.ts.map +1 -1
  108. package/lib/types/voice/call.d.ts +110 -0
  109. package/lib/types/voice/call.d.ts.map +1 -0
  110. package/lib/types/voice/softphone.d.ts +77 -0
  111. package/lib/types/voice/softphone.d.ts.map +1 -0
  112. package/lib/types/web-rtc-client.d.ts +1 -36
  113. package/lib/types/web-rtc-client.d.ts.map +1 -1
  114. package/lib/utils/sdp.js +27 -1
  115. package/lib/utils/sip-logger.js +37 -0
  116. package/lib/utils/webrtc.js +521 -1
  117. package/lib/voice/call.js +577 -0
  118. package/lib/voice/softphone.js +375 -0
  119. package/lib/web-rtc-client.js +89 -601
  120. package/package.json +3 -2
  121. package/src/__tests__/__voice__/call.test.ts +90 -0
  122. package/src/__tests__/__voice__/softphone.test.ts +82 -0
  123. package/src/checker/checks/webrtc.ts +2 -1
  124. package/src/domain/InvalidState.ts +13 -0
  125. package/src/domain/InvalidStateTransition.ts +17 -0
  126. package/src/domain/Phone/WebRTCPhone.ts +34 -36
  127. package/src/domain/types.ts +6 -1
  128. package/src/index.ts +2 -0
  129. package/src/simple/Phone.ts +2 -1
  130. package/src/simple/index.ts +9 -2
  131. package/src/simple/room/Room.ts +2 -1
  132. package/src/simple/room/SipRoom.ts +2 -1
  133. package/src/state-machine/call-state-machine.ts +131 -0
  134. package/src/state-machine/softphone-state-machine.ts +49 -0
  135. package/src/state-machine/utils.ts +53 -0
  136. package/src/utils/sdp.ts +36 -0
  137. package/src/utils/sip-logger.ts +40 -0
  138. package/src/utils/webrtc.ts +571 -0
  139. package/src/voice/call.ts +713 -0
  140. package/src/voice/softphone.ts +485 -0
  141. package/src/web-rtc-client.ts +90 -690
@@ -0,0 +1,577 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.MESSAGE_TRACK_UPDATED = exports.MESSAGE_TYPE_SIGNAL = exports.MESSAGE_TYPE_CHAT = exports.EVENT_TERMINATE_SOUND = exports.EVENT_CAMERA_DISABLED = exports.EVENT_CAMERA_RESUMED = exports.EVENT_WEB_RTC_STATS = exports.EVENT_SHARE_SCREEN_ENDED = exports.EVENT_SHARE_SCREEN_STARTED = exports.EVENT_SHARE_SCREEN_ENDING = exports.EVENT_REMOVE_STREAM = exports.EVENT_VIDEO_STREAM = exports.EVENT_AUDIO_STREAM = exports.EVENT_TRACK = exports.EVENT_REINVITE = exports.EVENT_CALL_RESUMED = exports.EVENT_CALL_HELD = exports.EVENT_CALL_UN_MUTED = exports.EVENT_CALL_MUTED = exports.EVENT_CALL_ENDED = exports.EVENT_CALL_TERMINATING = exports.EVENT_CALL_REJECTED = exports.EVENT_CALL_ERROR = exports.EVENT_EARLY_MEDIA = exports.EVENT_PROGRESS = exports.EVENT_CALL_CANCELED = exports.EVENT_CALL_ACCEPTED = void 0;
39
+ /* eslint-disable no-underscore-dangle */
40
+ const events_1 = require("events");
41
+ const xstate_1 = require("xstate");
42
+ const invitation_1 = require("sip.js/lib/api/invitation");
43
+ const session_state_1 = require("sip.js/lib/api/session-state");
44
+ const call_state_machine_1 = __importStar(require("../state-machine/call-state-machine"));
45
+ const IssueReporter_1 = __importDefault(require("../service/IssueReporter"));
46
+ const sdp_1 = require("../utils/sdp");
47
+ const utils_1 = require("../state-machine/utils");
48
+ const webrtc_1 = require("../utils/webrtc");
49
+ const logger = IssueReporter_1.default.loggerFor('call');
50
+ // Events
51
+ exports.EVENT_CALL_ACCEPTED = 'accepted';
52
+ exports.EVENT_CALL_CANCELED = 'canceled';
53
+ exports.EVENT_PROGRESS = 'progress';
54
+ exports.EVENT_EARLY_MEDIA = 'earlyMedia';
55
+ exports.EVENT_CALL_ERROR = 'error';
56
+ exports.EVENT_CALL_REJECTED = 'rejected';
57
+ exports.EVENT_CALL_TERMINATING = 'terminating';
58
+ exports.EVENT_CALL_ENDED = 'ended';
59
+ exports.EVENT_CALL_MUTED = 'muted';
60
+ exports.EVENT_CALL_UN_MUTED = 'unMuted';
61
+ exports.EVENT_CALL_HELD = 'held';
62
+ exports.EVENT_CALL_RESUMED = 'resumed';
63
+ exports.EVENT_REINVITE = 'reinvite';
64
+ exports.EVENT_TRACK = 'track';
65
+ exports.EVENT_AUDIO_STREAM = 'onAudioStream';
66
+ exports.EVENT_VIDEO_STREAM = 'onVideoStream';
67
+ exports.EVENT_REMOVE_STREAM = 'onRemoveStream';
68
+ exports.EVENT_SHARE_SCREEN_ENDING = 'onScreenShareEnding';
69
+ exports.EVENT_SHARE_SCREEN_STARTED = 'onScreenShareStarted';
70
+ exports.EVENT_SHARE_SCREEN_ENDED = 'onScreenShareEnded';
71
+ exports.EVENT_WEB_RTC_STATS = 'webRtcStats';
72
+ exports.EVENT_CAMERA_RESUMED = 'onCameraResumed';
73
+ exports.EVENT_CAMERA_DISABLED = 'onCameraDisabled';
74
+ exports.EVENT_TERMINATE_SOUND = 'terminateSound';
75
+ exports.MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
76
+ exports.MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
77
+ exports.MESSAGE_TRACK_UPDATED = 'onTrackUpdated';
78
+ class Call extends events_1.EventEmitter {
79
+ constructor(sipCall, phone) {
80
+ super();
81
+ this.shouldSendReinvite = false;
82
+ this.callActor = (0, xstate_1.createActor)(call_state_machine_1.default);
83
+ this.callActor.start();
84
+ this.creationTime = new Date();
85
+ this.phone = phone;
86
+ this.sipCall = sipCall;
87
+ this._bindEvents();
88
+ }
89
+ accept() {
90
+ return __awaiter(this, arguments, void 0, function* (options = {}) {
91
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.Actions.ACCEPT);
92
+ logger.info('call - accept', Object.assign({ id: this.id }, options));
93
+ this._sendAction(call_state_machine_1.Actions.ACCEPT);
94
+ this.phone.holdAllCalls(this);
95
+ this.shouldSendReinvite = false;
96
+ this.answerTime = new Date();
97
+ return this.phone.client.answer(this.sipCall, options.withCamera).then(() => {
98
+ logger.info('call - accepted', Object.assign({ id: this.id }, options));
99
+ return this.id;
100
+ }).catch(e => {
101
+ logger.error(e);
102
+ this.hangup();
103
+ throw e;
104
+ });
105
+ });
106
+ }
107
+ reject() {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.Actions.REJECT);
110
+ logger.info('call - reject', { id: this.id });
111
+ this.eventEmitter.emit(exports.EVENT_TERMINATE_SOUND, 'call rejected locally');
112
+ this.shouldSendReinvite = false;
113
+ return this.hangup();
114
+ });
115
+ }
116
+ hangup() {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.Actions.HANGUP);
119
+ logger.info('call - hangup', { id: this.id });
120
+ yield this.phone.client.hangup(this.sipCall);
121
+ // Remove this call from softphone's calls
122
+ this.phone.removeCall(this);
123
+ return true;
124
+ });
125
+ }
126
+ forceCancel() {
127
+ try {
128
+ // @ts-ignore: private
129
+ this.sipCall.outgoingInviteRequest.cancel();
130
+ }
131
+ catch (e) {
132
+ logger.info('call - force cancel, error', e);
133
+ }
134
+ }
135
+ startConference(withCalls) {
136
+ // @TODO
137
+ // const participants = [this, ...withCalls].reduce((acc: Record<string, any>, call: Call) => {
138
+ // acc[call.talkingToIds[0]] = call;
139
+ // return acc;
140
+ // }, {});
141
+ // const adHocConference = new AdHocAPIConference({
142
+ // phone: this.phone,
143
+ // host,
144
+ // participants,
145
+ // });
146
+ // return adHocConference.start();
147
+ }
148
+ mute() {
149
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.EstablishedActions.MUTE);
150
+ logger.info('call - mute', { id: this.id });
151
+ this._sendAction(call_state_machine_1.EstablishedActions.MUTE);
152
+ this.phone.client.mute(this.sipCall);
153
+ this.eventEmitter.emit(exports.EVENT_CALL_MUTED);
154
+ }
155
+ unMute() {
156
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.EstablishedActions.UN_MUTE);
157
+ logger.info('call - un-mute', { id: this.id });
158
+ this._sendAction(call_state_machine_1.EstablishedActions.UN_MUTE);
159
+ this.phone.client.unmute(this.sipCall);
160
+ this.eventEmitter.emit(exports.EVENT_CALL_UN_MUTED);
161
+ }
162
+ hold() {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.EstablishedActions.HOLD);
165
+ logger.info('call - hold', { id: this.id });
166
+ this._sendAction(call_state_machine_1.EstablishedActions.HOLD);
167
+ const hasVideo = (0, webrtc_1.hasLocalVideo)(this.sipCall);
168
+ // Stop screenshare if needed
169
+ if (this.currentScreenShare && this.currentScreenShare.sipSessionId === this.id) {
170
+ yield this.stopScreenSharing(false);
171
+ }
172
+ // Downgrade to audio if needed
173
+ if (hasVideo) {
174
+ yield (0, webrtc_1.downgradeToAudio)(this.sipCall);
175
+ }
176
+ const promise = this.phone.client.hold(this.sipCall, this.isConference(), hasVideo);
177
+ this.eventEmitter.emit(exports.EVENT_CALL_HELD);
178
+ return promise;
179
+ });
180
+ }
181
+ resume() {
182
+ return __awaiter(this, void 0, void 0, function* () {
183
+ (0, utils_1.assertCan)(this.callActor, call_state_machine_1.EstablishedActions.RESUME);
184
+ this._sendAction(call_state_machine_1.EstablishedActions.RESUME);
185
+ const isConference = this.isConference();
186
+ const hasVideo = (0, webrtc_1.hasLocalVideo)(this.sipCall);
187
+ const wasScreensharing = this.currentScreenShare && this.currentScreenShare.sipSessionId === this.id;
188
+ const wasDesktop = this.currentScreenShare && this.currentScreenShare.desktop;
189
+ logger.info('call - resume', { id: this.id, isConference, hasVideo, wasScreensharing, wasDesktop });
190
+ const promise = this.phone.client.unhold(this.sipCall, isConference);
191
+ if (hasVideo) {
192
+ const constraints = {
193
+ audio: false,
194
+ video: true,
195
+ screen: wasScreensharing,
196
+ desktop: wasDesktop,
197
+ };
198
+ yield (0, webrtc_1.upgradeToVideo)(this.sipCall, constraints, isConference);
199
+ }
200
+ const onScreenSharing = (stream) => {
201
+ const hadVideo = this.currentScreenShare && this.currentScreenShare.hadVideo;
202
+ if (stream) {
203
+ this._onScreenSharing(stream, hadVideo, wasDesktop);
204
+ }
205
+ };
206
+ this.eventEmitter.emit(exports.EVENT_CALL_RESUMED);
207
+ return promise.then(() => {
208
+ const stream = this.getLocalVideoStream();
209
+ if (wasScreensharing) {
210
+ onScreenSharing(this.getLocalVideoStream());
211
+ }
212
+ return stream;
213
+ });
214
+ });
215
+ }
216
+ turnCameraOn() {
217
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
218
+ logger.info('call - turn camera on', { id: this.id });
219
+ (0, webrtc_1.toggleVideo)(this.sipCall, true);
220
+ this.eventEmitter.emit(exports.EVENT_CAMERA_RESUMED);
221
+ }
222
+ turnCameraOff() {
223
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
224
+ logger.info('call - turn camera off', { id: this.id });
225
+ (0, webrtc_1.toggleVideo)(this.sipCall, false);
226
+ this.eventEmitter.emit(exports.EVENT_CAMERA_DISABLED);
227
+ }
228
+ isHeld() {
229
+ return (0, utils_1.getState)(this.callActor) === call_state_machine_1.EstablishedStates.HELD;
230
+ }
231
+ isEstablished() {
232
+ return (0, utils_1.getState)(this.callActor) === call_state_machine_1.States.ESTABLISHED;
233
+ }
234
+ isConference() {
235
+ var _a;
236
+ return (_a = (0, webrtc_1.getPeerConnection)(this.sipCall)) === null || _a === void 0 ? void 0 : _a.sfu;
237
+ }
238
+ transfer(target) {
239
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
240
+ logger.info('call - transfer', { id: this.id, target });
241
+ this.phone.client.transfer(this.sipCall, target);
242
+ }
243
+ atxfer() {
244
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
245
+ logger.info('call - atxfer', { id: this.id });
246
+ return this.phone.client.atxfer(this.sipCall);
247
+ }
248
+ sendDigits(digits) {
249
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
250
+ logger.info('call - send digits', { id: this.id, digits });
251
+ this.phone.client.sendDTMF(this.sipCall, digits);
252
+ }
253
+ startNetworkMonitoring(interval = 1000) {
254
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
255
+ logger.info('call - start network monitoring', { id: this.id });
256
+ return this.phone.client.startNetworkMonitoring(this.sipCall, interval);
257
+ }
258
+ stopNetworkMonitoring() {
259
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
260
+ logger.info('call - stop network monitoring', { id: this.id });
261
+ return this.phone.client.stopNetworkMonitoring(this.sipCall);
262
+ }
263
+ getStats() {
264
+ return __awaiter(this, void 0, void 0, function* () {
265
+ return (0, webrtc_1.getWebRtcStats)(this.sipCall);
266
+ });
267
+ }
268
+ sendReinvite() {
269
+ return __awaiter(this, arguments, void 0, function* (options = {}) {
270
+ var _a, _b;
271
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
272
+ logger.info('call - send reinvite', Object.assign({ id: this.id }, options));
273
+ const shouldScreenShare = (_a = options.constraints) === null || _a === void 0 ? void 0 : _a.screen;
274
+ const isUpgrade = shouldScreenShare || ((_b = options.constraints) === null || _b === void 0 ? void 0 : _b.video);
275
+ // Downgrade
276
+ if (options.constraints && !isUpgrade) {
277
+ // No reinvite needed
278
+ this._downgradeToAudio();
279
+ return;
280
+ }
281
+ if (isUpgrade) {
282
+ const shouldReinvite = yield this._upgradeToVideo(options);
283
+ if (!shouldReinvite) {
284
+ return;
285
+ }
286
+ }
287
+ return this.phone.client.reinvite(this.sipCall, options.constraints, options.conference, options.audioOnly, options.iceRestart);
288
+ });
289
+ }
290
+ sendMessage(body, contentType = 'text/plain') {
291
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
292
+ logger.info('call - send message', { id: this.id, body });
293
+ return this.phone.client.sendMessage(this.sipCall, body, contentType);
294
+ }
295
+ sendChat(content) {
296
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
297
+ logger.info('call - send chat', { id: this.id, content });
298
+ this.sendMessage(JSON.stringify({ type: exports.MESSAGE_TYPE_CHAT, content }), 'application/json');
299
+ }
300
+ sendSignal(content) {
301
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
302
+ logger.info('call - send signal', { id: this.id, content });
303
+ this.sendMessage(JSON.stringify({ type: exports.MESSAGE_TYPE_SIGNAL, content }), 'application/json');
304
+ }
305
+ startScreenSharing(constraints) {
306
+ return __awaiter(this, void 0, void 0, function* () {
307
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
308
+ logger.info('call - start screen sharing', { constraints, id: this.id });
309
+ const screenShareStream = yield (0, webrtc_1.getStreamFromConstraints)(constraints);
310
+ if (!screenShareStream) {
311
+ throw new Error(`Can't create media stream for screensharing with: ${JSON.stringify(constraints)}`);
312
+ }
313
+ const screenTrack = screenShareStream.getVideoTracks()[0];
314
+ const pc = (0, webrtc_1.getPeerConnection)(this.sipCall);
315
+ const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
316
+ const localStream = (0, webrtc_1.getLocalStream)(this.sipCall);
317
+ const videoTrack = localStream ? localStream.getTracks().find(track => track.kind === 'video') : null;
318
+ const hadVideo = !!videoTrack;
319
+ // Stop local video tracks
320
+ if (videoTrack && localStream) {
321
+ videoTrack.enabled = false;
322
+ videoTrack.stop();
323
+ localStream.removeTrack(videoTrack);
324
+ }
325
+ if (localStream) {
326
+ localStream.addTrack(screenTrack);
327
+ }
328
+ if (sender) {
329
+ sender.replaceTrack(screenTrack);
330
+ }
331
+ this._onScreenSharing(screenShareStream, hadVideo, constraints.desktop);
332
+ return screenShareStream;
333
+ });
334
+ }
335
+ stopScreenSharing() {
336
+ return __awaiter(this, arguments, void 0, function* (restoreLocalStream = true) {
337
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
338
+ if (!this.currentScreenShare) {
339
+ return;
340
+ }
341
+ logger.info('call - stop screen sharing', { restoreLocalStream });
342
+ let reinvited = null;
343
+ try {
344
+ if (this.currentScreenShare.stream) {
345
+ this.currentScreenShare.stream.getTracks()
346
+ .filter((track) => track.enabled && track.kind === 'video')
347
+ .forEach((track) => track.stop());
348
+ }
349
+ if (restoreLocalStream) {
350
+ const conference = this.isConference();
351
+ // When stopping screenshare and we had video before that, we have to re-upgrade
352
+ // When upgrading directly to screenshare (eg: we don't have a videoLocalStream to replace)
353
+ // We have to downgrade to audio.
354
+ const screenshareStopped = this.currentScreenShare.hadVideo;
355
+ const constraints = {
356
+ audio: false,
357
+ video: screenshareStopped,
358
+ };
359
+ // We have to remove the video sender to be able to re-upgrade to video in `sendReinvite` below
360
+ this._downgradeToAudio(false);
361
+ // Wait for the track to be removed, unless the video sender won't have a null track in `_upgradeToVideo`.
362
+ yield new Promise(resolve => setTimeout(resolve, 500));
363
+ reinvited = yield this.sendReinvite({ constraints, conference });
364
+ }
365
+ }
366
+ catch (e) {
367
+ console.warn(e);
368
+ }
369
+ this.eventEmitter.emit(exports.EVENT_SHARE_SCREEN_ENDED);
370
+ this.currentScreenShare = null;
371
+ return reinvited;
372
+ });
373
+ }
374
+ onScreenShareReinvite(response, desktop) {
375
+ const localStream = (0, webrtc_1.getLocalStream)(this.sipCall);
376
+ logger.info('call - updrading directly in screensharing mode', { id: this.id, tracks: localStream === null || localStream === void 0 ? void 0 : localStream.getTracks() });
377
+ this._onScreenSharing(localStream, false, desktop);
378
+ }
379
+ onAccepted() {
380
+ this._sendAction(call_state_machine_1.Actions.REMOTLY_ACCEPTED);
381
+ this.emit(exports.EVENT_CALL_ACCEPTED);
382
+ }
383
+ onProgress() {
384
+ this.emit(exports.EVENT_PROGRESS);
385
+ }
386
+ onEarlyMedia() {
387
+ this.emit(exports.EVENT_EARLY_MEDIA);
388
+ }
389
+ onTrack(event) {
390
+ this.emit(exports.EVENT_TRACK, event);
391
+ }
392
+ onError(error) {
393
+ this.emit(exports.EVENT_CALL_ERROR, error);
394
+ }
395
+ onRejected() {
396
+ this.endTime = new Date();
397
+ this.emit(exports.EVENT_CALL_REJECTED);
398
+ this.phone.removeCall(this);
399
+ }
400
+ onNetworkStats(stats, previousStats) {
401
+ this.emit(exports.EVENT_WEB_RTC_STATS, stats, previousStats);
402
+ }
403
+ getRemoteStream() {
404
+ return (0, webrtc_1.getRemoteStream)(this.sipCall);
405
+ }
406
+ getRemoteVideoStream() {
407
+ return (0, webrtc_1.getRemoteVideoStream)(this.sipCall);
408
+ }
409
+ hasRemoteVideo() {
410
+ return (0, webrtc_1.hasRemoteVideo)(this.sipCall);
411
+ }
412
+ hasLocalVideo() {
413
+ return (0, webrtc_1.hasLocalVideo)(this.sipCall);
414
+ }
415
+ getLocalStream() {
416
+ return (0, webrtc_1.getLocalStream)(this.sipCall);
417
+ }
418
+ getLocalVideoStream() {
419
+ return (0, webrtc_1.getLocalVideoStream)(this.sipCall);
420
+ }
421
+ hasALocalVideoTrack() {
422
+ return (0, webrtc_1.hasALocalVideoTrack)(this.sipCall);
423
+ }
424
+ isVideoRemotelyHeld() {
425
+ return (0, webrtc_1.isVideoRemotelyHeld)(this.sipCall);
426
+ }
427
+ onReinvite(request, updatedCalleeName, updatedNumber, hadRemoteVideo) {
428
+ this.emit(exports.EVENT_REINVITE, request, updatedCalleeName, updatedNumber, hadRemoteVideo);
429
+ }
430
+ get id() {
431
+ return (0, sdp_1.getSipSessionId)(this.sipCall);
432
+ }
433
+ get number() {
434
+ const identity = this.sipCall.remoteIdentity || this.sipCall.assertedIdentity;
435
+ // @ts-ignore: private
436
+ return identity ? identity.uri._normal.user : null;
437
+ }
438
+ _bindEvents() {
439
+ if (this.sipCall instanceof invitation_1.Invitation) {
440
+ // Monkey patch to know when canceled with the CANCEL message
441
+ const onCancel = this.sipCall._onCancel.bind(this.sipCall);
442
+ this.sipCall._onCancel = (message) => {
443
+ logger.trace('call - on sip session canceled', { callId: message.callId, id: this.id });
444
+ onCancel(message);
445
+ const elsewhere = message.data.indexOf('cause=26') !== -1 && message.data.indexOf('completed elsewhere') !== -1;
446
+ this.eventEmitter.emit(exports.EVENT_CALL_CANCELED, elsewhere);
447
+ };
448
+ }
449
+ this.sipCall.stateChange.addListener((newState) => {
450
+ switch (newState) {
451
+ case session_state_1.SessionState.Terminating:
452
+ logger.info('call - terminating', { id: this.id });
453
+ this.eventEmitter.emit(exports.EVENT_CALL_TERMINATING);
454
+ break;
455
+ case session_state_1.SessionState.Terminated:
456
+ {
457
+ logger.info('call - terminated', { id: this.id });
458
+ // Should be called before `_onCallTerminated` or the callCount will not decrement...
459
+ this.endTime = new Date();
460
+ return this.eventEmitter.emit(exports.EVENT_CALL_ENDED);
461
+ }
462
+ default:
463
+ break;
464
+ }
465
+ });
466
+ if (!this.sipCall.sessionDescriptionHandler) {
467
+ return;
468
+ }
469
+ // Video events
470
+ const sdh = this.sipCall.sessionDescriptionHandler;
471
+ const peerConnection = sdh.peerConnection;
472
+ if (!peerConnection) {
473
+ logger.warn('call - no peer connection', { id: this.id });
474
+ return;
475
+ }
476
+ peerConnection.ontrack = (rawEvent) => {
477
+ const event = rawEvent;
478
+ const [stream] = event.streams;
479
+ const kind = event && event.track && event.track.kind;
480
+ logger.info('call - on track stream called on the peer connection', {
481
+ id: this.id,
482
+ streamId: stream ? stream.id : null,
483
+ tracks: stream ? stream.getTracks() : null,
484
+ kind,
485
+ });
486
+ if (kind === 'audio') {
487
+ return this.eventEmitter.emit(exports.EVENT_AUDIO_STREAM, stream);
488
+ }
489
+ return this.eventEmitter.emit(exports.EVENT_VIDEO_STREAM, stream, event.track.id, event);
490
+ };
491
+ peerConnection.onremovestream = (event) => {
492
+ logger.info('call - on remove stream called on the peer connection', {
493
+ id: event.stream.id,
494
+ tracks: event.stream.getTracks(),
495
+ });
496
+ this.eventEmitter.emit(exports.EVENT_REMOVE_STREAM, event.stream);
497
+ };
498
+ // Client events
499
+ }
500
+ _downgradeToAudio(withMessage = true) {
501
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
502
+ logger.info('call - downgrade to audio', { id: this.id, withMessage });
503
+ (0, webrtc_1.downgradeToAudio)(this.sipCall);
504
+ if (withMessage) {
505
+ this._sendReinviteMessage(false);
506
+ }
507
+ return true;
508
+ }
509
+ // Returns true if we need to send a re-INVITE request
510
+ _upgradeToVideo() {
511
+ return __awaiter(this, arguments, void 0, function* (options = {}) {
512
+ var _a, _b;
513
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
514
+ logger.info('call - upgrade to video', Object.assign({ id: this.id }, options));
515
+ const shouldScreenShare = (_a = options.constraints) === null || _a === void 0 ? void 0 : _a.screen;
516
+ const desktop = (_b = options.constraints) === null || _b === void 0 ? void 0 : _b.desktop;
517
+ const sdhOptions = this.sipCall.sessionDescriptionHandlerOptionsReInvite;
518
+ const wasAudioOnly = sdhOptions === null || sdhOptions === void 0 ? void 0 : sdhOptions.audioOnly;
519
+ const newStream = yield (0, webrtc_1.upgradeToVideo)(this.sipCall, options.constraints || {}, !!options.conference);
520
+ // If no stream is returned, it means we have to reinvite
521
+ if (!newStream) {
522
+ return true;
523
+ }
524
+ this._sendReinviteMessage(true);
525
+ if (shouldScreenShare) {
526
+ this._onScreenSharing(newStream, false, desktop);
527
+ }
528
+ // We have to reinvite to change the direction on the bundle when upgrading from an audioOnly conference
529
+ if (options.conference && wasAudioOnly) {
530
+ return true;
531
+ }
532
+ // No reinvite needed here
533
+ return false;
534
+ });
535
+ }
536
+ _sendReinviteMessage(isUpgrade) {
537
+ (0, utils_1.assertState)(this.callActor, call_state_machine_1.States.ESTABLISHED);
538
+ logger.info('call - sending reinvite message', { id: this.id, isUpgrade });
539
+ // Have to send the message after a delay due to latency to update the remote peer
540
+ setTimeout(() => {
541
+ this.sendMessage(JSON.stringify({
542
+ type: exports.MESSAGE_TYPE_SIGNAL,
543
+ content: {
544
+ type: exports.MESSAGE_TRACK_UPDATED,
545
+ update: isUpgrade ? 'upgrade' : 'downgrade',
546
+ sipCallId: this.id,
547
+ callId: this.apiId,
548
+ number: this.number,
549
+ },
550
+ }));
551
+ }, 2500);
552
+ }
553
+ _onScreenSharing(screenStream, hadVideo, desktop) {
554
+ const screenTrack = screenStream.getVideoTracks()[0];
555
+ const pc = this.phone.client.getPeerConnection(this.id);
556
+ const sender = pc && pc.getSenders && pc.getSenders().find((s) => s && s.track && s.track.kind === 'video');
557
+ logger.info('call - on screensharing', { hadVideo, id: this.id, screenTrack });
558
+ if (screenTrack) {
559
+ screenTrack.onended = () => {
560
+ logger.info('call - on screenshare ended', { hadVideo, id: this.id, screenTrack });
561
+ this.eventEmitter.emit(exports.EVENT_SHARE_SCREEN_ENDING);
562
+ };
563
+ }
564
+ this.currentScreenShare = {
565
+ stream: screenStream,
566
+ hadVideo,
567
+ sender,
568
+ desktop,
569
+ };
570
+ (0, webrtc_1.setLocalMediaStream)(this.sipCall, screenStream);
571
+ this.eventEmitter.emit(exports.EVENT_SHARE_SCREEN_STARTED, screenStream);
572
+ }
573
+ _sendAction(action) {
574
+ this.callActor.send({ type: action });
575
+ }
576
+ }
577
+ exports.default = Call;