diff --git a/react/features/stream-effects/virtual-background/JitsiStreamBackgroundEffect.js b/react/features/stream-effects/virtual-background/JitsiStreamBackgroundEffect.js index 269cd96e3..8bbd6b760 100644 --- a/react/features/stream-effects/virtual-background/JitsiStreamBackgroundEffect.js +++ b/react/features/stream-effects/virtual-background/JitsiStreamBackgroundEffect.js @@ -17,7 +17,7 @@ import { export default class JitsiStreamBackgroundEffect { _model: Object; _options: Object; - _desktopShareDimensions: Object; + _stream: Object; _segmentationPixelCount: number; _inputVideoElement: HTMLVideoElement; _onMaskFrameTimer: Function; @@ -85,6 +85,12 @@ export default class JitsiStreamBackgroundEffect { * @returns {void} */ runPostProcessing() { + + const track = this._stream.getVideoTracks()[0]; + const { height, width } = track.getSettings() ?? track.getConstraints(); + + this._outputCanvasElement.height = height; + this._outputCanvasElement.width = width; this._outputCanvasCtx.globalCompositeOperation = 'copy'; // Draw segmentation mask. @@ -130,13 +136,23 @@ export default class JitsiStreamBackgroundEffect { ); } if (this._options.virtualBackground.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) { + + // save current context before applying transformations + this._outputCanvasCtx.save(); + + // flip the canvas and prevent mirror behaviour + this._outputCanvasCtx.scale(-1, 1); + this._outputCanvasCtx.translate(-this._outputCanvasElement.width, 0); this._outputCanvasCtx.drawImage( this._virtualVideo, 0, 0, - this._desktopShareDimensions.width, - this._desktopShareDimensions.height + this._outputCanvasElement.width, + this._outputCanvasElement.height ); + + // restore the canvas + this._outputCanvasCtx.restore(); } else { this._outputCanvasCtx.filter = `blur(${this._options.virtualBackground.blurValue}px)`; this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0); @@ -172,12 +188,6 @@ export default class JitsiStreamBackgroundEffect { * @returns {void} */ _renderMask() { - const desktopShareTrack = this._options?.virtualBackground?.virtualSource?.track; - - if (desktopShareTrack) { - this._desktopShareDimensions = desktopShareTrack.getSettings ? desktopShareTrack.getSettings() - : desktopShareTrack.getConstraints(); - } this.resizeSource(); this.runInference(); this.runPostProcessing(); @@ -239,9 +249,10 @@ export default class JitsiStreamBackgroundEffect { * @returns {MediaStream} - The stream with the applied effect. */ startEffect(stream: MediaStream) { + this._stream = stream; this._maskFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Blur effect worker' }); this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer; - const firstVideoTrack = stream.getVideoTracks()[0]; + const firstVideoTrack = this._stream.getVideoTracks()[0]; const { height, frameRate, width } = firstVideoTrack.getSettings ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints(); @@ -257,7 +268,7 @@ export default class JitsiStreamBackgroundEffect { this._inputVideoElement.width = parseInt(width, 10); this._inputVideoElement.height = parseInt(height, 10); this._inputVideoElement.autoplay = true; - this._inputVideoElement.srcObject = stream; + this._inputVideoElement.srcObject = this._stream; this._inputVideoElement.onloadeddata = () => { this._maskFrameTimerWorker.postMessage({ id: SET_TIMEOUT, diff --git a/react/features/virtual-background/components/VirtualBackgroundDialog.js b/react/features/virtual-background/components/VirtualBackgroundDialog.js index 1ec8fa00e..ea565e3c2 100644 --- a/react/features/virtual-background/components/VirtualBackgroundDialog.js +++ b/react/features/virtual-background/components/VirtualBackgroundDialog.js @@ -13,6 +13,7 @@ import { browser, JitsiTrackErrors } from '../../base/lib-jitsi-meet'; import { createLocalTrack } from '../../base/lib-jitsi-meet/functions'; import { VIDEO_TYPE } from '../../base/media'; import { connect } from '../../base/redux'; +import { updateSettings } from '../../base/settings'; import { Tooltip } from '../../base/tooltip'; import { getLocalVideoTrack } from '../../base/tracks'; import { showErrorNotification } from '../../notifications'; @@ -73,6 +74,11 @@ const images: Array = [ ]; type Props = { + /** + * The current local flip x status. + */ + _localFlipX: boolean, + /** * Returns the jitsi track that will have backgraund effect applied. */ @@ -121,7 +127,10 @@ const onError = event => { * @returns {{Props}} */ function _mapStateToProps(state): Object { + const { localFlipX } = state['features/base/settings']; + return { + _localFlipX: Boolean(localFlipX), _virtualBackground: state['features/virtual-background'], _selectedThumbnail: state['features/virtual-background'].selectedThumbnail, _jitsiTrack: getLocalVideoTrack(state['features/base/tracks'])?.jitsiTrack @@ -136,6 +145,7 @@ const VirtualBackgroundDialog = translate(connect(_mapStateToProps)(VirtualBackg * @returns {ReactElement} */ function VirtualBackground({ + _localFlipX, _jitsiTrack, _selectedThumbnail, _virtualBackground, @@ -178,7 +188,12 @@ function VirtualBackground({ if (storedImages.length === backgroundsLimit) { setStoredImages(storedImages.slice(1)); } - }, [ storedImages ]); + if (!_localFlipX) { + dispatch(updateSettings({ + localFlipX: !_localFlipX + })); + } + }, [ storedImages, _localFlipX ]); const enableBlur = useCallback(async () => { diff --git a/react/features/virtual-background/components/VirtualBackgroundPreview.js b/react/features/virtual-background/components/VirtualBackgroundPreview.js index cb46c6001..6a4ff8bf6 100644 --- a/react/features/virtual-background/components/VirtualBackgroundPreview.js +++ b/react/features/virtual-background/components/VirtualBackgroundPreview.js @@ -13,7 +13,7 @@ import { toggleBackgroundEffect } from '../actions'; import { VIRTUAL_BACKGROUND_TYPE } from '../constants'; import { localTrackStopped } from '../functions'; -const videoClassName = 'video-preview-video'; +const videoClassName = 'video-preview-video flipVideoX'; /** * The type of the React {@code PureComponent} props of {@link VirtualBackgroundPreview}.