@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
@@ -1,6 +1,67 @@
1
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
+ };
2
37
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseCandidate = void 0;
38
+ exports.fetchNetworkStats = exports.upgradeToVideo = exports.downgradeToAudio = exports.createUaOptions = exports.getStreamFromConstraints = exports.getMediaConfiguration = exports.getVideoConstraints = exports.getAudioConstraints = exports.isAudioOnly = exports.updateRemoteStream = exports.setLocalMediaStream = exports.cleanupStream = exports.toggleVideo = exports.toggleAudio = exports.getWebRtcStats = exports.sessionWantsToDoVideo = exports.hasAVideoTrack = exports.hasVideoTrack = exports.getRemoteVideoStreamFromPc = exports.getRemoteVideoStream = exports.hasARemoteVideoTrack = exports.hasRemoteVideo = exports.isVideoRemotelyHeld = exports.getRemoteTracks = exports.getLocalVideoStream = exports.hasALocalVideoTrack = exports.hasLocalVideo = exports.isVideoTrack = exports.getRemoteStream = exports.getLocalTracks = exports.getLocalStream = exports.getPeerConnection = exports.parseCandidate = exports.getIceServers = exports.isAPrivateIp = exports.isFirefox = exports.isWeb = void 0;
39
+ const peer_connection_configuration_default_1 = require("sip.js/lib/platform/web/session-description-handler/peer-connection-configuration-default");
40
+ const web_1 = require("sip.js/lib/platform/web");
41
+ const WazoSessionDescriptionHandler_1 = __importStar(require("../lib/WazoSessionDescriptionHandler"));
42
+ const sdp_1 = require("./sdp");
43
+ const IssueReporter_1 = __importDefault(require("../service/IssueReporter"));
44
+ const array_1 = require("./array");
45
+ const logger = IssueReporter_1.default.loggerFor('webrtc');
46
+ // setting a 24hr timeout and letting the backend define the actual value
47
+ const NO_ANSWER_TIMEOUT = 60 * 60 * 24; // in seconds
48
+ const DEFAULT_ICE_TIMEOUT = 3000;
49
+ const privateIpRegex = /^(?:10|127|172\.(?:1[6-9]|2[0-9]|3[01])|192\.168)\..*/;
50
+ const isWeb = () => typeof window === 'object' && typeof document === 'object';
51
+ exports.isWeb = isWeb;
52
+ const isFirefox = () => (0, exports.isWeb)() && navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
53
+ exports.isFirefox = isFirefox;
54
+ const isAPrivateIp = (ip) => privateIpRegex.exec(ip) == null;
55
+ exports.isAPrivateIp = isAPrivateIp;
56
+ const getIceServers = (ip) => {
57
+ if ((0, exports.isAPrivateIp)(ip)) {
58
+ return [{
59
+ urls: ['stun:stun.l.google.com:19302', 'stun:stun4.l.google.com:19302'],
60
+ }];
61
+ }
62
+ return [];
63
+ };
64
+ exports.getIceServers = getIceServers;
4
65
  const parseCandidate = (line) => {
5
66
  let parts;
6
67
  // Parse both variants.
@@ -39,3 +100,462 @@ const parseCandidate = (line) => {
39
100
  return candidate;
40
101
  };
41
102
  exports.parseCandidate = parseCandidate;
103
+ const getPeerConnection = (sipCall) => {
104
+ if (!sipCall) {
105
+ return null;
106
+ }
107
+ return sipCall.sessionDescriptionHandler ? sipCall.sessionDescriptionHandler.peerConnection : null;
108
+ };
109
+ exports.getPeerConnection = getPeerConnection;
110
+ const getLocalStream = (sipCall) => {
111
+ var _a;
112
+ return ((_a = sipCall === null || sipCall === void 0 ? void 0 : sipCall.sessionDescriptionHandler) === null || _a === void 0 ? void 0 : _a.localMediaStream) || null;
113
+ };
114
+ exports.getLocalStream = getLocalStream;
115
+ const getLocalTracks = (sipCall) => {
116
+ const localStream = (0, exports.getLocalStream)(sipCall);
117
+ if (!localStream) {
118
+ return [];
119
+ }
120
+ return localStream.getTracks();
121
+ };
122
+ exports.getLocalTracks = getLocalTracks;
123
+ const getRemoteStream = (sipCall) => {
124
+ var _a;
125
+ return ((_a = sipCall === null || sipCall === void 0 ? void 0 : sipCall.sessionDescriptionHandler) === null || _a === void 0 ? void 0 : _a.remoteMediaStream) || null;
126
+ };
127
+ exports.getRemoteStream = getRemoteStream;
128
+ const isVideoTrack = (track) => track.kind === 'video' && track.readyState === 'live';
129
+ exports.isVideoTrack = isVideoTrack;
130
+ const hasLocalVideo = (sipCall) => (0, exports.getLocalTracks)(sipCall).some(exports.isVideoTrack);
131
+ exports.hasLocalVideo = hasLocalVideo;
132
+ const hasALocalVideoTrack = (sipCall) => (0, exports.getLocalTracks)(sipCall).some(track => track.kind === 'video');
133
+ exports.hasALocalVideoTrack = hasALocalVideoTrack;
134
+ const getLocalVideoStream = (sipCall) => ((0, exports.hasLocalVideo)(sipCall) ? (0, exports.getLocalStream)(sipCall) : null);
135
+ exports.getLocalVideoStream = getLocalVideoStream;
136
+ const getRemoteTracks = (sipCall) => {
137
+ const remoteStream = (0, exports.getRemoteStream)(sipCall);
138
+ if (!remoteStream) {
139
+ return [];
140
+ }
141
+ return remoteStream.getTracks();
142
+ };
143
+ exports.getRemoteTracks = getRemoteTracks;
144
+ const isVideoRemotelyHeld = (sipCall) => {
145
+ const pc = (0, exports.getPeerConnection)(sipCall);
146
+ const sdp = pc && pc.remoteDescription ? pc.remoteDescription.sdp : null;
147
+ if (!sdp) {
148
+ return false;
149
+ }
150
+ const videoDirection = (0, sdp_1.getVideoDirection)(sdp);
151
+ return videoDirection === 'sendonly';
152
+ };
153
+ exports.isVideoRemotelyHeld = isVideoRemotelyHeld;
154
+ const hasRemoteVideo = (sipCall) => (0, exports.getRemoteTracks)(sipCall).some(exports.isVideoTrack);
155
+ exports.hasRemoteVideo = hasRemoteVideo;
156
+ const hasARemoteVideoTrack = (sipCall) => (0, exports.getRemoteTracks)(sipCall).some(track => track.kind === 'video');
157
+ exports.hasARemoteVideoTrack = hasARemoteVideoTrack;
158
+ const getRemoteVideoStream = (sipCall) => {
159
+ if ((0, exports.isVideoRemotelyHeld)(sipCall)) {
160
+ return null;
161
+ }
162
+ return (0, exports.hasRemoteVideo)(sipCall) ? (0, exports.getRemoteStream)(sipCall) : null;
163
+ };
164
+ exports.getRemoteVideoStream = getRemoteVideoStream;
165
+ const getRemoteVideoStreamFromPc = (sipCall) => {
166
+ const pc = (0, exports.getPeerConnection)(sipCall);
167
+ if (!pc) {
168
+ return null;
169
+ }
170
+ return pc.getRemoteStreams().find((stream) => !!stream.getVideoTracks().length);
171
+ };
172
+ exports.getRemoteVideoStreamFromPc = getRemoteVideoStreamFromPc;
173
+ const hasVideoTrack = (sipCall) => (0, exports.hasLocalVideo)(sipCall) || (0, exports.hasRemoteVideo)(sipCall);
174
+ exports.hasVideoTrack = hasVideoTrack;
175
+ const hasAVideoTrack = (sipCall) => (0, exports.hasALocalVideoTrack)(sipCall) || (0, exports.hasARemoteVideoTrack)(sipCall);
176
+ exports.hasAVideoTrack = hasAVideoTrack;
177
+ // eslint-disable-next-line no-unused-vars
178
+ const sessionWantsToDoVideo = (sipCall) => {
179
+ if (!sipCall) {
180
+ return false;
181
+ }
182
+ const { body, } = sipCall.request || sipCall;
183
+ // Sometimes with InviteClientContext the body is in the body attribute ...
184
+ const sdp = typeof body === 'object' && body ? body.body : body;
185
+ return (0, sdp_1.hasAnActiveVideo)(sdp);
186
+ };
187
+ exports.sessionWantsToDoVideo = sessionWantsToDoVideo;
188
+ const getWebRtcStats = (sipCall) => __awaiter(void 0, void 0, void 0, function* () {
189
+ const pc = (0, exports.getPeerConnection)(sipCall);
190
+ if (!pc) {
191
+ return null;
192
+ }
193
+ return pc.getStats(null);
194
+ });
195
+ exports.getWebRtcStats = getWebRtcStats;
196
+ const toggleAudio = (sipCall, muteAudio) => {
197
+ const sdh = sipCall.sessionDescriptionHandler;
198
+ const pc = ((sdh === null || sdh === void 0 ? void 0 : sdh.peerConnection) || null);
199
+ if (!pc) {
200
+ return;
201
+ }
202
+ if (pc.getSenders) {
203
+ pc.getSenders().forEach((sender) => {
204
+ if (sender && sender.track && sender.track.kind === 'audio') {
205
+ // eslint-disable-next-line
206
+ sender.track.enabled = !muteAudio;
207
+ }
208
+ });
209
+ }
210
+ else {
211
+ pc.getLocalStreams().forEach((stream) => {
212
+ stream.getAudioTracks().forEach(track => {
213
+ // eslint-disable-next-line
214
+ track.enabled = !muteAudio;
215
+ });
216
+ });
217
+ }
218
+ };
219
+ exports.toggleAudio = toggleAudio;
220
+ const toggleVideo = (sipCall, muteCamera) => {
221
+ const sdh = sipCall.sessionDescriptionHandler;
222
+ const pc = sdh === null || sdh === void 0 ? void 0 : sdh.peerConnection;
223
+ if (pc === null || pc === void 0 ? void 0 : pc.getSenders) {
224
+ pc.getSenders().forEach((sender) => {
225
+ if (sender && sender.track && sender.track.kind === 'video') {
226
+ // eslint-disable-next-line
227
+ sender.track.enabled = !muteCamera;
228
+ }
229
+ });
230
+ }
231
+ else {
232
+ pc.getLocalStreams().forEach((stream) => {
233
+ stream.getVideoTracks().forEach(track => {
234
+ // eslint-disable-next-line
235
+ track.enabled = !muteCamera;
236
+ });
237
+ });
238
+ }
239
+ };
240
+ exports.toggleVideo = toggleVideo;
241
+ const cleanupStream = (stream) => {
242
+ stream.getTracks().filter(track => track.enabled).forEach(track => track.stop());
243
+ };
244
+ exports.cleanupStream = cleanupStream;
245
+ const setLocalMediaStream = (sipCall, newStream) => {
246
+ logger.info('setting local media stream', {
247
+ id: sipCall.id,
248
+ tracks: newStream ? newStream.getTracks() : null,
249
+ });
250
+ if (sipCall.sessionDescriptionHandler) {
251
+ // eslint-disable-next-line no-underscore-dangle
252
+ // @ts-ignore: protected
253
+ sipSession.sessionDescriptionHandler._localMediaStream = newStream;
254
+ }
255
+ };
256
+ exports.setLocalMediaStream = setLocalMediaStream;
257
+ const updateRemoteStream = (sipCall, allTracks = true) => {
258
+ const remoteStream = (0, exports.getRemoteStream)(sipCall);
259
+ const pc = (0, exports.getPeerConnection)(sipCall);
260
+ logger.info('Updating remote stream', {
261
+ id: sipCall.id,
262
+ tracks: remoteStream ? remoteStream.getTracks() : null,
263
+ receiverTracks: pc && pc.getReceivers ? pc.getReceivers().map((receiver) => receiver.track) : null,
264
+ });
265
+ if (!pc || !remoteStream) {
266
+ return;
267
+ }
268
+ remoteStream.getTracks().forEach(track => {
269
+ if (allTracks || track.kind === 'video') {
270
+ remoteStream.removeTrack(track);
271
+ }
272
+ });
273
+ if (pc.getReceivers) {
274
+ pc.getReceivers().forEach((receiver) => {
275
+ if (allTracks || receiver.track.kind === 'video') {
276
+ remoteStream.addTrack(receiver.track);
277
+ }
278
+ });
279
+ }
280
+ };
281
+ exports.updateRemoteStream = updateRemoteStream;
282
+ const isAudioOnly = (sipCall) => {
283
+ return Boolean(sipCall.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === web_1.stripVideo));
284
+ };
285
+ exports.isAudioOnly = isAudioOnly;
286
+ // @ts-ignore: media constraints
287
+ const getAudioConstraints = (audio) => { var _a; return (((_a = audio === null || audio === void 0 ? void 0 : audio.deviceId) === null || _a === void 0 ? void 0 : _a.exact) ? audio : true); };
288
+ exports.getAudioConstraints = getAudioConstraints;
289
+ const getVideoConstraints = (video = false) => {
290
+ var _a, _b;
291
+ if (!video) {
292
+ return false;
293
+ }
294
+ // @ts-ignore: media constraints
295
+ return ((_b = (_a = this.video) === null || _a === void 0 ? void 0 : _a.deviceId) === null || _b === void 0 ? void 0 : _b.exact) ? this.video : true;
296
+ };
297
+ exports.getVideoConstraints = getVideoConstraints;
298
+ // eslint-disable-next-line no-unused-vars
299
+ const getRtcOptions = () => {
300
+ return {
301
+ mandatory: {
302
+ OfferToReceiveAudio: true,
303
+ OfferToReceiveVideo: false,
304
+ },
305
+ };
306
+ };
307
+ const getMediaConfiguration = (enableVideo, conference = false, constraints = null) => {
308
+ const screenSharing = constraints && 'screen' in constraints ? constraints.screen : false;
309
+ const isDesktop = constraints && 'desktop' in constraints ? constraints.desktop : false;
310
+ const withAudio = constraints && 'audio' in constraints ? constraints.audio : true;
311
+ const mandatoryVideo = constraints && typeof constraints.video === 'object' ? constraints.video.mandatory : {};
312
+ logger.info('Retrieving media a configuration', { enableVideo, screenSharing, isDesktop, withAudio, constraints });
313
+ return {
314
+ constraints: {
315
+ // Exact constraint are not supported with `getDisplayMedia` and we must have a video=false in desktop screenshare
316
+ audio: screenSharing ? !isDesktop : withAudio ? (0, exports.getAudioConstraints)(true) : false,
317
+ video: screenSharing ? isDesktop ? {
318
+ mandatory: Object.assign({ chromeMediaSource: 'desktop' }, (mandatoryVideo || {})),
319
+ } : {
320
+ cursor: 'always',
321
+ } : (0, exports.getVideoConstraints)(enableVideo),
322
+ screen: screenSharing,
323
+ desktop: isDesktop,
324
+ conference,
325
+ },
326
+ enableVideo,
327
+ conference,
328
+ offerOptions: {
329
+ OfferToReceiveAudio: true,
330
+ OfferToReceiveVideo: enableVideo,
331
+ mandatory: {
332
+ OfferToReceiveAudio: true,
333
+ OfferToReceiveVideo: enableVideo,
334
+ },
335
+ },
336
+ };
337
+ };
338
+ exports.getMediaConfiguration = getMediaConfiguration;
339
+ const getStreamFromConstraints = (constraints_1, ...args_1) => __awaiter(void 0, [constraints_1, ...args_1], void 0, function* (constraints, conference = false) {
340
+ const video = constraints && constraints.video;
341
+ const { constraints: newConstraints } = (0, exports.getMediaConfiguration)(video, conference, constraints);
342
+ let newStream = null;
343
+ try {
344
+ newStream = yield (0, WazoSessionDescriptionHandler_1.wazoMediaStreamFactory)(newConstraints);
345
+ }
346
+ catch (e) { // Nothing to do when the user cancel the screensharing prompt
347
+ }
348
+ if (!newStream) {
349
+ return null;
350
+ }
351
+ newStream.local = true;
352
+ return newStream;
353
+ });
354
+ exports.getStreamFromConstraints = getStreamFromConstraints;
355
+ const createUaOptions = (config, uaOptionsOverrides = {}, audio = false, video = false) => {
356
+ let { host } = config;
357
+ let port = config.port || 443;
358
+ if (config.websocketSip) {
359
+ const webSocketSip = config.websocketSip.split(':');
360
+ [host] = webSocketSip;
361
+ port = Number(webSocketSip[1]);
362
+ }
363
+ const { userUuid } = config;
364
+ const uaOptions = {
365
+ noAnswerTimeout: NO_ANSWER_TIMEOUT,
366
+ authorizationUsername: config.authorizationUser,
367
+ authorizationPassword: config.password,
368
+ displayName: config.displayName,
369
+ hackIpInContact: true,
370
+ contactParams: {
371
+ transport: 'wss',
372
+ },
373
+ logBuiltinEnabled: config.log ? config.log.builtinEnabled : null,
374
+ logLevel: config.log ? config.log.logLevel : null,
375
+ logConnector: config.log ? config.log.connector : null,
376
+ uri: (0, sdp_1.makeURI)(config.authorizationUser || '', config.host),
377
+ userAgentString: config.userAgentString || 'wazo-sdk',
378
+ reconnectionAttempts: 10000,
379
+ reconnectionDelay: 5,
380
+ // @ts-ignore
381
+ sessionDescriptionHandlerFactory: (sipCall, options = {}) => {
382
+ const uaLogger = sipCall.userAgent.getLogger('sip.WazoSessionDescriptionHandler');
383
+ const iceGatheringTimeout = 'peerConnectionOptions' in options ? options.peerConnectionOptions.iceGatheringTimeout || DEFAULT_ICE_TIMEOUT : DEFAULT_ICE_TIMEOUT;
384
+ const sdhOptions = Object.assign(Object.assign({}, options), { iceGatheringTimeout, peerConnectionConfiguration: Object.assign(Object.assign({}, (0, peer_connection_configuration_default_1.defaultPeerConnectionConfiguration)()), (options.peerConnectionConfiguration || {})) });
385
+ return new WazoSessionDescriptionHandler_1.default(uaLogger, WazoSessionDescriptionHandler_1.wazoMediaStreamFactory, sdhOptions, (0, exports.isWeb)(), sipCall);
386
+ },
387
+ transportOptions: {
388
+ traceSip: (uaOptionsOverrides === null || uaOptionsOverrides === void 0 ? void 0 : uaOptionsOverrides.traceSip) || false,
389
+ wsServers: `wss://${host}:${port}/api/asterisk/ws${userUuid ? `?userUuid=${userUuid}` : ''}`,
390
+ },
391
+ sessionDescriptionHandlerFactoryOptions: {
392
+ modifiers: [sdp_1.replaceLocalIpModifier],
393
+ alwaysAcquireMediaFirst: (0, exports.isFirefox)(),
394
+ constraints: {
395
+ audio: (0, exports.getAudioConstraints)(audio),
396
+ video: (0, exports.getVideoConstraints)(video),
397
+ },
398
+ peerConnectionOptions: {
399
+ iceCheckingTimeout: config.iceCheckingTimeout || 1000,
400
+ iceGatheringTimeout: config.iceCheckingTimeout || 1000,
401
+ rtcConfiguration: Object.assign(Object.assign({ rtcpMuxPolicy: 'require', iceServers: (0, exports.getIceServers)(config.host) }, getRtcOptions()), ((uaOptionsOverrides === null || uaOptionsOverrides === void 0 ? void 0 : uaOptionsOverrides.peerConnectionOptions) || {})),
402
+ },
403
+ // Configuration used in SDH to create the PeerConnection
404
+ peerConnectionConfiguration: Object.assign({ rtcpMuxPolicy: 'require', iceServers: (0, exports.getIceServers)(config.host) }, ((uaOptionsOverrides === null || uaOptionsOverrides === void 0 ? void 0 : uaOptionsOverrides.peerConnectionOptions) || {})),
405
+ },
406
+ };
407
+ uaOptionsOverrides === null || uaOptionsOverrides === void 0 ? true : delete uaOptionsOverrides.traceSip;
408
+ return Object.assign(Object.assign({}, uaOptions), uaOptionsOverrides);
409
+ };
410
+ exports.createUaOptions = createUaOptions;
411
+ const downgradeToAudio = (sipCall) => {
412
+ // Release local video stream when downgrading to audio
413
+ const sessionDescriptionHandler = sipCall.sessionDescriptionHandler;
414
+ const localStream = sessionDescriptionHandler === null || sessionDescriptionHandler === void 0 ? void 0 : sessionDescriptionHandler.localMediaStream;
415
+ const pc = sessionDescriptionHandler === null || sessionDescriptionHandler === void 0 ? void 0 : sessionDescriptionHandler.peerConnection;
416
+ const videoTracks = localStream.getVideoTracks();
417
+ // Remove video senders
418
+ if (pc === null || pc === void 0 ? void 0 : pc.getSenders) {
419
+ pc.getSenders().filter((sender) => sender.track && sender.track.kind === 'video').forEach((videoSender) => {
420
+ videoSender.replaceTrack(null);
421
+ });
422
+ }
423
+ videoTracks.forEach((videoTrack) => {
424
+ videoTrack.enabled = false;
425
+ videoTrack.stop();
426
+ localStream.removeTrack(videoTrack);
427
+ });
428
+ };
429
+ exports.downgradeToAudio = downgradeToAudio;
430
+ // Returns true if a re-INVITE is required
431
+ const upgradeToVideo = (sipCall, constraints, isConference) => __awaiter(void 0, void 0, void 0, function* () {
432
+ var _a;
433
+ const pc = (0, exports.getPeerConnection)(sipCall);
434
+ // Check if a video sender already exists
435
+ let videoSender;
436
+ if (isConference) {
437
+ // We search for the last transceiver without `video-` in the mid (video- means remote transceiver)
438
+ const transceivers = (pc === null || pc === void 0 ? void 0 : pc.getTransceivers()) || [];
439
+ const transceiverIdx = (0, array_1.lastIndexOf)(transceivers, transceiver => transceiver.sender.track === null && transceiver.mid && transceiver.mid.indexOf('video') === -1);
440
+ videoSender = transceiverIdx !== -1 ? transceivers[transceiverIdx].sender : null;
441
+ }
442
+ else {
443
+ videoSender = pc && pc.getSenders && pc.getSenders().find((sender) => sender.track === null);
444
+ }
445
+ if (!videoSender) {
446
+ // When no video sender found, it means that we're in the first video upgrade in 1:1
447
+ return;
448
+ }
449
+ // Reuse bidirectional video stream
450
+ const newStream = yield (0, exports.getStreamFromConstraints)(constraints);
451
+ if (!newStream) {
452
+ console.warn(`Can't create media stream with: ${JSON.stringify(constraints || {})}`);
453
+ return;
454
+ }
455
+ // Add previous local audio track
456
+ if (constraints && !constraints.audio) {
457
+ const localVideoStream = (_a = sipCall.sessionDescriptionHandler) === null || _a === void 0 ? void 0 : _a.localMediaStream;
458
+ const localAudioTrack = localVideoStream.getTracks().find(track => track.kind === 'audio');
459
+ if (localAudioTrack) {
460
+ newStream.addTrack(localAudioTrack);
461
+ }
462
+ }
463
+ const videoTrack = newStream.getVideoTracks()[0];
464
+ if (videoTrack) {
465
+ videoSender.replaceTrack(videoTrack);
466
+ }
467
+ (0, exports.setLocalMediaStream)(sipCall, newStream);
468
+ return newStream;
469
+ });
470
+ exports.upgradeToVideo = upgradeToVideo;
471
+ const fetchNetworkStats = (sipCall, lastNetworkStats) => __awaiter(void 0, void 0, void 0, function* () {
472
+ const stats = sipCall ? yield (0, exports.getWebRtcStats)(sipCall) : null;
473
+ if (!stats) {
474
+ return Promise.resolve(null);
475
+ }
476
+ const networkStats = {
477
+ audioBytesSent: 0,
478
+ videoBytesSent: 0,
479
+ videoBytesReceived: 0,
480
+ audioBytesReceived: 0,
481
+ };
482
+ const lastAudioSent = lastNetworkStats ? lastNetworkStats.audioBytesSent : 0;
483
+ const lastAudioContentSent = lastNetworkStats ? lastNetworkStats.audioContentSent : 0;
484
+ const lastVideoSent = lastNetworkStats ? lastNetworkStats.videoBytesSent : 0;
485
+ const lastAudioReceived = lastNetworkStats ? lastNetworkStats.audioBytesReceived : 0;
486
+ const lastAudioContentReceived = lastNetworkStats ? lastNetworkStats.audioContentReceived : 0;
487
+ const lastVideoReceived = lastNetworkStats ? lastNetworkStats.videoBytesReceived : 0;
488
+ const lastTransportSent = lastNetworkStats ? lastNetworkStats.transportSent : 0;
489
+ const lastTransportReceived = lastNetworkStats ? lastNetworkStats.transportReceived : 0;
490
+ let audioBytesSent = 0; // content + header
491
+ let audioBytesReceived = 0; // content + header
492
+ let audioContentSent = 0; // useful to detect blank call
493
+ let audioContentReceived = 0;
494
+ let videoBytesSent = 0;
495
+ let videoBytesReceived = 0;
496
+ let transportSent = 0;
497
+ let transportReceived = 0;
498
+ let packetsLost = 0;
499
+ stats.forEach(report => {
500
+ if (report.type === 'outbound-rtp' && report.kind === 'audio') {
501
+ audioBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
502
+ audioContentSent += Number(report.bytesSent);
503
+ }
504
+ if (report.type === 'remote-outbound-rtp' && report.kind === 'audio') {
505
+ audioBytesSent += Number(report.bytesSent);
506
+ }
507
+ if (report.type === 'outbound-rtp' && report.kind === 'video') {
508
+ videoBytesSent += Number(report.bytesSent) + Number(report.headerBytesSent);
509
+ }
510
+ if (report.type === 'inbound-rtp' && report.kind === 'audio') {
511
+ packetsLost += Number(report.packetsLost);
512
+ networkStats.packetsReceived = Number(report.packetsReceived);
513
+ audioBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
514
+ audioContentReceived += Number(report.bytesReceived);
515
+ }
516
+ if (report.type === 'inbound-rtp' && report.kind === 'video') {
517
+ videoBytesReceived += Number(report.bytesReceived) + Number(report.headerBytesReceived);
518
+ }
519
+ if (report.type === 'outbound-rtp' && report.kind === 'video') {
520
+ if ('framesPerSecond' in report) {
521
+ networkStats.framesPerSecond = Number(report.framesPerSecond);
522
+ }
523
+ if ('framerateMean' in report) {
524
+ // framerateMean is only available in FF
525
+ networkStats.framesPerSecond = Math.round(report.framerateMean);
526
+ }
527
+ }
528
+ if (report.type === 'remote-inbound-rtp' && report.kind === 'audio') {
529
+ packetsLost += Number(report.packetsLost);
530
+ networkStats.roundTripTime = Number(report.roundTripTime);
531
+ networkStats.jitter = Number(report.jitter);
532
+ }
533
+ if (report.type === 'remote-inbound-rtp' && report.kind === 'video') {
534
+ packetsLost += Number(report.packetsLost);
535
+ }
536
+ if (report.type === 'transport') {
537
+ transportSent += Number(report.bytesSent);
538
+ transportReceived += Number(report.bytesReceived);
539
+ }
540
+ });
541
+ networkStats.packetsLost = packetsLost;
542
+ networkStats.totalAudioBytesSent = audioBytesSent;
543
+ networkStats.audioBytesSent = audioBytesSent - lastAudioSent;
544
+ networkStats.totalAudioBytesReceived = audioBytesReceived - lastAudioReceived;
545
+ networkStats.audioBytesReceived = audioBytesReceived;
546
+ networkStats.totalAudioContentSent = audioContentSent;
547
+ networkStats.audioContentSent = audioContentSent - lastAudioContentSent;
548
+ networkStats.totalAudioBytesReceived = audioContentReceived - lastAudioContentReceived;
549
+ networkStats.audioContentReceived = audioContentReceived;
550
+ networkStats.totalVideoBytesSent = videoBytesSent;
551
+ networkStats.videoBytesSent = videoBytesSent - lastVideoSent;
552
+ networkStats.totalVideoBytesReceived = videoBytesReceived;
553
+ networkStats.videoBytesReceived = videoBytesReceived - lastVideoReceived;
554
+ networkStats.totalTransportSent = transportSent - lastTransportSent;
555
+ networkStats.transportSent = transportSent;
556
+ networkStats.totalTransportReceived = transportReceived;
557
+ networkStats.transportReceived = transportReceived - lastTransportReceived;
558
+ networkStats.bandwidth = networkStats.audioBytesSent + networkStats.audioBytesReceived + networkStats.videoBytesSent + networkStats.videoBytesReceived + networkStats.transportReceived + networkStats.transportSent;
559
+ return networkStats;
560
+ });
561
+ exports.fetchNetworkStats = fetchNetworkStats;