From fbb8cfb1884fc62beb3be22a5baef21b84803418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 23 Apr 2021 19:41:55 +0200 Subject: [PATCH] Rework how media element are handled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/voip/_VideoFeed.scss | 2 + src/components/views/voip/VideoFeed.tsx | 81 +++++++++++++++---------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/res/css/views/voip/_VideoFeed.scss b/res/css/views/voip/_VideoFeed.scss index 46cdf4f52c..6f51353552 100644 --- a/res/css/views/voip/_VideoFeed.scss +++ b/res/css/views/voip/_VideoFeed.scss @@ -22,6 +22,8 @@ limitations under the License. .mx_VideoFeed_video { background-color: #000; + width: 100%; + height: 100%; } .mx_VideoFeed_remote { diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index f3ffdd62c5..75206e177f 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -62,32 +62,40 @@ export default class VideoFeed extends React.Component { componentDidMount() { this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream); - - this.playMedia(); + this.playAllMedia(); } - private playMedia() { - const audioOutput = CallMediaHandler.getAudioOutput(); - const currentMedia = this.getCurrentMedia(); + componentWillUnmount() { + this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream); + this.video.current?.removeEventListener('resize', this.onResize); + this.stopAllMedia(); + } - currentMedia.srcObject = this.props.feed.stream; - currentMedia.autoplay = true; - // Don't play audio if the feed is local - currentMedia.muted = this.props.feed.isLocal(); + private playMediaElement(element: HTMLVideoElement | HTMLAudioElement) { + if (element instanceof HTMLAudioElement) { + const audioOutput = CallMediaHandler.getAudioOutput(); - try { - if (audioOutput) { - // This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where - // it fails. - // It seems reliable if you set the sink ID after setting the srcObject and then set the sink ID - // back to the default after the call is over - Dave - currentMedia.setSinkId(audioOutput); + // Don't play audio if the feed is local + element.muted = this.props.feed.isLocal(); + + if (audioOutput && !element.muted) { + try { + // This seems quite unreliable in Chrome, although I haven't yet managed to make a jsfiddle where + // it fails. + // It seems reliable if you set the sink ID after setting the srcObject and then set the sink ID + // back to the default after the call is over - Dave + element.setSinkId(audioOutput); + } catch (e) { + console.error("Couldn't set requested audio output device: using default", e); + logger.warn("Couldn't set requested audio output device: using default", e); + } } - } catch (e) { - console.error("Couldn't set requested audio output device: using default", e); - logger.warn("Couldn't set requested audio output device: using default", e); + } else { + element.muted = true; } + element.srcObject = this.props.feed.stream; + element.autoplay = true; try { // A note on calling methods on media elements: // We used to have queues per media element to serialise all calls on those elements. @@ -98,27 +106,30 @@ export default class VideoFeed extends React.Component { // should serialise the ones that need to be serialised but then be able to interrupt // them with another load() which will cancel the pending one, but since we don't call // load() explicitly, it shouldn't be a problem. - Dave - currentMedia.play() + element.play() } catch (e) { logger.info("Failed to play media element with feed", this.props.feed, e); } } - componentWillUnmount() { - this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream); - this.video.current?.removeEventListener('resize', this.onResize); + private stopMediaElement(element: HTMLAudioElement | HTMLVideoElement) { + element.pause(); + element.src = null; - const currentMedia = this.getCurrentMedia(); - currentMedia.pause(); - currentMedia.srcObject = null; // As per comment in componentDidMount, setting the sink ID back to the // default once the call is over makes setSinkId work reliably. - Dave // Since we are not using the same element anymore, the above doesn't // seem to be necessary - Šimon } - private getCurrentMedia() { - return this.audio.current || this.video.current; + private playAllMedia() { + this.playMediaElement(this.audio.current); + if (this.video.current) this.playMediaElement(this.video.current); + } + + private stopAllMedia() { + this.stopMediaElement(this.audio.current) + if (this.video.current) this.stopMediaElement(this.video.current); } private onNewStream = () => { @@ -126,7 +137,7 @@ export default class VideoFeed extends React.Component { audioMuted: this.props.feed.isAudioMuted(), videoMuted: this.props.feed.isVideoMuted(), }); - this.playMedia(); + this.playAllMedia(); }; private onResize = (e) => { @@ -141,13 +152,16 @@ export default class VideoFeed extends React.Component { mx_VideoFeed_local: this.props.feed.isLocal(), mx_VideoFeed_remote: !this.props.feed.isLocal(), mx_VideoFeed_voice: this.state.videoMuted, - mx_VideoFeed_video: !this.state.videoMuted, mx_VideoFeed_mirror: ( this.props.feed.isLocal() && SettingsStore.getValue('VideoView.flipVideoHorizontally') ), }; + const audio = ( +