@signalapp/ringrtc 2.23.0 → 2.24.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/ringrtc/Service.d.ts +6 -5
- package/dist/ringrtc/Service.js +71 -20
- package/dist/ringrtc/VideoSupport.js +2 -1
- package/dist/test/CallingClass.d.ts +25 -0
- package/dist/test/CallingClass.js +197 -0
- package/dist/test/RingRTC-test.js +145 -5
- package/dist/test/Utils.d.ts +7 -0
- package/dist/test/Utils.js +51 -0
- package/package.json +8 -3
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { GumVideoCaptureOptions, VideoPixelFormatEnum } from './VideoSupport';
|
|
3
3
|
declare class Config {
|
|
4
4
|
use_new_audio_device_module: boolean;
|
|
5
|
+
field_trials: Record<string, string> | undefined;
|
|
5
6
|
}
|
|
6
7
|
declare type GroupId = Buffer;
|
|
7
8
|
declare type GroupCallUserId = Buffer;
|
|
@@ -50,7 +51,8 @@ export declare class RingRTCType {
|
|
|
50
51
|
private _callInfoByCallId;
|
|
51
52
|
private getCallInfoKey;
|
|
52
53
|
handleOutgoingSignaling: ((remoteUserId: UserId, message: CallingMessage) => Promise<boolean>) | null;
|
|
53
|
-
handleIncomingCall: ((call: Call) => Promise<
|
|
54
|
+
handleIncomingCall: ((call: Call) => Promise<boolean>) | null;
|
|
55
|
+
handleStartCall: ((call: Call) => Promise<boolean>) | null;
|
|
54
56
|
handleAutoEndedIncomingCallRequest: ((callId: CallId, remoteUserId: UserId, reason: CallEndedReason, ageSec: number, wasVideoCall: boolean, receivedAtCounter: number | undefined) => void) | null;
|
|
55
57
|
handleLogMessage: ((level: CallLogLevel, fileName: string, line: number, message: string) => void) | null;
|
|
56
58
|
handleSendHttpRequest: ((requestId: number, url: string, method: HttpMethod, headers: {
|
|
@@ -62,11 +64,11 @@ export declare class RingRTCType {
|
|
|
62
64
|
constructor();
|
|
63
65
|
setConfig(config: Config): void;
|
|
64
66
|
setSelfUuid(uuid: Buffer): void;
|
|
65
|
-
startOutgoingCall(remoteUserId: UserId, isVideoCall: boolean, localDeviceId: DeviceId
|
|
67
|
+
startOutgoingCall(remoteUserId: UserId, isVideoCall: boolean, localDeviceId: DeviceId): Call;
|
|
66
68
|
cancelGroupRing(groupId: GroupId, ringId: bigint, reason: RingCancelReason | null): void;
|
|
67
69
|
onStartOutgoingCall(remoteUserId: UserId, callId: CallId): void;
|
|
68
70
|
onStartIncomingCall(remoteUserId: UserId, callId: CallId, isVideoCall: boolean): void;
|
|
69
|
-
|
|
71
|
+
proceed(callId: CallId, settings: CallSettings): void;
|
|
70
72
|
onCallState(remoteUserId: UserId, state: CallState): void;
|
|
71
73
|
onCallEnded(remoteUserId: UserId, callId: CallId, reason: CallEndedReason, ageSec: number): void;
|
|
72
74
|
onRemoteVideoEnabled(remoteUserId: UserId, enabled: boolean): void;
|
|
@@ -153,7 +155,6 @@ export declare class Call {
|
|
|
153
155
|
callId: CallId;
|
|
154
156
|
private readonly _isIncoming;
|
|
155
157
|
private readonly _isVideoCall;
|
|
156
|
-
settings: CallSettings | null;
|
|
157
158
|
private _state;
|
|
158
159
|
private _outgoingAudioEnabled;
|
|
159
160
|
private _outgoingVideoEnabled;
|
|
@@ -172,7 +173,7 @@ export declare class Call {
|
|
|
172
173
|
handleNetworkRouteChanged?: () => void;
|
|
173
174
|
handleAudioLevels?: () => void;
|
|
174
175
|
renderVideoFrame?: (width: number, height: number, buffer: Buffer) => void;
|
|
175
|
-
constructor(callManager: CallManager, remoteUserId: UserId, callId: CallId, isIncoming: boolean, isVideoCall: boolean,
|
|
176
|
+
constructor(callManager: CallManager, remoteUserId: UserId, callId: CallId, isIncoming: boolean, isVideoCall: boolean, state: CallState);
|
|
176
177
|
get remoteUserId(): UserId;
|
|
177
178
|
get isIncoming(): boolean;
|
|
178
179
|
get isVideoCall(): boolean;
|
package/dist/ringrtc/Service.js
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
// Copyright 2019-2021 Signal Messenger, LLC
|
|
4
4
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
5
5
|
//
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
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
|
+
};
|
|
6
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -15,8 +34,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
15
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
35
|
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
36
|
/* tslint:disable max-classes-per-file */
|
|
18
|
-
const os = require("os");
|
|
19
|
-
const process = require("process");
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const process = __importStar(require("process"));
|
|
20
39
|
// tslint:disable-next-line no-var-requires no-require-imports
|
|
21
40
|
const Native = require('../../build/' +
|
|
22
41
|
os.platform() +
|
|
@@ -38,7 +57,10 @@ class NativeCallManager {
|
|
|
38
57
|
this.createCallEndpoint(config);
|
|
39
58
|
}
|
|
40
59
|
createCallEndpoint(config) {
|
|
41
|
-
const
|
|
60
|
+
const fieldTrialsString = Object.entries(config.field_trials || {})
|
|
61
|
+
.map(([k, v]) => `${k}/${v}`)
|
|
62
|
+
.join('/') + '/';
|
|
63
|
+
const callEndpoint = Native.createCallEndpoint(this, config.use_new_audio_device_module, fieldTrialsString);
|
|
42
64
|
Object.defineProperty(this, Native.callEndpointPropertyKey, {
|
|
43
65
|
value: callEndpoint,
|
|
44
66
|
configurable: true, // allows it to be changed
|
|
@@ -207,6 +229,7 @@ class RingRTCType {
|
|
|
207
229
|
// Set by UX
|
|
208
230
|
this.handleOutgoingSignaling = null;
|
|
209
231
|
this.handleIncomingCall = null;
|
|
232
|
+
this.handleStartCall = null;
|
|
210
233
|
this.handleAutoEndedIncomingCallRequest = null;
|
|
211
234
|
this.handleLogMessage = null;
|
|
212
235
|
this.handleSendHttpRequest = null;
|
|
@@ -231,10 +254,10 @@ class RingRTCType {
|
|
|
231
254
|
this.callManager.setSelfUuid(uuid);
|
|
232
255
|
}
|
|
233
256
|
// Called by UX
|
|
234
|
-
startOutgoingCall(remoteUserId, isVideoCall, localDeviceId
|
|
257
|
+
startOutgoingCall(remoteUserId, isVideoCall, localDeviceId) {
|
|
235
258
|
const callId = this.callManager.createOutgoingCall(remoteUserId, isVideoCall, localDeviceId);
|
|
236
259
|
const isIncoming = false;
|
|
237
|
-
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall,
|
|
260
|
+
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall, CallState.Prering);
|
|
238
261
|
this._call = call;
|
|
239
262
|
// We won't actually send anything until the remote side accepts.
|
|
240
263
|
call.outgoingAudioEnabled = true;
|
|
@@ -250,11 +273,26 @@ class RingRTCType {
|
|
|
250
273
|
// Called by Rust
|
|
251
274
|
onStartOutgoingCall(remoteUserId, callId) {
|
|
252
275
|
const call = this._call;
|
|
253
|
-
if (!call || call.remoteUserId !== remoteUserId
|
|
276
|
+
if (!call || call.remoteUserId !== remoteUserId) {
|
|
254
277
|
return;
|
|
255
278
|
}
|
|
256
279
|
call.callId = callId;
|
|
257
|
-
this.
|
|
280
|
+
const handleStartCall = this.handleStartCall;
|
|
281
|
+
if (!handleStartCall) {
|
|
282
|
+
call.ignore();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
handleStartCall(call)
|
|
286
|
+
.then(result => {
|
|
287
|
+
if (!result) {
|
|
288
|
+
this.logWarn('RingRTC.handleStartCall failed for outgoing call. Call ignored.');
|
|
289
|
+
call.ignore();
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
.catch(e => {
|
|
293
|
+
this.logError('RingRTC.handleStartCall exception: ' + e.toString());
|
|
294
|
+
call.ignore();
|
|
295
|
+
});
|
|
258
296
|
}
|
|
259
297
|
// Called by Rust
|
|
260
298
|
onStartIncomingCall(remoteUserId, callId, isVideoCall) {
|
|
@@ -277,24 +315,38 @@ class RingRTCType {
|
|
|
277
315
|
return;
|
|
278
316
|
}
|
|
279
317
|
const isIncoming = true;
|
|
280
|
-
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall,
|
|
281
|
-
// Callback to UX not set
|
|
318
|
+
const call = new Call(this.callManager, remoteUserId, callId, isIncoming, isVideoCall, CallState.Prering);
|
|
282
319
|
const handleIncomingCall = this.handleIncomingCall;
|
|
283
|
-
|
|
320
|
+
const handleStartCall = this.handleStartCall;
|
|
321
|
+
if (!handleIncomingCall || !handleStartCall) {
|
|
284
322
|
call.ignore();
|
|
285
323
|
return;
|
|
286
324
|
}
|
|
287
325
|
this._call = call;
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
326
|
+
handleIncomingCall(call)
|
|
327
|
+
.then(success => {
|
|
328
|
+
if (!success) {
|
|
329
|
+
this.logWarn('RingRTC.handleIncomingCall failed for incoming call. Call ignored.');
|
|
292
330
|
call.ignore();
|
|
293
|
-
return;
|
|
294
331
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
332
|
+
else {
|
|
333
|
+
handleStartCall(call)
|
|
334
|
+
.then(success => {
|
|
335
|
+
if (!success) {
|
|
336
|
+
this.logWarn('RingRTC.handleStartCall failed for incoming call. Call ignored.');
|
|
337
|
+
call.ignore();
|
|
338
|
+
}
|
|
339
|
+
})
|
|
340
|
+
.catch(e => {
|
|
341
|
+
this.logError('RingRTC.handleStartCall exception: ' + e.toString());
|
|
342
|
+
call.ignore();
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
.catch(e => {
|
|
347
|
+
this.logError('RingRTC.handleIncomingCall exception: ' + e.toString());
|
|
348
|
+
call.ignore();
|
|
349
|
+
});
|
|
298
350
|
}
|
|
299
351
|
proceed(callId, settings) {
|
|
300
352
|
silly_deadlock_protection(() => {
|
|
@@ -861,7 +913,7 @@ class RingRTCType {
|
|
|
861
913
|
}
|
|
862
914
|
exports.RingRTCType = RingRTCType;
|
|
863
915
|
class Call {
|
|
864
|
-
constructor(callManager, remoteUserId, callId, isIncoming, isVideoCall,
|
|
916
|
+
constructor(callManager, remoteUserId, callId, isIncoming, isVideoCall, state) {
|
|
865
917
|
this._outgoingAudioEnabled = false;
|
|
866
918
|
this._outgoingVideoEnabled = false;
|
|
867
919
|
this._outgoingVideoIsScreenShare = false;
|
|
@@ -877,7 +929,6 @@ class Call {
|
|
|
877
929
|
this.callId = callId;
|
|
878
930
|
this._isIncoming = isIncoming;
|
|
879
931
|
this._isVideoCall = isVideoCall;
|
|
880
|
-
this.settings = settings;
|
|
881
932
|
this._state = state;
|
|
882
933
|
}
|
|
883
934
|
get remoteUserId() {
|
|
@@ -144,6 +144,7 @@ class GumVideoCapturer {
|
|
|
144
144
|
chromeMediaSourceId: options.screenShareSourceId,
|
|
145
145
|
maxWidth: options.maxWidth,
|
|
146
146
|
maxHeight: options.maxHeight,
|
|
147
|
+
minFrameRate: 1,
|
|
147
148
|
maxFrameRate: options.maxFramerate,
|
|
148
149
|
},
|
|
149
150
|
};
|
|
@@ -230,7 +231,7 @@ class GumVideoCapturer {
|
|
|
230
231
|
if (track == undefined || this.spawnedSenderRunning) {
|
|
231
232
|
return;
|
|
232
233
|
}
|
|
233
|
-
if (track.readyState ===
|
|
234
|
+
if (track.readyState === 'ended') {
|
|
234
235
|
this.stopCapturing();
|
|
235
236
|
index_1.RingRTC.logError(`spawnSender(): Video track ended before spawning sender`);
|
|
236
237
|
return;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare class CallingClass {
|
|
2
|
+
private _name;
|
|
3
|
+
private _id;
|
|
4
|
+
private _localDeviceId;
|
|
5
|
+
private _call;
|
|
6
|
+
private _delayIncomingCallSettingsRequest;
|
|
7
|
+
private _delayOutgoingCallSettingsRequest;
|
|
8
|
+
set delayIncomingCallSettingsRequest(value: number);
|
|
9
|
+
set delayOutgoingCallSettingsRequest(value: number);
|
|
10
|
+
constructor(name: string, id: string);
|
|
11
|
+
private setupCallCallbacks;
|
|
12
|
+
private handleOutgoingSignaling;
|
|
13
|
+
private handleIncomingCall;
|
|
14
|
+
private handleStartCall;
|
|
15
|
+
private handleAutoEndedIncomingCallRequest;
|
|
16
|
+
private handleLogMessage;
|
|
17
|
+
private handleSendHttpRequest;
|
|
18
|
+
private handleSendCallMessage;
|
|
19
|
+
private handleSendCallMessageToGroup;
|
|
20
|
+
private handleGroupCallRingUpdate;
|
|
21
|
+
private getCallSettings;
|
|
22
|
+
initialize(): void;
|
|
23
|
+
startOutgoingDirectCall(remoteUserId: string): Promise<void>;
|
|
24
|
+
hangup(): boolean;
|
|
25
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// Copyright 2023 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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.CallingClass = void 0;
|
|
20
|
+
const Service_1 = require("../ringrtc/Service");
|
|
21
|
+
const index_1 = require("../index");
|
|
22
|
+
const long_1 = __importDefault(require("long"));
|
|
23
|
+
const Utils_1 = require("./Utils");
|
|
24
|
+
// This class mimics the Desktop Client CallingClass in ts/services/calling.ts to facilitate testing
|
|
25
|
+
class CallingClass {
|
|
26
|
+
constructor(name, id) {
|
|
27
|
+
this._name = name;
|
|
28
|
+
this._id = id;
|
|
29
|
+
this._localDeviceId = 1;
|
|
30
|
+
this._delayIncomingCallSettingsRequest = 0;
|
|
31
|
+
this._delayOutgoingCallSettingsRequest = 0;
|
|
32
|
+
}
|
|
33
|
+
set delayIncomingCallSettingsRequest(value) {
|
|
34
|
+
this._delayIncomingCallSettingsRequest = value;
|
|
35
|
+
}
|
|
36
|
+
set delayOutgoingCallSettingsRequest(value) {
|
|
37
|
+
this._delayIncomingCallSettingsRequest = value;
|
|
38
|
+
}
|
|
39
|
+
setupCallCallbacks(call) {
|
|
40
|
+
// eslint-disable-next-line no-param-reassign
|
|
41
|
+
call.handleStateChanged = () => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
(0, Utils_1.log)('handleCallStateChanged');
|
|
43
|
+
(0, Utils_1.log)(`call.state === ${call.state}`);
|
|
44
|
+
if (call.state === Service_1.CallState.Ended) {
|
|
45
|
+
(0, Utils_1.log)(`call.endedReason === ${call.endedReason}`);
|
|
46
|
+
this._call = undefined;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
// eslint-disable-next-line no-param-reassign
|
|
50
|
+
call.handleRemoteVideoEnabled = () => {
|
|
51
|
+
(0, Utils_1.log)('handleRemoteVideoEnabled');
|
|
52
|
+
};
|
|
53
|
+
// eslint-disable-next-line no-param-reassign
|
|
54
|
+
call.handleRemoteSharingScreen = () => {
|
|
55
|
+
(0, Utils_1.log)('handleRemoteSharingScreen');
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
59
|
+
// Callbacks
|
|
60
|
+
handleOutgoingSignaling(remoteUserId, message, urgency) {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
(0, Utils_1.log)('handleOutgoingSignaling remoteUserId: ' + remoteUserId);
|
|
63
|
+
return true;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
handleIncomingCall(call) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
(0, Utils_1.log)('handleIncomingCall');
|
|
69
|
+
this._call = call;
|
|
70
|
+
this.setupCallCallbacks(call);
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
handleStartCall(call) {
|
|
75
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
76
|
+
const callSettings = yield this.getCallSettings(call.isIncoming);
|
|
77
|
+
index_1.RingRTC.proceed(call.callId, callSettings);
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
handleAutoEndedIncomingCallRequest(callId, remoteUserId, reason, ageInSeconds, wasVideoCall, receivedAtCounter) {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
(0, Utils_1.log)('handleAutoEndedIncomingCallRequest');
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
handleLogMessage(level, fileName, line, message) {
|
|
87
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
88
|
+
switch (level) {
|
|
89
|
+
case Service_1.CallLogLevel.Info:
|
|
90
|
+
// FgGray
|
|
91
|
+
console.log(`\x1b[90m${fileName}:${line} ${message}\x1b[0m`);
|
|
92
|
+
break;
|
|
93
|
+
case Service_1.CallLogLevel.Warn:
|
|
94
|
+
// FgYellow
|
|
95
|
+
console.warn(`\x1b[33m${fileName}:${line} ${message}\x1b[0m`);
|
|
96
|
+
break;
|
|
97
|
+
case Service_1.CallLogLevel.Error:
|
|
98
|
+
// FgRed
|
|
99
|
+
console.error(`\x1b[31m${fileName}:${line} ${message}\x1b[0m`);
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
handleSendHttpRequest(requestId, url, method, headers, body) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
(0, Utils_1.log)('handleSendHttpRequest');
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
handleSendCallMessage(recipient, data, urgency) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
(0, Utils_1.log)('handleSendCallMessage');
|
|
114
|
+
return true;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
handleSendCallMessageToGroup(groupIdBytes, data, urgency) {
|
|
118
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
119
|
+
(0, Utils_1.log)('handleSendCallMessageToGroup');
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
handleGroupCallRingUpdate(groupIdBytes, ringId, ringerBytes, update) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
(0, Utils_1.log)('handleGroupCallRingUpdate');
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
128
|
+
// Support
|
|
129
|
+
getCallSettings(isIncoming) {
|
|
130
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
+
if (isIncoming) {
|
|
132
|
+
(0, Utils_1.log)('getCallSettings delayed by ' +
|
|
133
|
+
this._delayIncomingCallSettingsRequest.toString() +
|
|
134
|
+
'ms');
|
|
135
|
+
yield (0, Utils_1.sleep)(this._delayIncomingCallSettingsRequest);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
(0, Utils_1.log)('getCallSettings delayed by ' +
|
|
139
|
+
this._delayOutgoingCallSettingsRequest.toString() +
|
|
140
|
+
'ms');
|
|
141
|
+
yield (0, Utils_1.sleep)(this._delayOutgoingCallSettingsRequest);
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
iceServer: {
|
|
145
|
+
urls: ['stun:turn3.voip.signal.org'],
|
|
146
|
+
},
|
|
147
|
+
hideIp: false,
|
|
148
|
+
bandwidthMode: Service_1.BandwidthMode.Normal,
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
153
|
+
// Actions
|
|
154
|
+
initialize() {
|
|
155
|
+
(0, Utils_1.log)('initialize');
|
|
156
|
+
index_1.RingRTC.setConfig({
|
|
157
|
+
use_new_audio_device_module: true,
|
|
158
|
+
field_trials: undefined,
|
|
159
|
+
});
|
|
160
|
+
index_1.RingRTC.handleOutgoingSignaling = this.handleOutgoingSignaling.bind(this);
|
|
161
|
+
index_1.RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this);
|
|
162
|
+
index_1.RingRTC.handleStartCall = this.handleStartCall.bind(this);
|
|
163
|
+
index_1.RingRTC.handleAutoEndedIncomingCallRequest =
|
|
164
|
+
this.handleAutoEndedIncomingCallRequest.bind(this);
|
|
165
|
+
index_1.RingRTC.handleLogMessage = this.handleLogMessage.bind(this);
|
|
166
|
+
index_1.RingRTC.handleSendHttpRequest = this.handleSendHttpRequest.bind(this);
|
|
167
|
+
index_1.RingRTC.handleSendCallMessage = this.handleSendCallMessage.bind(this);
|
|
168
|
+
index_1.RingRTC.handleSendCallMessageToGroup =
|
|
169
|
+
this.handleSendCallMessageToGroup.bind(this);
|
|
170
|
+
index_1.RingRTC.handleGroupCallRingUpdate =
|
|
171
|
+
this.handleGroupCallRingUpdate.bind(this);
|
|
172
|
+
index_1.RingRTC.setSelfUuid(Buffer.from((0, Utils_1.uuidToBytes)(this._id)));
|
|
173
|
+
}
|
|
174
|
+
startOutgoingDirectCall(remoteUserId) {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
(0, Utils_1.log)('startOutgoingDirectCall');
|
|
177
|
+
if (index_1.RingRTC.call && index_1.RingRTC.call.state !== Service_1.CallState.Ended) {
|
|
178
|
+
(0, Utils_1.log)('Call already in progress, new call not allowed.');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const call = index_1.RingRTC.startOutgoingCall(remoteUserId, false, this._localDeviceId);
|
|
182
|
+
(0, Utils_1.log)('Outgoing callId ' + long_1.default.fromValue(call.callId).toString());
|
|
183
|
+
index_1.RingRTC.setOutgoingAudio(call.callId, true);
|
|
184
|
+
this._call = call;
|
|
185
|
+
this.setupCallCallbacks(call);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
hangup() {
|
|
189
|
+
(0, Utils_1.log)('hangup');
|
|
190
|
+
if (this._call) {
|
|
191
|
+
index_1.RingRTC.hangup(this._call.callId);
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.CallingClass = CallingClass;
|
|
@@ -12,15 +12,46 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
13
|
});
|
|
14
14
|
};
|
|
15
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
16
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
17
|
+
};
|
|
15
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
19
|
const chai_1 = require("chai");
|
|
17
|
-
const
|
|
20
|
+
const chai_as_promised_1 = __importDefault(require("chai-as-promised"));
|
|
21
|
+
const crypto_1 = require("crypto");
|
|
18
22
|
const index_1 = require("../index");
|
|
19
|
-
|
|
23
|
+
const long_1 = __importDefault(require("long"));
|
|
24
|
+
const chai_2 = require("chai");
|
|
25
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
26
|
+
const sinon_chai_1 = __importDefault(require("sinon-chai"));
|
|
27
|
+
const CallingClass_1 = require("./CallingClass");
|
|
28
|
+
const Utils_1 = require("./Utils");
|
|
29
|
+
(0, chai_1.use)(chai_as_promised_1.default);
|
|
30
|
+
(0, chai_2.should)();
|
|
31
|
+
(0, chai_1.use)(sinon_chai_1.default);
|
|
32
|
+
function generateOfferCallingMessage(callId) {
|
|
33
|
+
// Audio-only hex based SDP generated from a direct client call
|
|
34
|
+
const audioOnlySdp = Buffer.from('22560a204b18bc751315cb718c643db7b3a65aaabe826c7094932afaf5aebc86d36bb6491204484b6b481a18524b3041496f63334245514e5670424b57786f38787051712204082e1034220408281034220208082880897a', 'hex');
|
|
35
|
+
return {
|
|
36
|
+
offer: {
|
|
37
|
+
callId: callId,
|
|
38
|
+
opaque: audioOnlySdp,
|
|
39
|
+
type: index_1.OfferType.AudioCall,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
20
43
|
describe('RingRTC', () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
44
|
+
const identity_key_length = 31;
|
|
45
|
+
const user1_name = 'user1';
|
|
46
|
+
const user1_id = '11';
|
|
47
|
+
const user1_device_id = 11;
|
|
48
|
+
const user1_identity_key = (0, crypto_1.randomBytes)(identity_key_length);
|
|
49
|
+
const user2_id = '22';
|
|
50
|
+
const user2_device_id = 22;
|
|
51
|
+
const user2_identity_key = (0, crypto_1.randomBytes)(identity_key_length);
|
|
52
|
+
let handleOutgoingSignalingSpy;
|
|
53
|
+
let handleIncomingCallSpy;
|
|
54
|
+
let handleAutoEndedIncomingCallRequestSpy;
|
|
24
55
|
it('reports an age for expired offers', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
56
|
const offer = {
|
|
26
57
|
offer: {
|
|
@@ -68,4 +99,113 @@ describe('RingRTC', () => {
|
|
|
68
99
|
index_1.RingRTC.handleAutoEndedIncomingCallRequest = null;
|
|
69
100
|
}
|
|
70
101
|
}));
|
|
102
|
+
function initializeSpies() {
|
|
103
|
+
handleAutoEndedIncomingCallRequestSpy = sinon_1.default.spy(index_1.RingRTC, 'handleAutoEndedIncomingCallRequest');
|
|
104
|
+
handleIncomingCallSpy = sinon_1.default.spy(index_1.RingRTC, 'handleIncomingCall');
|
|
105
|
+
handleOutgoingSignalingSpy = sinon_1.default.spy(index_1.RingRTC, 'handleOutgoingSignaling');
|
|
106
|
+
}
|
|
107
|
+
it('can initialize RingRTC', () => {
|
|
108
|
+
chai_1.assert.isNotNull(index_1.RingRTC, "RingRTC didn't initialize!");
|
|
109
|
+
});
|
|
110
|
+
it('can establish outgoing call', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
112
|
+
calling.initialize();
|
|
113
|
+
initializeSpies();
|
|
114
|
+
yield calling.startOutgoingDirectCall(user2_id);
|
|
115
|
+
yield (0, Utils_1.sleep)(1000);
|
|
116
|
+
// An offer and at least one ICE message should have been sent.
|
|
117
|
+
(0, chai_1.expect)(handleOutgoingSignalingSpy.callCount).to.be.gt(1);
|
|
118
|
+
yield (0, Utils_1.sleep)(2000);
|
|
119
|
+
// Cleanup.
|
|
120
|
+
const handleStateChangedSpy = sinon_1.default.spy(index_1.RingRTC.call, 'handleStateChanged');
|
|
121
|
+
(0, chai_1.expect)(calling.hangup()).to.be.true;
|
|
122
|
+
yield (0, Utils_1.sleep)(500);
|
|
123
|
+
handleStateChangedSpy.should.have.been.calledOnce;
|
|
124
|
+
(0, chai_1.expect)(calling.hangup()).to.be.false;
|
|
125
|
+
yield (0, Utils_1.sleep)(100);
|
|
126
|
+
}));
|
|
127
|
+
it('can establish incoming call', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
128
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
129
|
+
calling.initialize();
|
|
130
|
+
initializeSpies();
|
|
131
|
+
// Generate incoming calling message
|
|
132
|
+
const message_age_sec = 1;
|
|
133
|
+
const message_received_at_counter = 10;
|
|
134
|
+
const callId = new long_1.default(1, 1, true);
|
|
135
|
+
const offerCallingMessage = generateOfferCallingMessage(callId);
|
|
136
|
+
index_1.RingRTC.handleCallingMessage(user2_id, Buffer.from((0, Utils_1.uuidToBytes)(user2_id)), user2_device_id, user1_device_id, message_age_sec, message_received_at_counter, offerCallingMessage, user2_identity_key, user1_identity_key);
|
|
137
|
+
yield (0, Utils_1.sleep)(1000);
|
|
138
|
+
handleIncomingCallSpy.should.have.been.calledOnce;
|
|
139
|
+
chai_1.assert.equal(index_1.CallState.Prering, index_1.RingRTC.call.state);
|
|
140
|
+
// Hangup call
|
|
141
|
+
(0, chai_1.expect)(calling.hangup()).to.be.true;
|
|
142
|
+
yield (0, Utils_1.sleep)(500);
|
|
143
|
+
// Validate hangup related callbacks and call state
|
|
144
|
+
handleAutoEndedIncomingCallRequestSpy.should.have.been.calledOnce;
|
|
145
|
+
(0, chai_1.expect)(handleOutgoingSignalingSpy.callCount).to.be.gt(1);
|
|
146
|
+
chai_1.assert.equal(index_1.CallState.Ended, index_1.RingRTC.call.state);
|
|
147
|
+
}));
|
|
148
|
+
it('outgoing call wins glare when incoming call id is lower', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
149
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
150
|
+
calling.initialize();
|
|
151
|
+
initializeSpies();
|
|
152
|
+
yield runGlareScenario(calling, true, 0, 0);
|
|
153
|
+
}));
|
|
154
|
+
it('outgoing call wins glare when incoming call id is lower even when outgoing call settings are delayed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
155
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
156
|
+
calling.initialize();
|
|
157
|
+
initializeSpies();
|
|
158
|
+
yield runGlareScenario(calling, true, 0, 1000);
|
|
159
|
+
}));
|
|
160
|
+
it('outgoing call loses glare when incoming call id is higher even when outgoing call settings are delayed', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
162
|
+
calling.initialize();
|
|
163
|
+
initializeSpies();
|
|
164
|
+
yield runGlareScenario(calling, false, 0, 1000);
|
|
165
|
+
}));
|
|
166
|
+
it('outgoing call loses glare when incoming call id is higher', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
167
|
+
let calling = new CallingClass_1.CallingClass(user1_name, user1_id);
|
|
168
|
+
calling.initialize();
|
|
169
|
+
initializeSpies();
|
|
170
|
+
yield runGlareScenario(calling, false, 0, 0);
|
|
171
|
+
}));
|
|
172
|
+
function runGlareScenario(calling, outgoingWinner, delayIncomingCallSetings, delayOutgoingCallSetings) {
|
|
173
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
174
|
+
calling.delayOutgoingCallSettingsRequest = delayOutgoingCallSetings;
|
|
175
|
+
calling.delayIncomingCallSettingsRequest = delayIncomingCallSetings;
|
|
176
|
+
const outgoingCallLatch = (0, Utils_1.countDownLatch)(1);
|
|
177
|
+
calling
|
|
178
|
+
.startOutgoingDirectCall(user2_id)
|
|
179
|
+
.then(result => {
|
|
180
|
+
(0, Utils_1.log)('Outgoing call succeeded as expected');
|
|
181
|
+
outgoingCallLatch.countDown();
|
|
182
|
+
})
|
|
183
|
+
.catch(e => {
|
|
184
|
+
chai_1.assert.fail('Outgoing call should not have failed');
|
|
185
|
+
});
|
|
186
|
+
yield outgoingCallLatch.finished;
|
|
187
|
+
const outgoingCallId = long_1.default.fromValue(index_1.RingRTC.call.callId);
|
|
188
|
+
// Generate a call id based on the desired glare winner
|
|
189
|
+
const incomingCallId = outgoingCallId.unsigned
|
|
190
|
+
? new long_1.default(outgoingWinner ? outgoingCallId.low - 1 : outgoingCallId.low + 1, outgoingCallId.high, outgoingCallId.unsigned)
|
|
191
|
+
: new long_1.default(outgoingWinner ? outgoingCallId.low + 1 : outgoingCallId.low - 1, outgoingCallId.high, outgoingCallId.unsigned);
|
|
192
|
+
// Generate incoming calling message
|
|
193
|
+
const message_age_sec = 1;
|
|
194
|
+
const message_received_at_counter = 10;
|
|
195
|
+
const offerCallingMessage = generateOfferCallingMessage(incomingCallId);
|
|
196
|
+
// Initiate an incoming call
|
|
197
|
+
index_1.RingRTC.handleCallingMessage(user2_id, Buffer.from((0, Utils_1.uuidToBytes)(user2_id)), user2_device_id, user1_device_id, message_age_sec, message_received_at_counter, offerCallingMessage, user2_identity_key, user1_identity_key);
|
|
198
|
+
yield (0, Utils_1.sleep)(1000);
|
|
199
|
+
if (outgoingWinner) {
|
|
200
|
+
chai_1.assert.isTrue(outgoingCallId.eq(long_1.default.fromValue(index_1.RingRTC.call.callId)));
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
chai_1.assert.isTrue(incomingCallId.eq(long_1.default.fromValue(index_1.RingRTC.call.callId)));
|
|
204
|
+
}
|
|
205
|
+
// Cleanup.
|
|
206
|
+
(0, chai_1.expect)(calling.hangup()).to.be.true;
|
|
207
|
+
yield (0, Utils_1.sleep)(500);
|
|
208
|
+
chai_1.assert.equal(index_1.CallState.Ended, index_1.RingRTC.call.state);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
71
211
|
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function countDownLatch(count: number): {
|
|
2
|
+
countDown: () => void;
|
|
3
|
+
finished: Promise<unknown>;
|
|
4
|
+
};
|
|
5
|
+
export declare function log(line: string): void;
|
|
6
|
+
export declare let sleep: (timeout: number) => Promise<void>;
|
|
7
|
+
export declare function uuidToBytes(uuid: string): Uint8Array;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
//
|
|
3
|
+
// Copyright 2023 Signal Messenger, LLC
|
|
4
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
5
|
+
//
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.uuidToBytes = exports.sleep = exports.log = exports.countDownLatch = void 0;
|
|
8
|
+
const lodash_1 = require("lodash");
|
|
9
|
+
const chai_1 = require("chai");
|
|
10
|
+
function countDownLatch(count) {
|
|
11
|
+
(0, chai_1.assert)(count > 0, 'count must be a positive number');
|
|
12
|
+
let resolve;
|
|
13
|
+
const finished = new Promise(resolveInternal => {
|
|
14
|
+
resolve = resolveInternal;
|
|
15
|
+
});
|
|
16
|
+
const countDown = () => {
|
|
17
|
+
count--;
|
|
18
|
+
if (count == 0) {
|
|
19
|
+
resolve();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
countDown: countDown,
|
|
24
|
+
finished,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
exports.countDownLatch = countDownLatch;
|
|
28
|
+
function log(line) {
|
|
29
|
+
// Standard logging used for checkpoints.
|
|
30
|
+
// Use --renderer to see the log output. (edit: Maybe always shown now?)
|
|
31
|
+
// BgYellow
|
|
32
|
+
console.log(`\x1b[43m${line}\x1b[0m`);
|
|
33
|
+
}
|
|
34
|
+
exports.log = log;
|
|
35
|
+
let sleep = (timeout) => {
|
|
36
|
+
return new Promise(resolve => {
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
// BgBlue
|
|
39
|
+
console.log(`\x1b[44msleeping ${timeout} ms\x1b[0m`);
|
|
40
|
+
resolve();
|
|
41
|
+
}, timeout);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
exports.sleep = sleep;
|
|
45
|
+
function uuidToBytes(uuid) {
|
|
46
|
+
if (uuid.length !== 36) {
|
|
47
|
+
return new Uint8Array(0);
|
|
48
|
+
}
|
|
49
|
+
return Uint8Array.from((0, lodash_1.chunk)(uuid.replace(/-/g, ''), 2).map(pair => parseInt(pair.join(''), 16)));
|
|
50
|
+
}
|
|
51
|
+
exports.uuidToBytes = uuidToBytes;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalapp/ringrtc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "Signal Messenger voice and video calling library.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"clean": "rimraf dist",
|
|
14
|
-
"test": "electron-mocha --recursive dist/test",
|
|
14
|
+
"test": "electron-mocha --recursive dist/test --timeout 10000",
|
|
15
15
|
"eslint": "eslint --cache .",
|
|
16
16
|
"lint": "yarn format --list-different && yarn eslint",
|
|
17
17
|
"format": "prettier --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"config": {
|
|
22
22
|
"prebuildUrl": "https://build-artifacts.signal.org/libraries/ringrtc-desktop-build-v${npm_package_version}.tar.gz",
|
|
23
|
-
"prebuildChecksum": "
|
|
23
|
+
"prebuildChecksum": "7f7f7a795d4387d59e5262c6a3b8efb910f2d3f9c60a18316e49bb9a29d65343"
|
|
24
24
|
},
|
|
25
25
|
"author": "",
|
|
26
26
|
"license": "AGPL-3.0-only",
|
|
@@ -31,10 +31,12 @@
|
|
|
31
31
|
"@types/chai": "4.2.18",
|
|
32
32
|
"@types/chai-as-promised": "^7.1.3",
|
|
33
33
|
"@types/dom-mediacapture-transform": "0.1.2",
|
|
34
|
+
"@types/long": "4.0.1",
|
|
34
35
|
"@types/mocha": "5.2.7",
|
|
35
36
|
"@types/node": "14.14.37",
|
|
36
37
|
"@types/offscreencanvas": "^2019.6.4",
|
|
37
38
|
"@types/react": "16.8.5",
|
|
39
|
+
"@types/sinon-chai": "^3.2.9",
|
|
38
40
|
"chai": "4.3.5",
|
|
39
41
|
"chai-as-promised": "^7.1.1",
|
|
40
42
|
"electron": "15.3.2",
|
|
@@ -46,9 +48,12 @@
|
|
|
46
48
|
"eslint-plugin-mocha": "9.0.0",
|
|
47
49
|
"eslint-plugin-more": "1.0.5",
|
|
48
50
|
"eslint-plugin-react": "7.28.0",
|
|
51
|
+
"long": "4.0.0",
|
|
49
52
|
"mocha": "9.2.0",
|
|
50
53
|
"prettier": "^2.5.1",
|
|
51
54
|
"rimraf": "3.0.2",
|
|
55
|
+
"sinon": "^15.0.1",
|
|
56
|
+
"sinon-chai": "^3.7.0",
|
|
52
57
|
"typescript": "4.5.2",
|
|
53
58
|
"yarn-audit-fix": "^9.2.2"
|
|
54
59
|
}
|