From 4fb37c38eb7fab44831c2a7da8dd230147cce619 Mon Sep 17 00:00:00 2001 From: virtuacoplenny Date: Mon, 12 Feb 2018 16:29:29 -0800 Subject: [PATCH] fix(large-video): do not show background for Firefox and temasys (#2316) * ref(large-video): reactify background This is pre-requisite work for disabling the background on certain browsers, namely Firefox. By moving the component to react, and in general encapsulating background logic, selectively disabling the background will be easier. The component was left for LargeVideo to update so it can continue to coordinate update timing with the actual large video display. If the background were moved completely into react and redux with LargeVideo, then background updates would occur before large video updates causing visual jank. * fix(large-video): do not show background for Firefox and temasys Firefox has performance issues with adding filter effects on animated elements. On temasys, the background videos weren't really displaying anyway. * some props refactoring Instead of passing in classes to LargeVideoBackground, rely on explicit props. At some point LargeVideo will have to be reactified and the relationsihp between it and LargeVideoBackground might change, so for now make use of props to be explicit about how LargeVideoBackground can be modified. Also, set the jitsiTrack to display on LargeVideoBackground to null if the background is not displayed. This was an existing optimization, although previously done with pausing and playing. * squash: use newly exposed RTCBrowserType * squash: rebase and use new lib browser util * squash: move hiding logic all into LargeVideo * squash: remove hiding of background on stream change. hopefully doesnt break anything --- css/_utils.scss | 16 +- css/_videolayout_default.scss | 13 +- modules/UI/videolayout/VideoContainer.js | 145 ++++++++++-------- .../large-video/components/LargeVideo.web.js | 7 +- .../components/LargeVideoBackground.native.js | 0 .../components/LargeVideoBackground.web.js | 121 +++++++++++++++ .../features/large-video/components/index.js | 1 + 7 files changed, 230 insertions(+), 73 deletions(-) create mode 100644 react/features/large-video/components/LargeVideoBackground.native.js create mode 100644 react/features/large-video/components/LargeVideoBackground.web.js diff --git a/css/_utils.scss b/css/_utils.scss index ca14db617..629000c56 100644 --- a/css/_utils.scss +++ b/css/_utils.scss @@ -1,3 +1,11 @@ +.flip-x { + transform: scaleX(-1); +} + +.hidden { + display: none; +} + /** * Hides an element. */ @@ -5,6 +13,10 @@ display: none !important; } +.invisible { + visibility: hidden; +} + /** * Shows an element. */ @@ -36,7 +48,3 @@ display: -webkit-flex !important; display: flex !important; } - -.hidden { - display: none; -} diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index 6595286a8..169f0a89e 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -12,7 +12,8 @@ overflow: hidden; } -.video_blurred_container { +#largeVideoBackgroundContainer, +.large-video-background { height: 100%; filter: blur(40px); left: 0; @@ -20,6 +21,16 @@ position: absolute; top: 0; width: 100%; + + &.fit-full-height #largeVideoBackground { + height: 100%; + width: auto; + } + + .fit-full-width #largeVideoBackground { + height: auto; + width: 100%; + } } .videocontainer { diff --git a/modules/UI/videolayout/VideoContainer.js b/modules/UI/videolayout/VideoContainer.js index d7d6565b8..1ec9578f1 100644 --- a/modules/UI/videolayout/VideoContainer.js +++ b/modules/UI/videolayout/VideoContainer.js @@ -1,5 +1,16 @@ /* global $, APP, interfaceConfig */ +/* eslint-disable no-unused-vars */ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { browser } from '../../../react/features/base/lib-jitsi-meet'; +import { + ORIENTATION, + LargeVideoBackground +} from '../../../react/features/large-video'; +/* eslint-enable no-unused-vars */ + import Filmstrip from './Filmstrip'; import LargeContainer from './LargeContainer'; import UIEvents from '../../../service/UI/UIEvents'; @@ -10,7 +21,23 @@ export const VIDEO_CONTAINER_TYPE = 'camera'; const FADE_DURATION_MS = 300; -const logger = require('jitsi-meet-logger').getLogger(__filename); +/** + * The CSS class used to add a filter effect on the large video when there is + * a problem with local video. + * + * @private + * @type {string} + */ +const LOCAL_PROBLEM_FILTER_CLASS = 'videoProblemFilter'; + +/** + * The CSS class used to add a filter effect on the large video when there is + * a problem with remote video. + * + * @private + * @type {string} + */ +const REMOTE_PROBLEM_FILTER_CLASS = 'remoteVideoProblemFilter'; /** * Returns an array of the video dimensions, so that it keeps it's aspect @@ -167,13 +194,6 @@ export class VideoContainer extends LargeContainer { return $('#largeVideo'); } - /** - * - */ - get $videoBackground() { - return $('#largeVideoBackground'); - } - /** * */ @@ -199,6 +219,23 @@ export class VideoContainer extends LargeContainer { this.isVisible = false; + /** + * Whether the background should fit the height of the container + * (portrait) or fit the width of the container (landscape). + * + * @private + * @type {string|null} + */ + this._backgroundOrientation = null; + + /** + * Flag indicates whether or not the background should be rendered. + * If the background will not be visible then it is hidden to save + * on performance. + * @type {boolean} + */ + this._hideBackground = true; + /** * Flag indicates whether or not the avatar is currently displayed. * @type {boolean} @@ -277,8 +314,8 @@ export class VideoContainer extends LargeContainer { * false otherwise. */ enableLocalConnectionProblemFilter(enable) { - this.$video.toggleClass('videoProblemFilter', enable); - this.$videoBackground.toggleClass('videoProblemFilter', enable); + this.$video.toggleClass(LOCAL_PROBLEM_FILTER_CLASS, enable); + this._updateBackground(); } /** @@ -402,23 +439,19 @@ export class VideoContainer extends LargeContainer { return; } - this._hideVideoBackground(); - const [ width, height ] = this.getVideoSize(containerWidth, containerHeight); if ((containerWidth > width) || (containerHeight > height)) { - this._showVideoBackground(); - const css - = containerWidth > width - ? { width: '100%', - height: 'auto' } - : { width: 'auto', - height: '100%' }; - - this.$videoBackground.css(css); + this._backgroundOrientation = containerWidth > width + ? ORIENTATION.LANDSCAPE : ORIENTATION.PORTRAIT; + this._hideBackground = false; + } else { + this._hideBackground = true; } + this._updateBackground(); + const { horizontalIndent, verticalIndent } = this.getVideoPosition(width, height, containerWidth, containerHeight); @@ -484,7 +517,6 @@ export class VideoContainer extends LargeContainer { // detach old stream if (this.stream) { this.stream.detach(this.$video[0]); - this.stream.detach(this.$videoBackground[0]); } this.stream = stream; @@ -495,18 +527,14 @@ export class VideoContainer extends LargeContainer { } stream.attach(this.$video[0]); - stream.attach(this.$videoBackground[0]); - - this._hideVideoBackground(); const flipX = stream.isLocal() && this.localFlipX; this.$video.css({ transform: flipX ? 'scaleX(-1)' : 'none' }); - this.$videoBackground.css({ - transform: flipX ? 'scaleX(-1)' : 'none' - }); + + this._updateBackground(); // Reset the large video background depending on the stream. this.setLargeVideoBackground(this.avatarDisplayed); @@ -525,9 +553,7 @@ export class VideoContainer extends LargeContainer { transform: this.localFlipX ? 'scaleX(-1)' : 'none' }); - this.$videoBackground.css({ - transform: this.localFlipX ? 'scaleX(-1)' : 'none' - }); + this._updateBackground(); } @@ -567,10 +593,9 @@ export class VideoContainer extends LargeContainer { * the indication. */ showRemoteConnectionProblemIndicator(show) { - this.$video.toggleClass('remoteVideoProblemFilter', show); - this.$videoBackground.toggleClass('remoteVideoProblemFilter', show); - - this.$avatar.toggleClass('remoteVideoProblemFilter', show); + this.$video.toggleClass(REMOTE_PROBLEM_FILTER_CLASS, show); + this.$avatar.toggleClass(REMOTE_PROBLEM_FILTER_CLASS, show); + this._updateBackground(); } @@ -643,17 +668,6 @@ export class VideoContainer extends LargeContainer { ? '#000' : interfaceConfig.DEFAULT_BACKGROUND); } - /** - * Sets the blur background to be invisible and pauses any playing video. - * - * @private - * @returns {void} - */ - _hideVideoBackground() { - this.$videoBackground.css({ visibility: 'hidden' }); - this.$videoBackground[0].pause(); - } - /** * Callback invoked when the video element changes dimensions. * @@ -665,26 +679,33 @@ export class VideoContainer extends LargeContainer { } /** - * Sets the blur background to be visible and starts any loaded video. + * Attaches and/or updates a React Component to be used as a background for + * the large video, to display blurred video and fill up empty space not + * taken up by the large video. * * @private * @returns {void} */ - _showVideoBackground() { - this.$videoBackground.css({ visibility: 'visible' }); - - // XXX HTMLMediaElement.play's Promise may be rejected. Certain - // environments such as Google Chrome and React Native will report the - // rejection as unhandled. And that may appear scary depending on how - // the environment words the report. To reduce the risk of scaring a - // developer, make sure that the rejection is handled. We cannot really - // do anything substantial about the rejection and, more importantly, we - // do not care. Some browsers (at this time, only Edge is known) don't - // return a promise from .play(), so check before trying to catch. - const res = this.$videoBackground[0].play(); - - if (typeof res !== 'undefined') { - res.catch(reason => logger.error(reason)); + _updateBackground() { + if (browser.isFirefox() || browser.isTemasysPluginUsed()) { + return; } + + ReactDOM.render( +