Skip to content

Commit

Permalink
Group calling SFU URL should be configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanHahn-Signal committed Dec 7, 2020
1 parent ec35bdc commit 23fed9c
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 75 deletions.
1 change: 1 addition & 0 deletions config/default.json
Expand Up @@ -11,6 +11,7 @@
"contentProxyUrl": "http://contentproxy.signal.org:443",
"updatesUrl": "https://updates2.signal.org/desktop",
"updatesPublicKey": "fd7dd3de7149dc0a127909fee7de0f7620ddd0de061b37a2c303e37de802a401",
"sfuUrl": "https://sfu.voip.signal.org/",
"updatesEnabled": false,
"openDevTools": false,
"buildExpiration": 0,
Expand Down
1 change: 1 addition & 0 deletions main.js
Expand Up @@ -204,6 +204,7 @@ function prepareURL(pathSegments, moreKeys) {
appInstance: process.env.NODE_APP_INSTANCE,
proxyUrl: process.env.HTTPS_PROXY || process.env.https_proxy,
contentProxyUrl: config.contentProxyUrl,
sfuUrl: config.get('sfuUrl'),
importMode: importMode ? true : undefined, // for stringify()
serverPublicParams: config.get('serverPublicParams'),
serverTrustRoot: config.get('serverTrustRoot'),
Expand Down
1 change: 1 addition & 0 deletions preload.js
Expand Up @@ -45,6 +45,7 @@ try {
window.getHostName = () => config.hostname;
window.getServerTrustRoot = () => config.serverTrustRoot;
window.getServerPublicParams = () => config.serverPublicParams;
window.getSfuUrl = () => config.sfuUrl;
window.isBehindProxy = () => Boolean(config.proxyUrl);

function setSystemTheme() {
Expand Down
5 changes: 4 additions & 1 deletion ts/background.ts
Expand Up @@ -646,7 +646,10 @@ type WhatIsThis = import('./window.d').WhatIsThis;
window.reduxActions.updates,
window.Whisper.events
);
window.Signal.Services.calling.initialize(window.reduxActions.calling);
window.Signal.Services.calling.initialize(
window.reduxActions.calling,
window.getSfuUrl()
);
window.reduxActions.expiration.hydrateExpirationStatus(
window.Signal.Util.hasExpired()
);
Expand Down
153 changes: 79 additions & 74 deletions ts/services/calling.ts
Expand Up @@ -55,8 +55,6 @@ import { fetchMembershipProof, getMembershipList } from '../groups';
import { missingCaseError } from '../util/missingCaseError';
import { normalizeGroupCallTimestamp } from '../util/ringrtc/normalizeGroupCallTimestamp';

const RINGRTC_SFU_URL = 'https://sfu.voip.signal.org/';

const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
HttpMethod,
'GET' | 'PUT' | 'POST' | 'DELETE'
Expand Down Expand Up @@ -92,6 +90,8 @@ export class CallingClass {

private uxActions?: UxActionsType;

private sfuUrl?: string;

private lastMediaDeviceSettings?: MediaDeviceSettings;

private deviceReselectionTimer?: NodeJS.Timeout;
Expand All @@ -105,11 +105,14 @@ export class CallingClass {
this.callsByConversation = {};
}

initialize(uxActions: UxActionsType): void {
initialize(uxActions: UxActionsType, sfuUrl: string): void {
this.uxActions = uxActions;
if (!uxActions) {
throw new Error('CallingClass.initialize: Invalid uxActions.');
}

this.sfuUrl = sfuUrl;

RingRTC.handleOutgoingSignaling = this.handleOutgoingSignaling.bind(this);
RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this);
RingRTC.handleAutoEndedIncomingCallRequest = this.handleAutoEndedIncomingCallRequest.bind(
Expand Down Expand Up @@ -333,6 +336,10 @@ export class CallingClass {
return statefulPeekInfo;
}

if (!this.sfuUrl) {
throw new Error('Missing SFU URL; not peeking group call');
}

const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error('Missing conversation; not peeking group call');
Expand All @@ -352,7 +359,7 @@ export class CallingClass {
const membershipProof = new TextEncoder().encode(proof).buffer;

return RingRTC.peekGroupCall(
RINGRTC_SFU_URL,
this.sfuUrl,
membershipProof,
this.getGroupCallMembers(conversationId)
);
Expand Down Expand Up @@ -388,90 +395,88 @@ export class CallingClass {
return existing;
}

if (!this.sfuUrl) {
throw new Error('Missing SFU URL; not connecting group call');
}

const groupIdBuffer = base64ToArrayBuffer(groupId);

let updateMessageState = GroupCallUpdateMessageState.SentNothing;
let isRequestingMembershipProof = false;

const outerGroupCall = RingRTC.getGroupCall(
groupIdBuffer,
RINGRTC_SFU_URL,
{
onLocalDeviceStateChanged: groupCall => {
const localDeviceState = groupCall.getLocalDeviceState();
const { eraId } = groupCall.getPeekInfo() || {};
const outerGroupCall = RingRTC.getGroupCall(groupIdBuffer, this.sfuUrl, {
onLocalDeviceStateChanged: groupCall => {
const localDeviceState = groupCall.getLocalDeviceState();
const { eraId } = groupCall.getPeekInfo() || {};

if (localDeviceState.connectionState === ConnectionState.NotConnected) {
// NOTE: This assumes that only one call is active at a time. For example, if
// there are two calls using the camera, this will disable both of them.
// That's fine for now, but this will break if that assumption changes.
this.disableLocalCamera();

delete this.callsByConversation[conversationId];

if (
localDeviceState.connectionState === ConnectionState.NotConnected
updateMessageState === GroupCallUpdateMessageState.SentJoin &&
eraId
) {
// NOTE: This assumes that only one call is active at a time. For example, if
// there are two calls using the camera, this will disable both of them.
// That's fine for now, but this will break if that assumption changes.
this.disableLocalCamera();

delete this.callsByConversation[conversationId];
updateMessageState = GroupCallUpdateMessageState.SentLeft;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
} else {
this.callsByConversation[conversationId] = groupCall;

if (
updateMessageState === GroupCallUpdateMessageState.SentJoin &&
eraId
) {
updateMessageState = GroupCallUpdateMessageState.SentLeft;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
// NOTE: This assumes only one active call at a time. See comment above.
if (localDeviceState.videoMuted) {
this.disableLocalCamera();
} else {
this.callsByConversation[conversationId] = groupCall;

// NOTE: This assumes only one active call at a time. See comment above.
if (localDeviceState.videoMuted) {
this.disableLocalCamera();
} else {
this.videoCapturer.enableCaptureAndSend(groupCall);
}

if (
updateMessageState === GroupCallUpdateMessageState.SentNothing &&
localDeviceState.joinState === JoinState.Joined &&
eraId
) {
updateMessageState = GroupCallUpdateMessageState.SentJoin;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
this.videoCapturer.enableCaptureAndSend(groupCall);
}

this.syncGroupCallToRedux(conversationId, groupCall);
},
onRemoteDeviceStatesChanged: groupCall => {
this.syncGroupCallToRedux(conversationId, groupCall);
},
onPeekChanged: groupCall => {
this.syncGroupCallToRedux(conversationId, groupCall);
},
async requestMembershipProof(groupCall) {
if (isRequestingMembershipProof) {
return;
if (
updateMessageState === GroupCallUpdateMessageState.SentNothing &&
localDeviceState.joinState === JoinState.Joined &&
eraId
) {
updateMessageState = GroupCallUpdateMessageState.SentJoin;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
isRequestingMembershipProof = true;
try {
const proof = await fetchMembershipProof({
publicParams,
secretParams,
});
if (proof) {
const proofArray = new TextEncoder().encode(proof);
groupCall.setMembershipProof(proofArray.buffer);
}
} catch (err) {
window.log.error('Failed to fetch membership proof', err);
} finally {
isRequestingMembershipProof = false;
}

this.syncGroupCallToRedux(conversationId, groupCall);
},
onRemoteDeviceStatesChanged: groupCall => {
this.syncGroupCallToRedux(conversationId, groupCall);
},
onPeekChanged: groupCall => {
this.syncGroupCallToRedux(conversationId, groupCall);
},
async requestMembershipProof(groupCall) {
if (isRequestingMembershipProof) {
return;
}
isRequestingMembershipProof = true;
try {
const proof = await fetchMembershipProof({
publicParams,
secretParams,
});
if (proof) {
const proofArray = new TextEncoder().encode(proof);
groupCall.setMembershipProof(proofArray.buffer);
}
},
requestGroupMembers: groupCall => {
groupCall.setGroupMembers(this.getGroupCallMembers(conversationId));
},
onEnded: noop,
}
);
} catch (err) {
window.log.error('Failed to fetch membership proof', err);
} finally {
isRequestingMembershipProof = false;
}
},
requestGroupMembers: groupCall => {
groupCall.setGroupMembers(this.getGroupCallMembers(conversationId));
},
onEnded: noop,
});

if (!outerGroupCall) {
// This should be very rare, likely due to RingRTC not being able to get a lock
Expand Down
1 change: 1 addition & 0 deletions ts/window.d.ts
Expand Up @@ -114,6 +114,7 @@ declare global {
getMediaCameraPermissions: () => Promise<boolean>;
getMediaPermissions: () => Promise<boolean>;
getServerPublicParams: () => string;
getSfuUrl: () => string;
getSocketStatus: () => number;
getSyncRequest: () => WhatIsThis;
getTitle: () => string;
Expand Down

0 comments on commit 23fed9c

Please sign in to comment.