diff --git a/spec/unit/webrtc/groupCall.spec.ts b/spec/unit/webrtc/groupCall.spec.ts index 3c9266b98c8..a300ebac8ed 100644 --- a/spec/unit/webrtc/groupCall.spec.ts +++ b/spec/unit/webrtc/groupCall.spec.ts @@ -515,7 +515,7 @@ describe('Group Call', function() { it("sends metadata updates before unmuting in PTT mode", async () => { const mockCall = new MockCall(FAKE_ROOM_ID, groupCall.groupCallId); - groupCall.calls.set( + (groupCall as any).calls.set( mockCall.getOpponentMember() as RoomMember, new Map([[mockCall.getOpponentDeviceId(), mockCall.typed()]]), ); @@ -540,7 +540,7 @@ describe('Group Call', function() { it("sends metadata updates after muting in PTT mode", async () => { const mockCall = new MockCall(FAKE_ROOM_ID, groupCall.groupCallId); - groupCall.calls.set( + (groupCall as any).calls.set( mockCall.getOpponentMember() as RoomMember, new Map([[mockCall.getOpponentDeviceId(), mockCall.typed()]]), ); @@ -698,7 +698,7 @@ describe('Group Call', function() { expect(client1.sendToDevice).toHaveBeenCalled(); - const oldCall = groupCall1.calls.get( + const oldCall = (groupCall1 as any).calls.get( groupCall1.room.getMember(client2.userId)!, )!.get(client2.deviceId)!; oldCall.emit(CallEvent.Hangup, oldCall!); @@ -719,7 +719,7 @@ describe('Group Call', function() { // to even be created... let newCall: MatrixCall | undefined; while ( - (newCall = groupCall1.calls.get( + (newCall = (groupCall1 as any).calls.get( groupCall1.room.getMember(client2.userId)!, )?.get(client2.deviceId)) === undefined || newCall.peerConn === undefined @@ -763,7 +763,7 @@ describe('Group Call', function() { groupCall1.setMicrophoneMuted(false); groupCall1.setLocalVideoMuted(false); - const call = groupCall1.calls.get( + const call = (groupCall1 as any).calls.get( groupCall1.room.getMember(client2.userId)!, )!.get(client2.deviceId)!; call.isMicrophoneMuted = jest.fn().mockReturnValue(true); @@ -874,7 +874,9 @@ describe('Group Call', function() { // It takes a bit of time for the calls to get created await sleep(10); - const call = groupCall.calls.get(groupCall.room.getMember(FAKE_USER_ID_2)!)!.get(FAKE_DEVICE_ID_2)!; + const call = (groupCall as any).calls + .get(groupCall.room.getMember(FAKE_USER_ID_2)!)! + .get(FAKE_DEVICE_ID_2)!; call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; // @ts-ignore Mock call.pushRemoteFeed(new MockMediaStream("stream", [ @@ -897,7 +899,9 @@ describe('Group Call', function() { // It takes a bit of time for the calls to get created await sleep(10); - const call = groupCall.calls.get(groupCall.room.getMember(FAKE_USER_ID_2)!)!.get(FAKE_DEVICE_ID_2)!; + const call = (groupCall as any).calls + .get(groupCall.room.getMember(FAKE_USER_ID_2)!)! + .get(FAKE_DEVICE_ID_2)!; call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; // @ts-ignore Mock call.pushRemoteFeed(new MockMediaStream("stream", [ @@ -972,7 +976,7 @@ describe('Group Call', function() { expect(mockCall.reject).not.toHaveBeenCalled(); expect(mockCall.answerWithCallFeeds).toHaveBeenCalled(); - expect(groupCall.calls).toEqual(new Map([[ + expect((groupCall as any).calls).toEqual(new Map([[ groupCall.room.getMember(FAKE_USER_ID_1)!, new Map([[FAKE_DEVICE_ID_1, mockCall]]), ]])); @@ -989,7 +993,7 @@ describe('Group Call', function() { expect(oldMockCall.hangup).toHaveBeenCalled(); expect(newMockCall.answerWithCallFeeds).toHaveBeenCalled(); - expect(groupCall.calls).toEqual(new Map([[ + expect((groupCall as any).calls).toEqual(new Map([[ groupCall.room.getMember(FAKE_USER_ID_1)!, new Map([[FAKE_DEVICE_ID_1, newMockCall]]), ]])); @@ -1072,7 +1076,9 @@ describe('Group Call', function() { // It takes a bit of time for the calls to get created await sleep(10); - const call = groupCall.calls.get(groupCall.room.getMember(FAKE_USER_ID_2)!)!.get(FAKE_DEVICE_ID_2)!; + const call = (groupCall as any).calls + .get(groupCall.room.getMember(FAKE_USER_ID_2)!)! + .get(FAKE_DEVICE_ID_2)!; call.getOpponentMember = () => ({ userId: call.invitee }) as RoomMember; call.onNegotiateReceived({ getContent: () => ({ diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index b2d0c1e46db..ad5e6668bcd 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -656,6 +656,7 @@ export class MatrixCall extends TypedEventEmitter void; [CallFeedEvent.LocalVolumeChanged]: (localVolume: number) => void; [CallFeedEvent.VolumeChanged]: (volume: number) => void; + [CallFeedEvent.ConnectedChanged]: (connected: boolean) => void; [CallFeedEvent.Speaking]: (speaking: boolean) => void; [CallFeedEvent.Disposed]: () => void; }; @@ -69,6 +76,7 @@ export class CallFeed extends TypedEventEmitter public speakingVolumeSamples: number[]; private client: MatrixClient; + private call?: MatrixCall; private roomId?: string; private audioMuted: boolean; private videoMuted: boolean; @@ -81,11 +89,13 @@ export class CallFeed extends TypedEventEmitter private speaking = false; private volumeLooperTimeout?: ReturnType; private _disposed = false; + private _connected = false; public constructor(opts: ICallFeedOpts) { super(); this.client = opts.client; + this.call = opts.call; this.roomId = opts.roomId; this.userId = opts.userId; this.deviceId = opts.deviceId; @@ -101,6 +111,21 @@ export class CallFeed extends TypedEventEmitter if (this.hasAudioTrack) { this.initVolumeMeasuring(); } + + if (opts.call) { + opts.call.addListener(CallEvent.State, this.onCallState); + this.onCallState(opts.call.state); + } + } + + public get connected(): boolean { + // Local feeds are always considered connected + return this.isLocal() || this._connected; + } + + private set connected(connected: boolean) { + this._connected = connected; + this.emit(CallFeedEvent.ConnectedChanged, connected); } private get hasAudioTrack(): boolean { @@ -145,6 +170,14 @@ export class CallFeed extends TypedEventEmitter this.emit(CallFeedEvent.NewStream, this.stream); }; + private onCallState = (state: CallState): void => { + if (state === CallState.Connected) { + this.connected = true; + } else if (state === CallState.Connecting) { + this.connected = false; + } + }; + /** * Returns callRoom member * @returns member of the callRoom @@ -297,6 +330,7 @@ export class CallFeed extends TypedEventEmitter public dispose(): void { clearTimeout(this.volumeLooperTimeout); this.stream?.removeEventListener("addtrack", this.onAddTrack); + this.call?.removeListener(CallEvent.State, this.onCallState); if (this.audioContext) { this.audioContext = undefined; this.analyser = undefined; diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index fde46182bc1..5941b6a3728 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -168,11 +168,11 @@ export class GroupCall extends TypedEventEmitter< public localCallFeed?: CallFeed; public localScreenshareFeed?: CallFeed; public localDesktopCapturerSourceId?: string; - public readonly calls = new Map>(); public readonly userMediaFeeds: CallFeed[] = []; public readonly screenshareFeeds: CallFeed[] = []; public groupCallId: string; + private readonly calls = new Map>(); // RoomMember -> device ID -> MatrixCall private callHandlers = new Map>(); // User ID -> device ID -> handlers private activeSpeakerLoopInterval?: ReturnType; private retryCallLoopInterval?: ReturnType;