@signalapp/ringrtc 2.26.3 → 2.27.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 CHANGED
@@ -1,4 +1,5 @@
1
1
  import { RingRTCType } from './ringrtc/Service';
2
- export { AnswerMessage, AudioDevice, BandwidthMode, BusyMessage, Call, CallEndedReason, CallId, CallLogLevel, CallMessageUrgency, CallSettings, CallState, CallingMessage, ConnectionState, DeviceId, GroupCall, GroupCallEndReason, GroupCallObserver, GroupMemberInfo, HangupMessage, HangupType, HttpMethod, IceCandidateMessage, JoinState, LocalDeviceState, OfferMessage, OfferType, OpaqueMessage, PeekInfo, RemoteDeviceState, RingCancelReason, RingRTCType, RingUpdate, UserId, VideoCapturer, VideoRenderer, VideoRequest, callIdFromEra, callIdFromRingId, } from './ringrtc/Service';
2
+ export { AnswerMessage, AudioDevice, BandwidthMode, BusyMessage, Call, CallEndedReason, CallId, CallLogLevel, CallMessageUrgency, CallSettings, CallState, CallingMessage, ConnectionState, DeviceId, GroupCall, GroupCallEndReason, GroupCallObserver, GroupMemberInfo, HangupMessage, HangupType, HttpMethod, HttpResult, IceCandidateMessage, JoinState, LocalDeviceState, OfferMessage, OfferType, OpaqueMessage, PeekInfo, RemoteDeviceState, RingCancelReason, RingRTCType, RingUpdate, UserId, VideoCapturer, VideoRenderer, VideoRequest, callIdFromEra, callIdFromRingId, } from './ringrtc/Service';
3
3
  export { CanvasVideoRenderer, GumVideoCapturer, VideoFrameSource, MAX_VIDEO_CAPTURE_AREA, MAX_VIDEO_CAPTURE_BUFFER_SIZE, MAX_VIDEO_CAPTURE_HEIGHT, MAX_VIDEO_CAPTURE_WIDTH, } from './ringrtc/VideoSupport';
4
+ export { CallLinkRootKey, CallLinkRestrictions, CallLinkState, } from './ringrtc/CallLinks';
4
5
  export declare const RingRTC: RingRTCType;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  // SPDX-License-Identifier: AGPL-3.0-only
5
5
  //
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.RingRTC = exports.MAX_VIDEO_CAPTURE_WIDTH = exports.MAX_VIDEO_CAPTURE_HEIGHT = exports.MAX_VIDEO_CAPTURE_BUFFER_SIZE = exports.MAX_VIDEO_CAPTURE_AREA = exports.GumVideoCapturer = exports.CanvasVideoRenderer = exports.callIdFromRingId = exports.callIdFromEra = exports.VideoRequest = exports.RingUpdate = exports.RingRTCType = exports.RingCancelReason = exports.RemoteDeviceState = exports.PeekInfo = exports.OpaqueMessage = exports.OfferType = exports.OfferMessage = exports.LocalDeviceState = exports.JoinState = exports.IceCandidateMessage = exports.HttpMethod = exports.HangupType = exports.HangupMessage = exports.GroupMemberInfo = exports.GroupCallEndReason = exports.GroupCall = exports.ConnectionState = exports.CallingMessage = exports.CallState = exports.CallMessageUrgency = exports.CallLogLevel = exports.CallEndedReason = exports.Call = exports.BusyMessage = exports.BandwidthMode = exports.AnswerMessage = void 0;
7
+ exports.RingRTC = exports.CallLinkState = exports.CallLinkRestrictions = exports.CallLinkRootKey = exports.MAX_VIDEO_CAPTURE_WIDTH = exports.MAX_VIDEO_CAPTURE_HEIGHT = exports.MAX_VIDEO_CAPTURE_BUFFER_SIZE = exports.MAX_VIDEO_CAPTURE_AREA = exports.GumVideoCapturer = exports.CanvasVideoRenderer = exports.callIdFromRingId = exports.callIdFromEra = exports.VideoRequest = exports.RingUpdate = exports.RingRTCType = exports.RingCancelReason = exports.RemoteDeviceState = exports.PeekInfo = exports.OpaqueMessage = exports.OfferType = exports.OfferMessage = exports.LocalDeviceState = exports.JoinState = exports.IceCandidateMessage = exports.HttpMethod = exports.HangupType = exports.HangupMessage = exports.GroupMemberInfo = exports.GroupCallEndReason = exports.GroupCall = exports.ConnectionState = exports.CallingMessage = exports.CallState = exports.CallMessageUrgency = exports.CallLogLevel = exports.CallEndedReason = exports.Call = exports.BusyMessage = exports.BandwidthMode = exports.AnswerMessage = void 0;
8
8
  const Service_1 = require("./ringrtc/Service");
9
9
  var Service_2 = require("./ringrtc/Service");
10
10
  Object.defineProperty(exports, "AnswerMessage", { enumerable: true, get: function () { return Service_2.AnswerMessage; } });
@@ -44,5 +44,9 @@ Object.defineProperty(exports, "MAX_VIDEO_CAPTURE_AREA", { enumerable: true, get
44
44
  Object.defineProperty(exports, "MAX_VIDEO_CAPTURE_BUFFER_SIZE", { enumerable: true, get: function () { return VideoSupport_1.MAX_VIDEO_CAPTURE_BUFFER_SIZE; } });
45
45
  Object.defineProperty(exports, "MAX_VIDEO_CAPTURE_HEIGHT", { enumerable: true, get: function () { return VideoSupport_1.MAX_VIDEO_CAPTURE_HEIGHT; } });
46
46
  Object.defineProperty(exports, "MAX_VIDEO_CAPTURE_WIDTH", { enumerable: true, get: function () { return VideoSupport_1.MAX_VIDEO_CAPTURE_WIDTH; } });
47
+ var CallLinks_1 = require("./ringrtc/CallLinks");
48
+ Object.defineProperty(exports, "CallLinkRootKey", { enumerable: true, get: function () { return CallLinks_1.CallLinkRootKey; } });
49
+ Object.defineProperty(exports, "CallLinkRestrictions", { enumerable: true, get: function () { return CallLinks_1.CallLinkRestrictions; } });
50
+ Object.defineProperty(exports, "CallLinkState", { enumerable: true, get: function () { return CallLinks_1.CallLinkState; } });
47
51
  exports.RingRTC = new Service_1.RingRTCType();
48
52
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,23 @@
1
+ /// <reference types="node" />
2
+ export declare class CallLinkRootKey {
3
+ readonly bytes: Buffer;
4
+ private constructor();
5
+ static parse(str: string): CallLinkRootKey;
6
+ static fromBytes(bytes: Buffer): CallLinkRootKey;
7
+ static generate(): CallLinkRootKey;
8
+ static generateAdminPassKey(): Buffer;
9
+ deriveRoomId(): Buffer;
10
+ toString(): string;
11
+ }
12
+ export declare class CallLinkState {
13
+ name: string;
14
+ restrictions: CallLinkRestrictions;
15
+ revoked: boolean;
16
+ expiration: Date;
17
+ constructor(name: string, restrictions: CallLinkRestrictions, revoked: boolean, expiration: Date);
18
+ }
19
+ export declare enum CallLinkRestrictions {
20
+ None = 0,
21
+ AdminApproval = 1,
22
+ Unknown = 2
23
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ //
3
+ // Copyright 2023 Signal Messenger, LLC
4
+ // SPDX-License-Identifier: AGPL-3.0-only
5
+ //
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CallLinkRestrictions = exports.CallLinkState = exports.CallLinkRootKey = void 0;
11
+ const Native_1 = __importDefault(require("./Native"));
12
+ class CallLinkRootKey {
13
+ constructor(bytes) {
14
+ this.bytes = bytes;
15
+ }
16
+ static parse(str) {
17
+ return new CallLinkRootKey(Native_1.default.CallLinkRootKey_parse(str));
18
+ }
19
+ static fromBytes(bytes) {
20
+ Native_1.default.CallLinkRootKey_validate(bytes);
21
+ return new CallLinkRootKey(bytes);
22
+ }
23
+ static generate() {
24
+ return new CallLinkRootKey(Native_1.default.CallLinkRootKey_generate());
25
+ }
26
+ static generateAdminPassKey() {
27
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
28
+ return Native_1.default.CallLinkRootKey_generateAdminPasskey();
29
+ }
30
+ deriveRoomId() {
31
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
32
+ return Native_1.default.CallLinkRootKey_deriveRoomId(this.bytes);
33
+ }
34
+ toString() {
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
36
+ return Native_1.default.CallLinkRootKey_toFormattedString(this.bytes);
37
+ }
38
+ }
39
+ exports.CallLinkRootKey = CallLinkRootKey;
40
+ class CallLinkState {
41
+ constructor(name, restrictions, revoked, expiration) {
42
+ this.name = name;
43
+ this.restrictions = restrictions;
44
+ this.revoked = revoked;
45
+ this.expiration = expiration;
46
+ }
47
+ }
48
+ exports.CallLinkState = CallLinkState;
49
+ var CallLinkRestrictions;
50
+ (function (CallLinkRestrictions) {
51
+ CallLinkRestrictions[CallLinkRestrictions["None"] = 0] = "None";
52
+ CallLinkRestrictions[CallLinkRestrictions["AdminApproval"] = 1] = "AdminApproval";
53
+ CallLinkRestrictions[CallLinkRestrictions["Unknown"] = 2] = "Unknown";
54
+ })(CallLinkRestrictions = exports.CallLinkRestrictions || (exports.CallLinkRestrictions = {}));
55
+ //# sourceMappingURL=CallLinks.js.map
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ //
3
+ // Copyright 2023 Signal Messenger, LLC
4
+ // SPDX-License-Identifier: AGPL-3.0-only
5
+ //
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ const os = __importStar(require("os"));
31
+ const process = __importStar(require("process"));
32
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require
33
+ exports.default = require(`../../build/${os.platform()}/libringrtc-${process.arch}.node`);
34
+ //# sourceMappingURL=Native.js.map
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { GumVideoCaptureOptions, VideoPixelFormatEnum } from './VideoSupport';
3
+ import { CallLinkState, CallLinkRestrictions, CallLinkRootKey } from './CallLinks';
3
4
  export declare const callIdFromEra: (era: string) => CallId;
4
5
  export declare function callIdFromRingId(ringId: bigint): CallId;
5
6
  declare class Config {
@@ -45,11 +46,19 @@ export declare class ReceivedAudioLevel {
45
46
  level: RawAudioLevel;
46
47
  constructor(demuxId: number, level: RawAudioLevel);
47
48
  }
49
+ export type HttpResult<T> = {
50
+ success: true;
51
+ value: T;
52
+ } | {
53
+ success: false;
54
+ errorStatusCode: number;
55
+ };
48
56
  export declare class RingRTCType {
49
57
  private readonly callManager;
50
58
  private _call;
51
59
  private _groupCallByClientId;
52
60
  private _peekRequests;
61
+ private _callLinkRequests;
53
62
  private _callInfoByCallId;
54
63
  private getCallInfoKey;
55
64
  handleOutgoingSignaling: ((remoteUserId: UserId, message: CallingMessage) => Promise<boolean>) | null;
@@ -84,6 +93,101 @@ export declare class RingRTCType {
84
93
  onSendHangup(remoteUserId: UserId, remoteDeviceId: DeviceId, callId: CallId, broadcast: boolean, hangupType: HangupType, deviceId: DeviceId | null): void;
85
94
  onSendBusy(remoteUserId: UserId, remoteDeviceId: DeviceId, callId: CallId, broadcast: boolean): void;
86
95
  private sendSignaling;
96
+ /**
97
+ * Asynchronous request to get information about a call link.
98
+ *
99
+ * @param sfuUrl - the URL to use when accessing the SFU
100
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
101
+ * @param linkRootKey - the root key for the call link
102
+ *
103
+ * Expected failure codes include:
104
+ * - 404: the room does not exist (or expired so long ago that it has been removed from the server)
105
+ */
106
+ readCallLink(sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: CallLinkRootKey): Promise<HttpResult<CallLinkState>>;
107
+ /**
108
+ * Asynchronous request to create a new call link.
109
+ *
110
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
111
+ *
112
+ * @example
113
+ * const linkKey = CallLinkRootKey.generate();
114
+ * const adminPasskey = CallLinkRootKey.generateAdminPasskey();
115
+ * const roomId = linkKey.deriveRoomId();
116
+ * const credential = requestCreateCredentialFromChatServer(roomId); // using libsignal
117
+ * const secretParams = CallLinkSecretParams.deriveFromRootKey(linkKey.bytes);
118
+ * const credentialPresentation = credential.present(roomId, secretParams).serialize();
119
+ * const serializedPublicParams = secretParams.getPublicParams().serialize();
120
+ * const result = await RingRTC.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams);
121
+ * if (result.success) {
122
+ * const state = result.value;
123
+ * // In actuality you may not want to do this until the user clicks Done.
124
+ * saveToDatabase(linkKey.bytes, adminPasskey, state);
125
+ * syncToOtherDevices(linkKey.bytes, adminPasskey);
126
+ * } else {
127
+ * switch (result.errorStatusCode) {
128
+ * case 409:
129
+ * // The room already exists (and isn't yours), i.e. you've hit a 1-in-a-billion conflict.
130
+ * // Fall through to kicking the user out to try again later.
131
+ * default:
132
+ * // Unexpected error, kick the user out for now.
133
+ * }
134
+ * }
135
+ *
136
+ * @param sfuUrl - the URL to use when accessing the SFU
137
+ * @param createCredentialPresentation - a serialized CreateCallLinkCredentialPresentation
138
+ * @param linkRootKey - the root key for the call link
139
+ * @param adminPasskey - the arbitrary passkey to use for the new room
140
+ * @param callLinkPublicParams - the serialized CallLinkPublicParams for the new room
141
+ */
142
+ createCallLink(sfuUrl: string, createCredentialPresentation: Buffer, linkRootKey: CallLinkRootKey, adminPasskey: Buffer, callLinkPublicParams: Buffer): Promise<HttpResult<CallLinkState>>;
143
+ /**
144
+ * Asynchronous request to update a call link's name.
145
+ *
146
+ * Possible failure codes include:
147
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
148
+ * - 403: the admin passkey is incorrect
149
+ *
150
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
151
+ *
152
+ * @param sfuUrl - the URL to use when accessing the SFU
153
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
154
+ * @param linkRootKey - the root key for the call link
155
+ * @param adminPasskey - the passkey specified when the link was created
156
+ * @param newName - the new name to use
157
+ */
158
+ updateCallLinkName(sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: CallLinkRootKey, adminPasskey: Buffer, newName: string): Promise<HttpResult<CallLinkState>>;
159
+ /**
160
+ * Asynchronous request to update a call link's restrictions.
161
+ *
162
+ * Possible failure codes include:
163
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
164
+ * - 403: the admin passkey is incorrect
165
+ *
166
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
167
+ *
168
+ * @param sfuUrl - the URL to use when accessing the SFU
169
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
170
+ * @param linkRootKey - the root key for the call link
171
+ * @param adminPasskey - the passkey specified when the link was created
172
+ * @param restrictions - the new restrictions to use
173
+ */
174
+ updateCallLinkRestrictions(sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: CallLinkRootKey, adminPasskey: Buffer, restrictions: Exclude<CallLinkRestrictions, CallLinkRestrictions.Unknown>): Promise<HttpResult<CallLinkState>>;
175
+ /**
176
+ * Asynchronous request to revoke or un-revoke a call link.
177
+ *
178
+ * Possible failure codes include:
179
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
180
+ * - 403: the admin passkey is incorrect
181
+ *
182
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
183
+ *
184
+ * @param sfuUrl - the URL to use when accessing the SFU
185
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
186
+ * @param linkRootKey - the root key for the call link
187
+ * @param adminPasskey - the passkey specified when the link was created
188
+ * @param revoked - whether the link should now be revoked
189
+ */
190
+ updateCallLinkRevocation(sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: CallLinkRootKey, adminPasskey: Buffer, revoked: boolean): Promise<HttpResult<CallLinkState>>;
87
191
  receivedHttpResponse(requestId: number, status: number, body: Buffer): void;
88
192
  httpRequestFailed(requestId: number, debugInfo: string | undefined): void;
89
193
  getGroupCall(groupId: Buffer, sfuUrl: string, hkdfExtraInfo: Buffer, audioLevelsIntervalMillis: number | undefined, observer: GroupCallObserver): GroupCall | undefined;
@@ -97,6 +201,12 @@ export declare class RingRTCType {
97
201
  handleRemoteDevicesChanged(clientId: GroupCallClientId, remoteDeviceStates: Array<RemoteDeviceState>): void;
98
202
  handlePeekChanged(clientId: GroupCallClientId, info: PeekInfo): void;
99
203
  handlePeekResponse(requestId: number, info: PeekInfo): void;
204
+ handleCallLinkResponse(requestId: number, statusCode: number, state: {
205
+ name: string;
206
+ rawRestrictions: number;
207
+ revoked: boolean;
208
+ expiration: Date;
209
+ } | undefined): void;
100
210
  handleEnded(clientId: GroupCallClientId, reason: GroupCallEndReason): void;
101
211
  groupCallRingUpdate(groupId: GroupId, ringIdString: string, sender: GroupCallUserId, state: RingUpdate): void;
102
212
  onLogMessage(level: number, fileName: string, line: number, message: string): void;
@@ -455,6 +565,9 @@ export interface CallManager {
455
565
  setGroupMembers(clientId: GroupCallClientId, members: Array<GroupMemberInfo>): void;
456
566
  setMembershipProof(clientId: GroupCallClientId, proof: Buffer): void;
457
567
  receiveGroupCallVideoFrame(clientId: GroupCallClientId, remoteDemuxId: number, buffer: Buffer, maxWidth: number, maxHeight: number): [number, number] | undefined;
568
+ readCallLink(requestId: number, sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: Buffer): void;
569
+ createCallLink(requestId: number, sfuUrl: string, createCredentialPresentation: Buffer, linkRootKey: Buffer, adminPasskey: Buffer, callLinkPublicParams: Buffer): void;
570
+ updateCallLink(requestId: number, sfuUrl: string, authCredentialPresentation: Buffer, linkRootKey: Buffer, adminPasskey: Buffer, newName: string | undefined, newRestrictions: number | undefined, newRevoked: boolean | undefined): void;
458
571
  peekGroupCall(requestId: number, sfu_url: string, membership_proof: Buffer, group_members: Array<GroupMemberInfo>): void;
459
572
  getAudioInputs(): Array<AudioDevice>;
460
573
  setAudioInput(index: number): void;
@@ -35,14 +35,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
35
35
  step((generator = generator.apply(thisArg, _arguments || [])).next());
36
36
  });
37
37
  };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
38
41
  Object.defineProperty(exports, "__esModule", { value: true });
39
42
  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 = exports.callIdFromRingId = exports.callIdFromEra = void 0;
40
43
  /* eslint-disable max-classes-per-file */
41
- const os = __importStar(require("os"));
42
44
  const process = __importStar(require("process"));
43
- // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require
44
- const Native = require(`../../build/${os.platform()}/libringrtc-${process.arch}.node`);
45
- exports.callIdFromEra = Native.callIdFromEra;
45
+ const CallLinks_1 = require("./CallLinks");
46
+ const Native_1 = __importDefault(require("./Native"));
47
+ exports.callIdFromEra = Native_1.default.callIdFromEra;
46
48
  function callIdFromRingId(ringId) {
47
49
  return {
48
50
  low: Number(BigInt.asIntN(32, ringId)),
@@ -65,22 +67,25 @@ class NativeCallManager {
65
67
  this.createCallEndpoint(config);
66
68
  }
67
69
  createCallEndpoint(config) {
68
- const fieldTrials = Object.assign({ 'RingRTC-AnyAddressPortsKillSwitch': 'Enabled' }, config.field_trials);
70
+ const fieldTrials = Object.assign({
71
+ 'RingRTC-AnyAddressPortsKillSwitch': 'Enabled',
72
+ 'WebRTC-Audio-OpusSetSignalVoiceWithDtx': 'Enabled',
73
+ }, config.field_trials);
69
74
  /* eslint-disable prefer-template */
70
75
  const fieldTrialsString = Object.entries(fieldTrials)
71
76
  .map(([k, v]) => `${k}/${v}`)
72
77
  .join('/') + '/';
73
78
  /* eslint-enable prefer-template */
74
- Object.defineProperty(this, Native.callEndpointPropertyKey, {
79
+ Object.defineProperty(this, Native_1.default.callEndpointPropertyKey, {
75
80
  configurable: true,
76
81
  get() {
77
- const callEndpoint = Native.createCallEndpoint(this, config.use_new_audio_device_module, fieldTrialsString);
82
+ const callEndpoint = Native_1.default.createCallEndpoint(this, config.use_new_audio_device_module, fieldTrialsString);
78
83
  if (process.platform === 'darwin') {
79
84
  // Preload devices to work around
80
85
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1287628
81
86
  void window.navigator.mediaDevices.enumerateDevices();
82
87
  }
83
- Object.defineProperty(this, Native.callEndpointPropertyKey, {
88
+ Object.defineProperty(this, Native_1.default.callEndpointPropertyKey, {
84
89
  configurable: true,
85
90
  value: callEndpoint,
86
91
  });
@@ -92,76 +97,79 @@ class NativeCallManager {
92
97
  }
93
98
  // Mirror methods onto NativeCallManager.
94
99
  // This is done through direct assignment rather than wrapper methods to avoid indirection.
95
- NativeCallManager.prototype.setSelfUuid = Native.cm_setSelfUuid;
100
+ NativeCallManager.prototype.setSelfUuid = Native_1.default.cm_setSelfUuid;
96
101
  NativeCallManager.prototype.createOutgoingCall =
97
- Native.cm_createOutgoingCall;
98
- NativeCallManager.prototype.proceed = Native.cm_proceed;
99
- NativeCallManager.prototype.accept = Native.cm_accept;
100
- NativeCallManager.prototype.ignore = Native.cm_ignore;
101
- NativeCallManager.prototype.hangup = Native.cm_hangup;
102
+ Native_1.default.cm_createOutgoingCall;
103
+ NativeCallManager.prototype.proceed = Native_1.default.cm_proceed;
104
+ NativeCallManager.prototype.accept = Native_1.default.cm_accept;
105
+ NativeCallManager.prototype.ignore = Native_1.default.cm_ignore;
106
+ NativeCallManager.prototype.hangup = Native_1.default.cm_hangup;
102
107
  NativeCallManager.prototype.cancelGroupRing =
103
- Native.cm_cancelGroupRing;
108
+ Native_1.default.cm_cancelGroupRing;
104
109
  NativeCallManager.prototype.signalingMessageSent =
105
- Native.cm_signalingMessageSent;
110
+ Native_1.default.cm_signalingMessageSent;
106
111
  NativeCallManager.prototype.signalingMessageSendFailed =
107
- Native.cm_signalingMessageSendFailed;
112
+ Native_1.default.cm_signalingMessageSendFailed;
108
113
  NativeCallManager.prototype.updateBandwidthMode =
109
- Native.cm_updateBandwidthMode;
110
- NativeCallManager.prototype.receivedOffer = Native.cm_receivedOffer;
111
- NativeCallManager.prototype.receivedAnswer = Native.cm_receivedAnswer;
114
+ Native_1.default.cm_updateBandwidthMode;
115
+ NativeCallManager.prototype.receivedOffer = Native_1.default.cm_receivedOffer;
116
+ NativeCallManager.prototype.receivedAnswer = Native_1.default.cm_receivedAnswer;
112
117
  NativeCallManager.prototype.receivedIceCandidates =
113
- Native.cm_receivedIceCandidates;
114
- NativeCallManager.prototype.receivedHangup = Native.cm_receivedHangup;
115
- NativeCallManager.prototype.receivedBusy = Native.cm_receivedBusy;
118
+ Native_1.default.cm_receivedIceCandidates;
119
+ NativeCallManager.prototype.receivedHangup = Native_1.default.cm_receivedHangup;
120
+ NativeCallManager.prototype.receivedBusy = Native_1.default.cm_receivedBusy;
116
121
  NativeCallManager.prototype.receivedCallMessage =
117
- Native.cm_receivedCallMessage;
122
+ Native_1.default.cm_receivedCallMessage;
118
123
  NativeCallManager.prototype.receivedHttpResponse =
119
- Native.cm_receivedHttpResponse;
124
+ Native_1.default.cm_receivedHttpResponse;
120
125
  NativeCallManager.prototype.httpRequestFailed =
121
- Native.cm_httpRequestFailed;
126
+ Native_1.default.cm_httpRequestFailed;
122
127
  NativeCallManager.prototype.setOutgoingAudioEnabled =
123
- Native.cm_setOutgoingAudioEnabled;
128
+ Native_1.default.cm_setOutgoingAudioEnabled;
124
129
  NativeCallManager.prototype.setOutgoingVideoEnabled =
125
- Native.cm_setOutgoingVideoEnabled;
130
+ Native_1.default.cm_setOutgoingVideoEnabled;
126
131
  NativeCallManager.prototype.setOutgoingVideoIsScreenShare =
127
- Native.cm_setOutgoingVideoIsScreenShare;
128
- NativeCallManager.prototype.sendVideoFrame = Native.cm_sendVideoFrame;
132
+ Native_1.default.cm_setOutgoingVideoIsScreenShare;
133
+ NativeCallManager.prototype.sendVideoFrame = Native_1.default.cm_sendVideoFrame;
129
134
  NativeCallManager.prototype.receiveVideoFrame =
130
- Native.cm_receiveVideoFrame;
135
+ Native_1.default.cm_receiveVideoFrame;
131
136
  NativeCallManager.prototype.receiveGroupCallVideoFrame =
132
- Native.cm_receiveGroupCallVideoFrame;
137
+ Native_1.default.cm_receiveGroupCallVideoFrame;
133
138
  NativeCallManager.prototype.createGroupCallClient =
134
- Native.cm_createGroupCallClient;
139
+ Native_1.default.cm_createGroupCallClient;
135
140
  NativeCallManager.prototype.deleteGroupCallClient =
136
- Native.cm_deleteGroupCallClient;
137
- NativeCallManager.prototype.connect = Native.cm_connect;
138
- NativeCallManager.prototype.join = Native.cm_join;
139
- NativeCallManager.prototype.leave = Native.cm_leave;
140
- NativeCallManager.prototype.disconnect = Native.cm_disconnect;
141
- NativeCallManager.prototype.groupRing = Native.cm_groupRing;
141
+ Native_1.default.cm_deleteGroupCallClient;
142
+ NativeCallManager.prototype.connect = Native_1.default.cm_connect;
143
+ NativeCallManager.prototype.join = Native_1.default.cm_join;
144
+ NativeCallManager.prototype.leave = Native_1.default.cm_leave;
145
+ NativeCallManager.prototype.disconnect = Native_1.default.cm_disconnect;
146
+ NativeCallManager.prototype.groupRing = Native_1.default.cm_groupRing;
142
147
  NativeCallManager.prototype.setOutgoingAudioMuted =
143
- Native.cm_setOutgoingAudioMuted;
148
+ Native_1.default.cm_setOutgoingAudioMuted;
144
149
  NativeCallManager.prototype.setOutgoingVideoMuted =
145
- Native.cm_setOutgoingVideoMuted;
150
+ Native_1.default.cm_setOutgoingVideoMuted;
146
151
  NativeCallManager.prototype.setOutgoingGroupCallVideoIsScreenShare =
147
- Native.cm_setOutgoingGroupCallVideoIsScreenShare;
148
- NativeCallManager.prototype.setPresenting = Native.cm_setPresenting;
152
+ Native_1.default.cm_setOutgoingGroupCallVideoIsScreenShare;
153
+ NativeCallManager.prototype.setPresenting = Native_1.default.cm_setPresenting;
149
154
  NativeCallManager.prototype.resendMediaKeys =
150
- Native.cm_resendMediaKeys;
155
+ Native_1.default.cm_resendMediaKeys;
151
156
  NativeCallManager.prototype.setBandwidthMode =
152
- Native.cm_setBandwidthMode;
153
- NativeCallManager.prototype.requestVideo = Native.cm_requestVideo;
157
+ Native_1.default.cm_setBandwidthMode;
158
+ NativeCallManager.prototype.requestVideo = Native_1.default.cm_requestVideo;
154
159
  NativeCallManager.prototype.setGroupMembers =
155
- Native.cm_setGroupMembers;
160
+ Native_1.default.cm_setGroupMembers;
156
161
  NativeCallManager.prototype.setMembershipProof =
157
- Native.cm_setMembershipProof;
158
- NativeCallManager.prototype.peekGroupCall = Native.cm_peekGroupCall;
159
- NativeCallManager.prototype.getAudioInputs = Native.cm_getAudioInputs;
160
- NativeCallManager.prototype.setAudioInput = Native.cm_setAudioInput;
162
+ Native_1.default.cm_setMembershipProof;
163
+ NativeCallManager.prototype.readCallLink = Native_1.default.cm_readCallLink;
164
+ NativeCallManager.prototype.createCallLink = Native_1.default.cm_createCallLink;
165
+ NativeCallManager.prototype.updateCallLink = Native_1.default.cm_updateCallLink;
166
+ NativeCallManager.prototype.peekGroupCall = Native_1.default.cm_peekGroupCall;
167
+ NativeCallManager.prototype.getAudioInputs = Native_1.default.cm_getAudioInputs;
168
+ NativeCallManager.prototype.setAudioInput = Native_1.default.cm_setAudioInput;
161
169
  NativeCallManager.prototype.getAudioOutputs =
162
- Native.cm_getAudioOutputs;
163
- NativeCallManager.prototype.setAudioOutput = Native.cm_setAudioOutput;
164
- NativeCallManager.prototype.processEvents = Native.cm_processEvents;
170
+ Native_1.default.cm_getAudioOutputs;
171
+ NativeCallManager.prototype.setAudioOutput = Native_1.default.cm_setAudioOutput;
172
+ NativeCallManager.prototype.processEvents = Native_1.default.cm_processEvents;
165
173
  class PeekDeviceInfo {
166
174
  constructor(demuxId, userId) {
167
175
  this.demuxId = demuxId;
@@ -270,6 +278,7 @@ class RingRTCType {
270
278
  this._call = null;
271
279
  this._groupCallByClientId = new Map();
272
280
  this._peekRequests = new Requests();
281
+ this._callLinkRequests = new Requests();
273
282
  this._callInfoByCallId = new Map();
274
283
  }
275
284
  setConfig(config) {
@@ -542,6 +551,138 @@ class RingRTCType {
542
551
  }
543
552
  }))().catch(e => this.logError(e.toString()));
544
553
  }
554
+ // Call Links
555
+ /**
556
+ * Asynchronous request to get information about a call link.
557
+ *
558
+ * @param sfuUrl - the URL to use when accessing the SFU
559
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
560
+ * @param linkRootKey - the root key for the call link
561
+ *
562
+ * Expected failure codes include:
563
+ * - 404: the room does not exist (or expired so long ago that it has been removed from the server)
564
+ */
565
+ readCallLink(sfuUrl, authCredentialPresentation, linkRootKey) {
566
+ const [requestId, promise] = this._callLinkRequests.add();
567
+ // Response comes back via handleCallLinkResponse
568
+ sillyDeadlockProtection(() => {
569
+ this.callManager.readCallLink(requestId, sfuUrl, authCredentialPresentation, linkRootKey.bytes);
570
+ });
571
+ return promise;
572
+ }
573
+ /**
574
+ * Asynchronous request to create a new call link.
575
+ *
576
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
577
+ *
578
+ * @example
579
+ * const linkKey = CallLinkRootKey.generate();
580
+ * const adminPasskey = CallLinkRootKey.generateAdminPasskey();
581
+ * const roomId = linkKey.deriveRoomId();
582
+ * const credential = requestCreateCredentialFromChatServer(roomId); // using libsignal
583
+ * const secretParams = CallLinkSecretParams.deriveFromRootKey(linkKey.bytes);
584
+ * const credentialPresentation = credential.present(roomId, secretParams).serialize();
585
+ * const serializedPublicParams = secretParams.getPublicParams().serialize();
586
+ * const result = await RingRTC.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams);
587
+ * if (result.success) {
588
+ * const state = result.value;
589
+ * // In actuality you may not want to do this until the user clicks Done.
590
+ * saveToDatabase(linkKey.bytes, adminPasskey, state);
591
+ * syncToOtherDevices(linkKey.bytes, adminPasskey);
592
+ * } else {
593
+ * switch (result.errorStatusCode) {
594
+ * case 409:
595
+ * // The room already exists (and isn't yours), i.e. you've hit a 1-in-a-billion conflict.
596
+ * // Fall through to kicking the user out to try again later.
597
+ * default:
598
+ * // Unexpected error, kick the user out for now.
599
+ * }
600
+ * }
601
+ *
602
+ * @param sfuUrl - the URL to use when accessing the SFU
603
+ * @param createCredentialPresentation - a serialized CreateCallLinkCredentialPresentation
604
+ * @param linkRootKey - the root key for the call link
605
+ * @param adminPasskey - the arbitrary passkey to use for the new room
606
+ * @param callLinkPublicParams - the serialized CallLinkPublicParams for the new room
607
+ */
608
+ createCallLink(sfuUrl, createCredentialPresentation, linkRootKey, adminPasskey, callLinkPublicParams) {
609
+ const [requestId, promise] = this._callLinkRequests.add();
610
+ // Response comes back via handleCallLinkResponse
611
+ sillyDeadlockProtection(() => {
612
+ this.callManager.createCallLink(requestId, sfuUrl, createCredentialPresentation, linkRootKey.bytes, adminPasskey, callLinkPublicParams);
613
+ });
614
+ return promise;
615
+ }
616
+ /**
617
+ * Asynchronous request to update a call link's name.
618
+ *
619
+ * Possible failure codes include:
620
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
621
+ * - 403: the admin passkey is incorrect
622
+ *
623
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
624
+ *
625
+ * @param sfuUrl - the URL to use when accessing the SFU
626
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
627
+ * @param linkRootKey - the root key for the call link
628
+ * @param adminPasskey - the passkey specified when the link was created
629
+ * @param newName - the new name to use
630
+ */
631
+ updateCallLinkName(sfuUrl, authCredentialPresentation, linkRootKey, adminPasskey, newName) {
632
+ const [requestId, promise] = this._callLinkRequests.add();
633
+ // Response comes back via handleCallLinkResponse
634
+ sillyDeadlockProtection(() => {
635
+ this.callManager.updateCallLink(requestId, sfuUrl, authCredentialPresentation, linkRootKey.bytes, adminPasskey, newName, undefined, undefined);
636
+ });
637
+ return promise;
638
+ }
639
+ /**
640
+ * Asynchronous request to update a call link's restrictions.
641
+ *
642
+ * Possible failure codes include:
643
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
644
+ * - 403: the admin passkey is incorrect
645
+ *
646
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
647
+ *
648
+ * @param sfuUrl - the URL to use when accessing the SFU
649
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
650
+ * @param linkRootKey - the root key for the call link
651
+ * @param adminPasskey - the passkey specified when the link was created
652
+ * @param restrictions - the new restrictions to use
653
+ */
654
+ updateCallLinkRestrictions(sfuUrl, authCredentialPresentation, linkRootKey, adminPasskey, restrictions) {
655
+ const [requestId, promise] = this._callLinkRequests.add();
656
+ // Response comes back via handleCallLinkResponse
657
+ sillyDeadlockProtection(() => {
658
+ this.callManager.updateCallLink(requestId, sfuUrl, authCredentialPresentation, linkRootKey.bytes, adminPasskey, undefined, restrictions, undefined);
659
+ });
660
+ return promise;
661
+ }
662
+ /**
663
+ * Asynchronous request to revoke or un-revoke a call link.
664
+ *
665
+ * Possible failure codes include:
666
+ * - 401: the room does not exist (and this is the wrong API to create a new room)
667
+ * - 403: the admin passkey is incorrect
668
+ *
669
+ * This request is idempotent; if it fails due to a network issue, it is safe to retry.
670
+ *
671
+ * @param sfuUrl - the URL to use when accessing the SFU
672
+ * @param authCredentialPresentation - a serialized CallLinkAuthCredentialPresentation
673
+ * @param linkRootKey - the root key for the call link
674
+ * @param adminPasskey - the passkey specified when the link was created
675
+ * @param revoked - whether the link should now be revoked
676
+ */
677
+ updateCallLinkRevocation(sfuUrl, authCredentialPresentation, linkRootKey, adminPasskey, revoked) {
678
+ const [requestId, promise] = this._callLinkRequests.add();
679
+ // Response comes back via handleCallLinkResponse
680
+ sillyDeadlockProtection(() => {
681
+ this.callManager.updateCallLink(requestId, sfuUrl, authCredentialPresentation, linkRootKey.bytes, adminPasskey, undefined, undefined, revoked);
682
+ });
683
+ return promise;
684
+ }
685
+ // HTTP callbacks
545
686
  receivedHttpResponse(requestId, status, body) {
546
687
  sillyDeadlockProtection(() => {
547
688
  try {
@@ -676,6 +817,37 @@ class RingRTCType {
676
817
  });
677
818
  }
678
819
  // Called by Rust
820
+ handleCallLinkResponse(requestId, statusCode, state) {
821
+ sillyDeadlockProtection(() => {
822
+ // Recreate the state so that we have the correct prototype, in case we add more methods to CallLinkState.
823
+ let result;
824
+ if (state) {
825
+ let restrictions;
826
+ switch (state.rawRestrictions) {
827
+ case 0:
828
+ restrictions = CallLinks_1.CallLinkRestrictions.None;
829
+ break;
830
+ case 1:
831
+ restrictions = CallLinks_1.CallLinkRestrictions.AdminApproval;
832
+ break;
833
+ default:
834
+ restrictions = CallLinks_1.CallLinkRestrictions.Unknown;
835
+ break;
836
+ }
837
+ result = {
838
+ success: true,
839
+ value: new CallLinks_1.CallLinkState(state.name, restrictions, state.revoked, state.expiration),
840
+ };
841
+ }
842
+ else {
843
+ result = { success: false, errorStatusCode: statusCode };
844
+ }
845
+ if (!this._callLinkRequests.resolve(requestId, result)) {
846
+ this.logWarn(`Invalid request ID for handleCallLinkResponse: ${requestId}`);
847
+ }
848
+ });
849
+ }
850
+ // Called by Rust
679
851
  handleEnded(clientId, reason) {
680
852
  sillyDeadlockProtection(() => {
681
853
  const groupCall = this._groupCallByClientId.get(clientId);
@@ -178,6 +178,16 @@ class GumVideoCapturer {
178
178
  }
179
179
  return;
180
180
  }
181
+ if (this.mediaStream !== undefined &&
182
+ this.mediaStream.getVideoTracks().length > 0) {
183
+ // We have a stream and track for the requested camera already. Stop
184
+ // the duplicate track that we just started.
185
+ index_1.RingRTC.logWarn('startCapturing(): dropping duplicate call to startCapturing');
186
+ for (const track of mediaStream.getVideoTracks()) {
187
+ track.stop();
188
+ }
189
+ return;
190
+ }
181
191
  this.mediaStream = mediaStream;
182
192
  if (!this.spawnedSenderRunning &&
183
193
  this.mediaStream != undefined &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalapp/ringrtc",
3
- "version": "2.26.3",
3
+ "version": "2.27.0",
4
4
  "description": "Signal Messenger voice and video calling library.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "config": {
25
25
  "prebuildUrl": "https://build-artifacts.signal.org/libraries/ringrtc-desktop-build-v${npm_package_version}.tar.gz",
26
- "prebuildChecksum": "5d580631487a2d988c93c872792d6a4ab91e57f9fad2d2e29ed2991163bc8288"
26
+ "prebuildChecksum": "864c376c4998ae9a0a832c5753089e881d5466e2bbae5d11dae7486226129d5a"
27
27
  },
28
28
  "author": "",
29
29
  "license": "AGPL-3.0-only",