浏览代码

fix(blur-effect) enable blur effect on all platforms supporting canvas filters

That means all browsers except Safari, for now.

In addition, use the 96p model (instead of the 144p one) on browsers without SIMD support.
master
Tudor D. Pop 4 年前
父节点
当前提交
dd1f8339b1
没有帐户链接到提交者的电子邮件

+ 1
- 15
react/features/blur/components/VideoBlurButton.js 查看文件

@@ -1,12 +1,10 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
4
-
5 3
 import { createVideoBlurEvent, sendAnalytics } from '../../analytics';
6 4
 import { translate } from '../../base/i18n';
7 5
 import { IconBlurBackground } from '../../base/icons';
8 6
 import { connect } from '../../base/redux';
9
-import { AbstractButton, BetaTag } from '../../base/toolbox/components';
7
+import { AbstractButton } from '../../base/toolbox/components';
10 8
 import type { AbstractButtonProps } from '../../base/toolbox/components';
11 9
 import { toggleBlurEffect } from '../actions';
12 10
 
@@ -37,18 +35,6 @@ class VideoBlurButton extends AbstractButton<Props, *> {
37 35
     tooltip = 'toolbar.startvideoblur';
38 36
     toggledLabel = 'toolbar.stopvideoblur';
39 37
 
40
-    /**
41
-     * Helper function to be implemented by subclasses, which returns
42
-     * a React Element to display (a beta tag) at the end of the button.
43
-     *
44
-     * @override
45
-     * @protected
46
-     * @returns {ReactElement}
47
-     */
48
-    _getElementAfter() {
49
-        return <BetaTag />;
50
-    }
51
-
52 38
     /**
53 39
      * Handles clicking / pressing the button, and toggles the blur effect
54 40
      * state accordingly.

+ 20
- 0
react/features/blur/functions.js 查看文件

@@ -2,6 +2,8 @@
2 2
 
3 3
 import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
4 4
 
5
+let filterSupport;
6
+
5 7
 /**
6 8
  * Returns promise that resolves with the blur effect instance.
7 9
  *
@@ -16,3 +18,21 @@ export function getBlurEffect() {
16 18
 
17 19
     return loadScript('libs/video-blur-effect.min.js').then(() => ns.effects.createBlurEffect());
18 20
 }
21
+
22
+/**
23
+ * Checks context filter support.
24
+ *
25
+ * @returns {boolean} True if the filter is supported and false if the filter is not supported by the browser.
26
+ */
27
+export function checkBlurSupport() {
28
+    if (typeof filterSupport === 'undefined') {
29
+        const canvas = document.createElement('canvas');
30
+        const ctx = canvas.getContext('2d');
31
+
32
+        filterSupport = typeof ctx.filter !== 'undefined';
33
+
34
+        canvas.remove();
35
+    }
36
+
37
+    return filterSupport;
38
+}

+ 20
- 18
react/features/stream-effects/blur/JitsiStreamBlurEffect.js 查看文件

@@ -6,9 +6,6 @@ import {
6 6
     timerWorkerScript
7 7
 } from './TimerWorker';
8 8
 
9
-const segmentationWidth = 256;
10
-const segmentationHeight = 144;
11
-const segmentationPixelCount = segmentationWidth * segmentationHeight;
12 9
 const blurValue = '25px';
13 10
 
14 11
 /**
@@ -18,6 +15,8 @@ const blurValue = '25px';
18 15
  */
19 16
 export default class JitsiStreamBlurEffect {
20 17
     _model: Object;
18
+    _options: Object;
19
+    _segmentationPixelCount: number;
21 20
     _inputVideoElement: HTMLVideoElement;
22 21
     _onMaskFrameTimer: Function;
23 22
     _maskFrameTimerWorker: Worker;
@@ -35,10 +34,13 @@ export default class JitsiStreamBlurEffect {
35 34
      * Represents a modified video MediaStream track.
36 35
      *
37 36
      * @class
38
-     * @param {BodyPix} bpModel - BodyPix model.
37
+     * @param {Object} model - Meet model.
38
+     * @param {Object} options - Segmentation dimensions.
39 39
      */
40
-    constructor(bpModel: Object) {
41
-        this._model = bpModel;
40
+    constructor(model: Object, options: Object) {
41
+        this._model = model;
42
+        this._options = options;
43
+        this._segmentationPixelCount = this._options.width * this._options.height;
42 44
 
43 45
         // Bind event handler so it is only bound once for every instance.
44 46
         this._onMaskFrameTimer = this._onMaskFrameTimer.bind(this);
@@ -76,8 +78,8 @@ export default class JitsiStreamBlurEffect {
76 78
             this._segmentationMaskCanvas,
77 79
             0,
78 80
             0,
79
-            segmentationWidth,
80
-            segmentationHeight,
81
+            this._options.width,
82
+            this._options.height,
81 83
             0,
82 84
             0,
83 85
             this._inputVideoElement.width,
@@ -89,7 +91,7 @@ export default class JitsiStreamBlurEffect {
89 91
         this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
90 92
 
91 93
         this._outputCanvasCtx.globalCompositeOperation = 'destination-over';
92
-        this._outputCanvasCtx.filter = `blur(${blurValue})`; // FIXME Does not work on Safari.
94
+        this._outputCanvasCtx.filter = `blur(${blurValue})`;
93 95
         this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
94 96
     }
95 97
 
@@ -102,7 +104,7 @@ export default class JitsiStreamBlurEffect {
102 104
         this._model._runInference();
103 105
         const outputMemoryOffset = this._model._getOutputMemoryOffset() / 4;
104 106
 
105
-        for (let i = 0; i < segmentationPixelCount; i++) {
107
+        for (let i = 0; i < this._segmentationPixelCount; i++) {
106 108
             const background = this._model.HEAPF32[outputMemoryOffset + (i * 2)];
107 109
             const person = this._model.HEAPF32[outputMemoryOffset + (i * 2) + 1];
108 110
             const shift = Math.max(background, person);
@@ -146,19 +148,19 @@ export default class JitsiStreamBlurEffect {
146 148
             this._inputVideoElement.height,
147 149
             0,
148 150
             0,
149
-            segmentationWidth,
150
-            segmentationHeight
151
+            this._options.width,
152
+            this._options.height
151 153
         );
152 154
 
153 155
         const imageData = this._segmentationMaskCtx.getImageData(
154 156
             0,
155 157
             0,
156
-            segmentationWidth,
157
-            segmentationHeight
158
+            this._options.width,
159
+            this._options.height
158 160
         );
159 161
         const inputMemoryOffset = this._model._getInputMemoryOffset() / 4;
160 162
 
161
-        for (let i = 0; i < segmentationPixelCount; i++) {
163
+        for (let i = 0; i < this._segmentationPixelCount; i++) {
162 164
             this._model.HEAPF32[inputMemoryOffset + (i * 3)] = imageData.data[i * 4] / 255;
163 165
             this._model.HEAPF32[inputMemoryOffset + (i * 3) + 1] = imageData.data[(i * 4) + 1] / 255;
164 166
             this._model.HEAPF32[inputMemoryOffset + (i * 3) + 2] = imageData.data[(i * 4) + 2] / 255;
@@ -189,10 +191,10 @@ export default class JitsiStreamBlurEffect {
189 191
         const { height, frameRate, width }
190 192
             = firstVideoTrack.getSettings ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints();
191 193
 
192
-        this._segmentationMask = new ImageData(segmentationWidth, segmentationHeight);
194
+        this._segmentationMask = new ImageData(this._options.width, this._options.height);
193 195
         this._segmentationMaskCanvas = document.createElement('canvas');
194
-        this._segmentationMaskCanvas.width = segmentationWidth;
195
-        this._segmentationMaskCanvas.height = segmentationHeight;
196
+        this._segmentationMaskCanvas.width = this._options.width;
197
+        this._segmentationMaskCanvas.height = this._options.height;
196 198
         this._segmentationMaskCtx = this._segmentationMaskCanvas.getContext('2d');
197 199
         this._outputCanvasElement.width = parseInt(width, 10);
198 200
         this._outputCanvasElement.height = parseInt(height, 10);

+ 17
- 4
react/features/stream-effects/blur/index.js 查看文件

@@ -7,8 +7,19 @@ import createTFLiteModule from './vendor/tflite/tflite';
7 7
 import createTFLiteSIMDModule from './vendor/tflite/tflite-simd';
8 8
 
9 9
 const models = {
10
-    '96': 'libs/segm_lite_v681.tflite',
11
-    '144': 'libs/segm_full_v679.tflite'
10
+    'model96': 'libs/segm_lite_v681.tflite',
11
+    'model144': 'libs/segm_full_v679.tflite'
12
+};
13
+
14
+const segmentationDimensions = {
15
+    'model96': {
16
+        'height': 96,
17
+        'width': 160
18
+    },
19
+    'model144': {
20
+        'height': 144,
21
+        'width': 256
22
+    }
12 23
 };
13 24
 
14 25
 /**
@@ -31,7 +42,7 @@ export async function createBlurEffect() {
31 42
 
32 43
     const modelBufferOffset = tflite._getModelBufferMemoryOffset();
33 44
     const modelResponse = await fetch(
34
-        models['144']
45
+        wasmCheck.feature.simd ? models.model144 : models.model96
35 46
     );
36 47
 
37 48
     if (!modelResponse.ok) {
@@ -44,5 +55,7 @@ export async function createBlurEffect() {
44 55
 
45 56
     tflite._loadModel(model.byteLength);
46 57
 
47
-    return new JitsiStreamBlurEffect(tflite);
58
+    const options = wasmCheck.feature.simd ? segmentationDimensions.model144 : segmentationDimensions.model96;
59
+
60
+    return new JitsiStreamBlurEffect(tflite, options);
48 61
 }

+ 2
- 2
react/features/toolbox/components/web/Toolbox.js 查看文件

@@ -1,7 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 import React, { Component } from 'react';
4
-import * as wasmCheck from 'wasm-check';
5 4
 
6 5
 import {
7 6
     ACTION_SHORTCUT_TRIGGERED,
@@ -37,6 +36,7 @@ import { OverflowMenuItem } from '../../../base/toolbox/components';
37 36
 import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
38 37
 import { isVpaasMeeting } from '../../../billing-counter/functions';
39 38
 import { VideoBlurButton } from '../../../blur';
39
+import { checkBlurSupport } from '../../../blur/functions';
40 40
 import { CHAT_SIZE, ChatCounter, toggleChat } from '../../../chat';
41 41
 import { EmbedMeetingDialog } from '../../../embed-meeting';
42 42
 import { SharedDocumentButton } from '../../../etherpad';
@@ -1071,7 +1071,7 @@ class Toolbox extends Component<Props, State> {
1071 1071
                 && <VideoBlurButton
1072 1072
                     key = 'videobackgroundblur'
1073 1073
                     showLabel = { true }
1074
-                    visible = { !_screensharing && wasmCheck.feature.simd } />,
1074
+                    visible = { !_screensharing && checkBlurSupport() } />,
1075 1075
             this._shouldShowButton('settings')
1076 1076
                 && <SettingsButton
1077 1077
                     key = 'settings'

正在加载...
取消
保存