Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group Calls (WIP) #1902

Closed
wants to merge 218 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
218 commits
Select commit Hold shift + click to select a range
91f409e
Add invitee field
robertlong Jul 22, 2021
76f11be
Fix invitee glare detection and incoming call event
robertlong Jul 26, 2021
1c5101a
Add ice disconnected timeout
robertlong Aug 5, 2021
2cd5c81
Add local media stream functions to client
robertlong Aug 6, 2021
154e5c4
Clear localAVStream when stopping local media stream.
robertlong Aug 9, 2021
7ec84e9
Merge branch 'develop' into robertlong/full-mesh-voip
robertlong Aug 11, 2021
6dac6e5
Merge branch 'develop' into robertlong/full-mesh-voip
robertlong Aug 11, 2021
3971bf3
Merge branch 'master' into robertlong/full-mesh-voip
robertlong Aug 20, 2021
782fbb1
Stop all media on hangup
robertlong Aug 20, 2021
8fe6afd
Merge branch 'master' into robertlong/full-mesh-voip
robertlong Aug 31, 2021
d5aaed6
Merge branch 'develop' into robertlong/full-mesh-voip
robertlong Aug 31, 2021
19302ea
Fix initWithInvitePromise
robertlong Aug 31, 2021
03dfab1
Export CallFeed
robertlong Sep 2, 2021
60e1688
Properly dispose of call feeds
robertlong Sep 2, 2021
4fe115b
Add initial group call logic
robertlong Sep 8, 2021
2add1fc
Clean up group call events
robertlong Sep 8, 2021
503e954
Merge branch 'develop' into robertlong/group-call
robertlong Sep 9, 2021
7021f70
Move from constants to configureable public variables
robertlong Sep 9, 2021
07b2c57
Remove CallFeed export
robertlong Sep 9, 2021
1dbb776
Revert register types
robertlong Sep 10, 2021
2da14bd
Fix call feed changed event handler
robertlong Sep 10, 2021
f18d8ea
Fix usermedia feeds
robertlong Sep 10, 2021
eb2a476
Fix active speaker
robertlong Sep 10, 2021
fb3ca90
Fix private method signatures
robertlong Sep 10, 2021
c81b9d2
Merge branch 'develop' into robertlong/group-call
robertlong Sep 15, 2021
d25fb71
Merge branch 'robertlong/clone-streams' into robertlong/group-call
robertlong Sep 15, 2021
2427f75
Merge branch 'robertlong/datachannels' into robertlong/group-call
robertlong Sep 15, 2021
2d7e07f
Update to use latest datachannel / clone media stream PRs
robertlong Sep 15, 2021
a291f5a
Merge branch 'robertlong/clone-streams' into robertlong/group-call
robertlong Sep 15, 2021
f0916f1
Merge branch 'develop' into robertlong/group-call
robertlong Sep 15, 2021
8b1c173
Avoid changing member on replaced call
robertlong Sep 16, 2021
e4fc1f3
Merge branch 'develop' into robertlong/group-call
robertlong Sep 17, 2021
aef5d73
Fix emitting participants_changed event
robertlong Sep 21, 2021
18986cb
Fix typo
robertlong Sep 21, 2021
151b54e
Clean up GroupCallParticipant listeners on remove
robertlong Sep 21, 2021
bbeea51
Add callType to room state event
robertlong Sep 21, 2021
6e07c9e
Clean up group call event listeners properly on hangup
robertlong Sep 21, 2021
7f189b0
Add endCall
robertlong Sep 22, 2021
fe0a268
Merge branch 'robertlong/group-call' of github.com:matrix-org/matrix-…
robertlong Sep 22, 2021
5895ce3
Revert unintended babelrc edit
robertlong Sep 22, 2021
6a8e8ed
Merge branch 'develop' into robertlong/group-call
robertlong Sep 22, 2021
61a80a1
Export CONF_ROOM
SimonBrandner Sep 22, 2021
9996afe
Throw with no room
SimonBrandner Sep 22, 2021
55ef57e
Add GroupCallEventHandler
SimonBrandner Sep 22, 2021
d8d7bd5
Merge branch 'SimonBrandner-robertlong/group-call' into robertlong/gr…
robertlong Sep 22, 2021
9c7cb3c
Handle more edge cases around creating/ending group calls
robertlong Sep 22, 2021
ac70dcf
Expose call feed getters on call
robertlong Sep 23, 2021
89bda6c
Move from groupCallsParticipants to calls
robertlong Sep 24, 2021
454da84
Initialize activeSpeakerSamples
robertlong Sep 24, 2021
96f4892
Cleaning up group call state
robertlong Sep 24, 2021
f592d4d
Merge branch 'develop' into robertlong/group-call
robertlong Sep 26, 2021
fc07530
Add useToDevice to CallOpts
SimonBrandner Sep 25, 2021
3280394
Figure out opponentMember from the userId rather than the sender
SimonBrandner Sep 25, 2021
71ca424
Allow for sending toDevice messages
SimonBrandner Sep 25, 2021
027bc6b
Handle incoming to-device call signalling
SimonBrandner Sep 25, 2021
b535969
Use to-device messages in group calls
SimonBrandner Sep 25, 2021
6780237
Add a setState() method
SimonBrandner Sep 25, 2021
205c80e
Add groupCallId
SimonBrandner Sep 25, 2021
e9e65cf
Change type key
SimonBrandner Sep 25, 2021
a2f2390
Use groupCallId isntead of roomId in to-device messages
SimonBrandner Sep 25, 2021
715c457
Add a prop for not stopping local feeds on end
SimonBrandner Sep 26, 2021
2515ba3
Use createNewMatrixCall()
SimonBrandner Sep 26, 2021
d8ef7f9
pushLocalFeed() -> pushNewLocalFeed()
SimonBrandner Sep 26, 2021
df208e4
Avoid having duplicate call feeds
SimonBrandner Sep 26, 2021
cf1ba12
Use arrays of CallFeeds
SimonBrandner Sep 26, 2021
d873f14
Merge branch 'develop' into robertlong/group-call
robertlong Sep 27, 2021
efbf252
Merge pull request #1951 from SimonBrandner/robertlong/group-call
robertlong Sep 27, 2021
f85aa44
Remove duplicate FeedChanged event
robertlong Sep 27, 2021
7a142e9
Implement new group call state events
robertlong Sep 28, 2021
ce0b0ea
De-duplicate invite/answer code
SimonBrandner Sep 28, 2021
5251dcf
De-duplicate pushNewLocalFeed
SimonBrandner Sep 28, 2021
96bde1f
Fix field keys
SimonBrandner Sep 28, 2021
73cb5e1
Fix order of execution
SimonBrandner Sep 28, 2021
1a82475
Don't emit the same thing twice
SimonBrandner Sep 28, 2021
aee4459
Merge pull request #1955 from SimonBrandner/robertlong/group-call
robertlong Sep 28, 2021
f627507
Test prepare script for git installs
robertlong Sep 29, 2021
5111ca6
Change main path
robertlong Sep 29, 2021
529d61b
Add .npmignore
robertlong Sep 29, 2021
5da0727
Use prepack instead of prepare
robertlong Sep 29, 2021
1e65bfd
Fix initLocalCallFeed state
robertlong Sep 29, 2021
137fd2b
Export group call enums
robertlong Sep 29, 2021
625983a
Revert changes to package.json and .npmignore
robertlong Sep 29, 2021
3f4522b
Add more verbose logging for testing. Create group calls on first sync.
robertlong Sep 29, 2021
688327d
Handle new group call rooms
robertlong Sep 30, 2021
326aec9
Fix getUserMediaStream
robertlong Sep 30, 2021
bb504bc
Handle getUserMedia permissions blocked
robertlong Sep 30, 2021
0d96452
Add screensharing to GroupCall
robertlong Sep 30, 2021
8be4ca9
Add participants to GroupCall
robertlong Oct 4, 2021
ba57736
Remove log that's stalling FF
robertlong Oct 4, 2021
8ac3841
Handle joining a call after someone has started screen-sharing
SimonBrandner Oct 3, 2021
e2ed80f
Add removeLocalFeed()
SimonBrandner Oct 3, 2021
8232896
Don't run screen-sharing code for each 1:1 call, share one call feed …
SimonBrandner Oct 3, 2021
1409a4f
Merge branch 'develop' into robertlong/group-call
robertlong Oct 5, 2021
27eb88f
Update GroupCall to use new CallFeed constructor
robertlong Oct 5, 2021
04d674b
Merge branch 'SimonBrandner-robertlong/group-call' into robertlong/gr…
robertlong Oct 5, 2021
593f62c
Move to correct event types
robertlong Oct 5, 2021
7ef38ed
Fix speaking threshold
robertlong Oct 6, 2021
8dc608d
Fix connecting to a call without a webcam
robertlong Oct 7, 2021
4a8c3d2
Merge branch 'feature/answer-no-cam' into robertlong/group-call
robertlong Oct 12, 2021
a582b19
Merge branch 'robertlong/group-call' of github.com:matrix-org/matrix-…
robertlong Oct 12, 2021
4b6b159
Change media devices mid-call
robertlong Oct 12, 2021
18b1a44
Merge branch 'robertlong/change-media-device' into robertlong/group-call
robertlong Oct 12, 2021
a0f6eea
Add support for replacing existing sender tracks
robertlong Oct 12, 2021
1cdcebb
Merge branch 'robertlong/change-media-device' into robertlong/group-call
robertlong Oct 12, 2021
ec37eb8
Add support for switching media devices
robertlong Oct 12, 2021
2d231c0
Fix how streams are stopped
robertlong Oct 13, 2021
411b5f1
Fix talking indicator
robertlong Oct 13, 2021
5c8e7f2
Improve speaking detection using history
robertlong Oct 14, 2021
015d0f9
Don't set local user as active speaker
robertlong Oct 15, 2021
debeb66
Initialize speakingVolumeSamples for screenshare feeds
robertlong Oct 15, 2021
843973c
Remove left-over from old screen-sharing code
SimonBrandner Oct 15, 2021
4a82e1b
Merge pull request #1983 from SimonBrandner/robertlong/group-call
robertlong Oct 15, 2021
50e6a8f
Add session_id check to group calls
robertlong Oct 18, 2021
213f113
Reduce logging for group calls
robertlong Oct 18, 2021
0148ad0
Group call improvements (#1985)
SimonBrandner Oct 19, 2021
e9b52e2
Rermove session id
robertlong Oct 19, 2021
431d7a0
Merge branch 'develop' into robertlong/group-call
robertlong Oct 19, 2021
8131b39
Use glare resolution to manage group call setup
robertlong Oct 19, 2021
159e825
Fix unnecessary param to placeCallWithCallFeeds
robertlong Oct 19, 2021
0555f9d
Only send to device messages to a single device
robertlong Oct 20, 2021
305de54
Fix screensharing and webrtc races
robertlong Oct 21, 2021
5110e0b
Merge branch 'develop' into robertlong/group-call
robertlong Oct 21, 2021
3b0d1b2
Add check for existing group call session
robertlong Oct 21, 2021
b4d8c0b
Fix updating member state with no existing calls
robertlong Oct 22, 2021
fc8a867
Start processing member state events only after we've set out own (#2…
SimonBrandner Oct 25, 2021
119ce2e
Fix inbound calls in Safari
robertlong Oct 26, 2021
9f3f999
untested first cut at factoring out a encryptAndSendToDevices method
ara4n Oct 18, 2021
aeeed6e
clarify the factoring
ara4n Oct 18, 2021
24406d2
make it build
ara4n Oct 18, 2021
56c0c9b
fix example in readme
ara4n Oct 19, 2021
5a83635
switch encryptAndSendToDevices to return a promise rather than use a cb
ara4n Oct 20, 2021
53397ee
lint
ara4n Oct 20, 2021
f46355e
don't choke on missing promise
ara4n Oct 20, 2021
2f09e96
chain promises correctly; log rejects
ara4n Oct 20, 2021
a48546f
fix the tests (thanks @turt2live!!!)
ara4n Oct 20, 2021
841e6e9
handle promises normally now tests are fixed
ara4n Oct 20, 2021
067ac62
lint
ara4n Oct 20, 2021
32aca09
Merge branch 'to-device-olm' into robertlong/group-call
robertlong Oct 26, 2021
13d62e7
Fix stopping all media streams
robertlong Oct 26, 2021
923e9c4
Ensure that member call state is set correctly
robertlong Oct 28, 2021
8c5f88c
Fix handling null call
robertlong Oct 28, 2021
4c9648a
Sanitize call member state
robertlong Oct 28, 2021
d99428f
Remove duplicate call answer
robertlong Nov 2, 2021
6f695c1
Ignore call call state in glare resolution
robertlong Nov 2, 2021
5a92597
Check if call ended before getting user media
robertlong Nov 2, 2021
bdc12a2
Revert changes to gotCallFeedsForAnswer
robertlong Nov 3, 2021
7998817
Send candidate queue again on finish to flush out queue
robertlong Nov 4, 2021
c1f56ba
Fix indentation
robertlong Nov 4, 2021
b253ad9
Preserve the disabled tracks when updating local usermedia stream
robertlong Nov 5, 2021
17f7dc5
Keep track of original stream id for sdp stream metadata
robertlong Nov 5, 2021
f76f708
Ad a longer wait to safari media stream hack
robertlong Nov 5, 2021
4b87907
Update local usermedia streams serially
robertlong Nov 8, 2021
74c5a20
Temporarily disable safari hack
robertlong Nov 8, 2021
a2e255c
Merge branch 'robertlong/group-call' of github.com:matrix-org/matrix-…
robertlong Nov 8, 2021
f28cb48
Re-enable safari hack
robertlong Nov 8, 2021
97e8fce
Clean up replacing calls for Safari
robertlong Nov 9, 2021
e880cec
Add restart ICE
robertlong Nov 9, 2021
28da62c
Add retry call loop
robertlong Nov 9, 2021
7d48a83
Don't immediately start retry call loop
robertlong Nov 15, 2021
3139112
Clean up logging
robertlong Nov 15, 2021
4e26f29
Add unknown device errors
robertlong Nov 15, 2021
c2fe2ab
Add additional logging for removing feeds/tracks
robertlong Nov 15, 2021
139904f
Update sync state to error when aborting
robertlong Nov 18, 2021
3291846
Merge branch 'robertlong/abort-sync-error' into robertlong/group-call
robertlong Nov 18, 2021
5f06df8
Properly stop screensharing feed
robertlong Nov 18, 2021
64c3ac5
Stop screenshare when screensharing track ended
robertlong Nov 19, 2021
0683133
Dont start retry loop until weve sent the member state event
robertlong Nov 20, 2021
96ef535
Make unknown device error more useful
robertlong Nov 20, 2021
d0e37ee
Hopefully resolve a race condition with missing device ids
robertlong Nov 20, 2021
ffbd10a
Make updateLocalUsermediaStreams stop tracks
robertlong Nov 22, 2021
d1a62ed
Set initial audio/video input ids
robertlong Nov 24, 2021
fcc4b71
Add LocalStreamsChanged event to MediaHandler
robertlong Nov 29, 2021
e336ace
Expose webrtc related types/props
robertlong Nov 30, 2021
06d9d62
Send device id along with to device signaling messages
robertlong Nov 30, 2021
549f9b7
Disable retries
robertlong Jan 6, 2022
bef5579
Emit sent voip events
robertlong Jan 6, 2022
7993dd7
Log opponentDeviceId
robertlong Jan 6, 2022
f3f9e41
Emit sent voip events
robertlong Jan 6, 2022
18bb5c3
Log opponentDeviceId
robertlong Jan 6, 2022
87bf115
Use session ids to resolve refresh during invite/answer
robertlong Jan 10, 2022
c91617a
Force hangup replaced calls
robertlong Jan 11, 2022
487bfc8
Merge branch 'robertlong/group-call-session-id' into robertlong/group…
robertlong Jan 11, 2022
3fac6d7
Replace outbound calls only
robertlong Jan 11, 2022
7529af4
Add NewSession CallErrorCode
robertlong Jan 12, 2022
ee995cb
Ensure call events are processed once and in order
robertlong Jan 12, 2022
eeacf8c
Dont filter unstable call events
robertlong Jan 12, 2022
d8285aa
Remove call from callEventHandler after hangup
robertlong Jan 12, 2022
28f3169
Use replace error code when replacing incoming calls
robertlong Jan 12, 2022
42fef0e
Add user id to all send voip events
robertlong Jan 13, 2022
015eb5d
Add sender/dest session ids
robertlong Jan 14, 2022
592fb0c
Re-enable retries
robertlong Jan 18, 2022
bbf7020
Remove log
robertlong Jan 18, 2022
1a78301
Fix restartIce on FF Android
robertlong Feb 4, 2022
4a4465b
Don't send candidates after the call has ended
robertlong Feb 7, 2022
d42e2fe
Ignore duplicate streams when adding local feeds
robertlong Feb 10, 2022
b4fe00a
Add answer/negotiate response promise chain
robertlong Feb 10, 2022
c801690
Don't reuse local call feeds that have been added to a RTCPeerConnection
robertlong Feb 12, 2022
d8e597c
Avoid glare
robertlong Feb 12, 2022
d12bccd
Remove safari hack
robertlong Feb 15, 2022
a2a127d
Remove unused isSafari check
robertlong Feb 15, 2022
3db056a
Enable max-bundle (#2182)
dbkr Feb 16, 2022
fa5eae7
Log complete sync errors
robertlong Feb 17, 2022
7f21f56
Process toDevice events in order
robertlong Feb 17, 2022
353d6ba
Fix and add a test for toDevice ordering
robertlong Feb 18, 2022
7a249e3
Switch media devices on disconnect
robertlong Feb 18, 2022
0ef6c2e
Add callId to all logs
robertlong Feb 19, 2022
e270f07
Fix call log
robertlong Feb 23, 2022
17f5ab4
Move device changes to the application. Add methods to set device ids
robertlong Feb 23, 2022
c819ac6
Fix updating local media streams
robertlong Feb 24, 2022
e68cabc
Add logging for all stream creation/cloning/muting
robertlong Feb 24, 2022
49994ac
Add checks for call/groupCall ended for updateLocalUsermediaStream
robertlong Feb 25, 2022
09fee4a
Allow calls to terminate properly when calling stopClient
robertlong Feb 25, 2022
94c5e37
Fix import
robertlong Feb 25, 2022
6e25b13
Send / add end-of-candidates messages
robertlong Mar 1, 2022
2a0dc39
Fix bug with ine-way audio after a transfer (#2193)
dbkr Feb 23, 2022
ee4cbd1
Don't remove streams that still have tracks (#2104)
dbkr Jan 14, 2022
96ba061
Fix shouldRequestAudio logging
robertlong Mar 2, 2022
8d9cd0f
Support for PTT group call mode (#2338)
dbkr May 3, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 63 additions & 7 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { EventEmitter } from "events";
import { ISyncStateData, SyncApi } from "./sync";
import { EventStatus, IContent, IDecryptOptions, IEvent, MatrixEvent } from "./models/event";
import { StubStore } from "./store/stub";
import { createNewMatrixCall, MatrixCall } from "./webrtc/call";
import { createNewMatrixCall, MatrixCall, ConstraintsType, getUserMediaContraints, CallType } from "./webrtc/call";
import { Filter, IFilterDefinition } from "./filter";
import { CallEventHandler } from './webrtc/callEventHandler';
import * as utils from './utils';
Expand Down Expand Up @@ -144,6 +144,7 @@ import { IHierarchyRoom, ISpaceSummaryEvent, ISpaceSummaryRoom } from "./@types/
import { IPusher, IPusherRequest, IPushRules, PushRuleAction, PushRuleKind, RuleId } from "./@types/PushRules";
import { IThreepid } from "./@types/threepids";
import { CryptoStore } from "./crypto/store/base";
import { GroupCall } from "./webrtc/groupCall";

export type Store = IStore;
export type SessionStore = WebStorageSessionStore;
Expand Down Expand Up @@ -694,6 +695,8 @@ export class MatrixClient extends EventEmitter {
public iceCandidatePoolSize = 0; // XXX: Intended private, used in code.
public idBaseUrl: string;
public baseUrl: string;
private localAVStreamType: ConstraintsType;
private localAVStream: MediaStream;

// Note: these are all `protected` to let downstream consumers make mistakes if they want to.
// We don't technically support this usage, but have reasons to do this.
Expand Down Expand Up @@ -1258,15 +1261,68 @@ export class MatrixClient extends EventEmitter {
this.supportsCallTransfer = support;
}

public async getLocalVideoStream() {
robertlong marked this conversation as resolved.
Show resolved Hide resolved
if (this.localAVStreamType === ConstraintsType.Video) {
return this.localAVStream.clone();
}

const constraints = getUserMediaContraints(ConstraintsType.Video);
logger.log("Getting user media with constraints", constraints);
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
this.localAVStreamType = ConstraintsType.Video;
this.localAVStream = mediaStream;
return mediaStream;
}

public async getLocalAudioStream() {
if (this.localAVStreamType === ConstraintsType.Audio) {
return this.localAVStream.clone();
}

const constraints = getUserMediaContraints(ConstraintsType.Audio);
logger.log("Getting user media with constraints", constraints);
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
this.localAVStreamType = ConstraintsType.Audio;
this.localAVStream = mediaStream;
return mediaStream;
}

public stopLocalMediaStream() {
if (this.localAVStream) {
for (const track of this.localAVStream.getTracks()) {
track.stop();
}

this.localAVStreamType = null;
this.localAVStream = null;
}
}

/**
* Creates a new call.
* The place*Call methods on the returned call can be used to actually place a call
*
* @param {string} roomId The room the call is to be placed in.
* @param {string} invitee The user to call in the given room.
* @return {MatrixCall} the call or null if the browser doesn't support calling.
*/
public createCall(roomId: string): MatrixCall {
return createNewMatrixCall(this, roomId);
public createCall(roomId: string, invitee?: string): MatrixCall {
return createNewMatrixCall(this, roomId, { invitee });
}

/**
* Creates a new group call.
*
* @param {string} roomId The room the call is to be placed in.
* @return {GroupCall} the call or null if the browser doesn't support calling.
*/
public createGroupCall(
roomId: string,
type: CallType,
dataChannelsEnabled?: boolean,
dataChannelOptions?: RTCDataChannelInit,
): GroupCall {
return new GroupCall(this, roomId, type, dataChannelsEnabled, dataChannelOptions);
}

/**
Expand Down Expand Up @@ -6107,11 +6163,11 @@ export class MatrixClient extends EventEmitter {
public register(
username: string,
password: string,
sessionId: string,
sessionId: string | null,
robertlong marked this conversation as resolved.
Show resolved Hide resolved
auth: any,
bindThreepids: any,
guestAccessToken: string,
inhibitLogin: boolean,
bindThreepids?: any,
guestAccessToken?: string,
inhibitLogin?: boolean,
callback?: Callback,
): Promise<any> { // TODO: Types (many)
// backwards compat
Expand Down
6 changes: 6 additions & 0 deletions src/matrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,14 @@ export {
createNewMatrixCall,
setAudioInput as setMatrixCallAudioInput,
setVideoInput as setMatrixCallVideoInput,
CallType,
} from "./webrtc/call";

// TODO: This export is temporary and is only used for the local call feed for conference calls
// Ideally conference calls will become a first-class concept and we will have a local call feed with
// a lifecycle that matches the conference call, not individual calls to members.
export { CallFeed } from "./webrtc/callFeed";
robertlong marked this conversation as resolved.
Show resolved Hide resolved

// expose the underlying request object so different environments can use
// different request libs (e.g. request or browser-request)
let requestInstance;
Expand Down
63 changes: 53 additions & 10 deletions src/webrtc/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { ISendEventResponse } from "../@types/requests";

interface CallOpts {
roomId?: string;
invitee?: string;
client?: any; // Fix when client is TSified
forceTURN?: boolean;
turnServers?: Array<TurnServer>;
Expand Down Expand Up @@ -128,6 +129,7 @@ export enum CallEvent {

AssertedIdentityChanged = 'asserted_identity_changed',

DataChannel = 'datachannel',
LengthChanged = 'length_changed'
}

Expand Down Expand Up @@ -212,7 +214,7 @@ export enum CallErrorCode {
Transfered = 'transferred',
}

enum ConstraintsType {
export enum ConstraintsType {
Audio = "audio",
Video = "video",
}
Expand Down Expand Up @@ -273,6 +275,7 @@ export class MatrixCall extends EventEmitter {
public roomId: string;
public type: CallType = null;
public callId: string;
public invitee?: string;
public state = CallState.Fledgling;
public hangupParty: CallParty;
public hangupReason: string;
Expand Down Expand Up @@ -302,6 +305,7 @@ export class MatrixCall extends EventEmitter {
private opponentPartyId: string;
private opponentCaps: CallCapabilities;
private inviteTimeout: number;
private iceDisconnectedTimeout: number;

// The logic of when & if a call is on hold is nontrivial and explained in is*OnHold
// This flag represents whether we want the other party to be on hold
Expand Down Expand Up @@ -331,6 +335,7 @@ export class MatrixCall extends EventEmitter {
constructor(opts: CallOpts) {
super();
this.roomId = opts.roomId;
this.invitee = opts.invitee;
this.client = opts.client;
this.forceTURN = opts.forceTURN;
this.ourPartyId = this.client.deviceId;
Expand All @@ -354,9 +359,8 @@ export class MatrixCall extends EventEmitter {
public async placeVoiceCall(): Promise<void> {
logger.debug("placeVoiceCall");
this.checkForErrorListener();
const constraints = getUserMediaContraints(ConstraintsType.Audio);
this.type = CallType.Voice;
await this.placeCallWithConstraints(constraints);
await this.placeCallWithConstraints(ConstraintsType.Audio);
}

/**
Expand All @@ -366,9 +370,15 @@ export class MatrixCall extends EventEmitter {
public async placeVideoCall(): Promise<void> {
logger.debug("placeVideoCall");
this.checkForErrorListener();
const constraints = getUserMediaContraints(ConstraintsType.Video);
this.type = CallType.Video;
await this.placeCallWithConstraints(constraints);
await this.placeCallWithConstraints(ConstraintsType.Video);
}

public createDataChannel(label: string, options: RTCDataChannelInit) {
robertlong marked this conversation as resolved.
Show resolved Hide resolved
logger.debug("createDataChannel");
const dataChannel = this.peerConn.createDataChannel(label, options);
this.emit(CallEvent.DataChannel, dataChannel);
return dataChannel;
}

public getOpponentMember(): RoomMember {
Expand Down Expand Up @@ -558,6 +568,10 @@ export class MatrixCall extends EventEmitter {
}

private deleteAllFeeds(): void {
for (const feed of this.feeds) {
feed.dispose();
}

this.feeds = [];
this.emit(CallEvent.FeedsChanged, this.feeds);
}
Expand All @@ -571,6 +585,7 @@ export class MatrixCall extends EventEmitter {
return;
}

feed.dispose();
this.feeds.splice(this.feeds.indexOf(feed), 1);
this.emit(CallEvent.FeedsChanged, this.feeds);
}
Expand Down Expand Up @@ -697,7 +712,14 @@ export class MatrixCall extends EventEmitter {
this.waitForLocalAVStream = true;

try {
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
let mediaStream: MediaStream;

if (this.type === CallType.Voice) {
mediaStream = await this.client.getLocalAudioStream();
robertlong marked this conversation as resolved.
Show resolved Hide resolved
} else {
mediaStream = await this.client.getLocalVideoStream();
}

this.waitForLocalAVStream = false;
this.gotUserMediaForAnswer(mediaStream);
} catch (e) {
Expand Down Expand Up @@ -1384,6 +1406,10 @@ export class MatrixCall extends EventEmitter {
lifetime: CALL_TIMEOUT_MS,
} as MCallInviteNegotiate;

if (eventType === EventType.CallInvite && this.invitee) {
content.invitee = this.invitee;
}

// clunky because TypeScript can't follow the types through if we use an expression as the key
if (this.state === CallState.CreateOffer) {
content.offer = this.peerConn.localDescription;
Expand Down Expand Up @@ -1483,13 +1509,18 @@ export class MatrixCall extends EventEmitter {
// ideally we'd consider the call to be connected when we get media but
// chrome doesn't implement any of the 'onstarted' events yet
if (this.peerConn.iceConnectionState == 'connected') {
clearTimeout(this.iceDisconnectedTimeout);
this.setState(CallState.Connected);
this.callLengthInterval = setInterval(() => {
this.callLength++;
this.emit(CallEvent.LengthChanged, this.callLength);
}, 1000);
} else if (this.peerConn.iceConnectionState == 'failed') {
this.hangup(CallErrorCode.IceFailed, false);
} else if (this.peerConn.iceConnectionState == 'disconnected') {
this.iceDisconnectedTimeout = setTimeout(() => {
this.hangup(CallErrorCode.IceFailed, false);
}, 30 * 1000);
}
};

Expand All @@ -1511,6 +1542,10 @@ export class MatrixCall extends EventEmitter {
stream.addEventListener("removetrack", () => this.deleteFeedByStream(stream));
};

private onDataChannel = (ev: RTCDataChannelEvent): void => {
this.emit(CallEvent.DataChannel, ev.channel);
};

/**
* This method removes all video/rtx codecs from screensharing video
* transceivers. This is necessary since they can cause problems. Without
Expand Down Expand Up @@ -1826,8 +1861,7 @@ export class MatrixCall extends EventEmitter {
}
}

private async placeCallWithConstraints(constraints: MediaStreamConstraints): Promise<void> {
logger.log("Getting user media with constraints", constraints);
private async placeCallWithConstraints(constraintsType: ConstraintsType): Promise<void> {
// XXX Find a better way to do this
this.client.callEventHandler.calls.set(this.callId, this);
this.setState(CallState.WaitLocalMedia);
Expand All @@ -1845,7 +1879,14 @@ export class MatrixCall extends EventEmitter {
this.peerConn = this.createPeerConnection();

try {
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
let mediaStream: MediaStream;

if (constraintsType === ConstraintsType.Audio) {
mediaStream = await this.client.getLocalAudioStream();
} else {
mediaStream = await this.client.getLocalVideoStream();
}

this.gotUserMediaForInvite(mediaStream);
} catch (e) {
this.getUserMediaFailed(e);
Expand All @@ -1867,6 +1908,7 @@ export class MatrixCall extends EventEmitter {
pc.addEventListener('icegatheringstatechange', this.onIceGatheringStateChange);
pc.addEventListener('track', this.onTrack);
pc.addEventListener('negotiationneeded', this.onNegotiationNeeded);
pc.addEventListener('datachannel', this.onDataChannel);

return pc;
}
Expand Down Expand Up @@ -1962,7 +2004,7 @@ function setTracksEnabled(tracks: Array<MediaStreamTrack>, enabled: boolean): vo
}
}

function getUserMediaContraints(type: ConstraintsType): MediaStreamConstraints {
export function getUserMediaContraints(type: ConstraintsType): MediaStreamConstraints {
const isWebkit = !!navigator.webkitGetUserMedia;

switch (type) {
Expand Down Expand Up @@ -2085,6 +2127,7 @@ export function createNewMatrixCall(client: any, roomId: string, options?: CallO
const opts = {
client: client,
roomId: roomId,
invitee: options && options.invitee,
turnServers: client.getTurnServers(),
// call level options
forceTURN: client.forceTURN || optionsForceTURN,
Expand Down
5 changes: 5 additions & 0 deletions src/webrtc/callEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ export class CallEventHandler {
);
}

if (content.invitee && content.invitee !== this.client.getUserId()) {
return; // This invite was meant for another user in the room
}

const timeUntilTurnCresExpire = this.client.getTurnServersExpiry() - Date.now();
logger.info("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
call = createNewMatrixCall(
Expand Down Expand Up @@ -190,6 +194,7 @@ export class CallEventHandler {
if (
call.roomId === thisCall.roomId &&
thisCall.direction === CallDirection.Outbound &&
call.invitee === thisCall.invitee &&
isCalling
) {
existingCall = thisCall;
Expand Down
1 change: 1 addition & 0 deletions src/webrtc/callEventTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export interface MCallInviteNegotiate extends MCallBase {
description: RTCSessionDescription;
lifetime: number;
capabilities?: CallCapabilities;
invitee?: string;
[SDPStreamMetadataKey]: SDPStreamMetadata;
}

Expand Down
9 changes: 7 additions & 2 deletions src/webrtc/callFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export class CallFeed extends EventEmitter {
private analyser: AnalyserNode;
private frequencyBinCount: Float32Array;
private speakingThreshold = SPEAKING_THRESHOLD;
private speaking = false;
public speaking = false;
private volumeLooperTimeout: number;

constructor(
public stream: MediaStream,
Expand Down Expand Up @@ -166,7 +167,7 @@ export class CallFeed extends EventEmitter {
private volumeLooper(): void {
if (!this.analyser) return;

setTimeout(() => {
this.volumeLooperTimeout = setTimeout(() => {
if (!this.measuringVolumeActivity) return;

this.analyser.getFloatFrequencyData(this.frequencyBinCount);
Expand All @@ -188,4 +189,8 @@ export class CallFeed extends EventEmitter {
this.volumeLooper();
}, POLLING_INTERVAL);
}

public dispose(): void {
clearTimeout(this.volumeLooperTimeout);
robertlong marked this conversation as resolved.
Show resolved Hide resolved
}
}
Loading