diff --git a/spec/unit/webrtc/callFeed.spec.ts b/spec/unit/webrtc/callFeed.spec.ts index 6107ba93f95..4b8e7a34396 100644 --- a/spec/unit/webrtc/callFeed.spec.ts +++ b/spec/unit/webrtc/callFeed.spec.ts @@ -23,13 +23,7 @@ describe("CallFeed", () => { let client; beforeEach(() => { - client = new TestClient( - "@alice:foo", - "somedevice", - "token", - undefined, - {}, - ); + client = new TestClient("@alice:foo", "somedevice", "token", undefined, {}); }); afterEach(() => { @@ -65,17 +59,13 @@ describe("CallFeed", () => { describe("muting after adding a track", () => { it("should un-mute audio", () => { // @ts-ignore Mock - feed.stream.addTrack( - new MockMediaStreamTrack("track", "audio", true), - ); + feed.stream.addTrack(new MockMediaStreamTrack("track", "audio", true)); expect(feed.isAudioMuted()).toBeFalsy(); }); it("should un-mute video", () => { // @ts-ignore Mock - feed.stream.addTrack( - new MockMediaStreamTrack("track", "video", true), - ); + feed.stream.addTrack(new MockMediaStreamTrack("track", "video", true)); expect(feed.isVideoMuted()).toBeFalsy(); }); }); @@ -83,18 +73,14 @@ describe("CallFeed", () => { describe("muting after calling setAudioVideoMuted()", () => { it("should mute audio by default ", () => { // @ts-ignore Mock - feed.stream.addTrack( - new MockMediaStreamTrack("track", "audio", true), - ); + feed.stream.addTrack(new MockMediaStreamTrack("track", "audio", true)); feed.setAudioVideoMuted(true, false); expect(feed.isAudioMuted()).toBeTruthy(); }); it("should mute video by default", () => { // @ts-ignore Mock - feed.stream.addTrack( - new MockMediaStreamTrack("track", "video", true), - ); + feed.stream.addTrack(new MockMediaStreamTrack("track", "video", true)); feed.setAudioVideoMuted(false, true); expect(feed.isVideoMuted()).toBeTruthy(); }); diff --git a/src/webrtc/callFeed.ts b/src/webrtc/callFeed.ts index 064a2a432fb..dbe105f8f66 100644 --- a/src/webrtc/callFeed.ts +++ b/src/webrtc/callFeed.ts @@ -63,10 +63,7 @@ type EventHandlerMap = { [CallFeedEvent.Speaking]: (speaking: boolean) => void; [CallFeedEvent.VoiceActivityThresholdChanged]: (threshold: number) => void; }; -export class CallFeed extends TypedEventEmitter< - CallFeedEvent, - EventHandlerMap -> { +export class CallFeed extends TypedEventEmitter { public stream: MediaStream; public secondStream: MediaStream; public sdpMetadataStreamId: string; @@ -107,9 +104,7 @@ export class CallFeed extends TypedEventEmitter< this.purpose = opts.purpose; this.audioMuted = opts.audioMuted; this.videoMuted = opts.videoMuted; - this.speakingVolumeSamples = new Array(SPEAKING_SAMPLE_COUNT).fill( - -Infinity, - ); + this.speakingVolumeSamples = new Array(SPEAKING_SAMPLE_COUNT).fill(-Infinity); this.sdpMetadataStreamId = opts.stream.id; this.voiceActivityThreshold = -55; this.setVADMute = opts.setVADMute; @@ -159,13 +154,10 @@ export class CallFeed extends TypedEventEmitter< this.analyser.smoothingTimeConstant = 0.1; this.secondStream = this.stream.clone(); - const mediaStreamAudioSourceNode = - this.audioContext.createMediaStreamSource(this.secondStream); + const mediaStreamAudioSourceNode = this.audioContext.createMediaStreamSource(this.stream); mediaStreamAudioSourceNode.connect(this.analyser); - this.frequencyBinCount = new Float32Array( - this.analyser.frequencyBinCount, - ); + this.frequencyBinCount = new Float32Array(this.analyser.frequencyBinCount); } private onAddTrack = (): void => { @@ -229,10 +221,7 @@ export class CallFeed extends TypedEventEmitter< * Either value may be null to leave it as-is * @param muted is the feed's video muted? */ - public setAudioVideoMuted( - audioMuted: boolean | null, - videoMuted: boolean | null, - ): void { + public setAudioVideoMuted(audioMuted: boolean, videoMuted: boolean): void { if (audioMuted !== null) { if (this.audioMuted !== audioMuted) { this.speakingVolumeSamples.fill(-Infinity); @@ -240,17 +229,10 @@ export class CallFeed extends TypedEventEmitter< this.audioMuted = audioMuted; } if (videoMuted !== null) this.videoMuted = videoMuted; - this.emit( - CallFeedEvent.MuteStateChanged, - this.audioMuted, - this.videoMuted, - ); + this.emit(CallFeedEvent.MuteStateChanged, this.audioMuted, this.videoMuted); } - public setVadMuted( - audioMuted: boolean | null, - videoMuted: boolean | null, - ): void { + public setVadMuted(audioMuted: boolean | null, videoMuted: boolean | null): void { if (audioMuted !== null) { this.vadAudioMuted = audioMuted; } @@ -262,13 +244,7 @@ export class CallFeed extends TypedEventEmitter< */ public measureVolumeActivity(enabled: boolean): void { if (enabled) { - if ( - !this.analyser || - !this.frequencyBinCount || - !this.hasAudioTrack - ) { - return; - } + if (!this.analyser || !this.frequencyBinCount || !this.hasAudioTrack) return; this.measuringVolumeActivity = true; this.volumeLooper(); @@ -356,9 +332,7 @@ export class CallFeed extends TypedEventEmitter< public clone(): CallFeed { const mediaHandler = this.client.getMediaHandler(); const stream = this.stream.clone(); - logger.log( - `callFeed cloning stream ${this.stream.id} newStream ${stream.id}`, - ); + logger.log(`callFeed cloning stream ${this.stream.id} newStream ${stream.id}`); if (this.purpose === SDPStreamMetadataPurpose.Usermedia) { mediaHandler.userMediaStreams.push(stream); diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index 8470b17fedf..cdc933b63df 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -51,23 +51,13 @@ export enum GroupCallEvent { } export type GroupCallEventHandlerMap = { - [GroupCallEvent.GroupCallStateChanged]: ( - newState: GroupCallState, - oldState: GroupCallState, - ) => void; + [GroupCallEvent.GroupCallStateChanged]: (newState: GroupCallState, oldState: GroupCallState) => void; [GroupCallEvent.ActiveSpeakerChanged]: (activeSpeaker: string) => void; [GroupCallEvent.CallsChanged]: (calls: MatrixCall[]) => void; [GroupCallEvent.UserMediaFeedsChanged]: (feeds: CallFeed[]) => void; [GroupCallEvent.ScreenshareFeedsChanged]: (feeds: CallFeed[]) => void; - [GroupCallEvent.LocalScreenshareStateChanged]: ( - isScreensharing: boolean, - feed: CallFeed, - sourceId: string, - ) => void; - [GroupCallEvent.LocalMuteStateChanged]: ( - audioMuted: boolean, - videoMuted: boolean, - ) => void; + [GroupCallEvent.LocalScreenshareStateChanged]: (isScreensharing: boolean, feed: CallFeed, sourceId: string) => void; + [GroupCallEvent.LocalMuteStateChanged]: (audioMuted: boolean, videoMuted: boolean) => void; [GroupCallEvent.ParticipantsChanged]: (participants: RoomMember[]) => void; [GroupCallEvent.Error]: (error: GroupCallError) => void; }; @@ -95,10 +85,7 @@ export class GroupCallError extends Error { export class GroupCallUnknownDeviceError extends GroupCallError { constructor(public userId: string) { - super( - GroupCallErrorCode.UnknownDevice, - "No device found for " + userId, - ); + super(GroupCallErrorCode.UnknownDevice, "No device found for " + userId); } } @@ -121,9 +108,9 @@ export interface IGroupCallRoomMemberFeed { } export interface IGroupCallRoomMemberDevice { - device_id: string; - session_id: string; - feeds: IGroupCallRoomMemberFeed[]; + "device_id": string; + "session_id": string; + "feeds": IGroupCallRoomMemberFeed[]; } export interface IGroupCallRoomMemberCallState { @@ -158,10 +145,7 @@ const CALL_MEMBER_STATE_TIMEOUT = 1000 * 60 * 60; // 1 hour const callMemberStateIsExpired = (event: MatrixEvent): boolean => { const now = Date.now(); const content = event?.getContent() ?? {}; - const expiresAt = - typeof content["m.expires_ts"] === "number" - ? content["m.expires_ts"] - : -Infinity; + const expiresAt = typeof content["m.expires_ts"] === "number" ? content["m.expires_ts"] : -Infinity; return expiresAt <= now; }; @@ -196,10 +180,7 @@ export class GroupCall extends TypedEventEmitter< private retryCallCounts: Map = new Map(); private reEmitter: ReEmitter; private transmitTimer: ReturnType | null = null; - private memberStateExpirationTimers: Map< - string, - ReturnType - > = new Map(); + private memberStateExpirationTimers: Map> = new Map(); private resendMemberStateTimer: ReturnType | null = null; constructor( @@ -222,10 +203,7 @@ export class GroupCall extends TypedEventEmitter< } public async create() { - this.client.groupCallEventHandler.groupCalls.set( - this.room.roomId, - this, - ); + this.client.groupCallEventHandler.groupCalls.set(this.room.roomId, this); await this.client.sendStateEvent( this.room.roomId, @@ -235,8 +213,8 @@ export class GroupCall extends TypedEventEmitter< "m.type": this.type, "io.element.ptt": this.isPtt, // TODO: Specify datachannels - dataChannelsEnabled: this.dataChannelsEnabled, - dataChannelOptions: this.dataChannelOptions, + "dataChannelsEnabled": this.dataChannelsEnabled, + "dataChannelOptions": this.dataChannelOptions, }, this.groupCallId, ); @@ -268,9 +246,7 @@ export class GroupCall extends TypedEventEmitter< logger.log(`groupCall ${this.groupCallId} initLocalCallFeed`); if (this.state !== GroupCallState.LocalCallFeedUninitialized) { - throw new Error( - `Cannot initialize local call feed in the "${this.state}" state.`, - ); + throw new Error(`Cannot initialize local call feed in the "${this.state}" state.`); } this.setState(GroupCallState.InitializingLocalCallFeed); @@ -278,9 +254,7 @@ export class GroupCall extends TypedEventEmitter< let stream: MediaStream; try { - stream = await this.client - .getMediaHandler() - .getUserMediaStream(true, this.type === GroupCallType.Video); + stream = await this.client.getMediaHandler().getUserMediaStream(true, this.type === GroupCallType.Video); } catch (error) { this.setState(GroupCallState.LocalCallFeedUninitialized); throw error; @@ -319,9 +293,7 @@ export class GroupCall extends TypedEventEmitter< this.localCallFeed.setNewStream(stream); const micShouldBeMuted = this.localCallFeed.isAudioMuted(); const vidShouldBeMuted = this.localCallFeed.isVideoMuted(); - logger.log( - `groupCall ${this.groupCallId} updateLocalUsermediaStream oldStream ${oldStream.id} newStream ${stream.id} micShouldBeMuted ${micShouldBeMuted} vidShouldBeMuted ${vidShouldBeMuted}`, - ); + logger.log(`groupCall ${this.groupCallId} updateLocalUsermediaStream oldStream ${oldStream.id} newStream ${stream.id} micShouldBeMuted ${micShouldBeMuted} vidShouldBeMuted ${vidShouldBeMuted}`); setTracksEnabled(stream.getAudioTracks(), !micShouldBeMuted); setTracksEnabled(stream.getVideoTracks(), !vidShouldBeMuted); this.client.getMediaHandler().stopUserMediaStream(oldStream); @@ -329,12 +301,8 @@ export class GroupCall extends TypedEventEmitter< } public async enter() { - if ( - !( - this.state === GroupCallState.LocalCallFeedUninitialized || - this.state === GroupCallState.LocalCallFeedInitialized - ) - ) { + if (!(this.state === GroupCallState.LocalCallFeedUninitialized || + this.state === GroupCallState.LocalCallFeedInitialized)) { throw new Error(`Cannot enter call in the "${this.state}" state`); } @@ -366,10 +334,7 @@ export class GroupCall extends TypedEventEmitter< this.onMemberStateChanged(stateEvent); } - this.retryCallLoopTimeout = setTimeout( - this.onRetryCallLoop, - this.retryCallInterval, - ); + this.retryCallLoopTimeout = setTimeout(this.onRetryCallLoop, this.retryCallInterval); this.onActiveSpeakerLoop(); } @@ -381,9 +346,7 @@ export class GroupCall extends TypedEventEmitter< } if (this.localScreenshareFeed) { - this.client - .getMediaHandler() - .stopScreensharingStream(this.localScreenshareFeed.stream); + this.client.getMediaHandler().stopScreensharingStream(this.localScreenshareFeed.stream); this.removeScreenshareFeed(this.localScreenshareFeed); this.localScreenshareFeed = undefined; this.localDesktopCapturerSourceId = undefined; @@ -400,10 +363,7 @@ export class GroupCall extends TypedEventEmitter< this.removeMemberStateEvent(); while (this.calls.length > 0) { - this.removeCall( - this.calls[this.calls.length - 1], - CallErrorCode.UserHangup, - ); + this.removeCall(this.calls[this.calls.length - 1], CallErrorCode.UserHangup); } this.activeSpeaker = null; @@ -417,10 +377,7 @@ export class GroupCall extends TypedEventEmitter< this.transmitTimer = null; } - this.client.removeListener( - CallEventHandlerEvent.Incoming, - this.onIncomingCall, - ); + this.client.removeListener(CallEventHandlerEvent.Incoming, this.onIncomingCall); } public leave() { @@ -446,8 +403,7 @@ export class GroupCall extends TypedEventEmitter< if (emitStateEvent) { const existingStateEvent = this.room.currentState.getStateEvents( - EventType.GroupCallPrefix, - this.groupCallId, + EventType.GroupCallPrefix, this.groupCallId, ); await this.client.sendStateEvent( @@ -533,9 +489,7 @@ export class GroupCall extends TypedEventEmitter< } if (this.localCallFeed) { - logger.log( - `groupCall ${this.groupCallId} setMicrophoneMuted stream ${this.localCallFeed.stream.id} muted ${muted}`, - ); + logger.log(`groupCall ${this.groupCallId} setMicrophoneMuted stream ${this.localCallFeed.stream.id} muted ${muted}`); this.localCallFeed.setAudioVideoMuted(muted, null); // I don't believe its actually necessary to enable these tracks: they // are the one on the groupcall's own CallFeed and are cloned before being @@ -596,17 +550,11 @@ export class GroupCall extends TypedEventEmitter< // are the one on the groupcall's own CallFeed and are cloned before being // given to any of the actual calls, so these tracks don't actually go // anywhere. Let's do it anyway to avoid confusion. - setTracksEnabled( - this.localCallFeed.stream.getAudioTracks(), - !muted, - ); + setTracksEnabled(this.localCallFeed.stream.getAudioTracks(), !muted); } for (const call of this.calls) { - setTracksEnabled( - call.localUsermediaFeed.stream.getAudioTracks(), - !muted, - ); + setTracksEnabled(call.localUsermediaFeed.stream.getAudioTracks(), !muted); } return true; @@ -626,31 +574,21 @@ export class GroupCall extends TypedEventEmitter< } if (this.localCallFeed) { - logger.log( - `groupCall ${this.groupCallId} setLocalVideoMuted stream ${this.localCallFeed.stream.id} muted ${muted}`, - ); + logger.log(`groupCall ${this.groupCallId} setLocalVideoMuted stream ${this.localCallFeed.stream.id} muted ${muted}`); this.localCallFeed.setAudioVideoMuted(null, muted); - setTracksEnabled( - this.localCallFeed.stream.getVideoTracks(), - !muted, - ); + setTracksEnabled(this.localCallFeed.stream.getVideoTracks(), !muted); } for (const call of this.calls) { call.setLocalVideoMuted(muted); } - this.emit( - GroupCallEvent.LocalMuteStateChanged, - this.isMicrophoneMuted(), - muted, - ); + this.emit(GroupCallEvent.LocalMuteStateChanged, muted, this.isLocalVideoMuted()); return true; } public async setScreensharingEnabled( - enabled: boolean, - opts: IScreensharingOpts = {}, + enabled: boolean, opts: IScreensharingOpts = {}, ): Promise { if (enabled === this.isScreensharing()) { return enabled; @@ -672,9 +610,7 @@ export class GroupCall extends TypedEventEmitter< track.addEventListener("ended", onTrackEnded); } - logger.log( - "Screensharing permissions granted. Setting screensharing enabled on all calls", - ); + logger.log("Screensharing permissions granted. Setting screensharing enabled on all calls"); this.localDesktopCapturerSourceId = opts.desktopCapturerSourceId;