ref(large-video): permanently enable canvas based background (#3084)

* ref(large-video): permanently enable canvas based background

* squash: leave flag for disabling background
This commit is contained in:
virtuacoplenny 2018-06-15 14:41:37 -07:00 committed by Дамян Минков
parent fc643f06e4
commit 4ab8d98cd1
6 changed files with 196 additions and 304 deletions

View File

@ -5,6 +5,12 @@ var interfaceConfig = {
// methods allowing to use variables both in css and js. // methods allowing to use variables both in css and js.
DEFAULT_BACKGROUND: '#474747', DEFAULT_BACKGROUND: '#474747',
/**
* Whether or not the blurred video background for large video should be
* displayed on browsers that can support it.
*/
DISABLE_VIDEO_BACKGROUND: false,
INITIAL_TOOLBAR_TIMEOUT: 20000, INITIAL_TOOLBAR_TIMEOUT: 20000,
TOOLBAR_TIMEOUT: 4000, TOOLBAR_TIMEOUT: 4000,
TOOLBAR_ALWAYS_VISIBLE: false, TOOLBAR_ALWAYS_VISIBLE: false,
@ -142,17 +148,7 @@ var interfaceConfig = {
* *
* @type {boolean} * @type {boolean}
*/ */
VIDEO_QUALITY_LABEL_DISABLED: false, VIDEO_QUALITY_LABEL_DISABLED: false
/**
* Temporary feature flag to debug performance with the large video
* background blur. On initial implementation, blur was always enabled so a
* falsy value here will be used to keep blur enabled, as will the value
* "video", and will render the blur over a video element. The value
* "canvas" will display the blur over a canvas element, while the value
* "off" will prevent the background from rendering.
*/
_BACKGROUND_BLUR: 'canvas'
/** /**
* Specify custom URL for downloading android mobile app. * Specify custom URL for downloading android mobile app.

View File

@ -7,8 +7,7 @@ import ReactDOM from 'react-dom';
import { browser } from '../../../react/features/base/lib-jitsi-meet'; import { browser } from '../../../react/features/base/lib-jitsi-meet';
import { import {
ORIENTATION, ORIENTATION,
LargeVideoBackground, LargeVideoBackground
LargeVideoBackgroundCanvas
} from '../../../react/features/large-video'; } from '../../../react/features/large-video';
/* eslint-enable no-unused-vars */ /* eslint-enable no-unused-vars */
@ -689,21 +688,17 @@ export class VideoContainer extends LargeContainer {
*/ */
_updateBackground() { _updateBackground() {
// Do not the background display on browsers that might experience // Do not the background display on browsers that might experience
// performance issues from the presence of the background. // performance issues from the presence of the background or if
if (interfaceConfig._BACKGROUND_BLUR === 'off' // explicitly disabled.
if (interfaceConfig.DISABLE_VIDEO_BACKGROUND
|| browser.isFirefox() || browser.isFirefox()
|| browser.isSafariWithWebrtc() || browser.isSafariWithWebrtc()
|| browser.isTemasysPluginUsed()) { || browser.isTemasysPluginUsed()) {
return; return;
} }
// eslint-disable-next-line no-unused-vars
const Background = interfaceConfig._BACKGROUND_BLUR === 'canvas'
? LargeVideoBackgroundCanvas
: LargeVideoBackground;
ReactDOM.render( ReactDOM.render(
<Background <LargeVideoBackground
hidden = { this._hideBackground } hidden = { this._hideBackground }
mirror = { mirror = {
this.stream this.stream

View File

@ -1,7 +1,6 @@
import PropTypes from 'prop-types'; // @flow
import React, { Component } from 'react';
import { Video } from '../../base/media'; import React, { Component } from 'react';
/** /**
* Constants to describe the dimensions of the video. Landscape videos * Constants to describe the dimensions of the video. Landscape videos
@ -23,66 +22,125 @@ export const ORIENTATION = {
* @private * @private
* @type {Object} * @type {Object}
*/ */
export const ORIENTATION_TO_CLASS = { const ORIENTATION_TO_CLASS = {
[ORIENTATION.LANDSCAPE]: 'fit-full-width', [ORIENTATION.LANDSCAPE]: 'fit-full-width',
[ORIENTATION.PORTRAIT]: 'fit-full-height' [ORIENTATION.PORTRAIT]: 'fit-full-height'
}; };
/**
* The type of the React {@code Component} props of
* {@link LargeVideoBackgroundCanvas}.
*/
type Props = {
/**
* Additional CSS class names to add to the root of the component.
*/
className: String,
/**
* Whether or not the background should have its visibility hidden.
*/
hidden: boolean,
/**
* Whether or not the video should display flipped horizontally, so left
* becomes right and right becomes left.
*/
mirror: boolean,
/**
* Whether the component should ensure full width of the video is displayed
* (landscape) or full height (portrait).
*/
orientationFit: string,
/**
* Whether or not to display a filter on the video to visually indicate a
* problem with the video being displayed.
*/
showLocalProblemFilter: boolean,
/**
* Whether or not to display a filter on the video to visually indicate a
* problem with the video being displayed.
*/
showRemoteProblemFilter: boolean,
/**
* The video stream to display.
*/
videoElement: Object
};
/** /**
* Implements a React Component which shows a video element intended to be used * 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. * as a background to fill the empty space of container with another video.
* *
* @extends Component * @extends Component
*/ */
export class LargeVideoBackground extends Component { export class LargeVideoBackground extends Component<Props> {
_canvasEl: Object;
_updateCanvasInterval: *;
/** /**
* {@code LargeVideoBackground} component's property types. * Initializes new {@code LargeVideoBackground} instance.
* *
* @static * @param {*} props - The read-only properties with which the new instance
* is to be initialized.
*/ */
static propTypes = { constructor(props: Props) {
/** super(props);
* Additional CSS class names to add to the root of the component.
*/
className: PropTypes.string,
/** // Bind event handlers so they are only bound once per instance.
* Whether or not the background should have its visibility hidden. this._setCanvasEl = this._setCanvasEl.bind(this);
*/ this._updateCanvas = this._updateCanvas.bind(this);
hidden: PropTypes.bool, }
/** /**
* Whether or not the video should display flipped horizontally, so * If the canvas is not hidden, sets the initial interval to update the
* left becomes right and right becomes left. * image displayed in the canvas.
*/ *
mirror: PropTypes.bool, * @inheritdoc
* @returns {void}
*/
componentDidMount() {
if (this.props.videoElement && !this.props.hidden) {
this._updateCanvas();
this._setUpdateCanvasInterval();
}
}
/** /**
* Whether the component should ensure full width of the video is * Starts or stops the interval to update the image displayed in the canvas.
* displayed (landscape) or full height (portrait). *
*/ * @inheritdoc
orientationFit: PropTypes.oneOf([ * @param {Object} nextProps - The read-only React {@code Component} props
ORIENTATION.LANDSCAPE, * with which the new instance is to be initialized.
ORIENTATION.PORTRAIT */
]), componentWillReceiveProps(nextProps: Props) {
if (this.props.hidden && !nextProps.hidden) {
this._clearCanvas();
this._setUpdateCanvasInterval();
}
/** if ((!this.props.hidden && nextProps.hidden)
* Whether or not to display a filter on the video to visually indicate || !nextProps.videoElement) {
* a problem with the video being displayed. this._clearCanvas();
*/ this._clearUpdateCanvasInterval();
showLocalProblemFilter: PropTypes.bool, }
}
/** /**
* Whether or not to display a filter on the video to visually indicate * Clears the interval for updating the image displayed in the canvas.
* a problem with the video being displayed. *
*/ * @inheritdoc
showRemoteProblemFilter: PropTypes.bool, */
componentWillUnmount() {
/** this._clearUpdateCanvasInterval();
* The video stream to display. }
*/
videoTrack: PropTypes.object
};
/** /**
* Implements React's {@link Component#render()}. * Implements React's {@link Component#render()}.
@ -96,8 +154,7 @@ export class LargeVideoBackground extends Component {
mirror, mirror,
orientationFit, orientationFit,
showLocalProblemFilter, showLocalProblemFilter,
showRemoteProblemFilter, showRemoteProblemFilter
videoTrack
} = this.props; } = this.props;
const orientationClass = orientationFit const orientationClass = orientationFit
? ORIENTATION_TO_CLASS[orientationFit] : ''; ? ORIENTATION_TO_CLASS[orientationFit] : '';
@ -105,17 +162,91 @@ export class LargeVideoBackground extends Component {
hidden ? 'invisible' : ''} ${orientationClass} ${ hidden ? 'invisible' : ''} ${orientationClass} ${
showLocalProblemFilter ? 'videoProblemFilter' : ''} ${ showLocalProblemFilter ? 'videoProblemFilter' : ''} ${
showRemoteProblemFilter ? 'remoteVideoProblemFilter' : ''}`; showRemoteProblemFilter ? 'remoteVideoProblemFilter' : ''}`;
const videoTrackModel = {
jitsiTrack: hidden ? null : videoTrack
};
return ( return (
<div className = { classNames }> <div className = { classNames }>
<Video <canvas
autoPlay = { true }
id = 'largeVideoBackground' id = 'largeVideoBackground'
videoTrack = { videoTrackModel } /> ref = { this._setCanvasEl } />
</div> </div>
); );
} }
/**
* Removes any image displayed on the canvas.
*
* @private
* @returns {void}
*/
_clearCanvas() {
const cavnasContext = this._canvasEl.getContext('2d');
cavnasContext.clearRect(
0, 0, this._canvasEl.width, this._canvasEl.height);
}
/**
* Clears the interval for updating the image displayed in the canvas.
*
* @private
* @returns {void}
*/
_clearUpdateCanvasInterval() {
clearInterval(this._updateCanvasInterval);
}
_setCanvasEl: () => void;
/**
* Sets the instance variable for the component's canvas element so it can
* be accessed directly for drawing on.
*
* @param {Object} element - The DOM element for the component's canvas.
* @private
* @returns {void}
*/
_setCanvasEl(element) {
this._canvasEl = element;
}
/**
* Starts the interval for updating the image displayed in the canvas.
*
* @private
* @returns {void}
*/
_setUpdateCanvasInterval() {
this._clearUpdateCanvasInterval();
this._updateCanvasInterval = setInterval(this._updateCanvas, 200);
}
_updateCanvas: () => void;
/**
* Draws the current frame of the passed in video element onto the canvas.
*
* @private
* @returns {void}
*/
_updateCanvas() {
const { videoElement } = this.props;
const { videoWidth, videoHeight } = videoElement;
const {
height: canvasHeight,
width: canvasWidth
} = this._canvasEl;
const cavnasContext = this._canvasEl.getContext('2d');
if (this.props.orientationFit === ORIENTATION.LANDSCAPE) {
const heightScaledToFit = (canvasWidth / videoWidth) * videoHeight;
cavnasContext.drawImage(
videoElement, 0, 0, canvasWidth, heightScaledToFit);
} else {
const widthScaledToFit = (canvasHeight / videoHeight) * videoWidth;
cavnasContext.drawImage(
videoElement, 0, 0, widthScaledToFit, canvasHeight);
}
}
} }

View File

@ -1,229 +0,0 @@
// @flow
import React, { Component } from 'react';
import { ORIENTATION, ORIENTATION_TO_CLASS } from './LargeVideoBackground';
/**
* The type of the React {@code Component} props of
* {@link LargeVideoBackgroundCanvas}.
*/
type Props = {
/**
* Additional CSS class names to add to the root of the component.
*/
className: String,
/**
* Whether or not the background should have its visibility hidden.
*/
hidden: boolean,
/**
* Whether or not the video should display flipped horizontally, so left
* becomes right and right becomes left.
*/
mirror: boolean,
/**
* Whether the component should ensure full width of the video is displayed
* (landscape) or full height (portrait).
*/
orientationFit: String,
/**
* Whether or not to display a filter on the video to visually indicate a
* problem with the video being displayed.
*/
showLocalProblemFilter: boolean,
/**
* Whether or not to display a filter on the video to visually indicate a
* problem with the video being displayed.
*/
showRemoteProblemFilter: boolean,
/**
* The video stream to display.
*/
videoElement: Object
};
/**
* 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 LargeVideoBackgroundCanvas extends Component<Props> {
_canvasEl: Object;
_updateCanvasInterval: *;
/**
* Initializes new {@code LargeVideoBackgroundCanvas} instance.
*
* @param {*} props - The read-only properties with which the new instance
* is to be initialized.
*/
constructor(props: Props) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._setCanvasEl = this._setCanvasEl.bind(this);
this._updateCanvas = this._updateCanvas.bind(this);
}
/**
* If the canvas is not hidden, sets the initial interval to update the
* image displayed in the canvas.
*
* @inheritdoc
* @returns {void}
*/
componentDidMount() {
if (this.props.videoElement && !this.props.hidden) {
this._updateCanvas();
this._setUpdateCanvasInterval();
}
}
/**
* Starts or stops the interval to update the image displayed in the canvas.
*
* @inheritdoc
* @param {Object} nextProps - The read-only React {@code Component} props
* with which the new instance is to be initialized.
*/
componentWillReceiveProps(nextProps: Props) {
if (this.props.hidden && !nextProps.hidden) {
this._clearCanvas();
this._setUpdateCanvasInterval();
}
if ((!this.props.hidden && nextProps.hidden)
|| !nextProps.videoElement) {
this._clearCanvas();
this._clearUpdateCanvasInterval();
}
}
/**
* Clears the interval for updating the image displayed in the canvas.
*
* @inheritdoc
*/
componentWillUnmount() {
this._clearUpdateCanvasInterval();
}
/**
* Implements React's {@link Component#render()}.
*
* @inheritdoc
* @returns {ReactElement}
*/
render() {
const {
hidden,
mirror,
orientationFit,
showLocalProblemFilter,
showRemoteProblemFilter
} = this.props;
const orientationClass = orientationFit
? ORIENTATION_TO_CLASS[orientationFit] : '';
const classNames = `large-video-background ${mirror ? 'flip-x' : ''} ${
hidden ? 'invisible' : ''} ${orientationClass} ${
showLocalProblemFilter ? 'videoProblemFilter' : ''} ${
showRemoteProblemFilter ? 'remoteVideoProblemFilter' : ''}`;
return (
<div className = { classNames }>
<canvas
id = 'largeVideoBackground'
ref = { this._setCanvasEl } />
</div>
);
}
/**
* Removes any image displayed on the canvas.
*
* @private
* @returns {void}
*/
_clearCanvas() {
const cavnasContext = this._canvasEl.getContext('2d');
cavnasContext.clearRect(
0, 0, this._canvasEl.width, this._canvasEl.height);
}
/**
* Clears the interval for updating the image displayed in the canvas.
*
* @private
* @returns {void}
*/
_clearUpdateCanvasInterval() {
clearInterval(this._updateCanvasInterval);
}
_setCanvasEl: () => void;
/**
* Sets the instance variable for the component's canvas element so it can
* be accessed directly for drawing on.
*
* @param {Object} element - The DOM element for the component's canvas.
* @private
* @returns {void}
*/
_setCanvasEl(element) {
this._canvasEl = element;
}
/**
* Starts the interval for updating the image displayed in the canvas.
*
* @private
* @returns {void}
*/
_setUpdateCanvasInterval() {
this._clearUpdateCanvasInterval();
this._updateCanvasInterval = setInterval(this._updateCanvas, 200);
}
_updateCanvas: () => void;
/**
* Draws the current frame of the passed in video element onto the canvas.
*
* @private
* @returns {void}
*/
_updateCanvas() {
const { videoElement } = this.props;
const { videoWidth, videoHeight } = videoElement;
const {
height: canvasHeight,
width: canvasWidth
} = this._canvasEl;
const cavnasContext = this._canvasEl.getContext('2d');
if (this.props.orientationFit === ORIENTATION.LANDSCAPE) {
const heightScaledToFit = (canvasWidth / videoWidth) * videoHeight;
cavnasContext.drawImage(
videoElement, 0, 0, canvasWidth, heightScaledToFit);
} else {
const widthScaledToFit = (canvasHeight / videoHeight) * videoWidth;
cavnasContext.drawImage(
videoElement, 0, 0, widthScaledToFit, canvasHeight);
}
}
}

View File

@ -1,3 +1,2 @@
export { default as LargeVideo } from './LargeVideo'; export { default as LargeVideo } from './LargeVideo';
export * from './LargeVideoBackground'; export * from './LargeVideoBackground';
export * from './LargeVideoBackgroundCanvas';