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

+ 12
- 4
css/_utils.scss View File

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

+ 12
- 1
css/_videolayout_default.scss View File

12
     overflow: hidden;
12
     overflow: hidden;
13
 }
13
 }
14
 
14
 
15
-.video_blurred_container {
15
+#largeVideoBackgroundContainer,
16
+.large-video-background {
16
     height: 100%;
17
     height: 100%;
17
     filter: blur(40px);
18
     filter: blur(40px);
18
     left: 0;
19
     left: 0;
20
     position: absolute;
21
     position: absolute;
21
     top: 0;
22
     top: 0;
22
     width: 100%;
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
 .videocontainer {
36
 .videocontainer {

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

1
 /* global $, APP, interfaceConfig */
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
 import Filmstrip from './Filmstrip';
14
 import Filmstrip from './Filmstrip';
4
 import LargeContainer from './LargeContainer';
15
 import LargeContainer from './LargeContainer';
5
 import UIEvents from '../../../service/UI/UIEvents';
16
 import UIEvents from '../../../service/UI/UIEvents';
10
 
21
 
11
 const FADE_DURATION_MS = 300;
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
  * Returns an array of the video dimensions, so that it keeps it's aspect
43
  * Returns an array of the video dimensions, so that it keeps it's aspect
167
         return $('#largeVideo');
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
 
219
 
200
         this.isVisible = false;
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
          * Flag indicates whether or not the avatar is currently displayed.
240
          * Flag indicates whether or not the avatar is currently displayed.
204
          * @type {boolean}
241
          * @type {boolean}
277
      * <tt>false</tt> otherwise.
314
      * <tt>false</tt> otherwise.
278
      */
315
      */
279
     enableLocalConnectionProblemFilter(enable) {
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
             return;
439
             return;
403
         }
440
         }
404
 
441
 
405
-        this._hideVideoBackground();
406
-
407
         const [ width, height ]
442
         const [ width, height ]
408
             = this.getVideoSize(containerWidth, containerHeight);
443
             = this.getVideoSize(containerWidth, containerHeight);
409
 
444
 
410
         if ((containerWidth > width) || (containerHeight > height)) {
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
         const { horizontalIndent, verticalIndent }
455
         const { horizontalIndent, verticalIndent }
423
             = this.getVideoPosition(width, height,
456
             = this.getVideoPosition(width, height,
424
             containerWidth, containerHeight);
457
             containerWidth, containerHeight);
484
         // detach old stream
517
         // detach old stream
485
         if (this.stream) {
518
         if (this.stream) {
486
             this.stream.detach(this.$video[0]);
519
             this.stream.detach(this.$video[0]);
487
-            this.stream.detach(this.$videoBackground[0]);
488
         }
520
         }
489
 
521
 
490
         this.stream = stream;
522
         this.stream = stream;
495
         }
527
         }
496
 
528
 
497
         stream.attach(this.$video[0]);
529
         stream.attach(this.$video[0]);
498
-        stream.attach(this.$videoBackground[0]);
499
-
500
-        this._hideVideoBackground();
501
 
530
 
502
         const flipX = stream.isLocal() && this.localFlipX;
531
         const flipX = stream.isLocal() && this.localFlipX;
503
 
532
 
504
         this.$video.css({
533
         this.$video.css({
505
             transform: flipX ? 'scaleX(-1)' : 'none'
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
         // Reset the large video background depending on the stream.
539
         // Reset the large video background depending on the stream.
512
         this.setLargeVideoBackground(this.avatarDisplayed);
540
         this.setLargeVideoBackground(this.avatarDisplayed);
525
             transform: this.localFlipX ? 'scaleX(-1)' : 'none'
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
      * the indication.
593
      * the indication.
568
      */
594
      */
569
     showRemoteConnectionProblemIndicator(show) {
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
                 ? '#000' : interfaceConfig.DEFAULT_BACKGROUND);
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
      * Callback invoked when the video element changes dimensions.
672
      * Callback invoked when the video element changes dimensions.
659
      *
673
      *
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
      * @private
686
      * @private
671
      * @returns {void}
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
                 <div id = 'remotePresenceMessage' />
50
                 <div id = 'remotePresenceMessage' />
51
                 <span id = 'remoteConnectionMessage' />
51
                 <span id = 'remoteConnectionMessage' />
52
                 <div>
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

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
 export { default as LargeVideo } from './LargeVideo';
1
 export { default as LargeVideo } from './LargeVideo';
2
+export * from './LargeVideoBackground';

Loading…
Cancel
Save