@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.
- package/dist/wazo-sdk.js +2 -2
- package/esm/__tests__/__voice__/call.test.js +72 -0
- package/esm/__tests__/__voice__/softphone.test.js +66 -0
- package/esm/checker/checks/webrtc.js +2 -1
- package/esm/domain/InvalidState.js +8 -0
- package/esm/domain/InvalidStateTransition.js +10 -0
- package/esm/domain/Phone/WebRTCPhone.js +33 -35
- package/esm/index.js +2 -0
- package/esm/simple/Phone.js +2 -1
- package/esm/simple/index.js +8 -2
- package/esm/simple/room/Room.js +2 -1
- package/esm/simple/room/SipRoom.js +2 -1
- package/esm/state-machine/call-state-machine.js +114 -0
- package/esm/state-machine/softphone-state-machine.js +39 -0
- package/esm/state-machine/utils.js +40 -0
- package/esm/types/__tests__/__voice__/call.test.d.ts +2 -0
- package/esm/types/__tests__/__voice__/call.test.d.ts.map +1 -0
- package/esm/types/__tests__/__voice__/softphone.test.d.ts +2 -0
- package/esm/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
- package/esm/types/checker/checks/webrtc.d.ts.map +1 -1
- package/esm/types/domain/InvalidState.d.ts +6 -0
- package/esm/types/domain/InvalidState.d.ts.map +1 -0
- package/esm/types/domain/InvalidStateTransition.d.ts +7 -0
- package/esm/types/domain/InvalidStateTransition.d.ts.map +1 -0
- package/esm/types/domain/Phone/WebRTCPhone.d.ts +0 -1
- package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/esm/types/domain/types.d.ts +4 -0
- package/esm/types/domain/types.d.ts.map +1 -1
- package/esm/types/index.d.ts +2 -0
- package/esm/types/index.d.ts.map +1 -1
- package/esm/types/simple/Phone.d.ts.map +1 -1
- package/esm/types/simple/index.d.ts +7 -2
- package/esm/types/simple/index.d.ts.map +1 -1
- package/esm/types/simple/room/Room.d.ts.map +1 -1
- package/esm/types/simple/room/SipRoom.d.ts +1 -1
- package/esm/types/simple/room/SipRoom.d.ts.map +1 -1
- package/esm/types/state-machine/call-state-machine.d.ts +43 -0
- package/esm/types/state-machine/call-state-machine.d.ts.map +1 -0
- package/esm/types/state-machine/softphone-state-machine.d.ts +19 -0
- package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -0
- package/esm/types/state-machine/utils.d.ts +9 -0
- package/esm/types/state-machine/utils.d.ts.map +1 -0
- package/esm/types/utils/sdp.d.ts +6 -0
- package/esm/types/utils/sdp.d.ts.map +1 -1
- package/esm/types/utils/sip-logger.d.ts +4 -0
- package/esm/types/utils/sip-logger.d.ts.map +1 -0
- package/esm/types/utils/webrtc.d.ts +40 -0
- package/esm/types/utils/webrtc.d.ts.map +1 -1
- package/esm/types/voice/call.d.ts +110 -0
- package/esm/types/voice/call.d.ts.map +1 -0
- package/esm/types/voice/softphone.d.ts +77 -0
- package/esm/types/voice/softphone.d.ts.map +1 -0
- package/esm/types/web-rtc-client.d.ts +1 -36
- package/esm/types/web-rtc-client.d.ts.map +1 -1
- package/esm/utils/sdp.js +26 -0
- package/esm/utils/sip-logger.js +32 -0
- package/esm/utils/webrtc.js +465 -0
- package/esm/voice/call.js +527 -0
- package/esm/voice/softphone.js +347 -0
- package/esm/web-rtc-client.js +86 -584
- package/lib/__tests__/__voice__/call.test.js +86 -0
- package/lib/__tests__/__voice__/softphone.test.js +80 -0
- package/lib/checker/checks/webrtc.js +2 -1
- package/lib/domain/InvalidState.js +9 -0
- package/lib/domain/InvalidStateTransition.js +10 -0
- package/lib/domain/Phone/WebRTCPhone.js +33 -35
- package/lib/index.js +6 -2
- package/lib/simple/Phone.js +2 -1
- package/lib/simple/index.js +6 -0
- package/lib/simple/room/Room.js +4 -3
- package/lib/simple/room/SipRoom.js +2 -1
- package/lib/state-machine/call-state-machine.js +120 -0
- package/lib/state-machine/softphone-state-machine.js +42 -0
- package/lib/state-machine/utils.js +52 -0
- package/lib/types/__tests__/__voice__/call.test.d.ts +2 -0
- package/lib/types/__tests__/__voice__/call.test.d.ts.map +1 -0
- package/lib/types/__tests__/__voice__/softphone.test.d.ts +2 -0
- package/lib/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
- package/lib/types/checker/checks/webrtc.d.ts.map +1 -1
- package/lib/types/domain/InvalidState.d.ts +6 -0
- package/lib/types/domain/InvalidState.d.ts.map +1 -0
- package/lib/types/domain/InvalidStateTransition.d.ts +7 -0
- package/lib/types/domain/InvalidStateTransition.d.ts.map +1 -0
- package/lib/types/domain/Phone/WebRTCPhone.d.ts +0 -1
- package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
- package/lib/types/domain/types.d.ts +4 -0
- package/lib/types/domain/types.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/simple/Phone.d.ts.map +1 -1
- package/lib/types/simple/index.d.ts +7 -2
- package/lib/types/simple/index.d.ts.map +1 -1
- package/lib/types/simple/room/Room.d.ts.map +1 -1
- package/lib/types/simple/room/SipRoom.d.ts +1 -1
- package/lib/types/simple/room/SipRoom.d.ts.map +1 -1
- package/lib/types/state-machine/call-state-machine.d.ts +43 -0
- package/lib/types/state-machine/call-state-machine.d.ts.map +1 -0
- package/lib/types/state-machine/softphone-state-machine.d.ts +19 -0
- package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -0
- package/lib/types/state-machine/utils.d.ts +9 -0
- package/lib/types/state-machine/utils.d.ts.map +1 -0
- package/lib/types/utils/sdp.d.ts +6 -0
- package/lib/types/utils/sdp.d.ts.map +1 -1
- package/lib/types/utils/sip-logger.d.ts +4 -0
- package/lib/types/utils/sip-logger.d.ts.map +1 -0
- package/lib/types/utils/webrtc.d.ts +40 -0
- package/lib/types/utils/webrtc.d.ts.map +1 -1
- package/lib/types/voice/call.d.ts +110 -0
- package/lib/types/voice/call.d.ts.map +1 -0
- package/lib/types/voice/softphone.d.ts +77 -0
- package/lib/types/voice/softphone.d.ts.map +1 -0
- package/lib/types/web-rtc-client.d.ts +1 -36
- package/lib/types/web-rtc-client.d.ts.map +1 -1
- package/lib/utils/sdp.js +27 -1
- package/lib/utils/sip-logger.js +37 -0
- package/lib/utils/webrtc.js +521 -1
- package/lib/voice/call.js +577 -0
- package/lib/voice/softphone.js +375 -0
- package/lib/web-rtc-client.js +89 -601
- package/package.json +3 -2
- package/src/__tests__/__voice__/call.test.ts +90 -0
- package/src/__tests__/__voice__/softphone.test.ts +82 -0
- package/src/checker/checks/webrtc.ts +2 -1
- package/src/domain/InvalidState.ts +13 -0
- package/src/domain/InvalidStateTransition.ts +17 -0
- package/src/domain/Phone/WebRTCPhone.ts +34 -36
- package/src/domain/types.ts +6 -1
- package/src/index.ts +2 -0
- package/src/simple/Phone.ts +2 -1
- package/src/simple/index.ts +9 -2
- package/src/simple/room/Room.ts +2 -1
- package/src/simple/room/SipRoom.ts +2 -1
- package/src/state-machine/call-state-machine.ts +131 -0
- package/src/state-machine/softphone-state-machine.ts +49 -0
- package/src/state-machine/utils.ts +53 -0
- package/src/utils/sdp.ts +36 -0
- package/src/utils/sip-logger.ts +40 -0
- package/src/utils/webrtc.ts +571 -0
- package/src/voice/call.ts +713 -0
- package/src/voice/softphone.ts +485 -0
- package/src/web-rtc-client.ts +90 -690
package/lib/utils/webrtc.js
CHANGED
|
@@ -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;
|