Quellcode durchsuchen

fix(blur): many small issues.

master
Hristo Terezov vor 5 Jahren
Ursprung
Commit
3b0c5d0b6a

+ 9
- 23
conference.js Datei anzeigen

@@ -105,10 +105,8 @@ import {
105 105
     trackAdded,
106 106
     trackRemoved
107 107
 } from './react/features/base/tracks';
108
-import {
109
-    getJitsiMeetGlobalNS,
110
-    loadScript
111
-} from './react/features/base/util';
108
+import { getJitsiMeetGlobalNS } from './react/features/base/util';
109
+import { getBlurEffect } from './react/features/blur';
112 110
 import { addMessage } from './react/features/chat';
113 111
 import { showDesktopPicker } from './react/features/desktop-picker';
114 112
 import { appendSuffix } from './react/features/display-name';
@@ -562,25 +560,14 @@ export default {
562 560
             // Resolve with no tracks
563 561
             tryCreateLocalTracks = Promise.resolve([]);
564 562
         } else {
565
-
566 563
             const loadEffectsPromise = options.startWithBlurEnabled
567
-                ? loadScript('libs/video-blur-effect.min.js')
568
-                        .then(() =>
569
-                            getJitsiMeetGlobalNS().effects.createBlurEffect()
570
-                                .then(blurEffectInstance =>
571
-                                    Promise.resolve([ blurEffectInstance ])
572
-                                )
573
-                                .catch(error => {
574
-                                    logger.log('Failed to create JitsiStreamBlurEffect!', error);
575
-
576
-                                    return Promise.resolve([]);
577
-                                })
578
-                        )
579
-                        .catch(error => {
580
-                            logger.error('loadScript failed with error: ', error);
564
+                ? getBlurEffect()
565
+                    .then(blurEffect => [ blurEffect ])
566
+                    .catch(error => {
567
+                        logger.error('Failed to obtain the blur effect instance with error: ', error);
581 568
 
582
-                            return Promise.resolve([]);
583
-                        })
569
+                        return Promise.resolve([]);
570
+                    })
584 571
                 : Promise.resolve([]);
585 572
 
586 573
             tryCreateLocalTracks = loadEffectsPromise.then(trackEffects =>
@@ -678,7 +665,6 @@ export default {
678 665
      */
679 666
     init(options) {
680 667
         this.roomName = options.roomName;
681
-        const videoBlurEffectEnabled = APP.store.getState()['features/blur'].blurEnabled;
682 668
 
683 669
         return (
684 670
 
@@ -692,7 +678,7 @@ export default {
692 678
                     'initial device list initialization failed', error))
693 679
                 .then(() => this.createInitialLocalTracksAndConnect(
694 680
                 options.roomName, {
695
-                    startWithBlurEnabled: videoBlurEffectEnabled,
681
+                    startWithBlurEnabled: APP.store.getState()['features/blur'].blurEnabled,
696 682
                     startAudioOnly: config.startAudioOnly,
697 683
                     startScreenSharing: config.startScreenSharing,
698 684
                     startWithAudioMuted: config.startWithAudioMuted || config.startSilent,

+ 136
- 0
package-lock.json Datei anzeigen

@@ -2563,6 +2563,102 @@
2563 2563
         "component-url": "^0.2.1"
2564 2564
       }
2565 2565
     },
2566
+    "@tensorflow-models/body-pix": {
2567
+      "version": "1.1.1",
2568
+      "resolved": "https://registry.npmjs.org/@tensorflow-models/body-pix/-/body-pix-1.1.1.tgz",
2569
+      "integrity": "sha512-l9bd+b3QI7OzJjw/OuhEfeGRb5l2lRivgDHGMvQbT2Snn8nV7odHSRW55NzhU7Khl7vga00TWo5QDuVnkevQmQ=="
2570
+    },
2571
+    "@tensorflow/tfjs": {
2572
+      "version": "1.2.2",
2573
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs/-/tfjs-1.2.2.tgz",
2574
+      "integrity": "sha512-HfhSzL2eTWhlT0r/A5wmo+u3bHe+an16p5wsnFH3ujn21fQ8QtGpSfDHQZjWx1kVFaQnV6KBG+17MOrRHoHlLA==",
2575
+      "requires": {
2576
+        "@tensorflow/tfjs-converter": "1.2.2",
2577
+        "@tensorflow/tfjs-core": "1.2.2",
2578
+        "@tensorflow/tfjs-data": "1.2.2",
2579
+        "@tensorflow/tfjs-layers": "1.2.2"
2580
+      }
2581
+    },
2582
+    "@tensorflow/tfjs-converter": {
2583
+      "version": "1.2.2",
2584
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-converter/-/tfjs-converter-1.2.2.tgz",
2585
+      "integrity": "sha512-NM2NcPRHpCNeJdBxHcYpmW9ZHTQ2lJFJgmgGpQ8CxSC9CtQB05bFONs3SKcwMNDE/69QBRVom5DYqLCVUg+A+g=="
2586
+    },
2587
+    "@tensorflow/tfjs-core": {
2588
+      "version": "1.2.2",
2589
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-core/-/tfjs-core-1.2.2.tgz",
2590
+      "integrity": "sha512-2hCHMKjh3UNpLEjbAEaurrTGJyj/KpLtMSAraWgHA1vGY0kmk50BBSbgCDmXWUVm7lyh/SkCq4/GrGDZktEs3g==",
2591
+      "requires": {
2592
+        "@types/offscreencanvas": "~2019.3.0",
2593
+        "@types/seedrandom": "2.4.27",
2594
+        "@types/webgl-ext": "0.0.30",
2595
+        "@types/webgl2": "0.0.4",
2596
+        "node-fetch": "~2.1.2",
2597
+        "rollup-plugin-visualizer": "~1.1.1",
2598
+        "seedrandom": "2.4.3"
2599
+      },
2600
+      "dependencies": {
2601
+        "node-fetch": {
2602
+          "version": "2.1.2",
2603
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
2604
+          "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
2605
+        }
2606
+      }
2607
+    },
2608
+    "@tensorflow/tfjs-data": {
2609
+      "version": "1.2.2",
2610
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-data/-/tfjs-data-1.2.2.tgz",
2611
+      "integrity": "sha512-oHGBoGdnCl2RyouLKplQqo+iil0iJgPbi/aoHizhpO77UBuJXlKMblH8w5GbxVAw3hKxWlqzYpxPo6rVRgehNA==",
2612
+      "requires": {
2613
+        "@types/node-fetch": "^2.1.2",
2614
+        "node-fetch": "~2.1.2"
2615
+      },
2616
+      "dependencies": {
2617
+        "node-fetch": {
2618
+          "version": "2.1.2",
2619
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
2620
+          "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
2621
+        }
2622
+      }
2623
+    },
2624
+    "@tensorflow/tfjs-layers": {
2625
+      "version": "1.2.2",
2626
+      "resolved": "https://registry.npmjs.org/@tensorflow/tfjs-layers/-/tfjs-layers-1.2.2.tgz",
2627
+      "integrity": "sha512-yzWZaZrCVpEyTkSrzMe4OOP4aGUfaaROE/zR9fPsPGGF8wLlbLNZUJjeYUmjy3G3pXGaM0mQUbLR5Vd707CVtQ=="
2628
+    },
2629
+    "@types/node": {
2630
+      "version": "12.0.10",
2631
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.10.tgz",
2632
+      "integrity": "sha512-LcsGbPomWsad6wmMNv7nBLw7YYYyfdYcz6xryKYQhx89c3XXan+8Q6AJ43G5XDIaklaVkK3mE4fCb0SBvMiPSQ=="
2633
+    },
2634
+    "@types/node-fetch": {
2635
+      "version": "2.3.7",
2636
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.3.7.tgz",
2637
+      "integrity": "sha512-+bKtuxhj/TYSSP1r4CZhfmyA0vm/aDRQNo7vbAgf6/cZajn0SAniGGST07yvI4Q+q169WTa2/x9gEHfJrkcALw==",
2638
+      "requires": {
2639
+        "@types/node": "*"
2640
+      }
2641
+    },
2642
+    "@types/offscreencanvas": {
2643
+      "version": "2019.3.0",
2644
+      "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz",
2645
+      "integrity": "sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q=="
2646
+    },
2647
+    "@types/seedrandom": {
2648
+      "version": "2.4.27",
2649
+      "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-2.4.27.tgz",
2650
+      "integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE="
2651
+    },
2652
+    "@types/webgl-ext": {
2653
+      "version": "0.0.30",
2654
+      "resolved": "https://registry.npmjs.org/@types/webgl-ext/-/webgl-ext-0.0.30.tgz",
2655
+      "integrity": "sha512-LKVgNmBxN0BbljJrVUwkxwRYqzsAEPcZOe6S2T6ZaBDIrFp0qu4FNlpc5sM1tGbXUYFgdVQIoeLk1Y1UoblyEg=="
2656
+    },
2657
+    "@types/webgl2": {
2658
+      "version": "0.0.4",
2659
+      "resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz",
2660
+      "integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw=="
2661
+    },
2566 2662
     "@webassemblyjs/ast": {
2567 2663
       "version": "1.7.11",
2568 2664
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
@@ -13187,6 +13283,35 @@
13187 13283
         "inherits": "^2.0.1"
13188 13284
       }
13189 13285
     },
13286
+    "rollup-plugin-visualizer": {
13287
+      "version": "1.1.1",
13288
+      "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-1.1.1.tgz",
13289
+      "integrity": "sha512-7xkSKp+dyJmSC7jg2LXqViaHuOnF1VvIFCnsZEKjrgT5ZVyiLLSbeszxFcQSfNJILphqgAEmWAUz0Z4xYScrRw==",
13290
+      "optional": true,
13291
+      "requires": {
13292
+        "mkdirp": "^0.5.1",
13293
+        "opn": "^5.4.0",
13294
+        "source-map": "^0.7.3",
13295
+        "typeface-oswald": "0.0.54"
13296
+      },
13297
+      "dependencies": {
13298
+        "opn": {
13299
+          "version": "5.5.0",
13300
+          "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
13301
+          "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
13302
+          "optional": true,
13303
+          "requires": {
13304
+            "is-wsl": "^1.1.0"
13305
+          }
13306
+        },
13307
+        "source-map": {
13308
+          "version": "0.7.3",
13309
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
13310
+          "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
13311
+          "optional": true
13312
+        }
13313
+      }
13314
+    },
13190 13315
     "rsvp": {
13191 13316
       "version": "3.6.2",
13192 13317
       "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
@@ -13775,6 +13900,11 @@
13775 13900
       "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.3.0.tgz",
13776 13901
       "integrity": "sha1-V6lXWUIEHYV3qGnXx01MOgvYiPY="
13777 13902
     },
13903
+    "seedrandom": {
13904
+      "version": "2.4.3",
13905
+      "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
13906
+      "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw="
13907
+    },
13778 13908
     "select-hose": {
13779 13909
       "version": "2.0.0",
13780 13910
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -15363,6 +15493,12 @@
15363 15493
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
15364 15494
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
15365 15495
     },
15496
+    "typeface-oswald": {
15497
+      "version": "0.0.54",
15498
+      "resolved": "https://registry.npmjs.org/typeface-oswald/-/typeface-oswald-0.0.54.tgz",
15499
+      "integrity": "sha512-U1WMNp4qfy4/3khIfHMVAIKnNu941MXUfs3+H9R8PFgnoz42Hh9pboSFztWr86zut0eXC8byalmVhfkiKON/8Q==",
15500
+      "optional": true
15501
+    },
15366 15502
     "ua-parser-js": {
15367 15503
       "version": "0.7.17",
15368 15504
       "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",

+ 16
- 17
react/features/blur/actions.js Datei anzeigen

@@ -1,49 +1,48 @@
1 1
 // @flow
2 2
 
3
-import { getJitsiMeetGlobalNS } from '../base/util';
4 3
 import { getLocalVideoTrack } from '../../features/base/tracks';
5 4
 
6
-import {
7
-    BLUR_DISABLED,
8
-    BLUR_ENABLED
9
-} from './actionTypes';
5
+import { BLUR_DISABLED, BLUR_ENABLED } from './actionTypes';
6
+import { getBlurEffect } from './functions';
10 7
 
11 8
 const logger = require('jitsi-meet-logger').getLogger(__filename);
12 9
 
13 10
 /**
14
-* Signals the local participant is switching between blurred or
15
-* non blurred video.
16
-*
17
-* @param {boolean} enabled - If true enables video blur, false otherwise
11
+* Signals the local participant is switching between blurred or non blurred video.
18 12
 *
13
+* @param {boolean} enabled - If true enables video blur, false otherwise.
19 14
 * @returns {Promise}
20 15
 */
21 16
 export function toggleBlurEffect(enabled: boolean) {
22 17
     return function(dispatch: (Object) => Object, getState: () => any) {
23
-        if (getState()['features/blur'].blurEnabled !== enabled) {
24
-            const videoTrack = getLocalVideoTrack(getState()['features/base/tracks']).jitsiTrack;
18
+        const state = getState();
19
+
20
+        if (state['features/blur'].blurEnabled !== enabled) {
21
+            const { jitsiTrack } = getLocalVideoTrack(state['features/base/tracks']);
25 22
 
26
-            return getJitsiMeetGlobalNS().effects.createBlurEffect()
23
+            return getBlurEffect()
27 24
                 .then(blurEffectInstance =>
28
-                    videoTrack.enableEffect(enabled, blurEffectInstance)
25
+                    jitsiTrack.setEffect(enabled ? blurEffectInstance : undefined)
29 26
                         .then(() => {
30 27
                             enabled ? dispatch(blurEnabled()) : dispatch(blurDisabled());
31 28
                         })
32 29
                         .catch(error => {
33 30
                             enabled ? dispatch(blurDisabled()) : dispatch(blurEnabled());
34
-                            logger.log('enableEffect failed with error:', error);
31
+                            logger.error('setEffect failed with error:', error);
35 32
                         })
36 33
                 )
37 34
                 .catch(error => {
38 35
                     dispatch(blurDisabled());
39
-                    logger.log('createBlurEffect failed with error:', error);
36
+                    logger.error('getBlurEffect failed with error:', error);
40 37
                 });
41 38
         }
39
+
40
+        return Promise.resolve();
42 41
     };
43 42
 }
44 43
 
45 44
 /**
46
- * Signals the local participant that the blur has been enabled
45
+ * Signals the local participant that the blur has been enabled.
47 46
  *
48 47
  * @returns {{
49 48
  *      type: BLUR_ENABLED
@@ -56,7 +55,7 @@ export function blurEnabled() {
56 55
 }
57 56
 
58 57
 /**
59
- * Signals the local participant that the blur has been disabled
58
+ * Signals the local participant that the blur has been disabled.
60 59
  *
61 60
  * @returns {{
62 61
  *      type: BLUR_DISABLED

+ 5
- 36
react/features/blur/components/VideoBlurButton.js Datei anzeigen

@@ -5,15 +5,9 @@ import { translate } from '../../base/i18n';
5 5
 import { connect } from '../../base/redux';
6 6
 import { AbstractButton } from '../../base/toolbox';
7 7
 import type { AbstractButtonProps } from '../../base/toolbox';
8
-import {
9
-    getJitsiMeetGlobalNS,
10
-    loadScript
11
-} from '../../base/util';
12 8
 
13 9
 import { toggleBlurEffect } from '../actions';
14 10
 
15
-const logger = require('jitsi-meet-logger').getLogger(__filename);
16
-
17 11
 /**
18 12
  * The type of the React {@code Component} props of {@link VideoBlurButton}.
19 13
  */
@@ -49,27 +43,11 @@ class VideoBlurButton extends AbstractButton<Props, *> {
49 43
      * @returns {void}
50 44
      */
51 45
     _handleClick() {
52
-        const {
53
-            _isVideoBlurred,
54
-            dispatch
55
-        } = this.props;
56
-
57
-        if (!getJitsiMeetGlobalNS().effects
58
-            || !getJitsiMeetGlobalNS().effects.createBlurEffect) {
59
-
60
-            loadScript('libs/video-blur-effect.min.js')
61
-                .then(() => {
62
-                    this._handleClick();
63
-                })
64
-                .catch(error => {
65
-                    logger.error('Failed to load script with error: ', error);
66
-                });
46
+        const { _isVideoBlurred, dispatch } = this.props;
47
+        const value = !_isVideoBlurred;
67 48
 
68
-        } else {
69
-            sendAnalytics(createVideoBlurEvent(_isVideoBlurred ? 'started' : 'stopped'));
70
-
71
-            dispatch(toggleBlurEffect(!_isVideoBlurred));
72
-        }
49
+        sendAnalytics(createVideoBlurEvent(value ? 'started' : 'stopped'));
50
+        dispatch(toggleBlurEffect(value));
73 51
     }
74 52
 
75 53
     /**
@@ -80,16 +58,7 @@ class VideoBlurButton extends AbstractButton<Props, *> {
80 58
      * @returns {boolean}
81 59
      */
82 60
     _isToggled() {
83
-        const {
84
-            _isVideoBlurred
85
-        } = this.props;
86
-
87
-        if (!getJitsiMeetGlobalNS().effects
88
-            || !getJitsiMeetGlobalNS().effects.createBlurEffect) {
89
-            return false;
90
-        }
91
-
92
-        return _isVideoBlurred;
61
+        return this.props._isVideoBlurred;
93 62
     }
94 63
 }
95 64
 

+ 18
- 0
react/features/blur/functions.js Datei anzeigen

@@ -0,0 +1,18 @@
1
+// @flow
2
+
3
+import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
4
+
5
+/**
6
+ * Returns promise that resolves with the blur effect instance.
7
+ *
8
+ * @returns {Promise<JitsiStreamBlurEffect>} - Resolves with the blur effect instance.
9
+ */
10
+export function getBlurEffect() {
11
+    const ns = getJitsiMeetGlobalNS();
12
+
13
+    if (ns.effects && ns.effects.createBlurEffect) {
14
+        return ns.effects.createBlurEffect();
15
+    }
16
+
17
+    return loadScript('libs/video-blur-effect.min.js').then(() => ns.effects.createBlurEffect());
18
+}

+ 1
- 0
react/features/blur/index.js Datei anzeigen

@@ -1,4 +1,5 @@
1 1
 export * from './actions';
2 2
 export * from './components';
3
+export * from './functions';
3 4
 
4 5
 import './reducer';

+ 0
- 237
react/features/stream-effects/JitsiStreamBlurEffect.js Datei anzeigen

@@ -1,237 +0,0 @@
1
-
2
-import { getLogger } from 'jitsi-meet-logger';
3
-import {
4
-    drawBokehEffect,
5
-    load
6
-} from '@tensorflow-models/body-pix';
7
-
8
-import {
9
-    CLEAR_INTERVAL,
10
-    INTERVAL_TIMEOUT,
11
-    SET_INTERVAL,
12
-    timerWorkerScript
13
-} from './TimerWorker';
14
-
15
-const logger = getLogger(__filename);
16
-
17
-/**
18
- * This promise represents the loading of the BodyPix model that is used
19
- * to extract person segmentation. A multiplier of 0.25 is used to for
20
- * improved performance on a larger range of CPUs.
21
- */
22
-const bpModelPromise = load(0.25);
23
-
24
-/**
25
- * Represents a modified MediaStream that adds blur to video background.
26
- * <tt>JitsiStreamBlurEffect</tt> does the processing of the original
27
- * video stream.
28
- */
29
-class JitsiStreamBlurEffect {
30
-
31
-    /**
32
-     *
33
-     * Represents a modified video MediaStream track.
34
-     *
35
-     * @class
36
-     * @param {BodyPix} bpModel - BodyPix model
37
-     */
38
-    constructor(bpModel) {
39
-        this._bpModel = bpModel;
40
-
41
-        this._outputCanvasElement = document.createElement('canvas');
42
-        this._maskCanvasElement = document.createElement('canvas');
43
-        this._inputVideoElement = document.createElement('video');
44
-
45
-        this._renderVideo = this._renderVideo.bind(this);
46
-        this._renderMask = this._renderMask.bind(this);
47
-
48
-        this._videoFrameTimerWorker = new Worker(timerWorkerScript);
49
-        this._maskFrameTimerWorker = new Worker(timerWorkerScript);
50
-
51
-        this._onMaskFrameTimer = this._onMaskFrameTimer.bind(this);
52
-        this._onVideoFrameTimer = this._onVideoFrameTimer.bind(this);
53
-        this._videoFrameTimerWorker.onmessage = this._onVideoFrameTimer;
54
-        this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer;
55
-    }
56
-
57
-    /**
58
-     * EventHandler onmessage for the videoFrameTimerWorker WebWorker
59
-     *
60
-     * @private
61
-     * @param {EventHandler} response - onmessage EventHandler parameter
62
-     * @returns {void}
63
-     */
64
-    _onVideoFrameTimer(response) {
65
-        switch (response.data.id) {
66
-        case INTERVAL_TIMEOUT: {
67
-            this._renderVideo();
68
-            break;
69
-        }
70
-        }
71
-    }
72
-
73
-    /**
74
-     * EventHandler onmessage for the maskFrameTimerWorker WebWorker
75
-     *
76
-     * @private
77
-     * @param {EventHandler} response - onmessage EventHandler parameter
78
-     * @returns {void}
79
-     */
80
-    _onMaskFrameTimer(response) {
81
-        switch (response.data.id) {
82
-        case INTERVAL_TIMEOUT: {
83
-            this._renderMask();
84
-            break;
85
-        }
86
-        }
87
-    }
88
-
89
-    /**
90
-     * Starts loop to capture video frame and render the segmentation mask.
91
-     *
92
-     * @param {MediaStream} stream - Stream to be used for processing
93
-     *
94
-     * @returns {void}
95
-     */
96
-    startEffect(stream) {
97
-        this._stream = stream;
98
-
99
-        const firstVideoTrack = this._stream.getVideoTracks()[0];
100
-        const { height, frameRate, width } = firstVideoTrack.getSettings
101
-            ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints();
102
-
103
-        if (!firstVideoTrack.getSettings && !firstVideoTrack.getConstraints) {
104
-            throw new Error('JitsiStreamBlurEffect not supported!');
105
-        }
106
-
107
-        this._frameRate = frameRate;
108
-        this._height = height;
109
-        this._width = width;
110
-
111
-        this._outputCanvasElement.width = width;
112
-        this._outputCanvasElement.height = height;
113
-
114
-        this._maskCanvasElement.width = this._width;
115
-        this._maskCanvasElement.height = this._height;
116
-
117
-        this._inputVideoElement.width = width;
118
-        this._inputVideoElement.height = height;
119
-
120
-        this._maskCanvasContext = this._maskCanvasElement.getContext('2d');
121
-
122
-        this._inputVideoElement.autoplay = true;
123
-        this._inputVideoElement.srcObject = this._stream;
124
-
125
-        this._videoFrameTimerWorker.postMessage({
126
-            id: SET_INTERVAL,
127
-            timeMs: 1000 / this._frameRate
128
-        });
129
-
130
-        this._maskFrameTimerWorker.postMessage({
131
-            id: SET_INTERVAL,
132
-            timeMs: 200
133
-        });
134
-    }
135
-
136
-    /**
137
-     * Stops the capture and render loop.
138
-     *
139
-     * @returns {void}
140
-     */
141
-    stopEffect() {
142
-        this._videoFrameTimerWorker.postMessage({
143
-            id: CLEAR_INTERVAL
144
-        });
145
-
146
-        this._maskFrameTimerWorker.postMessage({
147
-            id: CLEAR_INTERVAL
148
-        });
149
-    }
150
-
151
-    /**
152
-     * Get the modified stream.
153
-     *
154
-     * @returns {MediaStream}
155
-     */
156
-    getStreamWithEffect() {
157
-        return this._outputCanvasElement.captureStream(this._frameRate);
158
-    }
159
-
160
-    /**
161
-     * Loop function to render the video frame input and draw blur effect.
162
-     *
163
-     * @private
164
-     * @returns {void}
165
-     */
166
-    _renderVideo() {
167
-        if (this._bpModel) {
168
-            this._maskCanvasContext.drawImage(this._inputVideoElement,
169
-                                                0,
170
-                                                0,
171
-                                                this._width,
172
-                                                this._height);
173
-
174
-            if (this._segmentationData) {
175
-
176
-                drawBokehEffect(this._outputCanvasElement,
177
-                                this._inputVideoElement,
178
-                                this._segmentationData,
179
-                                7, // Constant for background blur, integer values between 0-20
180
-                                7); // Constant for edge blur, integer values between 0-20
181
-            }
182
-        } else {
183
-            this._outputCanvasElement
184
-                .getContext('2d')
185
-                .drawImage(this._inputVideoElement,
186
-                                                0,
187
-                                                0,
188
-                                                this._width,
189
-                                                this._height);
190
-        }
191
-    }
192
-
193
-    /**
194
-     * Loop function to render the background mask.
195
-     *
196
-     * @private
197
-     * @returns {void}
198
-     */
199
-    _renderMask() {
200
-        if (this._bpModel) {
201
-            this._bpModel.estimatePersonSegmentation(this._maskCanvasElement,
202
-                                                    32, // Chose 32 for better performance
203
-                                                    0.75) // Represents probability that a pixel belongs to a person
204
-                .then(value => {
205
-                    this._segmentationData = value;
206
-                });
207
-        }
208
-    }
209
-
210
-    /**
211
-     * Checks if the local track supports this effect.
212
-     *
213
-     * @param {JitsiLocalTrack} jitsiLocalTrack - Track to apply effect
214
-     *
215
-     * @returns {boolean} Returns true if this effect can run on the specified track
216
-     * false otherwise
217
-     */
218
-    isEnabled(jitsiLocalTrack) {
219
-        return jitsiLocalTrack.isVideoTrack();
220
-    }
221
-}
222
-
223
-/**
224
- * Creates a new instance of JitsiStreamBlurEffect.
225
- *
226
- * @returns {Promise<JitsiStreamBlurEffect>}
227
- */
228
-export function createBlurEffect() {
229
-    return bpModelPromise
230
-        .then(bpmodel =>
231
-            Promise.resolve(new JitsiStreamBlurEffect(bpmodel))
232
-        )
233
-        .catch(error => {
234
-            logger.error('Failed to load BodyPix model. Fallback to original stream!', error);
235
-            throw error;
236
-        });
237
-}

+ 167
- 0
react/features/stream-effects/blur/JitsiStreamBlurEffect.js Datei anzeigen

@@ -0,0 +1,167 @@
1
+
2
+import { drawBokehEffect } from '@tensorflow-models/body-pix';
3
+
4
+import {
5
+    CLEAR_INTERVAL,
6
+    INTERVAL_TIMEOUT,
7
+    SET_INTERVAL,
8
+    timerWorkerScript
9
+} from './TimerWorker';
10
+
11
+/**
12
+ * Represents a modified MediaStream that adds blur to video background.
13
+ * <tt>JitsiStreamBlurEffect</tt> does the processing of the original
14
+ * video stream.
15
+ */
16
+export default class JitsiStreamBlurEffect {
17
+    /**
18
+     * Represents a modified video MediaStream track.
19
+     *
20
+     * @class
21
+     * @param {BodyPix} bpModel - BodyPix model.
22
+     */
23
+    constructor(bpModel) {
24
+        this._bpModel = bpModel;
25
+
26
+        // Bind event handler so it is only bound once for every instance.
27
+        this._onMaskFrameTimer = this._onMaskFrameTimer.bind(this);
28
+        this._onVideoFrameTimer = this._onVideoFrameTimer.bind(this);
29
+
30
+        this._outputCanvasElement = document.createElement('canvas');
31
+        this._maskCanvasElement = document.createElement('canvas');
32
+        this._inputVideoElement = document.createElement('video');
33
+
34
+        this._videoFrameTimerWorker = new Worker(timerWorkerScript);
35
+        this._maskFrameTimerWorker = new Worker(timerWorkerScript);
36
+        this._videoFrameTimerWorker.onmessage = this._onVideoFrameTimer;
37
+        this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer;
38
+    }
39
+
40
+    /**
41
+     * EventHandler onmessage for the videoFrameTimerWorker WebWorker.
42
+     *
43
+     * @private
44
+     * @param {EventHandler} response - The onmessage EventHandler parameter.
45
+     * @returns {void}
46
+     */
47
+    _onVideoFrameTimer(response) {
48
+        if (response.data.id === INTERVAL_TIMEOUT) {
49
+            this._renderVideo();
50
+        }
51
+    }
52
+
53
+    /**
54
+     * EventHandler onmessage for the maskFrameTimerWorker WebWorker.
55
+     *
56
+     * @private
57
+     * @param {EventHandler} response - The onmessage EventHandler parameter.
58
+     * @returns {void}
59
+     */
60
+    _onMaskFrameTimer(response) {
61
+        if (response.data.id === INTERVAL_TIMEOUT) {
62
+            this._renderMask();
63
+        }
64
+    }
65
+
66
+    /**
67
+     * Starts loop to capture video frame and render the segmentation mask.
68
+     *
69
+     * @param {MediaStream} stream - Stream to be used for processing.
70
+     * @returns {MediaStream} - The stream with the applied effect.
71
+     */
72
+    startEffect(stream) {
73
+        const firstVideoTrack = stream.getVideoTracks()[0];
74
+        const { height, frameRate, width }
75
+            = firstVideoTrack.getSettings ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints();
76
+
77
+        this._frameRate = frameRate;
78
+        this._height = height;
79
+        this._width = width;
80
+
81
+        this._outputCanvasElement.width = width;
82
+        this._outputCanvasElement.height = height;
83
+
84
+        this._maskCanvasElement.width = width;
85
+        this._maskCanvasElement.height = height;
86
+
87
+        this._maskCanvasContext = this._maskCanvasElement.getContext('2d');
88
+        this._inputVideoElement.width = width;
89
+        this._inputVideoElement.height = height;
90
+        this._inputVideoElement.autoplay = true;
91
+        this._inputVideoElement.srcObject = stream;
92
+
93
+        this._videoFrameTimerWorker.postMessage({
94
+            id: SET_INTERVAL,
95
+            timeMs: 1000 / this._frameRate
96
+        });
97
+        this._maskFrameTimerWorker.postMessage({
98
+            id: SET_INTERVAL,
99
+            timeMs: 200
100
+        });
101
+
102
+        return this._outputCanvasElement.captureStream(this._frameRate);
103
+    }
104
+
105
+    /**
106
+     * Stops the capture and render loop.
107
+     *
108
+     * @returns {void}
109
+     */
110
+    stopEffect() {
111
+        this._videoFrameTimerWorker.postMessage({
112
+            id: CLEAR_INTERVAL
113
+        });
114
+        this._maskFrameTimerWorker.postMessage({
115
+            id: CLEAR_INTERVAL
116
+        });
117
+    }
118
+
119
+    /**
120
+     * Loop function to render the video frame input and draw blur effect.
121
+     *
122
+     * @private
123
+     * @returns {void}
124
+     */
125
+    _renderVideo() {
126
+        this._maskCanvasContext.drawImage(this._inputVideoElement, 0, 0, this._width, this._height);
127
+        if (this._segmentationData) {
128
+            drawBokehEffect(
129
+                this._outputCanvasElement,
130
+                this._inputVideoElement,
131
+                this._segmentationData,
132
+                7, // Constant for background blur, integer values between 0-20
133
+                7 // Constant for edge blur, integer values between 0-20
134
+            );
135
+        }
136
+    }
137
+
138
+    /**
139
+     * Loop function to render the background mask.
140
+     *
141
+     * @private
142
+     * @returns {void}
143
+     */
144
+    _renderMask() {
145
+        this._bpModel.estimatePersonSegmentation(
146
+            this._maskCanvasElement,
147
+            32, // Chose 32 for better performance
148
+            0.75 // Represents probability that a pixel belongs to a person
149
+        )
150
+        .then(value => {
151
+            this._segmentationData = value;
152
+        });
153
+    }
154
+
155
+    /**
156
+     * Checks if the local track supports this effect.
157
+     *
158
+     * @param {JitsiLocalTrack} jitsiLocalTrack - Track to apply effect.
159
+     * @returns {boolean} - Returns true if this effect can run on the specified track
160
+     * false otherwise.
161
+     */
162
+    isEnabled(jitsiLocalTrack) {
163
+        return jitsiLocalTrack.isVideoTrack();
164
+    }
165
+}
166
+
167
+

react/features/stream-effects/TimerWorker.js → react/features/stream-effects/blur/TimerWorker.js Datei anzeigen

@@ -9,7 +9,7 @@
9 9
  *      timeMs: 33
10 10
  * }
11 11
  */
12
-export const SET_INTERVAL = 2;
12
+export const SET_INTERVAL = 1;
13 13
 
14 14
 /**
15 15
  * CLEAR_INTERVAL constant is used to clear the interval and it is set in
@@ -19,7 +19,7 @@ export const SET_INTERVAL = 2;
19 19
  *      id: CLEAR_INTERVAL
20 20
  * }
21 21
  */
22
-export const CLEAR_INTERVAL = 3;
22
+export const CLEAR_INTERVAL = 2;
23 23
 
24 24
 /**
25 25
  * INTERVAL_TIMEOUT constant is used as response and it is set in the id property.
@@ -28,15 +28,15 @@ export const CLEAR_INTERVAL = 3;
28 28
  *      id: INTERVAL_TIMEOUT
29 29
  * }
30 30
  */
31
-export const INTERVAL_TIMEOUT = 22;
31
+export const INTERVAL_TIMEOUT = 3;
32 32
 
33 33
 /**
34 34
  * The following code is needed as string to create a URL from a Blob.
35 35
  * The URL is then passed to a WebWorker. Reason for this is to enable
36 36
  * use of setInterval that is not throttled when tab is inactive.
37 37
  */
38
-const code
39
-= `   let timer = null;
38
+const code = `
39
+    var timer;
40 40
 
41 41
     onmessage = function(request) {
42 42
         switch (request.data.id) {
@@ -47,13 +47,13 @@ const code
47 47
             break;
48 48
         }
49 49
         case ${CLEAR_INTERVAL}: {
50
-            clearInterval(timer);
50
+            if (timer) {
51
+                clearInterval(timer);
52
+            }
51 53
             break;
52 54
         }
53 55
         }
54 56
     };
55 57
 `;
56 58
 
57
-const blob = new Blob([ code ], { type: 'application/javascript' });
58
-
59
-export const timerWorkerScript = URL.createObjectURL(blob);
59
+export const timerWorkerScript = URL.createObjectURL(new Blob([ code ], { type: 'application/javascript' }));

+ 25
- 0
react/features/stream-effects/blur/index.js Datei anzeigen

@@ -0,0 +1,25 @@
1
+// @flow
2
+
3
+import { load } from '@tensorflow-models/body-pix';
4
+
5
+import JitsiStreamBlurEffect from './JitsiStreamBlurEffect';
6
+
7
+/**
8
+ * This promise represents the loading of the BodyPix model that is used
9
+ * to extract person segmentation. A multiplier of 0.25 is used to for
10
+ * improved performance on a larger range of CPUs.
11
+ */
12
+const bpModelPromise = load(0.25);
13
+
14
+/**
15
+ * Creates a new instance of JitsiStreamBlurEffect.
16
+ *
17
+ * @returns {Promise<JitsiStreamBlurEffect>}
18
+ */
19
+export function createBlurEffect() {
20
+    if (!MediaStreamTrack.prototype.getSettings && !MediaStreamTrack.prototype.getConstraints) {
21
+        return Promise.reject(new Error('JitsiStreamBlurEffect not supported!'));
22
+    }
23
+
24
+    return bpModelPromise.then(bpmodel => new JitsiStreamBlurEffect(bpmodel));
25
+}

+ 1
- 3
react/features/toolbox/components/web/Toolbox.js Datei anzeigen

@@ -18,9 +18,7 @@ import {
18 18
 import { connect } from '../../../base/redux';
19 19
 import { OverflowMenuItem } from '../../../base/toolbox';
20 20
 import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
21
-import {
22
-    VideoBlurButton
23
-} from '../../../blur';
21
+import { VideoBlurButton } from '../../../blur';
24 22
 import { ChatCounter, toggleChat } from '../../../chat';
25 23
 import { toggleDocument } from '../../../etherpad';
26 24
 import { openFeedbackDialog } from '../../../feedback';

+ 1
- 2
webpack.config.js Datei anzeigen

@@ -153,8 +153,7 @@ module.exports = [
153 153
     }),
154 154
     Object.assign({}, config, {
155 155
         entry: {
156
-            'video-blur-effect':
157
-                './react/features/stream-effects/JitsiStreamBlurEffect.js'
156
+            'video-blur-effect': './react/features/stream-effects/blur/index.js'
158 157
         },
159 158
         output: Object.assign({}, config.output, {
160 159
             library: [ 'JitsiMeetJS', 'app', 'effects' ],

Laden…
Abbrechen
Speichern