diff --git a/.eslintignore b/.eslintignore
index 85a31624d..b7f50b49e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -6,6 +6,8 @@ build/*
flow-typed/*
libs/*
+react/features/stream-effects/blur/vendor/*
+
# ESLint will by default ignore its own configuration file. However, there does
# not seem to be a reason why we will want to risk being inconsistent with our
# remaining JavaScript source code.
diff --git a/Makefile b/Makefile
index cd8d857f8..ded315580 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,8 @@ LIBJITSIMEET_DIR = node_modules/lib-jitsi-meet/
LIBFLAC_DIR = node_modules/libflacjs/dist/min/
OLM_DIR = node_modules/olm
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
+TFLITE_WASM = react/features/stream-effects/blur/vendor/tflite
+MEET_MODELS_DIR = react/features/stream-effects/blur/vendor/models/
NODE_SASS = ./node_modules/.bin/sass
NPM = npm
OUTPUT_DIR = .
@@ -26,7 +28,7 @@ clean:
rm -fr $(BUILD_DIR)
.NOTPARALLEL:
-deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local
+deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local
deploy-init:
rm -fr $(DEPLOY_DIR)
@@ -82,6 +84,16 @@ deploy-rnnoise-binary:
$(RNNOISE_WASM_DIR)/rnnoise.wasm \
$(DEPLOY_DIR)
+deploy-tflite:
+ cp \
+ $(TFLITE_WASM)/*.wasm \
+ $(DEPLOY_DIR)
+
+deploy-meet-models:
+ cp \
+ $(MEET_MODELS_DIR)/*.tflite \
+ $(DEPLOY_DIR)
+
deploy-css:
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
$(CLEANCSS) --skip-rebase $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
@@ -91,7 +103,7 @@ deploy-local:
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
.NOTPARALLEL:
-dev: deploy-init deploy-css deploy-rnnoise-binary deploy-lib-jitsi-meet deploy-libflac deploy-olm
+dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm
$(WEBPACK_DEV_SERVER) --detect-circular-deps
source-package:
diff --git a/package-lock.json b/package-lock.json
index f4385ef3d..89b25f048 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15491,11 +15491,6 @@
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
"integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA=="
},
- "stackblur-canvas": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.3.0.tgz",
- "integrity": "sha512-3ZHJv+43D8YttgumssIxkfs3hBXW7XaMS5Ux65fOBhKDYMjbG5hF8Ey8a90RiiJ58aQnAhWbGilPzZ9rkIlWgQ=="
- },
"stacktrace-parser": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.8.tgz",
@@ -17071,6 +17066,11 @@
"loose-envify": "^1.0.0"
}
},
+ "wasm-check": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wasm-check/-/wasm-check-2.0.1.tgz",
+ "integrity": "sha512-5otny2JrfRNKIc+zi1YSOrNxXe47trEQbpY6g/MtHrFwLumKSJyAIobGXH1tlEBezE95eIsmDokBbUZtIZTvvA=="
+ },
"watchpack": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz",
diff --git a/package.json b/package.json
index b35517d2c..5a68fab0d 100644
--- a/package.json
+++ b/package.json
@@ -95,10 +95,10 @@
"redux-thunk": "2.2.0",
"rnnoise-wasm": "github:jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
"rtcstats": "github:jitsi/rtcstats#v6.2.0",
- "stackblur-canvas": "2.3.0",
"styled-components": "3.4.9",
"util": "0.12.1",
"uuid": "3.1.0",
+ "wasm-check": "2.0.1",
"windows-iana": "^3.1.0",
"xmldom": "0.1.27",
"zxcvbn": "4.4.2"
diff --git a/react/features/stream-effects/blur/JitsiStreamBlurEffect.js b/react/features/stream-effects/blur/JitsiStreamBlurEffect.js
index 2d7b65c42..33bae5bc2 100644
--- a/react/features/stream-effects/blur/JitsiStreamBlurEffect.js
+++ b/react/features/stream-effects/blur/JitsiStreamBlurEffect.js
@@ -1,7 +1,4 @@
// @flow
-
-import * as StackBlur from 'stackblur-canvas';
-
import {
CLEAR_TIMEOUT,
TIMEOUT_TICK,
@@ -9,21 +6,27 @@ import {
timerWorkerScript
} from './TimerWorker';
+const segmentationWidth = 256;
+const segmentationHeight = 144;
+const segmentationPixelCount = segmentationWidth * segmentationHeight;
+const blurValue = '25px';
+
/**
* Represents a modified MediaStream that adds blur to video background.
* JitsiStreamBlurEffect does the processing of the original
* video stream.
*/
export default class JitsiStreamBlurEffect {
- _bpModel: Object;
+ _model: Object;
_inputVideoElement: HTMLVideoElement;
- _inputVideoCanvasElement: HTMLCanvasElement;
_onMaskFrameTimer: Function;
_maskFrameTimerWorker: Worker;
- _maskInProgress: boolean;
_outputCanvasElement: HTMLCanvasElement;
+ _outputCanvasCtx: Object;
+ _segmentationMaskCtx: Object;
+ _segmentationMask: Object;
+ _segmentationMaskCanvas: Object;
_renderMask: Function;
- _segmentationData: Object;
isEnabled: Function;
startEffect: Function;
stopEffect: Function;
@@ -35,7 +38,7 @@ export default class JitsiStreamBlurEffect {
* @param {BodyPix} bpModel - BodyPix model.
*/
constructor(bpModel: Object) {
- this._bpModel = bpModel;
+ this._model = bpModel;
// Bind event handler so it is only bound once for every instance.
this._onMaskFrameTimer = this._onMaskFrameTimer.bind(this);
@@ -44,7 +47,6 @@ export default class JitsiStreamBlurEffect {
this._outputCanvasElement = document.createElement('canvas');
this._outputCanvasElement.getContext('2d');
this._inputVideoElement = document.createElement('video');
- this._inputVideoCanvasElement = document.createElement('canvas');
}
/**
@@ -60,62 +62,109 @@ export default class JitsiStreamBlurEffect {
}
}
+ /**
+ * Represents the run post processing.
+ *
+ * @returns {void}
+ */
+ runPostProcessing() {
+ this._outputCanvasCtx.globalCompositeOperation = 'copy';
+
+ // Draw segmentation mask.
+ this._outputCanvasCtx.filter = `blur(${blurValue})`;
+ this._outputCanvasCtx.drawImage(
+ this._segmentationMaskCanvas,
+ 0,
+ 0,
+ segmentationWidth,
+ segmentationHeight,
+ 0,
+ 0,
+ this._inputVideoElement.width,
+ this._inputVideoElement.height
+ );
+
+ this._outputCanvasCtx.globalCompositeOperation = 'source-in';
+ this._outputCanvasCtx.filter = 'none';
+ this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
+
+ this._outputCanvasCtx.globalCompositeOperation = 'destination-over';
+ this._outputCanvasCtx.filter = `blur(${blurValue})`; // FIXME Does not work on Safari.
+ this._outputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
+ }
+
+ /**
+ * Represents the run Tensorflow Interference.
+ *
+ * @returns {void}
+ */
+ runInference() {
+ this._model._runInference();
+ const outputMemoryOffset = this._model._getOutputMemoryOffset() / 4;
+
+ for (let i = 0; i < segmentationPixelCount; i++) {
+ const background = this._model.HEAPF32[outputMemoryOffset + (i * 2)];
+ const person = this._model.HEAPF32[outputMemoryOffset + (i * 2) + 1];
+ const shift = Math.max(background, person);
+ const backgroundExp = Math.exp(background - shift);
+ const personExp = Math.exp(person - shift);
+
+ // Sets only the alpha component of each pixel.
+ this._segmentationMask.data[(i * 4) + 3] = (255 * personExp) / (backgroundExp + personExp);
+ }
+ this._segmentationMaskCtx.putImageData(this._segmentationMask, 0, 0);
+ }
+
/**
* Loop function to render the background mask.
*
* @private
* @returns {void}
*/
- async _renderMask() {
- if (!this._maskInProgress) {
- this._maskInProgress = true;
- this._bpModel.segmentPerson(this._inputVideoElement, {
- internalResolution: 'low', // resized to 0.5 times of the original resolution before inference
- maxDetections: 1, // max. number of person poses to detect per image
- segmentationThreshold: 0.7, // represents probability that a pixel belongs to a person
- flipHorizontal: false,
- scoreThreshold: 0.2
- }).then(data => {
- this._segmentationData = data;
- this._maskInProgress = false;
- });
- }
- const inputCanvasCtx = this._inputVideoCanvasElement.getContext('2d');
+ _renderMask() {
+ this.resizeSource();
+ this.runInference();
+ this.runPostProcessing();
- inputCanvasCtx.drawImage(this._inputVideoElement, 0, 0);
-
- const currentFrame = inputCanvasCtx.getImageData(
- 0,
- 0,
- this._inputVideoCanvasElement.width,
- this._inputVideoCanvasElement.height
- );
-
- if (this._segmentationData) {
- const blurData = new ImageData(currentFrame.data.slice(), currentFrame.width, currentFrame.height);
-
- StackBlur.imageDataRGB(blurData, 0, 0, currentFrame.width, currentFrame.height, 12);
-
- for (let x = 0; x < this._outputCanvasElement.width; x++) {
- for (let y = 0; y < this._outputCanvasElement.height; y++) {
- const n = (y * this._outputCanvasElement.width) + x;
-
- if (this._segmentationData.data[n] === 0) {
- currentFrame.data[n * 4] = blurData.data[n * 4];
- currentFrame.data[(n * 4) + 1] = blurData.data[(n * 4) + 1];
- currentFrame.data[(n * 4) + 2] = blurData.data[(n * 4) + 2];
- currentFrame.data[(n * 4) + 3] = blurData.data[(n * 4) + 3];
- }
- }
- }
- }
- this._outputCanvasElement.getContext('2d').putImageData(currentFrame, 0, 0);
this._maskFrameTimerWorker.postMessage({
id: SET_TIMEOUT,
timeMs: 1000 / 30
});
}
+ /**
+ * Represents the resize source process.
+ *
+ * @returns {void}
+ */
+ resizeSource() {
+ this._segmentationMaskCtx.drawImage(
+ this._inputVideoElement,
+ 0,
+ 0,
+ this._inputVideoElement.width,
+ this._inputVideoElement.height,
+ 0,
+ 0,
+ segmentationWidth,
+ segmentationHeight
+ );
+
+ const imageData = this._segmentationMaskCtx.getImageData(
+ 0,
+ 0,
+ segmentationWidth,
+ segmentationHeight
+ );
+ const inputMemoryOffset = this._model._getInputMemoryOffset() / 4;
+
+ for (let i = 0; i < segmentationPixelCount; i++) {
+ this._model.HEAPF32[inputMemoryOffset + (i * 3)] = imageData.data[i * 4] / 255;
+ this._model.HEAPF32[inputMemoryOffset + (i * 3) + 1] = imageData.data[(i * 4) + 1] / 255;
+ this._model.HEAPF32[inputMemoryOffset + (i * 3) + 2] = imageData.data[(i * 4) + 2] / 255;
+ }
+ }
+
/**
* Checks if the local track supports this effect.
*
@@ -136,15 +185,18 @@ export default class JitsiStreamBlurEffect {
startEffect(stream: MediaStream) {
this._maskFrameTimerWorker = new Worker(timerWorkerScript, { name: 'Blur effect worker' });
this._maskFrameTimerWorker.onmessage = this._onMaskFrameTimer;
-
const firstVideoTrack = stream.getVideoTracks()[0];
const { height, frameRate, width }
= firstVideoTrack.getSettings ? firstVideoTrack.getSettings() : firstVideoTrack.getConstraints();
+ this._segmentationMask = new ImageData(segmentationWidth, segmentationHeight);
+ this._segmentationMaskCanvas = document.createElement('canvas');
+ this._segmentationMaskCanvas.width = segmentationWidth;
+ this._segmentationMaskCanvas.height = segmentationHeight;
+ this._segmentationMaskCtx = this._segmentationMaskCanvas.getContext('2d');
this._outputCanvasElement.width = parseInt(width, 10);
this._outputCanvasElement.height = parseInt(height, 10);
- this._inputVideoCanvasElement.width = parseInt(width, 10);
- this._inputVideoCanvasElement.height = parseInt(height, 10);
+ this._outputCanvasCtx = this._outputCanvasElement.getContext('2d');
this._inputVideoElement.width = parseInt(width, 10);
this._inputVideoElement.height = parseInt(height, 10);
this._inputVideoElement.autoplay = true;
diff --git a/react/features/stream-effects/blur/index.js b/react/features/stream-effects/blur/index.js
index 62a4f0b79..1d219be92 100644
--- a/react/features/stream-effects/blur/index.js
+++ b/react/features/stream-effects/blur/index.js
@@ -1,8 +1,15 @@
// @flow
-import * as bodyPix from '@tensorflow-models/body-pix';
+import * as wasmCheck from 'wasm-check';
import JitsiStreamBlurEffect from './JitsiStreamBlurEffect';
+import createTFLiteModule from './vendor/tflite/tflite';
+import createTFLiteSIMDModule from './vendor/tflite/tflite-simd';
+
+const models = {
+ '96': '/libs/segm_lite_v681.tflite',
+ '144': '/libs/segm_full_v679.tflite'
+};
/**
* Creates a new instance of JitsiStreamBlurEffect. This loads the bodyPix model that is used to
@@ -14,15 +21,24 @@ export async function createBlurEffect() {
if (!MediaStreamTrack.prototype.getSettings && !MediaStreamTrack.prototype.getConstraints) {
throw new Error('JitsiStreamBlurEffect not supported!');
}
+ let tflite;
- // An output stride of 16 and a multiplier of 0.5 are used for improved
- // performance on a larger range of CPUs.
- const bpModel = await bodyPix.load({
- architecture: 'MobileNetV1',
- outputStride: 16,
- multiplier: 0.50,
- quantBytes: 2
- });
+ if (wasmCheck.feature.simd) {
+ tflite = await createTFLiteSIMDModule();
+ } else {
+ tflite = await createTFLiteModule();
+ }
- return new JitsiStreamBlurEffect(bpModel);
+ const modelBufferOffset = tflite._getModelBufferMemoryOffset();
+ const modelResponse = await fetch(
+ models['144']
+ );
+
+ const model = await modelResponse.arrayBuffer();
+
+ tflite.HEAPU8.set(new Uint8Array(model), modelBufferOffset);
+
+ tflite._loadModel(model.byteLength);
+
+ return new JitsiStreamBlurEffect(tflite);
}
diff --git a/react/features/stream-effects/blur/vendor/README.md b/react/features/stream-effects/blur/vendor/README.md
new file mode 100644
index 000000000..1614ed872
--- /dev/null
+++ b/react/features/stream-effects/blur/vendor/README.md
@@ -0,0 +1,24 @@
+# Virtual Background on stream effects
+
+> Inspired from https://ai.googleblog.com/2020/10/background-features-in-google-meet.html and https://github.com/Volcomix/virtual-background.git
+
+#### Canvas 2D + CPU
+
+This rendering pipeline is pretty much the same as for BodyPix. It relies on Canvas compositing properties to blend rendering layers according to the segmentation mask.
+
+Interactions with TFLite inference tool are executed on CPU to convert from UInt8 to Float32 for the model input and to apply softmax on the model output.
+
+The framerate is higher and the quality looks better than BodyPix
+
+#### SIMD and non-SIMD
+
+How to test on SIMD:
+1. Go to chrome://flags/
+2. Search for SIMD flag
+3. Enable WebAssembly SIMD support(Enables support for the WebAssembly SIMD proposal).
+4. Reopen Google Chrome
+
+More details:
+- [WebAssembly](https://webassembly.org/)
+- [WebAssembly SIMD](https://github.com/WebAssembly/simd)
+- [TFLite](https://blog.tensorflow.org/2020/07/accelerating-tensorflow-lite-xnnpack-integration.html)
\ No newline at end of file
diff --git a/react/features/stream-effects/blur/vendor/models/segm_full_v679.tflite b/react/features/stream-effects/blur/vendor/models/segm_full_v679.tflite
new file mode 100644
index 000000000..1f56ae7cd
Binary files /dev/null and b/react/features/stream-effects/blur/vendor/models/segm_full_v679.tflite differ
diff --git a/react/features/stream-effects/blur/vendor/models/segm_lite_v681.tflite b/react/features/stream-effects/blur/vendor/models/segm_lite_v681.tflite
new file mode 100644
index 000000000..593b591b6
Binary files /dev/null and b/react/features/stream-effects/blur/vendor/models/segm_lite_v681.tflite differ
diff --git a/react/features/stream-effects/blur/vendor/tflite/tflite-simd.js b/react/features/stream-effects/blur/vendor/tflite/tflite-simd.js
new file mode 100644
index 000000000..56bb011cb
--- /dev/null
+++ b/react/features/stream-effects/blur/vendor/tflite/tflite-simd.js
@@ -0,0 +1,21 @@
+
+var createTFLiteSIMDModule = (function() {
+ var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
+ if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
+ return (
+function(createTFLiteSIMDModule) {
+ createTFLiteSIMDModule = createTFLiteSIMDModule || {};
+
+var Module=typeof createTFLiteSIMDModule!=="undefined"?createTFLiteSIMDModule:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="tflite-simd.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["q"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["D"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function _abort(){abort()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else _emscripten_get_now=function(){return performance.now()};var _emscripten_get_now_is_monotonic=true;function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if((clk_id===1||clk_id===4)&&_emscripten_get_now_is_monotonic){now=_emscripten_get_now()}else{setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _dlopen(filename,flag){abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking")}function _dlsym(handle,symbol){abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _emscripten_thread_sleep(msecs){var start=_emscripten_get_now();while(_emscripten_get_now()-start>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _exit(status){exit(status)}function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _pthread_create(){return 6}function _pthread_join(){return 28}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:case 80:case 81:case 79:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var asmLibraryArg={"a":_abort,"n":_clock_gettime,"i":_dlopen,"e":_dlsym,"l":_emscripten_memcpy_big,"m":_emscripten_resize_heap,"o":_emscripten_thread_sleep,"p":_environ_get,"g":_environ_sizes_get,"j":_exit,"h":_fd_close,"k":_fd_seek,"c":_fd_write,"d":_pthread_create,"f":_pthread_join,"b":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["r"]).apply(null,arguments)};var _getModelBufferMemoryOffset=Module["_getModelBufferMemoryOffset"]=function(){return(_getModelBufferMemoryOffset=Module["_getModelBufferMemoryOffset"]=Module["asm"]["s"]).apply(null,arguments)};var _getInputMemoryOffset=Module["_getInputMemoryOffset"]=function(){return(_getInputMemoryOffset=Module["_getInputMemoryOffset"]=Module["asm"]["t"]).apply(null,arguments)};var _getInputHeight=Module["_getInputHeight"]=function(){return(_getInputHeight=Module["_getInputHeight"]=Module["asm"]["u"]).apply(null,arguments)};var _getInputWidth=Module["_getInputWidth"]=function(){return(_getInputWidth=Module["_getInputWidth"]=Module["asm"]["v"]).apply(null,arguments)};var _getInputChannelCount=Module["_getInputChannelCount"]=function(){return(_getInputChannelCount=Module["_getInputChannelCount"]=Module["asm"]["w"]).apply(null,arguments)};var _getOutputMemoryOffset=Module["_getOutputMemoryOffset"]=function(){return(_getOutputMemoryOffset=Module["_getOutputMemoryOffset"]=Module["asm"]["x"]).apply(null,arguments)};var _getOutputHeight=Module["_getOutputHeight"]=function(){return(_getOutputHeight=Module["_getOutputHeight"]=Module["asm"]["y"]).apply(null,arguments)};var _getOutputWidth=Module["_getOutputWidth"]=function(){return(_getOutputWidth=Module["_getOutputWidth"]=Module["asm"]["z"]).apply(null,arguments)};var _getOutputChannelCount=Module["_getOutputChannelCount"]=function(){return(_getOutputChannelCount=Module["_getOutputChannelCount"]=Module["asm"]["A"]).apply(null,arguments)};var _loadModel=Module["_loadModel"]=function(){return(_loadModel=Module["_loadModel"]=Module["asm"]["B"]).apply(null,arguments)};var _runInference=Module["_runInference"]=function(){return(_runInference=Module["_runInference"]=Module["asm"]["C"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["E"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&noExitRuntime&&status===0){return}if(noExitRuntime){}else{EXITSTATUS=status;exitRuntime();if(Module["onExit"])Module["onExit"](status);ABORT=true}quit_(status,new ExitStatus(status))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
+
+
+ return createTFLiteSIMDModule.ready
+}
+);
+})();
+if (typeof exports === 'object' && typeof module === 'object')
+ module.exports = createTFLiteSIMDModule;
+else if (typeof define === 'function' && define['amd'])
+ define([], function() { return createTFLiteSIMDModule; });
+else if (typeof exports === 'object')
+ exports["createTFLiteSIMDModule"] = createTFLiteSIMDModule;
diff --git a/react/features/stream-effects/blur/vendor/tflite/tflite-simd.wasm b/react/features/stream-effects/blur/vendor/tflite/tflite-simd.wasm
new file mode 100755
index 000000000..61bee0b9a
Binary files /dev/null and b/react/features/stream-effects/blur/vendor/tflite/tflite-simd.wasm differ
diff --git a/react/features/stream-effects/blur/vendor/tflite/tflite.js b/react/features/stream-effects/blur/vendor/tflite/tflite.js
new file mode 100644
index 000000000..38454c827
--- /dev/null
+++ b/react/features/stream-effects/blur/vendor/tflite/tflite.js
@@ -0,0 +1,21 @@
+
+var createTFLiteModule = (function() {
+ var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
+ if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
+ return (
+function(createTFLiteModule) {
+ createTFLiteModule = createTFLiteModule || {};
+
+var Module=typeof createTFLiteModule!=="undefined"?createTFLiteModule:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="tflite.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["q"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["D"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function _abort(){abort()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else _emscripten_get_now=function(){return performance.now()};var _emscripten_get_now_is_monotonic=true;function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if((clk_id===1||clk_id===4)&&_emscripten_get_now_is_monotonic){now=_emscripten_get_now()}else{setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _dlopen(filename,flag){abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking")}function _dlsym(handle,symbol){abort("To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}function _emscripten_thread_sleep(msecs){var start=_emscripten_get_now();while(_emscripten_get_now()-start>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},get64:function(low,high){return low}};function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _exit(status){exit(status)}function _fd_close(fd){return 0}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){}function _fd_write(fd,iov,iovcnt,pnum){var num=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j>2]=num;return 0}function _pthread_create(){return 6}function _pthread_join(){return 28}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:case 80:case 81:case 79:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var asmLibraryArg={"a":_abort,"n":_clock_gettime,"i":_dlopen,"e":_dlsym,"l":_emscripten_memcpy_big,"m":_emscripten_resize_heap,"o":_emscripten_thread_sleep,"p":_environ_get,"g":_environ_sizes_get,"j":_exit,"h":_fd_close,"k":_fd_seek,"c":_fd_write,"d":_pthread_create,"f":_pthread_join,"b":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["r"]).apply(null,arguments)};var _getModelBufferMemoryOffset=Module["_getModelBufferMemoryOffset"]=function(){return(_getModelBufferMemoryOffset=Module["_getModelBufferMemoryOffset"]=Module["asm"]["s"]).apply(null,arguments)};var _getInputMemoryOffset=Module["_getInputMemoryOffset"]=function(){return(_getInputMemoryOffset=Module["_getInputMemoryOffset"]=Module["asm"]["t"]).apply(null,arguments)};var _getInputHeight=Module["_getInputHeight"]=function(){return(_getInputHeight=Module["_getInputHeight"]=Module["asm"]["u"]).apply(null,arguments)};var _getInputWidth=Module["_getInputWidth"]=function(){return(_getInputWidth=Module["_getInputWidth"]=Module["asm"]["v"]).apply(null,arguments)};var _getInputChannelCount=Module["_getInputChannelCount"]=function(){return(_getInputChannelCount=Module["_getInputChannelCount"]=Module["asm"]["w"]).apply(null,arguments)};var _getOutputMemoryOffset=Module["_getOutputMemoryOffset"]=function(){return(_getOutputMemoryOffset=Module["_getOutputMemoryOffset"]=Module["asm"]["x"]).apply(null,arguments)};var _getOutputHeight=Module["_getOutputHeight"]=function(){return(_getOutputHeight=Module["_getOutputHeight"]=Module["asm"]["y"]).apply(null,arguments)};var _getOutputWidth=Module["_getOutputWidth"]=function(){return(_getOutputWidth=Module["_getOutputWidth"]=Module["asm"]["z"]).apply(null,arguments)};var _getOutputChannelCount=Module["_getOutputChannelCount"]=function(){return(_getOutputChannelCount=Module["_getOutputChannelCount"]=Module["asm"]["A"]).apply(null,arguments)};var _loadModel=Module["_loadModel"]=function(){return(_loadModel=Module["_loadModel"]=Module["asm"]["B"]).apply(null,arguments)};var _runInference=Module["_runInference"]=function(){return(_runInference=Module["_runInference"]=Module["asm"]["C"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["E"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){if(implicit&&noExitRuntime&&status===0){return}if(noExitRuntime){}else{EXITSTATUS=status;exitRuntime();if(Module["onExit"])Module["onExit"](status);ABORT=true}quit_(status,new ExitStatus(status))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run();
+
+
+ return createTFLiteModule.ready
+}
+);
+})();
+if (typeof exports === 'object' && typeof module === 'object')
+ module.exports = createTFLiteModule;
+else if (typeof define === 'function' && define['amd'])
+ define([], function() { return createTFLiteModule; });
+else if (typeof exports === 'object')
+ exports["createTFLiteModule"] = createTFLiteModule;
diff --git a/react/features/stream-effects/blur/vendor/tflite/tflite.wasm b/react/features/stream-effects/blur/vendor/tflite/tflite.wasm
new file mode 100755
index 000000000..8824ba77b
Binary files /dev/null and b/react/features/stream-effects/blur/vendor/tflite/tflite.wasm differ
diff --git a/react/features/toolbox/components/web/Toolbox.js b/react/features/toolbox/components/web/Toolbox.js
index fb6d6ef0c..a8c9bbaa6 100644
--- a/react/features/toolbox/components/web/Toolbox.js
+++ b/react/features/toolbox/components/web/Toolbox.js
@@ -1,6 +1,7 @@
// @flow
import React, { Component } from 'react';
+import * as wasmCheck from 'wasm-check';
import {
ACTION_SHORTCUT_TRIGGERED,
@@ -1069,7 +1070,7 @@ class Toolbox extends Component {
&& ,
+ visible = { !_screensharing && wasmCheck.feature.simd } />,
this._shouldShowButton('settings')
&&