|
@@ -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
|
}
|