From 495642d0414ffef9d4f2a6ae8bf5fe70e1ca5805 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2023 14:04:59 +0000 Subject: [PATCH] Handle group call getting initialised twice in quick succession This happens in dev mode with React 18 and caused extra user media stream to end up floating around. Fixes https://github.com/vector-im/element-call/issues/847 --- src/webrtc/groupCall.ts | 10 ++++++---- src/webrtc/mediaHandler.ts | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/webrtc/groupCall.ts b/src/webrtc/groupCall.ts index 27bc6e3e85e..064188a64ae 100644 --- a/src/webrtc/groupCall.ts +++ b/src/webrtc/groupCall.ts @@ -347,7 +347,7 @@ export class GroupCall extends TypedEventEmitter< ); } - public async initLocalCallFeed(): Promise { + public async initLocalCallFeed(): Promise { logger.log(`groupCall ${this.groupCallId} initLocalCallFeed`); if (this.state !== GroupCallState.LocalCallFeedUninitialized) { @@ -376,7 +376,11 @@ export class GroupCall extends TypedEventEmitter< } // The call could've been disposed while we were waiting - if (disposed) throw new Error("Group call disposed"); + if (disposed) { + logger.info("Group call disposed while gathering media stream"); + this.client.getMediaHandler().stopUserMediaStream(stream); + return; + } const callFeed = new CallFeed({ client: this.client, @@ -396,8 +400,6 @@ export class GroupCall extends TypedEventEmitter< this.addUserMediaFeed(callFeed); this.state = GroupCallState.LocalCallFeedInitialized; - - return callFeed; } public async updateLocalUsermediaStream(stream: MediaStream): Promise { diff --git a/src/webrtc/mediaHandler.ts b/src/webrtc/mediaHandler.ts index 338701d7189..b167ce593ba 100644 --- a/src/webrtc/mediaHandler.ts +++ b/src/webrtc/mediaHandler.ts @@ -57,6 +57,9 @@ export class MediaHandler extends TypedEventEmitter< public userMediaStreams: MediaStream[] = []; public screensharingStreams: MediaStream[] = []; + // Promise chain to serialise calls to getMediaStream + private getMediaStreamPromise?: Promise; + public constructor(private client: MatrixClient) { super(); } @@ -196,6 +199,19 @@ export class MediaHandler extends TypedEventEmitter< * @returns based on passed parameters */ public async getUserMediaStream(audio: boolean, video: boolean, reusable = true): Promise { + // Serialise calls, othertwise we can't sensibly re-use the stream + if (this.getMediaStreamPromise) { + this.getMediaStreamPromise = this.getMediaStreamPromise.then(() => { + return this.getUserMediaStreamInternal(audio, video, reusable); + }); + } else { + this.getMediaStreamPromise = this.getUserMediaStreamInternal(audio, video, reusable); + } + + return this.getMediaStreamPromise; + } + + private async getUserMediaStreamInternal(audio: boolean, video: boolean, reusable: boolean): Promise { const shouldRequestAudio = audio && (await this.hasAudioDevice()); const shouldRequestVideo = video && (await this.hasVideoDevice());