@signalapp/ringrtc 2.23.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/index.d.ts +4 -0
- package/dist/index.js +41 -0
- package/dist/ringrtc/Service.d.ts +520 -0
- package/dist/ringrtc/Service.js +1462 -0
- package/dist/ringrtc/VideoSupport.d.ts +66 -0
- package/dist/ringrtc/VideoSupport.js +421 -0
- package/dist/test/RingRTC-test.d.ts +1 -0
- package/dist/test/RingRTC-test.js +71 -0
- package/package.json +55 -0
- package/scripts/fetch-prebuild.js +86 -0
|
@@ -0,0 +1,1462 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// Copyright 2019-2021 Signal Messenger, LLC
|
|
4
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
5
|
+
//
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.CallLogLevel = exports.CallEndedReason = exports.CallState = exports.RingCancelReason = exports.BandwidthMode = exports.HangupType = exports.OpaqueMessage = exports.HangupMessage = exports.BusyMessage = exports.IceCandidateMessage = exports.AnswerMessage = exports.OfferType = exports.OfferMessage = exports.CallingMessage = exports.GroupCall = exports.VideoRequest = exports.GroupMemberInfo = exports.RemoteDeviceState = exports.LocalDeviceState = exports.HttpMethod = exports.RingUpdate = exports.CallMessageUrgency = exports.GroupCallEndReason = exports.JoinState = exports.ConnectionState = exports.Call = exports.RingRTCType = exports.ReceivedAudioLevel = exports.NetworkRoute = exports.PeekInfo = exports.PeekDeviceInfo = void 0;
|
|
17
|
+
/* tslint:disable max-classes-per-file */
|
|
18
|
+
const os = require("os");
|
|
19
|
+
const process = require("process");
|
|
20
|
+
// tslint:disable-next-line no-var-requires no-require-imports
|
|
21
|
+
const Native = require('../../build/' +
|
|
22
|
+
os.platform() +
|
|
23
|
+
'/libringrtc-' +
|
|
24
|
+
process.arch +
|
|
25
|
+
'.node');
|
|
26
|
+
class Config {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.use_new_audio_device_module = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// tslint:disable-next-line no-unnecessary-class
|
|
32
|
+
class NativeCallManager {
|
|
33
|
+
constructor(observer) {
|
|
34
|
+
this.observer = observer;
|
|
35
|
+
this.createCallEndpoint(new Config());
|
|
36
|
+
}
|
|
37
|
+
setConfig(config) {
|
|
38
|
+
this.createCallEndpoint(config);
|
|
39
|
+
}
|
|
40
|
+
createCallEndpoint(config) {
|
|
41
|
+
const callEndpoint = Native.createCallEndpoint(this, config.use_new_audio_device_module);
|
|
42
|
+
Object.defineProperty(this, Native.callEndpointPropertyKey, {
|
|
43
|
+
value: callEndpoint,
|
|
44
|
+
configurable: true, // allows it to be changed
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Mirror methods onto NativeCallManager.
|
|
49
|
+
// This is done through direct assignment rather than wrapper methods to avoid indirection.
|
|
50
|
+
NativeCallManager.prototype.setSelfUuid = Native.cm_setSelfUuid;
|
|
51
|
+
NativeCallManager.prototype.createOutgoingCall =
|
|
52
|
+
Native.cm_createOutgoingCall;
|
|
53
|
+
NativeCallManager.prototype.proceed = Native.cm_proceed;
|
|
54
|
+
NativeCallManager.prototype.accept = Native.cm_accept;
|
|
55
|
+
NativeCallManager.prototype.ignore = Native.cm_ignore;
|
|
56
|
+
NativeCallManager.prototype.hangup = Native.cm_hangup;
|
|
57
|
+
NativeCallManager.prototype.cancelGroupRing =
|
|
58
|
+
Native.cm_cancelGroupRing;
|
|
59
|
+
NativeCallManager.prototype.signalingMessageSent =
|
|
60
|
+
Native.cm_signalingMessageSent;
|
|
61
|
+
NativeCallManager.prototype.signalingMessageSendFailed =
|
|
62
|
+
Native.cm_signalingMessageSendFailed;
|
|
63
|
+
NativeCallManager.prototype.updateBandwidthMode =
|
|
64
|
+
Native.cm_updateBandwidthMode;
|
|
65
|
+
NativeCallManager.prototype.receivedOffer = Native.cm_receivedOffer;
|
|
66
|
+
NativeCallManager.prototype.receivedAnswer = Native.cm_receivedAnswer;
|
|
67
|
+
NativeCallManager.prototype.receivedIceCandidates =
|
|
68
|
+
Native.cm_receivedIceCandidates;
|
|
69
|
+
NativeCallManager.prototype.receivedHangup = Native.cm_receivedHangup;
|
|
70
|
+
NativeCallManager.prototype.receivedBusy = Native.cm_receivedBusy;
|
|
71
|
+
NativeCallManager.prototype.receivedCallMessage =
|
|
72
|
+
Native.cm_receivedCallMessage;
|
|
73
|
+
NativeCallManager.prototype.receivedHttpResponse =
|
|
74
|
+
Native.cm_receivedHttpResponse;
|
|
75
|
+
NativeCallManager.prototype.httpRequestFailed =
|
|
76
|
+
Native.cm_httpRequestFailed;
|
|
77
|
+
NativeCallManager.prototype.setOutgoingAudioEnabled =
|
|
78
|
+
Native.cm_setOutgoingAudioEnabled;
|
|
79
|
+
NativeCallManager.prototype.setOutgoingVideoEnabled =
|
|
80
|
+
Native.cm_setOutgoingVideoEnabled;
|
|
81
|
+
NativeCallManager.prototype.setOutgoingVideoIsScreenShare =
|
|
82
|
+
Native.cm_setOutgoingVideoIsScreenShare;
|
|
83
|
+
NativeCallManager.prototype.sendVideoFrame = Native.cm_sendVideoFrame;
|
|
84
|
+
NativeCallManager.prototype.receiveVideoFrame =
|
|
85
|
+
Native.cm_receiveVideoFrame;
|
|
86
|
+
NativeCallManager.prototype.receiveGroupCallVideoFrame =
|
|
87
|
+
Native.cm_receiveGroupCallVideoFrame;
|
|
88
|
+
NativeCallManager.prototype.createGroupCallClient =
|
|
89
|
+
Native.cm_createGroupCallClient;
|
|
90
|
+
NativeCallManager.prototype.deleteGroupCallClient =
|
|
91
|
+
Native.cm_deleteGroupCallClient;
|
|
92
|
+
NativeCallManager.prototype.connect = Native.cm_connect;
|
|
93
|
+
NativeCallManager.prototype.join = Native.cm_join;
|
|
94
|
+
NativeCallManager.prototype.leave = Native.cm_leave;
|
|
95
|
+
NativeCallManager.prototype.disconnect = Native.cm_disconnect;
|
|
96
|
+
NativeCallManager.prototype.groupRing = Native.cm_groupRing;
|
|
97
|
+
NativeCallManager.prototype.setOutgoingAudioMuted =
|
|
98
|
+
Native.cm_setOutgoingAudioMuted;
|
|
99
|
+
NativeCallManager.prototype.setOutgoingVideoMuted =
|
|
100
|
+
Native.cm_setOutgoingVideoMuted;
|
|
101
|
+
NativeCallManager.prototype.setOutgoingGroupCallVideoIsScreenShare =
|
|
102
|
+
Native.cm_setOutgoingGroupCallVideoIsScreenShare;
|
|
103
|
+
NativeCallManager.prototype.setPresenting = Native.cm_setPresenting;
|
|
104
|
+
NativeCallManager.prototype.resendMediaKeys =
|
|
105
|
+
Native.cm_resendMediaKeys;
|
|
106
|
+
NativeCallManager.prototype.setBandwidthMode =
|
|
107
|
+
Native.cm_setBandwidthMode;
|
|
108
|
+
NativeCallManager.prototype.requestVideo = Native.cm_requestVideo;
|
|
109
|
+
NativeCallManager.prototype.setGroupMembers =
|
|
110
|
+
Native.cm_setGroupMembers;
|
|
111
|
+
NativeCallManager.prototype.setMembershipProof =
|
|
112
|
+
Native.cm_setMembershipProof;
|
|
113
|
+
NativeCallManager.prototype.peekGroupCall = Native.cm_peekGroupCall;
|
|
114
|
+
NativeCallManager.prototype.getAudioInputs = Native.cm_getAudioInputs;
|
|
115
|
+
NativeCallManager.prototype.setAudioInput = Native.cm_setAudioInput;
|
|
116
|
+
NativeCallManager.prototype.getAudioOutputs =
|
|
117
|
+
Native.cm_getAudioOutputs;
|
|
118
|
+
NativeCallManager.prototype.setAudioOutput = Native.cm_setAudioOutput;
|
|
119
|
+
NativeCallManager.prototype.processEvents = Native.cm_processEvents;
|
|
120
|
+
class PeekDeviceInfo {
|
|
121
|
+
constructor(demuxId, userId) {
|
|
122
|
+
this.demuxId = demuxId;
|
|
123
|
+
this.userId = userId;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.PeekDeviceInfo = PeekDeviceInfo;
|
|
127
|
+
class PeekInfo {
|
|
128
|
+
constructor() {
|
|
129
|
+
this.devices = [];
|
|
130
|
+
this.deviceCount = 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.PeekInfo = PeekInfo;
|
|
134
|
+
// In sync with WebRTC's PeerConnection.AdapterType.
|
|
135
|
+
// Despite how it looks, this is not an option set.
|
|
136
|
+
// A network adapter type can only be one of the listed values.
|
|
137
|
+
// And there are a few oddities to note:
|
|
138
|
+
// - Cellular means we don't know if it's 2G, 3G, 4G, 5G, ...
|
|
139
|
+
// If we know, it will be one of those corresponding enum values.
|
|
140
|
+
// This means to know if something is cellular or not, you must
|
|
141
|
+
// check all of those values.
|
|
142
|
+
// - Default means we don't know the adapter type (like Unknown)
|
|
143
|
+
// but it's because we bound to the default IP address (0.0.0.0)
|
|
144
|
+
// so it's probably the default adapter (wifi if available, for example)
|
|
145
|
+
// This is unlikely to happen in practice.
|
|
146
|
+
var NetworkAdapterType;
|
|
147
|
+
(function (NetworkAdapterType) {
|
|
148
|
+
NetworkAdapterType[NetworkAdapterType["Unknown"] = 0] = "Unknown";
|
|
149
|
+
NetworkAdapterType[NetworkAdapterType["Ethernet"] = 1] = "Ethernet";
|
|
150
|
+
NetworkAdapterType[NetworkAdapterType["Wifi"] = 2] = "Wifi";
|
|
151
|
+
NetworkAdapterType[NetworkAdapterType["Cellular"] = 4] = "Cellular";
|
|
152
|
+
NetworkAdapterType[NetworkAdapterType["Vpn"] = 8] = "Vpn";
|
|
153
|
+
NetworkAdapterType[NetworkAdapterType["Loopback"] = 16] = "Loopback";
|
|
154
|
+
NetworkAdapterType[NetworkAdapterType["Default"] = 32] = "Default";
|
|
155
|
+
NetworkAdapterType[NetworkAdapterType["Cellular2G"] = 64] = "Cellular2G";
|
|
156
|
+
NetworkAdapterType[NetworkAdapterType["Cellular3G"] = 128] = "Cellular3G";
|
|
157
|
+
NetworkAdapterType[NetworkAdapterType["Cellular4G"] = 256] = "Cellular4G";
|
|
158
|
+
NetworkAdapterType[NetworkAdapterType["Cellular5G"] = 512] = "Cellular5G";
|
|
159
|
+
})(NetworkAdapterType || (NetworkAdapterType = {}));
|
|
160
|
+
// Information about the network route being used for sending audio/video/data
|
|
161
|
+
class NetworkRoute {
|
|
162
|
+
constructor() {
|
|
163
|
+
this.localAdapterType = NetworkAdapterType.Unknown;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
exports.NetworkRoute = NetworkRoute;
|
|
167
|
+
class ReceivedAudioLevel {
|
|
168
|
+
constructor(demuxId, level) {
|
|
169
|
+
this.demuxId = demuxId;
|
|
170
|
+
this.level = level;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.ReceivedAudioLevel = ReceivedAudioLevel;
|
|
174
|
+
function normalizeAudioLevel(raw) {
|
|
175
|
+
return raw / 32767;
|
|
176
|
+
}
|
|
177
|
+
class Requests {
|
|
178
|
+
constructor() {
|
|
179
|
+
this._resolveById = new Map();
|
|
180
|
+
this._nextId = 1;
|
|
181
|
+
}
|
|
182
|
+
add() {
|
|
183
|
+
const id = this._nextId++;
|
|
184
|
+
const promise = new Promise((resolve, _reject) => {
|
|
185
|
+
this._resolveById.set(id, resolve);
|
|
186
|
+
});
|
|
187
|
+
return [id, promise];
|
|
188
|
+
}
|
|
189
|
+
resolve(id, response) {
|
|
190
|
+
const resolve = this._resolveById.get(id);
|
|
191
|
+
if (!resolve) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
resolve(response);
|
|
195
|
+
this._resolveById.delete(id);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
class CallInfo {
|
|
200
|
+
constructor(isVideoCall, receivedAtCounter) {
|
|
201
|
+
this.isVideoCall = isVideoCall;
|
|
202
|
+
this.receivedAtCounter = receivedAtCounter;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
class RingRTCType {
|
|
206
|
+
constructor() {
|
|
207
|
+
// Set by UX
|
|
208
|
+
this.handleOutgoingSignaling = null;
|
|
209
|
+
this.handleIncomingCall = null;
|
|
210
|
+
this.handleAutoEndedIncomingCallRequest = null;
|
|
211
|
+
this.handleLogMessage = null;
|
|
212
|
+
this.handleSendHttpRequest = null;
|
|
213
|
+
this.handleSendCallMessage = null;
|
|
214
|
+
this.handleSendCallMessageToGroup = null;
|
|
215
|
+
this.handleGroupCallRingUpdate = null;
|
|
216
|
+
this.callManager = new NativeCallManager(this);
|
|
217
|
+
this._call = null;
|
|
218
|
+
this._groupCallByClientId = new Map();
|
|
219
|
+
this._peekRequests = new Requests();
|
|
220
|
+
this._callInfoByCallId = new Map();
|
|
221
|
+
}
|
|
222
|
+
getCallInfoKey(callId) {
|
|
223
|
+
// CallId is u64 so use a string key instead.
|
|
224
|
+
return callId.high.toString() + callId.low.toString();
|
|
225
|
+
}
|
|
226
|
+
setConfig(config) {
|
|
227
|
+
this.callManager.setConfig(config);
|
|
228
|
+
}
|
|
229
|
+
// Called by UX
|
|
230
|
+
setSelfUuid(uuid) {
|
|
231
|
+
this.callManager.setSelfUuid(uuid);
|
|
232
|
+
}
|
|
233
|
+
// Called by UX
|
|
234
|
+
startOutgoingCall(remoteUserId, isVideoCall, localDeviceId, settings) {
|
|
235
|
+
const callId = this.callManager.createOutgoingCall(remoteUserId, isVideoCall, localDeviceId);
|
|
236
|
+
const isIncoming = false;
|
|
237
|
+
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall, settings, CallState.Prering);
|
|
238
|
+
this._call = call;
|
|
239
|
+
// We won't actually send anything until the remote side accepts.
|
|
240
|
+
call.outgoingAudioEnabled = true;
|
|
241
|
+
call.outgoingVideoEnabled = isVideoCall;
|
|
242
|
+
return call;
|
|
243
|
+
}
|
|
244
|
+
// Called by UX
|
|
245
|
+
cancelGroupRing(groupId, ringId, reason) {
|
|
246
|
+
silly_deadlock_protection(() => {
|
|
247
|
+
this.callManager.cancelGroupRing(groupId, ringId.toString(), reason);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// Called by Rust
|
|
251
|
+
onStartOutgoingCall(remoteUserId, callId) {
|
|
252
|
+
const call = this._call;
|
|
253
|
+
if (!call || call.remoteUserId !== remoteUserId || !call.settings) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
call.callId = callId;
|
|
257
|
+
this.proceed(callId, call.settings);
|
|
258
|
+
}
|
|
259
|
+
// Called by Rust
|
|
260
|
+
onStartIncomingCall(remoteUserId, callId, isVideoCall) {
|
|
261
|
+
// Temporary: Force hangup in all glare scenarios until handled gracefully.
|
|
262
|
+
// In case of a glare loser, an incoming call will be generated right
|
|
263
|
+
// after the outgoing call is ended. In that case, ignore it once.
|
|
264
|
+
if (this._call &&
|
|
265
|
+
(this._call.endedReason === CallEndedReason.Glare ||
|
|
266
|
+
this._call.endedReason === CallEndedReason.ReCall)) {
|
|
267
|
+
this._call.endedReason = undefined;
|
|
268
|
+
// EVIL HACK: We are the "loser" of a glare collision and have ended the outgoing call
|
|
269
|
+
// and are now receiving the incoming call from the remote side (the "winner").
|
|
270
|
+
// However, the Desktop client has a bug where it re-orders the events so that
|
|
271
|
+
// instead of seeing ("outgoing call ended", "incoming call"), it sees
|
|
272
|
+
// ("incoming call", "call ended") and it gets messed up.
|
|
273
|
+
// The solution? Delay processing the incoming call.
|
|
274
|
+
setTimeout(() => {
|
|
275
|
+
this.onStartIncomingCall(remoteUserId, callId, isVideoCall);
|
|
276
|
+
}, 500);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
const isIncoming = true;
|
|
280
|
+
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall, null, CallState.Prering);
|
|
281
|
+
// Callback to UX not set
|
|
282
|
+
const handleIncomingCall = this.handleIncomingCall;
|
|
283
|
+
if (!handleIncomingCall) {
|
|
284
|
+
call.ignore();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this._call = call;
|
|
288
|
+
// tslint:disable no-floating-promises
|
|
289
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
const settings = yield handleIncomingCall(call);
|
|
291
|
+
if (!settings) {
|
|
292
|
+
call.ignore();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
call.settings = settings;
|
|
296
|
+
this.proceed(callId, settings);
|
|
297
|
+
}))();
|
|
298
|
+
}
|
|
299
|
+
proceed(callId, settings) {
|
|
300
|
+
silly_deadlock_protection(() => {
|
|
301
|
+
this.callManager.proceed(callId, settings.iceServer.username || '', settings.iceServer.password || '', settings.iceServer.urls, settings.hideIp, settings.bandwidthMode, settings.audioLevelsIntervalMillis || 0);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
// Called by Rust
|
|
305
|
+
onCallState(remoteUserId, state) {
|
|
306
|
+
const call = this._call;
|
|
307
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
call.state = state;
|
|
311
|
+
}
|
|
312
|
+
// Called by Rust
|
|
313
|
+
onCallEnded(remoteUserId, callId, reason, ageSec) {
|
|
314
|
+
let callInfo = this._callInfoByCallId.get(this.getCallInfoKey(callId));
|
|
315
|
+
const { isVideoCall, receivedAtCounter } = callInfo || {
|
|
316
|
+
isVideoCall: false,
|
|
317
|
+
receivedAtCounter: undefined,
|
|
318
|
+
};
|
|
319
|
+
this._callInfoByCallId.delete(this.getCallInfoKey(callId));
|
|
320
|
+
const call = this._call;
|
|
321
|
+
if (call && reason == CallEndedReason.ReceivedOfferWithGlare) {
|
|
322
|
+
// The current call is the outgoing call.
|
|
323
|
+
// The ended call is the incoming call.
|
|
324
|
+
// We're the "winner", so ignore the incoming call and keep going with the outgoing call.
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (call &&
|
|
328
|
+
(reason === CallEndedReason.Glare || reason === CallEndedReason.ReCall)) {
|
|
329
|
+
// The current call is the outgoing call.
|
|
330
|
+
// The ended call is the outgoing call.
|
|
331
|
+
// We're the "loser", so end the outgoing/current call and wait for a new incoming call.
|
|
332
|
+
// (proceeded down to the code below)
|
|
333
|
+
}
|
|
334
|
+
// If there is no call or the remoteUserId doesn't match that of the current
|
|
335
|
+
// call, or if one of the "receive offer while already in a call or because
|
|
336
|
+
// it expired" reasons are provided, don't end the current call, because
|
|
337
|
+
// there isn't one for this Ended notification, just update the call history.
|
|
338
|
+
// If the incoming call ends while in the prering state, also immediately
|
|
339
|
+
// update the call history because it is just a replay of messages.
|
|
340
|
+
if (!call ||
|
|
341
|
+
call.remoteUserId !== remoteUserId ||
|
|
342
|
+
reason === CallEndedReason.ReceivedOfferWhileActive ||
|
|
343
|
+
reason === CallEndedReason.ReceivedOfferExpired ||
|
|
344
|
+
(call.state === CallState.Prering && call.isIncoming)) {
|
|
345
|
+
if (this.handleAutoEndedIncomingCallRequest) {
|
|
346
|
+
this.handleAutoEndedIncomingCallRequest(callId, remoteUserId, reason, ageSec, isVideoCall, receivedAtCounter);
|
|
347
|
+
}
|
|
348
|
+
if (call && call.state === CallState.Prering && call.isIncoming) {
|
|
349
|
+
// Set the state to Ended without triggering a state update since we
|
|
350
|
+
// already notified the client.
|
|
351
|
+
call.endedReason = reason;
|
|
352
|
+
call.setCallEnded();
|
|
353
|
+
}
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
// Send the end reason first because setting the state triggers
|
|
357
|
+
// call.handleStateChanged, which may look at call.endedReason.
|
|
358
|
+
call.endedReason = reason;
|
|
359
|
+
call.state = CallState.Ended;
|
|
360
|
+
}
|
|
361
|
+
onRemoteVideoEnabled(remoteUserId, enabled) {
|
|
362
|
+
const call = this._call;
|
|
363
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
call.remoteVideoEnabled = enabled;
|
|
367
|
+
if (call.handleRemoteVideoEnabled) {
|
|
368
|
+
call.handleRemoteVideoEnabled();
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
onRemoteSharingScreen(remoteUserId, enabled) {
|
|
372
|
+
const call = this._call;
|
|
373
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
call.remoteSharingScreen = enabled;
|
|
377
|
+
if (call.handleRemoteSharingScreen) {
|
|
378
|
+
call.handleRemoteSharingScreen();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
onNetworkRouteChanged(remoteUserId, localNetworkAdapterType) {
|
|
382
|
+
const call = this._call;
|
|
383
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
call.networkRoute.localAdapterType = localNetworkAdapterType;
|
|
387
|
+
if (call.handleNetworkRouteChanged) {
|
|
388
|
+
call.handleNetworkRouteChanged();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
onAudioLevels(remoteUserId, capturedLevel, receivedLevel) {
|
|
392
|
+
const call = this._call;
|
|
393
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
call.outgoingAudioLevel = normalizeAudioLevel(capturedLevel);
|
|
397
|
+
call.remoteAudioLevel = normalizeAudioLevel(receivedLevel);
|
|
398
|
+
if (call.handleAudioLevels) {
|
|
399
|
+
call.handleAudioLevels();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
renderVideoFrame(width, height, buffer) {
|
|
403
|
+
var _a, _b;
|
|
404
|
+
const call = this._call;
|
|
405
|
+
if (!call) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (!!((_a = this._call) === null || _a === void 0 ? void 0 : _a.renderVideoFrame)) {
|
|
409
|
+
(_b = this._call) === null || _b === void 0 ? void 0 : _b.renderVideoFrame(width, height, buffer);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// Called by Rust
|
|
413
|
+
onSendOffer(remoteUserId, remoteDeviceId, callId, broadcast, offerType, opaque) {
|
|
414
|
+
const message = new CallingMessage();
|
|
415
|
+
message.offer = new OfferMessage();
|
|
416
|
+
message.offer.callId = callId;
|
|
417
|
+
message.offer.type = offerType;
|
|
418
|
+
message.offer.opaque = opaque;
|
|
419
|
+
this.sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message);
|
|
420
|
+
}
|
|
421
|
+
// Called by Rust
|
|
422
|
+
onSendAnswer(remoteUserId, remoteDeviceId, callId, broadcast, opaque) {
|
|
423
|
+
const message = new CallingMessage();
|
|
424
|
+
message.answer = new AnswerMessage();
|
|
425
|
+
message.answer.callId = callId;
|
|
426
|
+
message.answer.opaque = opaque;
|
|
427
|
+
this.sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message);
|
|
428
|
+
}
|
|
429
|
+
// Called by Rust
|
|
430
|
+
onSendIceCandidates(remoteUserId, remoteDeviceId, callId, broadcast, candidates) {
|
|
431
|
+
const message = new CallingMessage();
|
|
432
|
+
message.iceCandidates = [];
|
|
433
|
+
for (const candidate of candidates) {
|
|
434
|
+
const copy = new IceCandidateMessage();
|
|
435
|
+
copy.callId = callId;
|
|
436
|
+
copy.opaque = candidate;
|
|
437
|
+
message.iceCandidates.push(copy);
|
|
438
|
+
}
|
|
439
|
+
this.sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message);
|
|
440
|
+
}
|
|
441
|
+
// Called by Rust
|
|
442
|
+
onSendHangup(remoteUserId, remoteDeviceId, callId, broadcast, hangupType, deviceId) {
|
|
443
|
+
const message = new CallingMessage();
|
|
444
|
+
message.hangup = new HangupMessage();
|
|
445
|
+
message.hangup.callId = callId;
|
|
446
|
+
message.hangup.type = hangupType;
|
|
447
|
+
message.hangup.deviceId = deviceId || 0;
|
|
448
|
+
this.sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message);
|
|
449
|
+
}
|
|
450
|
+
// Called by Rust
|
|
451
|
+
onSendBusy(remoteUserId, remoteDeviceId, callId, broadcast) {
|
|
452
|
+
const message = new CallingMessage();
|
|
453
|
+
message.busy = new BusyMessage();
|
|
454
|
+
message.busy.callId = callId;
|
|
455
|
+
this.sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message);
|
|
456
|
+
}
|
|
457
|
+
sendSignaling(remoteUserId, remoteDeviceId, callId, broadcast, message) {
|
|
458
|
+
message.supportsMultiRing = true;
|
|
459
|
+
if (!broadcast) {
|
|
460
|
+
message.destinationDeviceId = remoteDeviceId;
|
|
461
|
+
}
|
|
462
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
463
|
+
if (this.handleOutgoingSignaling) {
|
|
464
|
+
const signalingResult = yield this.handleOutgoingSignaling(remoteUserId, message);
|
|
465
|
+
if (signalingResult) {
|
|
466
|
+
this.callManager.signalingMessageSent(callId);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
this.callManager.signalingMessageSendFailed(callId);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
this.callManager.signalingMessageSendFailed(callId);
|
|
474
|
+
}
|
|
475
|
+
}))();
|
|
476
|
+
}
|
|
477
|
+
receivedHttpResponse(requestId, status, body) {
|
|
478
|
+
silly_deadlock_protection(() => {
|
|
479
|
+
try {
|
|
480
|
+
this.callManager.receivedHttpResponse(requestId, status, body);
|
|
481
|
+
}
|
|
482
|
+
catch (_a) {
|
|
483
|
+
// We may not have an active connection any more.
|
|
484
|
+
// In which case it doesn't matter
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
httpRequestFailed(requestId, debugInfo) {
|
|
489
|
+
silly_deadlock_protection(() => {
|
|
490
|
+
try {
|
|
491
|
+
this.callManager.httpRequestFailed(requestId, debugInfo);
|
|
492
|
+
}
|
|
493
|
+
catch (_a) {
|
|
494
|
+
// We may not have an active connection any more.
|
|
495
|
+
// In which case it doesn't matter
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
// Group Calls
|
|
500
|
+
// Called by UX
|
|
501
|
+
getGroupCall(groupId, sfuUrl, hkdfExtraInfo, audioLevelsIntervalMillis, observer) {
|
|
502
|
+
const groupCall = new GroupCall(this.callManager, groupId, sfuUrl, hkdfExtraInfo, audioLevelsIntervalMillis, observer);
|
|
503
|
+
this._groupCallByClientId.set(groupCall.clientId, groupCall);
|
|
504
|
+
return groupCall;
|
|
505
|
+
}
|
|
506
|
+
// Called by UX
|
|
507
|
+
// Returns a list of user IDs
|
|
508
|
+
peekGroupCall(sfuUrl, membershipProof, groupMembers) {
|
|
509
|
+
let [requestId, promise] = this._peekRequests.add();
|
|
510
|
+
// Response comes back via handlePeekResponse
|
|
511
|
+
silly_deadlock_protection(() => {
|
|
512
|
+
this.callManager.peekGroupCall(requestId, sfuUrl, membershipProof, groupMembers);
|
|
513
|
+
});
|
|
514
|
+
return promise;
|
|
515
|
+
}
|
|
516
|
+
// Called by Rust
|
|
517
|
+
requestMembershipProof(clientId) {
|
|
518
|
+
silly_deadlock_protection(() => {
|
|
519
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
520
|
+
if (!groupCall) {
|
|
521
|
+
let error = new Error();
|
|
522
|
+
this.logError('requestMembershipProof(): GroupCall not found in map!');
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
groupCall.requestMembershipProof();
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
// Called by Rust
|
|
529
|
+
requestGroupMembers(clientId) {
|
|
530
|
+
silly_deadlock_protection(() => {
|
|
531
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
532
|
+
if (!groupCall) {
|
|
533
|
+
let error = new Error();
|
|
534
|
+
this.logError('requestGroupMembers(): GroupCall not found in map!');
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
groupCall.requestGroupMembers();
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// Called by Rust
|
|
541
|
+
handleConnectionStateChanged(clientId, connectionState) {
|
|
542
|
+
silly_deadlock_protection(() => {
|
|
543
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
544
|
+
if (!groupCall) {
|
|
545
|
+
let error = new Error();
|
|
546
|
+
this.logError('handleConnectionStateChanged(): GroupCall not found in map!');
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
groupCall.handleConnectionStateChanged(connectionState);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
// Called by Rust
|
|
553
|
+
handleJoinStateChanged(clientId, joinState, demuxId) {
|
|
554
|
+
silly_deadlock_protection(() => {
|
|
555
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
556
|
+
if (!groupCall) {
|
|
557
|
+
let error = new Error();
|
|
558
|
+
this.logError('handleJoinStateChanged(): GroupCall not found in map!');
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
groupCall.handleJoinStateChanged(joinState, demuxId);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// Called by Rust
|
|
565
|
+
handleNetworkRouteChanged(clientId, localNetworkAdapterType) {
|
|
566
|
+
silly_deadlock_protection(() => {
|
|
567
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
568
|
+
if (!groupCall) {
|
|
569
|
+
this.logError('handleNetworkRouteChanged(): GroupCall not found in map!');
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
groupCall.handleNetworkRouteChanged(localNetworkAdapterType);
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
// Called by Rust
|
|
576
|
+
handleAudioLevels(clientId, capturedLevel, receivedLevels) {
|
|
577
|
+
silly_deadlock_protection(() => {
|
|
578
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
579
|
+
if (!!groupCall) {
|
|
580
|
+
groupCall.handleAudioLevels(capturedLevel, receivedLevels);
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
// Called by Rust
|
|
585
|
+
handleRemoteDevicesChanged(clientId, remoteDeviceStates) {
|
|
586
|
+
silly_deadlock_protection(() => {
|
|
587
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
588
|
+
if (!groupCall) {
|
|
589
|
+
let error = new Error();
|
|
590
|
+
this.logError('handleRemoteDevicesChanged(): GroupCall not found in map!');
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
groupCall.handleRemoteDevicesChanged(remoteDeviceStates);
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
// Called by Rust
|
|
597
|
+
handlePeekChanged(clientId, info) {
|
|
598
|
+
silly_deadlock_protection(() => {
|
|
599
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
600
|
+
if (!groupCall) {
|
|
601
|
+
let error = new Error();
|
|
602
|
+
this.logError('handlePeekChanged(): GroupCall not found in map!');
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
groupCall.handlePeekChanged(info);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
// Called by Rust
|
|
609
|
+
handlePeekResponse(request_id, info) {
|
|
610
|
+
silly_deadlock_protection(() => {
|
|
611
|
+
if (!this._peekRequests.resolve(request_id, info)) {
|
|
612
|
+
this.logWarn(`Invalid request ID for handlePeekResponse: ${request_id}`);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
// Called by Rust
|
|
617
|
+
handleEnded(clientId, reason) {
|
|
618
|
+
silly_deadlock_protection(() => {
|
|
619
|
+
let groupCall = this._groupCallByClientId.get(clientId);
|
|
620
|
+
if (!groupCall) {
|
|
621
|
+
let error = new Error();
|
|
622
|
+
this.logError('handleEnded(): GroupCall not found in map!');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
this._groupCallByClientId.delete(clientId);
|
|
626
|
+
groupCall.handleEnded(reason);
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
// Called by Rust
|
|
630
|
+
groupCallRingUpdate(groupId, ringIdString, sender, state) {
|
|
631
|
+
silly_deadlock_protection(() => {
|
|
632
|
+
if (this.handleGroupCallRingUpdate) {
|
|
633
|
+
const ringId = BigInt(ringIdString);
|
|
634
|
+
this.handleGroupCallRingUpdate(groupId, ringId, sender, state);
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
this.logError('RingRTC.handleGroupCallRingUpdate is not set!');
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
// Called by Rust
|
|
642
|
+
onLogMessage(level, fileName, line, message) {
|
|
643
|
+
if (this.handleLogMessage) {
|
|
644
|
+
this.handleLogMessage(level, fileName, line, message);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
// Called from here
|
|
648
|
+
logError(message) {
|
|
649
|
+
this.onLogMessage(CallLogLevel.Error, 'Service.ts', 0, message);
|
|
650
|
+
}
|
|
651
|
+
// Called from here
|
|
652
|
+
logWarn(message) {
|
|
653
|
+
this.onLogMessage(CallLogLevel.Warn, 'Service.ts', 0, message);
|
|
654
|
+
}
|
|
655
|
+
// Called from here
|
|
656
|
+
logInfo(message) {
|
|
657
|
+
this.onLogMessage(CallLogLevel.Info, 'Service.ts', 0, message);
|
|
658
|
+
}
|
|
659
|
+
// Called by MessageReceiver
|
|
660
|
+
// tslint:disable-next-line cyclomatic-complexity
|
|
661
|
+
handleCallingMessage(remoteUserId, remoteUuid, remoteDeviceId, localDeviceId, messageAgeSec, messageReceivedAtCounter, message, senderIdentityKey, receiverIdentityKey) {
|
|
662
|
+
if (message.destinationDeviceId &&
|
|
663
|
+
message.destinationDeviceId !== localDeviceId) {
|
|
664
|
+
// Drop the message as it isn't for this device, handleIgnoredCall() is not needed.
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (message.offer && message.offer.callId) {
|
|
668
|
+
const callId = message.offer.callId;
|
|
669
|
+
const opaque = to_buffer(message.offer.opaque);
|
|
670
|
+
// opaque is required. sdp is obsolete, but it might still come with opaque.
|
|
671
|
+
if (!opaque) {
|
|
672
|
+
// TODO: Remove once the proto is updated to only support opaque and require it.
|
|
673
|
+
this.logError('handleCallingMessage(): opaque not received for offer, remote should update');
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const offerType = message.offer.type || OfferType.AudioCall;
|
|
677
|
+
// Save the call details for later when the call is ended.
|
|
678
|
+
let callInfo = new CallInfo(offerType === OfferType.VideoCall, messageReceivedAtCounter);
|
|
679
|
+
this._callInfoByCallId.set(this.getCallInfoKey(callId), callInfo);
|
|
680
|
+
this.callManager.receivedOffer(remoteUserId, remoteDeviceId, localDeviceId, messageAgeSec, callId, offerType, opaque, senderIdentityKey, receiverIdentityKey);
|
|
681
|
+
}
|
|
682
|
+
if (message.answer && message.answer.callId) {
|
|
683
|
+
const callId = message.answer.callId;
|
|
684
|
+
const opaque = to_buffer(message.answer.opaque);
|
|
685
|
+
// opaque is required. sdp is obsolete, but it might still come with opaque.
|
|
686
|
+
if (!opaque) {
|
|
687
|
+
// TODO: Remove once the proto is updated to only support opaque and require it.
|
|
688
|
+
this.logError('handleCallingMessage(): opaque not received for answer, remote should update');
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
this.callManager.receivedAnswer(remoteUserId, remoteDeviceId, callId, opaque, senderIdentityKey, receiverIdentityKey);
|
|
692
|
+
}
|
|
693
|
+
if (message.iceCandidates && message.iceCandidates.length > 0) {
|
|
694
|
+
// We assume they all have the same .callId
|
|
695
|
+
let callId = message.iceCandidates[0].callId;
|
|
696
|
+
// We have to copy them to do the .toArrayBuffer() thing.
|
|
697
|
+
const candidates = [];
|
|
698
|
+
for (const candidate of message.iceCandidates) {
|
|
699
|
+
const copy = to_buffer(candidate.opaque);
|
|
700
|
+
if (copy) {
|
|
701
|
+
candidates.push(copy);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
// TODO: Remove once the proto is updated to only support opaque and require it.
|
|
705
|
+
this.logError('handleCallingMessage(): opaque not received for ice candidate, remote should update');
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (candidates.length == 0) {
|
|
710
|
+
this.logWarn('handleCallingMessage(): No ice candidates in ice message, remote should update');
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
this.callManager.receivedIceCandidates(remoteUserId, remoteDeviceId, callId, candidates);
|
|
714
|
+
}
|
|
715
|
+
if (message.hangup && message.hangup.callId) {
|
|
716
|
+
const callId = message.hangup.callId;
|
|
717
|
+
const hangupType = message.hangup.type || HangupType.Normal;
|
|
718
|
+
const hangupDeviceId = message.hangup.deviceId || null;
|
|
719
|
+
this.callManager.receivedHangup(remoteUserId, remoteDeviceId, callId, hangupType, hangupDeviceId);
|
|
720
|
+
}
|
|
721
|
+
if (message.legacyHangup && message.legacyHangup.callId) {
|
|
722
|
+
const callId = message.legacyHangup.callId;
|
|
723
|
+
const hangupType = message.legacyHangup.type || HangupType.Normal;
|
|
724
|
+
const hangupDeviceId = message.legacyHangup.deviceId || null;
|
|
725
|
+
this.callManager.receivedHangup(remoteUserId, remoteDeviceId, callId, hangupType, hangupDeviceId);
|
|
726
|
+
}
|
|
727
|
+
if (message.busy && message.busy.callId) {
|
|
728
|
+
const callId = message.busy.callId;
|
|
729
|
+
this.callManager.receivedBusy(remoteUserId, remoteDeviceId, callId);
|
|
730
|
+
}
|
|
731
|
+
if (message.opaque) {
|
|
732
|
+
if (remoteUuid == null) {
|
|
733
|
+
this.logError('handleCallingMessage(): opaque message received without UUID!');
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const data = to_buffer(message.opaque.data);
|
|
737
|
+
if (data == undefined) {
|
|
738
|
+
this.logError('handleCallingMessage(): opaque message received without data!');
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
this.callManager.receivedCallMessage(remoteUuid, remoteDeviceId, localDeviceId, data, messageAgeSec);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
// Called by Rust
|
|
745
|
+
sendHttpRequest(requestId, url, method, headers, body) {
|
|
746
|
+
if (this.handleSendHttpRequest) {
|
|
747
|
+
this.handleSendHttpRequest(requestId, url, method, headers, body);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
this.logError('RingRTC.handleSendHttpRequest is not set!');
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
// Called by Rust
|
|
754
|
+
sendCallMessage(recipientUuid, message, urgency) {
|
|
755
|
+
if (this.handleSendCallMessage) {
|
|
756
|
+
this.handleSendCallMessage(recipientUuid, message, urgency);
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
this.logError('RingRTC.handleSendCallMessage is not set!');
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
// Called by Rust
|
|
763
|
+
sendCallMessageToGroup(groupId, message, urgency) {
|
|
764
|
+
if (this.handleSendCallMessageToGroup) {
|
|
765
|
+
this.handleSendCallMessageToGroup(groupId, message, urgency);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
this.logError('RingRTC.handleSendCallMessageToGroup is not set!');
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
// These are convenience methods. One could use the Call class instead.
|
|
772
|
+
get call() {
|
|
773
|
+
return this._call;
|
|
774
|
+
}
|
|
775
|
+
getCall(callId) {
|
|
776
|
+
const { call } = this;
|
|
777
|
+
if (call &&
|
|
778
|
+
call.callId.high === callId.high &&
|
|
779
|
+
call.callId.low === call.callId.low) {
|
|
780
|
+
return call;
|
|
781
|
+
}
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
accept(callId, asVideoCall) {
|
|
785
|
+
const call = this.getCall(callId);
|
|
786
|
+
if (!call) {
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
call.accept();
|
|
790
|
+
call.outgoingAudioEnabled = true;
|
|
791
|
+
call.outgoingVideoEnabled = asVideoCall;
|
|
792
|
+
}
|
|
793
|
+
decline(callId) {
|
|
794
|
+
const call = this.getCall(callId);
|
|
795
|
+
if (!call) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
call.decline();
|
|
799
|
+
}
|
|
800
|
+
ignore(callId) {
|
|
801
|
+
const call = this.getCall(callId);
|
|
802
|
+
if (!call) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
call.ignore();
|
|
806
|
+
}
|
|
807
|
+
hangup(callId) {
|
|
808
|
+
const call = this.getCall(callId);
|
|
809
|
+
if (!call) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
call.hangup();
|
|
813
|
+
}
|
|
814
|
+
setOutgoingAudio(callId, enabled) {
|
|
815
|
+
const call = this.getCall(callId);
|
|
816
|
+
if (!call) {
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
call.outgoingAudioEnabled = enabled;
|
|
820
|
+
}
|
|
821
|
+
setOutgoingVideo(callId, enabled) {
|
|
822
|
+
const call = this.getCall(callId);
|
|
823
|
+
if (!call) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
call.outgoingVideoEnabled = enabled;
|
|
827
|
+
}
|
|
828
|
+
setOutgoingVideoIsScreenShare(callId, isScreenShare) {
|
|
829
|
+
const call = this.getCall(callId);
|
|
830
|
+
if (!call) {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
call.outgoingVideoIsScreenShare = isScreenShare;
|
|
834
|
+
}
|
|
835
|
+
setVideoCapturer(callId, capturer) {
|
|
836
|
+
const call = this.getCall(callId);
|
|
837
|
+
if (!call) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
call.videoCapturer = capturer;
|
|
841
|
+
}
|
|
842
|
+
setVideoRenderer(callId, renderer) {
|
|
843
|
+
const call = this.getCall(callId);
|
|
844
|
+
if (!call) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
call.videoRenderer = renderer;
|
|
848
|
+
}
|
|
849
|
+
getAudioInputs() {
|
|
850
|
+
return this.callManager.getAudioInputs();
|
|
851
|
+
}
|
|
852
|
+
setAudioInput(index) {
|
|
853
|
+
this.callManager.setAudioInput(index);
|
|
854
|
+
}
|
|
855
|
+
getAudioOutputs() {
|
|
856
|
+
return this.callManager.getAudioOutputs();
|
|
857
|
+
}
|
|
858
|
+
setAudioOutput(index) {
|
|
859
|
+
this.callManager.setAudioOutput(index);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
exports.RingRTCType = RingRTCType;
|
|
863
|
+
class Call {
|
|
864
|
+
constructor(callManager, remoteUserId, callId, isIncoming, isVideoCall, settings, state) {
|
|
865
|
+
this._outgoingAudioEnabled = false;
|
|
866
|
+
this._outgoingVideoEnabled = false;
|
|
867
|
+
this._outgoingVideoIsScreenShare = false;
|
|
868
|
+
this._remoteVideoEnabled = false;
|
|
869
|
+
this.outgoingAudioLevel = 0;
|
|
870
|
+
this.remoteAudioLevel = 0;
|
|
871
|
+
this.remoteSharingScreen = false;
|
|
872
|
+
this.networkRoute = new NetworkRoute();
|
|
873
|
+
this._videoCapturer = null;
|
|
874
|
+
this._videoRenderer = null;
|
|
875
|
+
this._callManager = callManager;
|
|
876
|
+
this._remoteUserId = remoteUserId;
|
|
877
|
+
this.callId = callId;
|
|
878
|
+
this._isIncoming = isIncoming;
|
|
879
|
+
this._isVideoCall = isVideoCall;
|
|
880
|
+
this.settings = settings;
|
|
881
|
+
this._state = state;
|
|
882
|
+
}
|
|
883
|
+
get remoteUserId() {
|
|
884
|
+
return this._remoteUserId;
|
|
885
|
+
}
|
|
886
|
+
get isIncoming() {
|
|
887
|
+
return this._isIncoming;
|
|
888
|
+
}
|
|
889
|
+
get isVideoCall() {
|
|
890
|
+
return this._isVideoCall;
|
|
891
|
+
}
|
|
892
|
+
get state() {
|
|
893
|
+
return this._state;
|
|
894
|
+
}
|
|
895
|
+
set state(state) {
|
|
896
|
+
if (state == this._state) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
this._state = state;
|
|
900
|
+
this.enableOrDisableCapturer();
|
|
901
|
+
this.enableOrDisableRenderer();
|
|
902
|
+
if (!!this.handleStateChanged) {
|
|
903
|
+
this.handleStateChanged();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
setCallEnded() {
|
|
907
|
+
this._state = CallState.Ended;
|
|
908
|
+
}
|
|
909
|
+
set videoCapturer(capturer) {
|
|
910
|
+
this._videoCapturer = capturer;
|
|
911
|
+
this.enableOrDisableCapturer();
|
|
912
|
+
}
|
|
913
|
+
set videoRenderer(renderer) {
|
|
914
|
+
this._videoRenderer = renderer;
|
|
915
|
+
this.enableOrDisableRenderer();
|
|
916
|
+
}
|
|
917
|
+
accept() {
|
|
918
|
+
this._callManager.accept(this.callId);
|
|
919
|
+
}
|
|
920
|
+
decline() {
|
|
921
|
+
this.hangup();
|
|
922
|
+
}
|
|
923
|
+
ignore() {
|
|
924
|
+
this._callManager.ignore(this.callId);
|
|
925
|
+
}
|
|
926
|
+
hangup() {
|
|
927
|
+
// This is a little faster than waiting for the
|
|
928
|
+
// change in call state to come back.
|
|
929
|
+
if (this._videoCapturer) {
|
|
930
|
+
this._videoCapturer.disable();
|
|
931
|
+
}
|
|
932
|
+
if (this._videoRenderer) {
|
|
933
|
+
this._videoRenderer.disable();
|
|
934
|
+
}
|
|
935
|
+
// This assumes we only have one active call.
|
|
936
|
+
silly_deadlock_protection(() => {
|
|
937
|
+
this._callManager.hangup();
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
get outgoingAudioEnabled() {
|
|
941
|
+
return this._outgoingAudioEnabled;
|
|
942
|
+
}
|
|
943
|
+
set outgoingAudioEnabled(enabled) {
|
|
944
|
+
this._outgoingAudioEnabled = enabled;
|
|
945
|
+
// This assumes we only have one active call.
|
|
946
|
+
silly_deadlock_protection(() => {
|
|
947
|
+
this._callManager.setOutgoingAudioEnabled(enabled);
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
get outgoingVideoEnabled() {
|
|
951
|
+
return this._outgoingVideoEnabled;
|
|
952
|
+
}
|
|
953
|
+
set outgoingVideoEnabled(enabled) {
|
|
954
|
+
this._outgoingVideoEnabled = enabled;
|
|
955
|
+
this.enableOrDisableCapturer();
|
|
956
|
+
}
|
|
957
|
+
set outgoingVideoIsScreenShare(isScreenShare) {
|
|
958
|
+
// This assumes we only have one active call.
|
|
959
|
+
this._outgoingVideoIsScreenShare = isScreenShare;
|
|
960
|
+
silly_deadlock_protection(() => {
|
|
961
|
+
this._callManager.setOutgoingVideoIsScreenShare(isScreenShare);
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
get remoteVideoEnabled() {
|
|
965
|
+
return this._remoteVideoEnabled;
|
|
966
|
+
}
|
|
967
|
+
set remoteVideoEnabled(enabled) {
|
|
968
|
+
this._remoteVideoEnabled = enabled;
|
|
969
|
+
this.enableOrDisableRenderer();
|
|
970
|
+
}
|
|
971
|
+
// With this method, a Call is a VideoFrameSender
|
|
972
|
+
sendVideoFrame(width, height, format, buffer) {
|
|
973
|
+
// This assumes we only have one active all.
|
|
974
|
+
this._callManager.sendVideoFrame(width, height, format, buffer);
|
|
975
|
+
}
|
|
976
|
+
// With this method, a Call is a VideoFrameSource
|
|
977
|
+
receiveVideoFrame(buffer) {
|
|
978
|
+
// This assumes we only have one active all.
|
|
979
|
+
return this._callManager.receiveVideoFrame(buffer);
|
|
980
|
+
}
|
|
981
|
+
enableOrDisableCapturer() {
|
|
982
|
+
if (!this._videoCapturer) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
if (!this.outgoingVideoEnabled) {
|
|
986
|
+
this._videoCapturer.disable();
|
|
987
|
+
if (this.state === CallState.Accepted) {
|
|
988
|
+
this.setOutgoingVideoEnabled(false);
|
|
989
|
+
}
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
switch (this.state) {
|
|
993
|
+
case CallState.Prering:
|
|
994
|
+
case CallState.Ringing:
|
|
995
|
+
this._videoCapturer.enableCapture();
|
|
996
|
+
break;
|
|
997
|
+
case CallState.Accepted:
|
|
998
|
+
this._videoCapturer.enableCaptureAndSend(this);
|
|
999
|
+
this.setOutgoingVideoEnabled(true);
|
|
1000
|
+
if (this._outgoingVideoIsScreenShare) {
|
|
1001
|
+
// Make sure the status gets sent.
|
|
1002
|
+
this.outgoingVideoIsScreenShare = true;
|
|
1003
|
+
}
|
|
1004
|
+
break;
|
|
1005
|
+
case CallState.Reconnecting:
|
|
1006
|
+
this._videoCapturer.enableCaptureAndSend(this);
|
|
1007
|
+
// Don't send status until we're reconnected.
|
|
1008
|
+
break;
|
|
1009
|
+
case CallState.Ended:
|
|
1010
|
+
this._videoCapturer.disable();
|
|
1011
|
+
break;
|
|
1012
|
+
default:
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
setOutgoingVideoEnabled(enabled) {
|
|
1016
|
+
silly_deadlock_protection(() => {
|
|
1017
|
+
try {
|
|
1018
|
+
this._callManager.setOutgoingVideoEnabled(enabled);
|
|
1019
|
+
}
|
|
1020
|
+
catch (_a) {
|
|
1021
|
+
// We may not have an active connection any more.
|
|
1022
|
+
// In which case it doesn't matter
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
updateBandwidthMode(bandwidthMode) {
|
|
1027
|
+
silly_deadlock_protection(() => {
|
|
1028
|
+
try {
|
|
1029
|
+
this._callManager.updateBandwidthMode(bandwidthMode);
|
|
1030
|
+
}
|
|
1031
|
+
catch (_a) {
|
|
1032
|
+
// We may not have an active connection any more.
|
|
1033
|
+
// In which case it doesn't matter
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
enableOrDisableRenderer() {
|
|
1038
|
+
if (!this._videoRenderer) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (!this.remoteVideoEnabled) {
|
|
1042
|
+
this._videoRenderer.disable();
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
switch (this.state) {
|
|
1046
|
+
case CallState.Prering:
|
|
1047
|
+
case CallState.Ringing:
|
|
1048
|
+
this._videoRenderer.disable();
|
|
1049
|
+
break;
|
|
1050
|
+
case CallState.Accepted:
|
|
1051
|
+
case CallState.Reconnecting:
|
|
1052
|
+
this._videoRenderer.enable(this);
|
|
1053
|
+
break;
|
|
1054
|
+
case CallState.Ended:
|
|
1055
|
+
this._videoRenderer.disable();
|
|
1056
|
+
break;
|
|
1057
|
+
default:
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
exports.Call = Call;
|
|
1062
|
+
// Represents the connection state to a media server for a group call.
|
|
1063
|
+
var ConnectionState;
|
|
1064
|
+
(function (ConnectionState) {
|
|
1065
|
+
ConnectionState[ConnectionState["NotConnected"] = 0] = "NotConnected";
|
|
1066
|
+
ConnectionState[ConnectionState["Connecting"] = 1] = "Connecting";
|
|
1067
|
+
ConnectionState[ConnectionState["Connected"] = 2] = "Connected";
|
|
1068
|
+
ConnectionState[ConnectionState["Reconnecting"] = 3] = "Reconnecting";
|
|
1069
|
+
})(ConnectionState = exports.ConnectionState || (exports.ConnectionState = {}));
|
|
1070
|
+
// Represents whether or not a user is joined to a group call and can exchange media.
|
|
1071
|
+
var JoinState;
|
|
1072
|
+
(function (JoinState) {
|
|
1073
|
+
JoinState[JoinState["NotJoined"] = 0] = "NotJoined";
|
|
1074
|
+
JoinState[JoinState["Joining"] = 1] = "Joining";
|
|
1075
|
+
JoinState[JoinState["Joined"] = 2] = "Joined";
|
|
1076
|
+
})(JoinState = exports.JoinState || (exports.JoinState = {}));
|
|
1077
|
+
// If not ended purposely by the user, gives the reason why a group call ended.
|
|
1078
|
+
var GroupCallEndReason;
|
|
1079
|
+
(function (GroupCallEndReason) {
|
|
1080
|
+
// Normal events
|
|
1081
|
+
GroupCallEndReason[GroupCallEndReason["DeviceExplicitlyDisconnected"] = 0] = "DeviceExplicitlyDisconnected";
|
|
1082
|
+
GroupCallEndReason[GroupCallEndReason["ServerExplicitlyDisconnected"] = 1] = "ServerExplicitlyDisconnected";
|
|
1083
|
+
// Things that can go wrong
|
|
1084
|
+
GroupCallEndReason[GroupCallEndReason["CallManagerIsBusy"] = 2] = "CallManagerIsBusy";
|
|
1085
|
+
GroupCallEndReason[GroupCallEndReason["SfuClientFailedToJoin"] = 3] = "SfuClientFailedToJoin";
|
|
1086
|
+
GroupCallEndReason[GroupCallEndReason["FailedToCreatePeerConnectionFactory"] = 4] = "FailedToCreatePeerConnectionFactory";
|
|
1087
|
+
GroupCallEndReason[GroupCallEndReason["FailedToNegotiateSrtpKeys"] = 5] = "FailedToNegotiateSrtpKeys";
|
|
1088
|
+
GroupCallEndReason[GroupCallEndReason["FailedToCreatePeerConnection"] = 6] = "FailedToCreatePeerConnection";
|
|
1089
|
+
GroupCallEndReason[GroupCallEndReason["FailedToStartPeerConnection"] = 7] = "FailedToStartPeerConnection";
|
|
1090
|
+
GroupCallEndReason[GroupCallEndReason["FailedToUpdatePeerConnection"] = 8] = "FailedToUpdatePeerConnection";
|
|
1091
|
+
GroupCallEndReason[GroupCallEndReason["FailedToSetMaxSendBitrate"] = 9] = "FailedToSetMaxSendBitrate";
|
|
1092
|
+
GroupCallEndReason[GroupCallEndReason["IceFailedWhileConnecting"] = 10] = "IceFailedWhileConnecting";
|
|
1093
|
+
GroupCallEndReason[GroupCallEndReason["IceFailedAfterConnected"] = 11] = "IceFailedAfterConnected";
|
|
1094
|
+
GroupCallEndReason[GroupCallEndReason["ServerChangedDemuxId"] = 12] = "ServerChangedDemuxId";
|
|
1095
|
+
GroupCallEndReason[GroupCallEndReason["HasMaxDevices"] = 13] = "HasMaxDevices";
|
|
1096
|
+
})(GroupCallEndReason = exports.GroupCallEndReason || (exports.GroupCallEndReason = {}));
|
|
1097
|
+
var CallMessageUrgency;
|
|
1098
|
+
(function (CallMessageUrgency) {
|
|
1099
|
+
CallMessageUrgency[CallMessageUrgency["Droppable"] = 0] = "Droppable";
|
|
1100
|
+
CallMessageUrgency[CallMessageUrgency["HandleImmediately"] = 1] = "HandleImmediately";
|
|
1101
|
+
})(CallMessageUrgency = exports.CallMessageUrgency || (exports.CallMessageUrgency = {}));
|
|
1102
|
+
var RingUpdate;
|
|
1103
|
+
(function (RingUpdate) {
|
|
1104
|
+
/// The sender is trying to ring this user.
|
|
1105
|
+
RingUpdate[RingUpdate["Requested"] = 0] = "Requested";
|
|
1106
|
+
/// The sender tried to ring this user, but it's been too long.
|
|
1107
|
+
RingUpdate[RingUpdate["ExpiredRequest"] = 1] = "ExpiredRequest";
|
|
1108
|
+
/// Call was accepted elsewhere by a different device.
|
|
1109
|
+
RingUpdate[RingUpdate["AcceptedOnAnotherDevice"] = 2] = "AcceptedOnAnotherDevice";
|
|
1110
|
+
/// Call was declined elsewhere by a different device.
|
|
1111
|
+
RingUpdate[RingUpdate["DeclinedOnAnotherDevice"] = 3] = "DeclinedOnAnotherDevice";
|
|
1112
|
+
/// This device is currently on a different call.
|
|
1113
|
+
RingUpdate[RingUpdate["BusyLocally"] = 4] = "BusyLocally";
|
|
1114
|
+
/// A different device is currently on a different call.
|
|
1115
|
+
RingUpdate[RingUpdate["BusyOnAnotherDevice"] = 5] = "BusyOnAnotherDevice";
|
|
1116
|
+
/// The sender cancelled the ring request.
|
|
1117
|
+
RingUpdate[RingUpdate["CancelledByRinger"] = 6] = "CancelledByRinger";
|
|
1118
|
+
})(RingUpdate = exports.RingUpdate || (exports.RingUpdate = {}));
|
|
1119
|
+
// HTTP request methods.
|
|
1120
|
+
var HttpMethod;
|
|
1121
|
+
(function (HttpMethod) {
|
|
1122
|
+
HttpMethod[HttpMethod["Get"] = 0] = "Get";
|
|
1123
|
+
HttpMethod[HttpMethod["Put"] = 1] = "Put";
|
|
1124
|
+
HttpMethod[HttpMethod["Post"] = 2] = "Post";
|
|
1125
|
+
HttpMethod[HttpMethod["Delete"] = 3] = "Delete";
|
|
1126
|
+
})(HttpMethod = exports.HttpMethod || (exports.HttpMethod = {}));
|
|
1127
|
+
// The local device state for a group call.
|
|
1128
|
+
class LocalDeviceState {
|
|
1129
|
+
constructor() {
|
|
1130
|
+
this.connectionState = ConnectionState.NotConnected;
|
|
1131
|
+
this.joinState = JoinState.NotJoined;
|
|
1132
|
+
// By default audio and video are muted.
|
|
1133
|
+
this.audioMuted = true;
|
|
1134
|
+
this.videoMuted = true;
|
|
1135
|
+
this.audioLevel = 0;
|
|
1136
|
+
this.presenting = false;
|
|
1137
|
+
this.sharingScreen = false;
|
|
1138
|
+
this.networkRoute = new NetworkRoute();
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
exports.LocalDeviceState = LocalDeviceState;
|
|
1142
|
+
// All remote devices in a group call and their associated state.
|
|
1143
|
+
class RemoteDeviceState {
|
|
1144
|
+
constructor(demuxId, userId, mediaKeysReceived) {
|
|
1145
|
+
this.demuxId = demuxId;
|
|
1146
|
+
this.userId = userId;
|
|
1147
|
+
this.mediaKeysReceived = mediaKeysReceived;
|
|
1148
|
+
this.audioLevel = 0;
|
|
1149
|
+
this.isHigherResolutionPending = false;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
exports.RemoteDeviceState = RemoteDeviceState;
|
|
1153
|
+
// Used to communicate the group membership to RingRTC for a group call.
|
|
1154
|
+
class GroupMemberInfo {
|
|
1155
|
+
constructor(userId, userIdCipherText) {
|
|
1156
|
+
this.userId = userId;
|
|
1157
|
+
this.userIdCipherText = userIdCipherText;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
exports.GroupMemberInfo = GroupMemberInfo;
|
|
1161
|
+
// Used for the application to communicate the actual resolutions of
|
|
1162
|
+
// each device in a group call to RingRTC and the SFU.
|
|
1163
|
+
class VideoRequest {
|
|
1164
|
+
constructor(demuxId, width, height, framerate) {
|
|
1165
|
+
this.demuxId = demuxId;
|
|
1166
|
+
this.width = width;
|
|
1167
|
+
this.height = height;
|
|
1168
|
+
this.framerate = framerate;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
exports.VideoRequest = VideoRequest;
|
|
1172
|
+
class GroupCall {
|
|
1173
|
+
// Called by UI via RingRTC object
|
|
1174
|
+
constructor(callManager, groupId, sfuUrl, hkdfExtraInfo, audioLevelsIntervalMillis, observer) {
|
|
1175
|
+
this._callManager = callManager;
|
|
1176
|
+
this._observer = observer;
|
|
1177
|
+
this._localDeviceState = new LocalDeviceState();
|
|
1178
|
+
this._clientId = this._callManager.createGroupCallClient(groupId, sfuUrl, hkdfExtraInfo, audioLevelsIntervalMillis || 0);
|
|
1179
|
+
}
|
|
1180
|
+
get clientId() {
|
|
1181
|
+
return this._clientId;
|
|
1182
|
+
}
|
|
1183
|
+
// Called by UI
|
|
1184
|
+
connect() {
|
|
1185
|
+
this._callManager.connect(this._clientId);
|
|
1186
|
+
}
|
|
1187
|
+
// Called by UI
|
|
1188
|
+
join() {
|
|
1189
|
+
this._callManager.join(this._clientId);
|
|
1190
|
+
}
|
|
1191
|
+
// Called by UI
|
|
1192
|
+
leave() {
|
|
1193
|
+
this._callManager.leave(this._clientId);
|
|
1194
|
+
}
|
|
1195
|
+
// Called by UI
|
|
1196
|
+
disconnect() {
|
|
1197
|
+
this._callManager.disconnect(this._clientId);
|
|
1198
|
+
}
|
|
1199
|
+
// Called by UI
|
|
1200
|
+
getLocalDeviceState() {
|
|
1201
|
+
return this._localDeviceState;
|
|
1202
|
+
}
|
|
1203
|
+
// Called by UI
|
|
1204
|
+
getRemoteDeviceStates() {
|
|
1205
|
+
return this._remoteDeviceStates;
|
|
1206
|
+
}
|
|
1207
|
+
// Called by UI
|
|
1208
|
+
getPeekInfo() {
|
|
1209
|
+
return this._peekInfo;
|
|
1210
|
+
}
|
|
1211
|
+
// Called by UI
|
|
1212
|
+
setOutgoingAudioMuted(muted) {
|
|
1213
|
+
this._localDeviceState.audioMuted = muted;
|
|
1214
|
+
this._callManager.setOutgoingAudioMuted(this._clientId, muted);
|
|
1215
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1216
|
+
}
|
|
1217
|
+
// Called by UI
|
|
1218
|
+
setOutgoingVideoMuted(muted) {
|
|
1219
|
+
this._localDeviceState.videoMuted = muted;
|
|
1220
|
+
this._callManager.setOutgoingVideoMuted(this._clientId, muted);
|
|
1221
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1222
|
+
}
|
|
1223
|
+
// Called by UI
|
|
1224
|
+
setPresenting(presenting) {
|
|
1225
|
+
this._localDeviceState.presenting = presenting;
|
|
1226
|
+
this._callManager.setPresenting(this._clientId, presenting);
|
|
1227
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1228
|
+
}
|
|
1229
|
+
// Called by UI
|
|
1230
|
+
setOutgoingVideoIsScreenShare(isScreenShare) {
|
|
1231
|
+
this._localDeviceState.sharingScreen = isScreenShare;
|
|
1232
|
+
this._callManager.setOutgoingGroupCallVideoIsScreenShare(this._clientId, isScreenShare);
|
|
1233
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1234
|
+
}
|
|
1235
|
+
// Called by UI
|
|
1236
|
+
ringAll() {
|
|
1237
|
+
this._callManager.groupRing(this._clientId, undefined);
|
|
1238
|
+
}
|
|
1239
|
+
// Called by UI
|
|
1240
|
+
resendMediaKeys() {
|
|
1241
|
+
this._callManager.resendMediaKeys(this._clientId);
|
|
1242
|
+
}
|
|
1243
|
+
// Called by UI
|
|
1244
|
+
setBandwidthMode(bandwidthMode) {
|
|
1245
|
+
this._callManager.setBandwidthMode(this._clientId, bandwidthMode);
|
|
1246
|
+
}
|
|
1247
|
+
// Called by UI
|
|
1248
|
+
requestVideo(resolutions, activeSpeakerHeight) {
|
|
1249
|
+
this._callManager.requestVideo(this._clientId, resolutions, activeSpeakerHeight);
|
|
1250
|
+
}
|
|
1251
|
+
// Called by UI
|
|
1252
|
+
setGroupMembers(members) {
|
|
1253
|
+
this._callManager.setGroupMembers(this._clientId, members);
|
|
1254
|
+
}
|
|
1255
|
+
// Called by UI
|
|
1256
|
+
setMembershipProof(proof) {
|
|
1257
|
+
this._callManager.setMembershipProof(this._clientId, proof);
|
|
1258
|
+
}
|
|
1259
|
+
// Called by Rust via RingRTC object
|
|
1260
|
+
requestMembershipProof() {
|
|
1261
|
+
this._observer.requestMembershipProof(this);
|
|
1262
|
+
}
|
|
1263
|
+
// Called by Rust via RingRTC object
|
|
1264
|
+
requestGroupMembers() {
|
|
1265
|
+
this._observer.requestGroupMembers(this);
|
|
1266
|
+
}
|
|
1267
|
+
// Called by Rust via RingRTC object
|
|
1268
|
+
handleConnectionStateChanged(connectionState) {
|
|
1269
|
+
this._localDeviceState.connectionState = connectionState;
|
|
1270
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1271
|
+
}
|
|
1272
|
+
// Called by Rust via RingRTC object
|
|
1273
|
+
handleJoinStateChanged(joinState, demuxId) {
|
|
1274
|
+
this._localDeviceState.joinState = joinState;
|
|
1275
|
+
// Don't set to undefined after we leave so we can still know the demuxId after we leave.
|
|
1276
|
+
if (demuxId != undefined) {
|
|
1277
|
+
this._localDeviceState.demuxId = demuxId;
|
|
1278
|
+
}
|
|
1279
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1280
|
+
}
|
|
1281
|
+
// Called by Rust via RingRTC object
|
|
1282
|
+
handleNetworkRouteChanged(localNetworkAdapterType) {
|
|
1283
|
+
this._localDeviceState.networkRoute.localAdapterType =
|
|
1284
|
+
localNetworkAdapterType;
|
|
1285
|
+
this._observer.onLocalDeviceStateChanged(this);
|
|
1286
|
+
}
|
|
1287
|
+
handleAudioLevels(capturedLevel, receivedLevels) {
|
|
1288
|
+
this._localDeviceState.audioLevel = normalizeAudioLevel(capturedLevel);
|
|
1289
|
+
if (this._remoteDeviceStates != undefined) {
|
|
1290
|
+
for (const received of receivedLevels) {
|
|
1291
|
+
for (let remoteDeviceState of this._remoteDeviceStates) {
|
|
1292
|
+
if (remoteDeviceState.demuxId == received.demuxId) {
|
|
1293
|
+
remoteDeviceState.audioLevel = normalizeAudioLevel(received.level);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
this._observer.onAudioLevels(this);
|
|
1299
|
+
}
|
|
1300
|
+
// Called by Rust via RingRTC object
|
|
1301
|
+
handleRemoteDevicesChanged(remoteDeviceStates) {
|
|
1302
|
+
var _a;
|
|
1303
|
+
// We don't get aspect ratios from RingRTC, so make sure to copy them over.
|
|
1304
|
+
for (const noo of remoteDeviceStates) {
|
|
1305
|
+
const old = (_a = this._remoteDeviceStates) === null || _a === void 0 ? void 0 : _a.find(old => old.demuxId == noo.demuxId);
|
|
1306
|
+
noo.videoAspectRatio = old === null || old === void 0 ? void 0 : old.videoAspectRatio;
|
|
1307
|
+
}
|
|
1308
|
+
this._remoteDeviceStates = remoteDeviceStates;
|
|
1309
|
+
this._observer.onRemoteDeviceStatesChanged(this);
|
|
1310
|
+
}
|
|
1311
|
+
// Called by Rust via RingRTC object
|
|
1312
|
+
handlePeekChanged(info) {
|
|
1313
|
+
this._peekInfo = info;
|
|
1314
|
+
this._observer.onPeekChanged(this);
|
|
1315
|
+
}
|
|
1316
|
+
// Called by Rust via RingRTC object
|
|
1317
|
+
handleEnded(reason) {
|
|
1318
|
+
this._observer.onEnded(this, reason);
|
|
1319
|
+
this._callManager.deleteGroupCallClient(this._clientId);
|
|
1320
|
+
}
|
|
1321
|
+
// With this, a GroupCall is a VideoFrameSender
|
|
1322
|
+
sendVideoFrame(width, height, format, buffer) {
|
|
1323
|
+
// This assumes we only have one active all.
|
|
1324
|
+
this._callManager.sendVideoFrame(width, height, format, buffer);
|
|
1325
|
+
}
|
|
1326
|
+
// With this, a GroupCall can provide a VideoFrameSource for each remote device.
|
|
1327
|
+
getVideoSource(remoteDemuxId) {
|
|
1328
|
+
return new GroupCallVideoFrameSource(this._callManager, this, remoteDemuxId);
|
|
1329
|
+
}
|
|
1330
|
+
// Called by the GroupCallVideoFrameSource when it receives a video frame.
|
|
1331
|
+
setRemoteAspectRatio(remoteDemuxId, aspectRatio) {
|
|
1332
|
+
var _a;
|
|
1333
|
+
const remoteDevice = (_a = this._remoteDeviceStates) === null || _a === void 0 ? void 0 : _a.find(device => device.demuxId == remoteDemuxId);
|
|
1334
|
+
if (!!remoteDevice && remoteDevice.videoAspectRatio != aspectRatio) {
|
|
1335
|
+
remoteDevice.videoAspectRatio = aspectRatio;
|
|
1336
|
+
this._observer.onRemoteDeviceStatesChanged(this);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
exports.GroupCall = GroupCall;
|
|
1341
|
+
// Implements VideoSource for use in CanvasVideoRenderer
|
|
1342
|
+
class GroupCallVideoFrameSource {
|
|
1343
|
+
constructor(callManager, groupCall, remoteDemuxId // Uint32
|
|
1344
|
+
) {
|
|
1345
|
+
this._callManager = callManager;
|
|
1346
|
+
this._groupCall = groupCall;
|
|
1347
|
+
this._remoteDemuxId = remoteDemuxId;
|
|
1348
|
+
}
|
|
1349
|
+
receiveVideoFrame(buffer) {
|
|
1350
|
+
// This assumes we only have one active all.
|
|
1351
|
+
const frame = this._callManager.receiveGroupCallVideoFrame(this._groupCall.clientId, this._remoteDemuxId, buffer);
|
|
1352
|
+
if (!!frame) {
|
|
1353
|
+
const [width, height] = frame;
|
|
1354
|
+
this._groupCall.setRemoteAspectRatio(this._remoteDemuxId, width / height);
|
|
1355
|
+
}
|
|
1356
|
+
return frame;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
function to_buffer(pbab) {
|
|
1360
|
+
if (!pbab) {
|
|
1361
|
+
return pbab;
|
|
1362
|
+
}
|
|
1363
|
+
if (pbab instanceof Buffer) {
|
|
1364
|
+
return pbab;
|
|
1365
|
+
}
|
|
1366
|
+
return Buffer.from(pbab.toArrayBuffer());
|
|
1367
|
+
}
|
|
1368
|
+
class CallingMessage {
|
|
1369
|
+
}
|
|
1370
|
+
exports.CallingMessage = CallingMessage;
|
|
1371
|
+
class OfferMessage {
|
|
1372
|
+
}
|
|
1373
|
+
exports.OfferMessage = OfferMessage;
|
|
1374
|
+
var OfferType;
|
|
1375
|
+
(function (OfferType) {
|
|
1376
|
+
OfferType[OfferType["AudioCall"] = 0] = "AudioCall";
|
|
1377
|
+
OfferType[OfferType["VideoCall"] = 1] = "VideoCall";
|
|
1378
|
+
})(OfferType = exports.OfferType || (exports.OfferType = {}));
|
|
1379
|
+
class AnswerMessage {
|
|
1380
|
+
}
|
|
1381
|
+
exports.AnswerMessage = AnswerMessage;
|
|
1382
|
+
class IceCandidateMessage {
|
|
1383
|
+
}
|
|
1384
|
+
exports.IceCandidateMessage = IceCandidateMessage;
|
|
1385
|
+
class BusyMessage {
|
|
1386
|
+
}
|
|
1387
|
+
exports.BusyMessage = BusyMessage;
|
|
1388
|
+
class HangupMessage {
|
|
1389
|
+
}
|
|
1390
|
+
exports.HangupMessage = HangupMessage;
|
|
1391
|
+
class OpaqueMessage {
|
|
1392
|
+
}
|
|
1393
|
+
exports.OpaqueMessage = OpaqueMessage;
|
|
1394
|
+
var HangupType;
|
|
1395
|
+
(function (HangupType) {
|
|
1396
|
+
HangupType[HangupType["Normal"] = 0] = "Normal";
|
|
1397
|
+
HangupType[HangupType["Accepted"] = 1] = "Accepted";
|
|
1398
|
+
HangupType[HangupType["Declined"] = 2] = "Declined";
|
|
1399
|
+
HangupType[HangupType["Busy"] = 3] = "Busy";
|
|
1400
|
+
HangupType[HangupType["NeedPermission"] = 4] = "NeedPermission";
|
|
1401
|
+
})(HangupType = exports.HangupType || (exports.HangupType = {}));
|
|
1402
|
+
var BandwidthMode;
|
|
1403
|
+
(function (BandwidthMode) {
|
|
1404
|
+
BandwidthMode[BandwidthMode["VeryLow"] = 0] = "VeryLow";
|
|
1405
|
+
BandwidthMode[BandwidthMode["Low"] = 1] = "Low";
|
|
1406
|
+
BandwidthMode[BandwidthMode["Normal"] = 2] = "Normal";
|
|
1407
|
+
})(BandwidthMode = exports.BandwidthMode || (exports.BandwidthMode = {}));
|
|
1408
|
+
/// Describes why a ring was cancelled.
|
|
1409
|
+
var RingCancelReason;
|
|
1410
|
+
(function (RingCancelReason) {
|
|
1411
|
+
/// The user explicitly clicked "Decline".
|
|
1412
|
+
RingCancelReason[RingCancelReason["DeclinedByUser"] = 0] = "DeclinedByUser";
|
|
1413
|
+
/// The device is busy with another call.
|
|
1414
|
+
RingCancelReason[RingCancelReason["Busy"] = 1] = "Busy";
|
|
1415
|
+
})(RingCancelReason = exports.RingCancelReason || (exports.RingCancelReason = {}));
|
|
1416
|
+
var CallState;
|
|
1417
|
+
(function (CallState) {
|
|
1418
|
+
CallState["Prering"] = "idle";
|
|
1419
|
+
CallState["Ringing"] = "ringing";
|
|
1420
|
+
CallState["Accepted"] = "connected";
|
|
1421
|
+
CallState["Reconnecting"] = "connecting";
|
|
1422
|
+
CallState["Ended"] = "ended";
|
|
1423
|
+
})(CallState = exports.CallState || (exports.CallState = {}));
|
|
1424
|
+
var CallEndedReason;
|
|
1425
|
+
(function (CallEndedReason) {
|
|
1426
|
+
CallEndedReason["LocalHangup"] = "LocalHangup";
|
|
1427
|
+
CallEndedReason["RemoteHangup"] = "RemoteHangup";
|
|
1428
|
+
CallEndedReason["RemoteHangupNeedPermission"] = "RemoteHangupNeedPermission";
|
|
1429
|
+
CallEndedReason["Declined"] = "Declined";
|
|
1430
|
+
CallEndedReason["Busy"] = "Busy";
|
|
1431
|
+
CallEndedReason["Glare"] = "Glare";
|
|
1432
|
+
CallEndedReason["ReCall"] = "ReCall";
|
|
1433
|
+
CallEndedReason["ReceivedOfferExpired"] = "ReceivedOfferExpired";
|
|
1434
|
+
CallEndedReason["ReceivedOfferWhileActive"] = "ReceivedOfferWhileActive";
|
|
1435
|
+
CallEndedReason["ReceivedOfferWithGlare"] = "ReceivedOfferWithGlare";
|
|
1436
|
+
CallEndedReason["SignalingFailure"] = "SignalingFailure";
|
|
1437
|
+
CallEndedReason["GlareFailure"] = "GlareFailure";
|
|
1438
|
+
CallEndedReason["ConnectionFailure"] = "ConnectionFailure";
|
|
1439
|
+
CallEndedReason["InternalFailure"] = "InternalFailure";
|
|
1440
|
+
CallEndedReason["Timeout"] = "Timeout";
|
|
1441
|
+
CallEndedReason["AcceptedOnAnotherDevice"] = "AcceptedOnAnotherDevice";
|
|
1442
|
+
CallEndedReason["DeclinedOnAnotherDevice"] = "DeclinedOnAnotherDevice";
|
|
1443
|
+
CallEndedReason["BusyOnAnotherDevice"] = "BusyOnAnotherDevice";
|
|
1444
|
+
})(CallEndedReason = exports.CallEndedReason || (exports.CallEndedReason = {}));
|
|
1445
|
+
var CallLogLevel;
|
|
1446
|
+
(function (CallLogLevel) {
|
|
1447
|
+
CallLogLevel[CallLogLevel["Off"] = 0] = "Off";
|
|
1448
|
+
CallLogLevel[CallLogLevel["Error"] = 1] = "Error";
|
|
1449
|
+
CallLogLevel[CallLogLevel["Warn"] = 2] = "Warn";
|
|
1450
|
+
CallLogLevel[CallLogLevel["Info"] = 3] = "Info";
|
|
1451
|
+
CallLogLevel[CallLogLevel["Debug"] = 4] = "Debug";
|
|
1452
|
+
CallLogLevel[CallLogLevel["Trace"] = 5] = "Trace";
|
|
1453
|
+
})(CallLogLevel = exports.CallLogLevel || (exports.CallLogLevel = {}));
|
|
1454
|
+
function silly_deadlock_protection(f) {
|
|
1455
|
+
// tslint:disable no-floating-promises
|
|
1456
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
1457
|
+
// This is a silly way of preventing a deadlock.
|
|
1458
|
+
// tslint:disable-next-line await-promise
|
|
1459
|
+
yield 0;
|
|
1460
|
+
f();
|
|
1461
|
+
}))();
|
|
1462
|
+
}
|