diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index 79df07b43..f1af549ca 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -816,9 +816,7 @@ export class MatrixCall extends EventEmitter { for (const sender of this.screensharingSenders) { this.peerConn.removeTrack(sender); } - for (const track of this.localScreensharingStream.getTracks()) { - track.stop(); - } + this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream); this.deleteFeedByStream(this.localScreensharingStream); return false; } @@ -866,9 +864,7 @@ export class MatrixCall extends EventEmitter { }); sender.replaceTrack(track); - for (const track of this.localScreensharingStream.getTracks()) { - track.stop(); - } + this.client.getMediaHandler().stopScreensharingStream(this.localScreensharingStream); this.deleteFeedByStream(this.localScreensharingStream); return false; @@ -1751,8 +1747,14 @@ export class MatrixCall extends EventEmitter { logger.debug(`stopAllMedia (stream=${this.localUsermediaStream})`); for (const feed of this.feeds) { - for (const track of feed.stream.getTracks()) { - track.stop(); + if (feed.isLocal() && feed.purpose === SDPStreamMetadataPurpose.Usermedia) { + this.client.getMediaHandler().stopUserMediaStream(feed.stream); + } else if (feed.isLocal() && feed.purpose === SDPStreamMetadataPurpose.Screenshare) { + this.client.getMediaHandler().stopScreensharingStream(feed.stream); + } else { + for (const track of feed.stream.getTracks()) { + track.stop(); + } } } } diff --git a/src/webrtc/mediaHandler.ts b/src/webrtc/mediaHandler.ts index b41e39ba8..97edf1774 100644 --- a/src/webrtc/mediaHandler.ts +++ b/src/webrtc/mediaHandler.ts @@ -22,6 +22,8 @@ import { logger } from "../logger"; export class MediaHandler { private audioInput: string; private videoInput: string; + private userMediaStreams: MediaStream[] = []; + private screensharingStreams: MediaStream[] = []; /** * Set an audio input device to use for MatrixCalls @@ -45,27 +47,97 @@ export class MediaHandler { * @returns {MediaStream} based on passed parameters */ public async getUserMediaStream(audio: boolean, video: boolean): Promise { - const constraints = this.getUserMediaContraints(audio, video); - logger.log("Getting user media with constraints", constraints); - return await navigator.mediaDevices.getUserMedia(constraints); + let stream: MediaStream; + + if (this.userMediaStreams.length === 0) { + const constraints = this.getUserMediaContraints(audio, video); + logger.log("Getting user media with constraints", constraints); + stream = await navigator.mediaDevices.getUserMedia(constraints); + } else { + stream = this.userMediaStreams[this.userMediaStreams.length - 1].clone(); + } + + this.userMediaStreams.push(stream); + + return stream; + } + + /** + * Stops all tracks on the provided usermedia stream + */ + public stopUserMediaStream(mediaStream: MediaStream) { + for (const track of mediaStream.getTracks()) { + track.stop(); + } + + const index = this.userMediaStreams.indexOf(mediaStream); + + if (index !== -1) { + this.userMediaStreams.splice(index, 0); + } } /** * @returns {MediaStream} based on passed parameters */ - public async getScreensharingStream(desktopCapturerSourceId: string): Promise { - const screenshareConstraints = this.getScreenshareContraints(desktopCapturerSourceId); - if (!screenshareConstraints) return null; + public async getScreensharingStream(desktopCapturerSourceId: string): Promise { + let stream: MediaStream; - if (desktopCapturerSourceId) { - // We are using Electron - logger.debug("Getting screen stream using getUserMedia()..."); - return await navigator.mediaDevices.getUserMedia(screenshareConstraints); + if (this.screensharingStreams.length === 0) { + const screenshareConstraints = this.getScreenshareContraints(desktopCapturerSourceId); + if (!screenshareConstraints) return null; + + if (desktopCapturerSourceId) { + // We are using Electron + logger.debug("Getting screen stream using getUserMedia()..."); + stream = await navigator.mediaDevices.getUserMedia(screenshareConstraints); + } else { + // We are not using Electron + logger.debug("Getting screen stream using getDisplayMedia()..."); + stream = await navigator.mediaDevices.getDisplayMedia(screenshareConstraints); + } } else { - // We are not using Electron - logger.debug("Getting screen stream using getDisplayMedia()..."); - return await navigator.mediaDevices.getDisplayMedia(screenshareConstraints); + stream = this.screensharingStreams[this.screensharingStreams.length - 1].clone(); } + + this.screensharingStreams.push(stream); + + return stream; + } + + /** + * Stops all tracks on the provided screensharing stream + */ + public stopScreensharingStream(mediaStream: MediaStream) { + for (const track of mediaStream.getTracks()) { + track.stop(); + } + + const index = this.userMediaStreams.indexOf(mediaStream); + + if (index !== -1) { + this.userMediaStreams.splice(index, 0); + } + } + + /** + * Stops all local media tracks + */ + public stopAllStreams() { + for (const stream of this.userMediaStreams) { + for (const track of stream.getTracks()) { + track.stop(); + } + } + + for (const stream of this.screensharingStreams) { + for (const track of stream.getTracks()) { + track.stop(); + } + } + + this.userMediaStreams = []; + this.screensharingStreams = []; } private getUserMediaContraints(audio: boolean, video: boolean): MediaStreamConstraints {