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
This commit is contained in:
parent
f3b5ed2ef4
commit
4fb37c38eb
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
|||
* <tt>false</tt> 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(
|
||||
<LargeVideoBackground
|
||||
hidden = { this._hideBackground }
|
||||
mirror = {
|
||||
this.stream
|
||||
&& this.stream.isLocal()
|
||||
&& this.localFlipX
|
||||
}
|
||||
orientationFit = { this._backgroundOrientation }
|
||||
showLocalProblemFilter
|
||||
= { this.$video.hasClass(LOCAL_PROBLEM_FILTER_CLASS) }
|
||||
showRemoteProblemFilter
|
||||
= { this.$video.hasClass(REMOTE_PROBLEM_FILTER_CLASS) }
|
||||
videoTrack = { this.stream } />,
|
||||
document.getElementById('largeVideoBackgroundContainer')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,12 +50,7 @@ export default class LargeVideo extends Component<*> {
|
|||
<div id = 'remotePresenceMessage' />
|
||||
<span id = 'remoteConnectionMessage' />
|
||||
<div>
|
||||
<div className = 'video_blurred_container'>
|
||||
<video
|
||||
autoPlay = { true }
|
||||
id = 'largeVideoBackground'
|
||||
muted = 'true' />
|
||||
</div>
|
||||
<div id = 'largeVideoBackgroundContainer' />
|
||||
{
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { Video } from '../../base/media';
|
||||
|
||||
/**
|
||||
* Constants to describe the dimensions of the video. Landscape videos
|
||||
* are wider than they are taller and portrait videos are taller than they
|
||||
* are wider. The dimensions will determine how {@code LargeVideoBackground}
|
||||
* will strech to fill its container.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const ORIENTATION = {
|
||||
LANDSCAPE: 'landscape',
|
||||
PORTRAIT: 'portrait'
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of orientations to a class that should fit the
|
||||
* {@code LargeVideoBackground} into its container.
|
||||
*
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
const ORIENTATION_TO_CLASS = {
|
||||
[ORIENTATION.LANDSCAPE]: 'fit-full-width',
|
||||
[ORIENTATION.PORTRAIT]: 'fit-full-height'
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React Component which shows a video element intended to be used
|
||||
* as a background to fill the empty space of container with another video.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export class LargeVideoBackground extends Component {
|
||||
/**
|
||||
* {@code LargeVideoBackground} component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* Additional CSS class names to add to the root of the component.
|
||||
*/
|
||||
className: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Whether or not the background should have its visibility hidden.
|
||||
*/
|
||||
hidden: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not the video should display flipped horizontally, so
|
||||
* left becomes right and right becomes left.
|
||||
*/
|
||||
mirror: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether the component should ensure full width of the video is
|
||||
* displayed (landscape) or full height (portrait).
|
||||
*/
|
||||
orientationFit: PropTypes.oneOf([
|
||||
ORIENTATION.LANDSCAPE,
|
||||
ORIENTATION.PORTRAIT
|
||||
]),
|
||||
|
||||
/**
|
||||
* Whether or not to display a filter on the video to visually indicate
|
||||
* a problem with the video being displayed.
|
||||
*/
|
||||
showLocalProblemFilter: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* Whether or not to display a filter on the video to visually indicate
|
||||
* a problem with the video being displayed.
|
||||
*/
|
||||
showRemoteProblemFilter: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The video stream to display.
|
||||
*/
|
||||
videoTrack: PropTypes.object
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
hidden,
|
||||
mirror,
|
||||
orientationFit,
|
||||
showLocalProblemFilter,
|
||||
showRemoteProblemFilter,
|
||||
videoTrack
|
||||
} = this.props;
|
||||
const orientationClass = orientationFit
|
||||
? ORIENTATION_TO_CLASS[orientationFit] : '';
|
||||
const classNames = `large-video-background ${mirror ? 'flip-x' : ''} ${
|
||||
hidden ? 'invisible' : ''} ${orientationClass} ${
|
||||
showLocalProblemFilter ? 'videoProblemFilter' : ''} ${
|
||||
showRemoteProblemFilter ? 'remoteVideoProblemFilter' : ''}`;
|
||||
const videoTrackModel = {
|
||||
jitsiTrack: hidden ? null : videoTrack
|
||||
};
|
||||
|
||||
return (
|
||||
<div className = { classNames }>
|
||||
<Video
|
||||
autoPlay = { true }
|
||||
id = 'largeVideoBackground'
|
||||
videoTrack = { videoTrackModel } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
export { default as LargeVideo } from './LargeVideo';
|
||||
export * from './LargeVideoBackground';
|
||||
|
|
Loading…
Reference in New Issue