@wazo/sdk 0.43.6 → 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/wazo-sdk.js +3 -3
- package/esm/__tests__/__voice__/call.test.js +72 -0
- package/esm/__tests__/__voice__/softphone.test.js +66 -0
- package/esm/__tests__/api-client.test.js +1 -20
- package/esm/api/auth.js +2 -13
- 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/simple/Auth.js +12 -6
- package/esm/simple/Phone.js +2 -1
- package/esm/simple/__tests__/Auth.test.js +64 -8
- 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/api/auth.d.ts.map +1 -1
- 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/simple/Auth.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/api-requester.d.ts.map +1 -1
- 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/__tests__/api-requester.test.js +0 -17
- package/esm/utils/api-requester.js +1 -9
- 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/__tests__/api-client.test.js +1 -20
- package/lib/api/auth.js +2 -13
- 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/simple/Auth.js +12 -6
- package/lib/simple/Phone.js +2 -1
- package/lib/simple/__tests__/Auth.test.js +64 -11
- 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/api/auth.d.ts.map +1 -1
- 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/simple/Auth.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/api-requester.d.ts.map +1 -1
- 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/__tests__/api-requester.test.js +0 -17
- package/lib/utils/api-requester.js +1 -9
- 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/__tests__/api-client.test.ts +1 -22
- package/src/api/auth.ts +3 -25
- 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/simple/Auth.ts +13 -6
- package/src/simple/Phone.ts +2 -1
- package/src/simple/__tests__/Auth.test.ts +87 -9
- 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/__tests__/api-requester.test.ts +0 -20
- package/src/utils/api-requester.ts +1 -9
- 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
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { createActor } from 'xstate';
|
|
4
|
+
import InvalidState from '../domain/InvalidState';
|
|
5
|
+
import Wazo from '..';
|
|
6
|
+
import softphoneStateMachine, { Actions, States } from '../state-machine/softphone-state-machine';
|
|
7
|
+
import { Actions as CallActions } from '../state-machine/call-state-machine';
|
|
8
|
+
import WazoWebRTCClient from '../web-rtc-client';
|
|
9
|
+
import IssueReporter from '../service/IssueReporter';
|
|
10
|
+
import configureLogger from '../utils/sip-logger';
|
|
11
|
+
import Call from './call';
|
|
12
|
+
import { assertCan, can, getState, waitUntilState } from '../state-machine/utils';
|
|
13
|
+
export const EVENT_INCOMING = 'incoming';
|
|
14
|
+
export const EVENT_HEARTBEAT = 'heartbeat';
|
|
15
|
+
export const EVENT_HEARTBEAT_TIMEOUT = 'heartbeatTimeout';
|
|
16
|
+
export const EVENT_REGISTERED = 'registered';
|
|
17
|
+
export const EVENT_UNREGISTERED = 'unregistered';
|
|
18
|
+
export const EVENT_DISCONNECTED = 'disconnected';
|
|
19
|
+
export const EVENT_VIDEO_INPUT_CHANGE = 'videoInputChange';
|
|
20
|
+
export const EVENT_ON_MESSAGE = 'message';
|
|
21
|
+
export const EVENT_ON_CHAT = 'phone/ON_CHAT';
|
|
22
|
+
export const EVENT_ON_SIGNAL = 'phone/ON_SIGNAL';
|
|
23
|
+
export const MESSAGE_TYPE_CHAT = 'message/TYPE_CHAT';
|
|
24
|
+
export const MESSAGE_TYPE_SIGNAL = 'message/TYPE_SIGNAL';
|
|
25
|
+
const logger = IssueReporter.loggerFor('softphone');
|
|
26
|
+
export class Softphone extends EventEmitter {
|
|
27
|
+
audioOutputDeviceId;
|
|
28
|
+
audioRingDeviceId;
|
|
29
|
+
audioOutputVolume;
|
|
30
|
+
audioRingVolume;
|
|
31
|
+
eventEmitter;
|
|
32
|
+
calls;
|
|
33
|
+
client;
|
|
34
|
+
session;
|
|
35
|
+
sipLine;
|
|
36
|
+
softphoneActor;
|
|
37
|
+
options;
|
|
38
|
+
shouldSendReinvite;
|
|
39
|
+
constructor() {
|
|
40
|
+
super();
|
|
41
|
+
this.calls = [];
|
|
42
|
+
this.softphoneActor = createActor(softphoneStateMachine);
|
|
43
|
+
this.softphoneActor.start();
|
|
44
|
+
}
|
|
45
|
+
connect(options = {}) {
|
|
46
|
+
this.calls = [];
|
|
47
|
+
this.options = options;
|
|
48
|
+
this.audioOutputVolume = 1;
|
|
49
|
+
this.audioRingVolume = 1;
|
|
50
|
+
const server = Wazo.Auth.getHost();
|
|
51
|
+
const session = Wazo.Auth.getSession();
|
|
52
|
+
if (!server || !session) {
|
|
53
|
+
throw new Error('Please connect to the server using `Wazo.Auth.logIn` or `Wazo.Auth.authenticate` before using Room.connect().');
|
|
54
|
+
}
|
|
55
|
+
this.session = session;
|
|
56
|
+
this.sipLine = this.getPrimaryWebRtcLine();
|
|
57
|
+
if (!this.sipLine) {
|
|
58
|
+
throw new Error('Sorry, no sip lines found for this user');
|
|
59
|
+
}
|
|
60
|
+
this.options.userUuid = session.uuid || '';
|
|
61
|
+
return this.connectWithCredentials(server, this.sipLine, session.displayName(), options);
|
|
62
|
+
}
|
|
63
|
+
async disconnect() {
|
|
64
|
+
assertCan(this.softphoneActor, Actions.UNREGISTER);
|
|
65
|
+
if (!this.client) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Terminate all calls
|
|
69
|
+
await Promise.all(this.calls.map(call => call.hangup()));
|
|
70
|
+
return this.client.unregister();
|
|
71
|
+
}
|
|
72
|
+
getPrimaryWebRtcLine() {
|
|
73
|
+
return this.session?.primaryWebRtcLine() || null;
|
|
74
|
+
}
|
|
75
|
+
async connectWithCredentials(server, sipLine, displayName, rawOptions = {}) {
|
|
76
|
+
assertCan(this.softphoneActor, Actions.REGISTER);
|
|
77
|
+
this.softphoneActor.send({ type: Actions.REGISTER });
|
|
78
|
+
const [host, port = 443] = server.split(':');
|
|
79
|
+
let options = { ...rawOptions };
|
|
80
|
+
options.media = options.media || {
|
|
81
|
+
audio: true,
|
|
82
|
+
video: false,
|
|
83
|
+
};
|
|
84
|
+
options.uaConfigOverrides = options.uaConfigOverrides || {};
|
|
85
|
+
if (IssueReporter.enabled) {
|
|
86
|
+
options = configureLogger(options);
|
|
87
|
+
}
|
|
88
|
+
this.client = new WazoWebRTCClient({
|
|
89
|
+
host,
|
|
90
|
+
port: typeof port === 'string' ? parseInt(port, 10) : port,
|
|
91
|
+
displayName,
|
|
92
|
+
authorizationUser: sipLine.username,
|
|
93
|
+
password: sipLine.secret,
|
|
94
|
+
uri: `${sipLine.username}@${server}`,
|
|
95
|
+
...options,
|
|
96
|
+
}, undefined, options.uaConfigOverrides);
|
|
97
|
+
this._bindEvents();
|
|
98
|
+
return this.client.register().catch(error => {
|
|
99
|
+
// Avoid exception on `t.server.scheme` in sip transport when losing the webrtc socket connection
|
|
100
|
+
logger.error('softphone - register error', { message: error.message, stack: error.stack });
|
|
101
|
+
this._sendAction(Actions.UNREGISTER);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
reconnect() {
|
|
105
|
+
logger.info('softphone - reconnect', { client: !!this.client });
|
|
106
|
+
if (!this.client) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this.client.attemptReconnection();
|
|
110
|
+
}
|
|
111
|
+
async call(options) {
|
|
112
|
+
logger.info('softphone - call', options);
|
|
113
|
+
if (!options.params.To) {
|
|
114
|
+
logger.warn('softphone - calling with empty number, bailing.');
|
|
115
|
+
return Promise.resolve(null);
|
|
116
|
+
}
|
|
117
|
+
if (this.getState() !== States.UNREGISTERED) {
|
|
118
|
+
throw new InvalidState(`Invalid state ${States.UNREGISTERED}`, States.UNREGISTERED);
|
|
119
|
+
}
|
|
120
|
+
if (this.getState() !== States.REGISTERING) {
|
|
121
|
+
logger.info('softphone - calling when registering, waiting for Softphone to be registered ...');
|
|
122
|
+
await waitUntilState(this.softphoneActor, States.REGISTERED);
|
|
123
|
+
}
|
|
124
|
+
// Hold other calls
|
|
125
|
+
this.holdAllCalls();
|
|
126
|
+
let sipCall;
|
|
127
|
+
try {
|
|
128
|
+
sipCall = this.client.call(options.params.To, options.withCamera, options.audioOnly, options.conference, options);
|
|
129
|
+
const call = new Call(sipCall, this);
|
|
130
|
+
return call;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
logger.warn('softphone - call, error', { message: error.message, stack: error.stack });
|
|
134
|
+
return Promise.resolve(null);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
removeCall(callToRemove) {
|
|
138
|
+
this.calls = this.calls.filter((call) => call.id !== callToRemove.id);
|
|
139
|
+
}
|
|
140
|
+
holdAllCalls(exceptCall = null) {
|
|
141
|
+
this.calls.filter(call => !call.isHeld() && call.id === exceptCall?.id).forEach(call => call.hold());
|
|
142
|
+
}
|
|
143
|
+
startHeartbeat() {
|
|
144
|
+
logger.info('softphone - start heartbeat', { client: !!this.client, hasHeartbeat: this.client.hasHeartbeat() });
|
|
145
|
+
if (!this.client || this.client.hasHeartbeat()) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.client.startHeartbeat();
|
|
149
|
+
}
|
|
150
|
+
stopHeartbeat() {
|
|
151
|
+
logger.info('softphone - stop heartbeat', { client: !!this.client });
|
|
152
|
+
if (!this.client) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.client.stopHeartbeat();
|
|
156
|
+
}
|
|
157
|
+
setMediaConstraints(media) {
|
|
158
|
+
this.client.setMediaConstraints(media);
|
|
159
|
+
}
|
|
160
|
+
changeAudioOutputDevice(id) {
|
|
161
|
+
logger.info('softphone - change audio device', { deviceId: id });
|
|
162
|
+
this.audioOutputDeviceId = id;
|
|
163
|
+
return this.client.changeAudioOutputDevice(id);
|
|
164
|
+
}
|
|
165
|
+
changeAudioInputDevice(id, force) {
|
|
166
|
+
logger.info('softphone - changeAudio input device', { deviceId: id });
|
|
167
|
+
const currentCall = this.getCurrentCall();
|
|
168
|
+
return this.client.changeAudioInputDevice(id, currentCall?.sipCall, force);
|
|
169
|
+
}
|
|
170
|
+
changeVideoInputDevice(id) {
|
|
171
|
+
logger.info('softphone - change video input device', { deviceId: id });
|
|
172
|
+
const currentCall = this.getCurrentCall();
|
|
173
|
+
return this.client.changeVideoInputDevice(id, currentCall?.sipCall);
|
|
174
|
+
}
|
|
175
|
+
changeRingDevice(id) {
|
|
176
|
+
logger.info('softphone - changing ring device', { id });
|
|
177
|
+
this.audioRingDeviceId = id;
|
|
178
|
+
}
|
|
179
|
+
hasSfu() {
|
|
180
|
+
return this.sipLine?.hasVideoConference() || false;
|
|
181
|
+
}
|
|
182
|
+
// volume is a value between 0 and 1
|
|
183
|
+
changeAudioVolume(volume) {
|
|
184
|
+
logger.info('softphone - changing audio volume', { volume });
|
|
185
|
+
this.audioOutputVolume = volume;
|
|
186
|
+
this.client.changeAudioOutputVolume(volume);
|
|
187
|
+
}
|
|
188
|
+
// volume is a value between 0 and 1
|
|
189
|
+
changeRingVolume(volume) {
|
|
190
|
+
logger.info('softphone - changing ring volume', { volume });
|
|
191
|
+
this.audioRingVolume = volume;
|
|
192
|
+
}
|
|
193
|
+
getCalls() {
|
|
194
|
+
return this.calls;
|
|
195
|
+
}
|
|
196
|
+
getCurrentCall() {
|
|
197
|
+
return this.calls.find(call => call.isEstablished());
|
|
198
|
+
}
|
|
199
|
+
getState() {
|
|
200
|
+
return this.softphoneActor.getSnapshot().value;
|
|
201
|
+
}
|
|
202
|
+
getUserAgent() {
|
|
203
|
+
return this.client?.config?.userAgentString || 'softphone';
|
|
204
|
+
}
|
|
205
|
+
_bindEvents() {
|
|
206
|
+
this.client.on(this.client.INVITE, (sipSession) => {
|
|
207
|
+
const call = new Call(sipSession, this);
|
|
208
|
+
call._sendAction(CallActions.INCOMING_CALL);
|
|
209
|
+
this.calls.push(call);
|
|
210
|
+
this.eventEmitter.emit(EVENT_INCOMING, call);
|
|
211
|
+
});
|
|
212
|
+
this.client.on(this.client.ON_REINVITE, (sipCall, request, updatedCalleeName, updatedNumber, hadRemoteVideo) => {
|
|
213
|
+
logger.info('softphone - on reinvite', {
|
|
214
|
+
callId: sipCall.id,
|
|
215
|
+
inviteId: request.callId,
|
|
216
|
+
updatedCalleeName,
|
|
217
|
+
updatedNumber,
|
|
218
|
+
hadRemoteVideo,
|
|
219
|
+
});
|
|
220
|
+
this._onCallEvent(sipCall, 'onReinvite', request, updatedCalleeName, updatedNumber, hadRemoteVideo);
|
|
221
|
+
});
|
|
222
|
+
this.client.on(this.client.ACCEPTED, async (sipCall) => {
|
|
223
|
+
this._onCallEvent(sipCall, 'onAccepted');
|
|
224
|
+
if (this.audioOutputDeviceId) {
|
|
225
|
+
await this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
this.client.on(this.client.ON_ERROR, (e, sipCall) => {
|
|
229
|
+
this._onCallEvent(sipCall, 'onError', e);
|
|
230
|
+
});
|
|
231
|
+
this.client.on(this.client.REJECTED, (sipCall) => {
|
|
232
|
+
this._onCallEvent(sipCall, 'onRejected');
|
|
233
|
+
});
|
|
234
|
+
this.client.on(this.client.ON_PROGRESS, (sipCall) => {
|
|
235
|
+
this._onCallEvent(sipCall, 'onProgress');
|
|
236
|
+
});
|
|
237
|
+
this.client.on(this.client.ON_EARLY_MEDIA, (sipCall) => {
|
|
238
|
+
this._onCallEvent(sipCall, 'onEarlyMedia');
|
|
239
|
+
});
|
|
240
|
+
this.client.on(this.client.ON_TRACK, (sipCall, event) => {
|
|
241
|
+
this._onCallEvent(sipCall, 'onTrack', event);
|
|
242
|
+
});
|
|
243
|
+
this.client.on(this.client.ON_NETWORK_STATS, (sipCall, stats, previousStats) => {
|
|
244
|
+
this._onCallEvent(sipCall, 'onNetworkStats', stats, previousStats);
|
|
245
|
+
});
|
|
246
|
+
// Used when upgrading directly in screenshare mode
|
|
247
|
+
this.client.on(this.client.ON_SCREEN_SHARING_REINVITE, (sipCall, response, desktop) => {
|
|
248
|
+
this._onCallEvent(sipCall, 'onScreenshareReinvite', response, desktop);
|
|
249
|
+
});
|
|
250
|
+
this.client.on(this.client.UNREGISTERED, () => {
|
|
251
|
+
logger.info('softphone - unregistered');
|
|
252
|
+
this.eventEmitter.emit(EVENT_UNREGISTERED);
|
|
253
|
+
this._sendAction(Actions.UNREGISTERED);
|
|
254
|
+
});
|
|
255
|
+
this.client.on(this.client.ON_DISCONNECTED, () => {
|
|
256
|
+
logger.info('softphone - disconnected');
|
|
257
|
+
this.eventEmitter.emit(EVENT_DISCONNECTED);
|
|
258
|
+
this._sendAction(Actions.TRANSPORT_CLOSED);
|
|
259
|
+
});
|
|
260
|
+
this.client.on(this.client.REGISTERED, () => {
|
|
261
|
+
logger.info('softphone - registered', { state: getState(this.softphoneActor) });
|
|
262
|
+
if (!can(this.softphoneActor, Actions.REGISTER_DONE)) {
|
|
263
|
+
logger.warn('softphone - registered but not in REGISTERING state', { state: getState(this.softphoneActor) });
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
this.stopHeartbeat();
|
|
267
|
+
this._sendAction(Actions.REGISTER_DONE);
|
|
268
|
+
this.eventEmitter.emit(EVENT_REGISTERED);
|
|
269
|
+
// If the phone registered with a current callSession (eg: when switching network):
|
|
270
|
+
// send a reinvite to renegociate ICE with new IP
|
|
271
|
+
if (this.shouldSendReinvite) {
|
|
272
|
+
this.shouldSendReinvite = false;
|
|
273
|
+
// Send reinvite for all active calls
|
|
274
|
+
this.calls.filter(call => call.isEstablished())
|
|
275
|
+
.forEach(call => { call.sendReinvite({ conference: call.isConference(), iceRestart: true }); });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
this.client.on(this.client.CONNECTED, () => {
|
|
279
|
+
logger.info('softphone - connected');
|
|
280
|
+
this.stopHeartbeat();
|
|
281
|
+
});
|
|
282
|
+
this.client.on(this.client.DISCONNECTED, () => {
|
|
283
|
+
logger.info('softphone - disconnected');
|
|
284
|
+
this.eventEmitter.emit(EVENT_DISCONNECTED);
|
|
285
|
+
// Do not trigger heartbeat if already running
|
|
286
|
+
if (!this.client.hasHeartbeat()) {
|
|
287
|
+
this.startHeartbeat();
|
|
288
|
+
}
|
|
289
|
+
// Tell to send reinvite when reconnecting
|
|
290
|
+
this.shouldSendReinvite = this.calls.length > 0;
|
|
291
|
+
});
|
|
292
|
+
this.client.on('onVideoInputChange', stream => {
|
|
293
|
+
this.eventEmitter.emit(EVENT_VIDEO_INPUT_CHANGE, stream);
|
|
294
|
+
});
|
|
295
|
+
this.client.on(this.client.MESSAGE, (message) => {
|
|
296
|
+
this._onMessage(message);
|
|
297
|
+
this.eventEmitter.emit(EVENT_ON_MESSAGE, message);
|
|
298
|
+
});
|
|
299
|
+
this.client.setOnHeartbeatTimeout(() => {
|
|
300
|
+
this.eventEmitter.emit(EVENT_HEARTBEAT_TIMEOUT);
|
|
301
|
+
});
|
|
302
|
+
this.client.setOnHeartbeatCallback(() => {
|
|
303
|
+
this.eventEmitter.emit(EVENT_HEARTBEAT);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
_sendAction(action) {
|
|
307
|
+
this.softphoneActor.send({ type: action });
|
|
308
|
+
}
|
|
309
|
+
_getCall(sipCall) {
|
|
310
|
+
return this.calls.find(call => call.id === sipCall.id);
|
|
311
|
+
}
|
|
312
|
+
_onCallEvent(sipCall, eventName, ...args) {
|
|
313
|
+
logger.warn('softphone - received call event', { eventName, sipId: sipCall.id });
|
|
314
|
+
const call = this._getCall(sipCall);
|
|
315
|
+
if (!call) {
|
|
316
|
+
logger.warn('softphone - no call found to send the event', { eventName, sipId: sipCall.id });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
// @ts-ignore
|
|
320
|
+
call[eventName](...args);
|
|
321
|
+
}
|
|
322
|
+
_onMessage(message) {
|
|
323
|
+
if (!message || message.method !== 'MESSAGE') {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
let body;
|
|
327
|
+
try {
|
|
328
|
+
body = JSON.parse(message.body || '');
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const { type, content, } = body;
|
|
334
|
+
switch (type) {
|
|
335
|
+
case MESSAGE_TYPE_CHAT:
|
|
336
|
+
this.eventEmitter.emit(EVENT_ON_CHAT, content);
|
|
337
|
+
break;
|
|
338
|
+
case MESSAGE_TYPE_SIGNAL:
|
|
339
|
+
{
|
|
340
|
+
this.eventEmitter.emit(EVENT_ON_SIGNAL, content);
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
default:
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
export default new Softphone();
|