Browse Source

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
j8
virtuacoplenny 7 years ago
parent
commit
4fb37c38eb

+ 12
- 4
css/_utils.scss View File

@@ -1,3 +1,11 @@
1
+.flip-x {
2
+    transform: scaleX(-1);
3
+}
4
+
5
+.hidden {
6
+  display: none;
7
+}
8
+
1 9
 /**
2 10
  * Hides an element.
3 11
  */
@@ -5,6 +13,10 @@
5 13
     display: none !important;
6 14
 }
7 15
 
16
+.invisible {
17
+    visibility: hidden;
18
+}
19
+
8 20
 /**
9 21
  * Shows an element.
10 22
  */
@@ -36,7 +48,3 @@
36 48
     display: -webkit-flex !important;
37 49
     display: flex !important;
38 50
 }
39
-
40
-.hidden {
41
-  display: none;
42
-}

+ 12
- 1
css/_videolayout_default.scss View File

@@ -12,7 +12,8 @@
12 12
     overflow: hidden;
13 13
 }
14 14
 
15
-.video_blurred_container {
15
+#largeVideoBackgroundContainer,
16
+.large-video-background {
16 17
     height: 100%;
17 18
     filter: blur(40px);
18 19
     left: 0;
@@ -20,6 +21,16 @@
20 21
     position: absolute;
21 22
     top: 0;
22 23
     width: 100%;
24
+
25
+    &.fit-full-height #largeVideoBackground {
26
+        height: 100%;
27
+        width: auto;
28
+    }
29
+
30
+    .fit-full-width #largeVideoBackground {
31
+        height: auto;
32
+        width: 100%;
33
+    }
23 34
 }
24 35
 
25 36
 .videocontainer {

+ 83
- 62
modules/UI/videolayout/VideoContainer.js View File

@@ -1,5 +1,16 @@
1 1
 /* global $, APP, interfaceConfig */
2 2
 
3
+/* eslint-disable no-unused-vars */
4
+import React from 'react';
5
+import ReactDOM from 'react-dom';
6
+
7
+import { browser } from '../../../react/features/base/lib-jitsi-meet';
8
+import {
9
+    ORIENTATION,
10
+    LargeVideoBackground
11
+} from '../../../react/features/large-video';
12
+/* eslint-enable no-unused-vars */
13
+
3 14
 import Filmstrip from './Filmstrip';
4 15
 import LargeContainer from './LargeContainer';
5 16
 import UIEvents from '../../../service/UI/UIEvents';
@@ -10,7 +21,23 @@ export const VIDEO_CONTAINER_TYPE = 'camera';
10 21
 
11 22
 const FADE_DURATION_MS = 300;
12 23
 
13
-const logger = require('jitsi-meet-logger').getLogger(__filename);
24
+/**
25
+ * The CSS class used to add a filter effect on the large video when there is
26
+ * a problem with local video.
27
+ *
28
+ * @private
29
+ * @type {string}
30
+ */
31
+const LOCAL_PROBLEM_FILTER_CLASS = 'videoProblemFilter';
32
+
33
+/**
34
+ * The CSS class used to add a filter effect on the large video when there is
35
+ * a problem with remote video.
36
+ *
37
+ * @private
38
+ * @type {string}
39
+ */
40
+const REMOTE_PROBLEM_FILTER_CLASS = 'remoteVideoProblemFilter';
14 41
 
15 42
 /**
16 43
  * Returns an array of the video dimensions, so that it keeps it's aspect
@@ -167,13 +194,6 @@ export class VideoContainer extends LargeContainer {
167 194
         return $('#largeVideo');
168 195
     }
169 196
 
170
-    /**
171
-     *
172
-     */
173
-    get $videoBackground() {
174
-        return $('#largeVideoBackground');
175
-    }
176
-
177 197
     /**
178 198
      *
179 199
      */
@@ -199,6 +219,23 @@ export class VideoContainer extends LargeContainer {
199 219
 
200 220
         this.isVisible = false;
201 221
 
222
+        /**
223
+         * Whether the background should fit the height of the container
224
+         * (portrait) or fit the width of the container (landscape).
225
+         *
226
+         * @private
227
+         * @type {string|null}
228
+         */
229
+        this._backgroundOrientation = null;
230
+
231
+        /**
232
+         * Flag indicates whether or not the background should be rendered.
233
+         * If the background will not be visible then it is hidden to save
234
+         * on performance.
235
+         * @type {boolean}
236
+         */
237
+        this._hideBackground = true;
238
+
202 239
         /**
203 240
          * Flag indicates whether or not the avatar is currently displayed.
204 241
          * @type {boolean}
@@ -277,8 +314,8 @@ export class VideoContainer extends LargeContainer {
277 314
      * <tt>false</tt> otherwise.
278 315
      */
279 316
     enableLocalConnectionProblemFilter(enable) {
280
-        this.$video.toggleClass('videoProblemFilter', enable);
281
-        this.$videoBackground.toggleClass('videoProblemFilter', enable);
317
+        this.$video.toggleClass(LOCAL_PROBLEM_FILTER_CLASS, enable);
318
+        this._updateBackground();
282 319
     }
283 320
 
284 321
     /**
@@ -402,23 +439,19 @@ export class VideoContainer extends LargeContainer {
402 439
             return;
403 440
         }
404 441
 
405
-        this._hideVideoBackground();
406
-
407 442
         const [ width, height ]
408 443
             = this.getVideoSize(containerWidth, containerHeight);
409 444
 
410 445
         if ((containerWidth > width) || (containerHeight > height)) {
411
-            this._showVideoBackground();
412
-            const css
413
-                = containerWidth > width
414
-                    ? { width: '100%',
415
-                        height: 'auto' }
416
-                    : { width: 'auto',
417
-                        height: '100%' };
418
-
419
-            this.$videoBackground.css(css);
446
+            this._backgroundOrientation = containerWidth > width
447
+                ? ORIENTATION.LANDSCAPE : ORIENTATION.PORTRAIT;
448
+            this._hideBackground = false;
449
+        } else {
450
+            this._hideBackground = true;
420 451
         }
421 452
 
453
+        this._updateBackground();
454
+
422 455
         const { horizontalIndent, verticalIndent }
423 456
             = this.getVideoPosition(width, height,
424 457
             containerWidth, containerHeight);
@@ -484,7 +517,6 @@ export class VideoContainer extends LargeContainer {
484 517
         // detach old stream
485 518
         if (this.stream) {
486 519
             this.stream.detach(this.$video[0]);
487
-            this.stream.detach(this.$videoBackground[0]);
488 520
         }
489 521
 
490 522
         this.stream = stream;
@@ -495,18 +527,14 @@ export class VideoContainer extends LargeContainer {
495 527
         }
496 528
 
497 529
         stream.attach(this.$video[0]);
498
-        stream.attach(this.$videoBackground[0]);
499
-
500
-        this._hideVideoBackground();
501 530
 
502 531
         const flipX = stream.isLocal() && this.localFlipX;
503 532
 
504 533
         this.$video.css({
505 534
             transform: flipX ? 'scaleX(-1)' : 'none'
506 535
         });
507
-        this.$videoBackground.css({
508
-            transform: flipX ? 'scaleX(-1)' : 'none'
509
-        });
536
+
537
+        this._updateBackground();
510 538
 
511 539
         // Reset the large video background depending on the stream.
512 540
         this.setLargeVideoBackground(this.avatarDisplayed);
@@ -525,9 +553,7 @@ export class VideoContainer extends LargeContainer {
525 553
             transform: this.localFlipX ? 'scaleX(-1)' : 'none'
526 554
         });
527 555
 
528
-        this.$videoBackground.css({
529
-            transform: this.localFlipX ? 'scaleX(-1)' : 'none'
530
-        });
556
+        this._updateBackground();
531 557
     }
532 558
 
533 559
 
@@ -567,10 +593,9 @@ export class VideoContainer extends LargeContainer {
567 593
      * the indication.
568 594
      */
569 595
     showRemoteConnectionProblemIndicator(show) {
570
-        this.$video.toggleClass('remoteVideoProblemFilter', show);
571
-        this.$videoBackground.toggleClass('remoteVideoProblemFilter', show);
572
-
573
-        this.$avatar.toggleClass('remoteVideoProblemFilter', show);
596
+        this.$video.toggleClass(REMOTE_PROBLEM_FILTER_CLASS, show);
597
+        this.$avatar.toggleClass(REMOTE_PROBLEM_FILTER_CLASS, show);
598
+        this._updateBackground();
574 599
     }
575 600
 
576 601
 
@@ -643,17 +668,6 @@ export class VideoContainer extends LargeContainer {
643 668
                 ? '#000' : interfaceConfig.DEFAULT_BACKGROUND);
644 669
     }
645 670
 
646
-    /**
647
-     * Sets the blur background to be invisible and pauses any playing video.
648
-     *
649
-     * @private
650
-     * @returns {void}
651
-     */
652
-    _hideVideoBackground() {
653
-        this.$videoBackground.css({ visibility: 'hidden' });
654
-        this.$videoBackground[0].pause();
655
-    }
656
-
657 671
     /**
658 672
      * Callback invoked when the video element changes dimensions.
659 673
      *
@@ -665,26 +679,33 @@ export class VideoContainer extends LargeContainer {
665 679
     }
666 680
 
667 681
     /**
668
-     * Sets the blur background to be visible and starts any loaded video.
682
+     * Attaches and/or updates a React Component to be used as a background for
683
+     * the large video, to display blurred video and fill up empty space not
684
+     * taken up by the large video.
669 685
      *
670 686
      * @private
671 687
      * @returns {void}
672 688
      */
673
-    _showVideoBackground() {
674
-        this.$videoBackground.css({ visibility: 'visible' });
675
-
676
-        // XXX HTMLMediaElement.play's Promise may be rejected. Certain
677
-        // environments such as Google Chrome and React Native will report the
678
-        // rejection as unhandled. And that may appear scary depending on how
679
-        // the environment words the report. To reduce the risk of scaring a
680
-        // developer, make sure that the rejection is handled. We cannot really
681
-        // do anything substantial about the rejection and, more importantly, we
682
-        // do not care. Some browsers (at this time, only Edge is known) don't
683
-        // return a promise from .play(), so check before trying to catch.
684
-        const res = this.$videoBackground[0].play();
685
-
686
-        if (typeof res !== 'undefined') {
687
-            res.catch(reason => logger.error(reason));
689
+    _updateBackground() {
690
+        if (browser.isFirefox() || browser.isTemasysPluginUsed()) {
691
+            return;
688 692
         }
693
+
694
+        ReactDOM.render(
695
+            <LargeVideoBackground
696
+                hidden = { this._hideBackground }
697
+                mirror = {
698
+                    this.stream
699
+                    && this.stream.isLocal()
700
+                    && this.localFlipX
701
+                }
702
+                orientationFit = { this._backgroundOrientation }
703
+                showLocalProblemFilter
704
+                    = { this.$video.hasClass(LOCAL_PROBLEM_FILTER_CLASS) }
705
+                showRemoteProblemFilter
706
+                    = { this.$video.hasClass(REMOTE_PROBLEM_FILTER_CLASS) }
707
+                videoTrack = { this.stream } />,
708
+            document.getElementById('largeVideoBackgroundContainer')
709
+        );
689 710
     }
690 711
 }

+ 1
- 6
react/features/large-video/components/LargeVideo.web.js View File

@@ -50,12 +50,7 @@ export default class LargeVideo extends Component<*> {
50 50
                 <div id = 'remotePresenceMessage' />
51 51
                 <span id = 'remoteConnectionMessage' />
52 52
                 <div>
53
-                    <div className = 'video_blurred_container'>
54
-                        <video
55
-                            autoPlay = { true }
56
-                            id = 'largeVideoBackground'
57
-                            muted = 'true' />
58
-                    </div>
53
+                    <div id = 'largeVideoBackgroundContainer' />
59 54
                     {
60 55
 
61 56
                         /**

+ 0
- 0
react/features/large-video/components/LargeVideoBackground.native.js View File


+ 121
- 0
react/features/large-video/components/LargeVideoBackground.web.js View File

@@ -0,0 +1,121 @@
1
+import PropTypes from 'prop-types';
2
+import React, { Component } from 'react';
3
+
4
+import { Video } from '../../base/media';
5
+
6
+/**
7
+ * Constants to describe the dimensions of the video. Landscape videos
8
+ * are wider than they are taller and portrait videos are taller than they
9
+ * are wider. The dimensions will determine how {@code LargeVideoBackground}
10
+ * will strech to fill its container.
11
+ *
12
+ * @type {Object}
13
+ */
14
+export const ORIENTATION = {
15
+    LANDSCAPE: 'landscape',
16
+    PORTRAIT: 'portrait'
17
+};
18
+
19
+/**
20
+ * A mapping of orientations to a class that should fit the
21
+ * {@code LargeVideoBackground} into its container.
22
+ *
23
+ * @private
24
+ * @type {Object}
25
+ */
26
+const ORIENTATION_TO_CLASS = {
27
+    [ORIENTATION.LANDSCAPE]: 'fit-full-width',
28
+    [ORIENTATION.PORTRAIT]: 'fit-full-height'
29
+};
30
+
31
+/**
32
+ * Implements a React Component which shows a video element intended to be used
33
+ * as a background to fill the empty space of container with another video.
34
+ *
35
+ * @extends Component
36
+ */
37
+export class LargeVideoBackground extends Component {
38
+    /**
39
+     * {@code LargeVideoBackground} component's property types.
40
+     *
41
+     * @static
42
+     */
43
+    static propTypes = {
44
+        /**
45
+         * Additional CSS class names to add to the root of the component.
46
+         */
47
+        className: PropTypes.string,
48
+
49
+        /**
50
+         * Whether or not the background should have its visibility hidden.
51
+         */
52
+        hidden: PropTypes.bool,
53
+
54
+        /**
55
+         * Whether or not the video should display flipped horizontally, so
56
+         * left becomes right and right becomes left.
57
+         */
58
+        mirror: PropTypes.bool,
59
+
60
+        /**
61
+         * Whether the component should ensure full width of the video is
62
+         * displayed (landscape) or full height (portrait).
63
+         */
64
+        orientationFit: PropTypes.oneOf([
65
+            ORIENTATION.LANDSCAPE,
66
+            ORIENTATION.PORTRAIT
67
+        ]),
68
+
69
+        /**
70
+         * Whether or not to display a filter on the video to visually indicate
71
+         * a problem with the video being displayed.
72
+         */
73
+        showLocalProblemFilter: PropTypes.bool,
74
+
75
+        /**
76
+         * Whether or not to display a filter on the video to visually indicate
77
+         * a problem with the video being displayed.
78
+         */
79
+        showRemoteProblemFilter: PropTypes.bool,
80
+
81
+        /**
82
+         * The video stream to display.
83
+         */
84
+        videoTrack: PropTypes.object
85
+    };
86
+
87
+    /**
88
+     * Implements React's {@link Component#render()}.
89
+     *
90
+     * @inheritdoc
91
+     * @returns {ReactElement}
92
+     */
93
+    render() {
94
+        const {
95
+            hidden,
96
+            mirror,
97
+            orientationFit,
98
+            showLocalProblemFilter,
99
+            showRemoteProblemFilter,
100
+            videoTrack
101
+        } = this.props;
102
+        const orientationClass = orientationFit
103
+            ? ORIENTATION_TO_CLASS[orientationFit] : '';
104
+        const classNames = `large-video-background ${mirror ? 'flip-x' : ''} ${
105
+            hidden ? 'invisible' : ''} ${orientationClass} ${
106
+            showLocalProblemFilter ? 'videoProblemFilter' : ''} ${
107
+            showRemoteProblemFilter ? 'remoteVideoProblemFilter' : ''}`;
108
+        const videoTrackModel = {
109
+            jitsiTrack: hidden ? null : videoTrack
110
+        };
111
+
112
+        return (
113
+            <div className = { classNames }>
114
+                <Video
115
+                    autoPlay = { true }
116
+                    id = 'largeVideoBackground'
117
+                    videoTrack = { videoTrackModel } />
118
+            </div>
119
+        );
120
+    }
121
+}

+ 1
- 0
react/features/large-video/components/index.js View File

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

Loading…
Cancel
Save