This is an automated email from the ASF dual-hosted git repository.

solomax pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openmeetings.git


The following commit(s) were added to refs/heads/master by this push:
     new 2d3e407  [OPENMEETINGS-2000] moving JS code to npm
2d3e407 is described below

commit 2d3e4079795a736893fe1a06fdecc73a0eacc74c
Author: Maxim Solodovnik <solomax...@gmail.com>
AuthorDate: Thu Dec 24 13:41:16 2020 +0700

    [OPENMEETINGS-2000] moving JS code to npm
---
 .../src/main/assembly/components/templates.xml     |   2 -
 openmeetings-web/pom.xml                           |  47 +-
 openmeetings-web/src/main/front/room/src/volume.js | 121 +++++
 .../src/main/front/settings/package.json           |  22 +
 .../src/main/front/settings/src/index.js           |  15 +
 .../src/main/front/settings/src/mic-level.js       |  91 ++++
 .../src/main/front/settings/src/ring-buffer.js     |  18 +
 .../src/main/front/settings/src/settings.js        | 497 +++++++++++++++++
 .../src/main/front/settings/src/video-util.js      | 333 ++++++++++++
 .../apache/openmeetings/web/room/raw-settings.js   | 605 ---------------------
 .../apache/openmeetings/web/room/raw-video-util.js | 445 ---------------
 .../org/apache/openmeetings/web/room/raw-video.js  |   2 +-
 pom.xml                                            |   2 +-
 13 files changed, 1117 insertions(+), 1083 deletions(-)

diff --git a/openmeetings-server/src/main/assembly/components/templates.xml 
b/openmeetings-server/src/main/assembly/components/templates.xml
index 7e7ba2d..43f059c 100644
--- a/openmeetings-server/src/main/assembly/components/templates.xml
+++ b/openmeetings-server/src/main/assembly/components/templates.xml
@@ -35,8 +35,6 @@
                                <exclude>**/raw-*.js</exclude>
                                <exclude>**/fabric.js</exclude>
                                <exclude>**/MathJax*.js</exclude>
-                               <exclude>**/adapter-latest.js</exclude>
-                               <exclude>**/kurento-utils.js</exclude>
                                <exclude>**/NoSleep.js</exclude>
                        </excludes>
                </fileSet>
diff --git a/openmeetings-web/pom.xml b/openmeetings-web/pom.xml
index c128ca1..50b654b 100644
--- a/openmeetings-web/pom.xml
+++ b/openmeetings-web/pom.xml
@@ -102,6 +102,24 @@
                                                        </environmentVariables>
                                                </configuration>
                                        </execution>
+                                       <execution>
+                                               <id>settings-install</id>
+                                               <goals><goal>npm</goal></goals>
+                                               <configuration>
+                                                       
<workingDirectory>src/main/front/main</workingDirectory>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>settings</id>
+                                               <goals><goal>npm</goal></goals>
+                                               <configuration>
+                                                       <arguments>run 
build</arguments>
+                                                       
<workingDirectory>src/main/front/main</workingDirectory>
+                                                       <environmentVariables>
+                                                               
<outDir>${project.build.directory}/generated-sources/js/org/apache/openmeetings/web/room/</outDir>
+                                                       </environmentVariables>
+                                               </configuration>
+                                       </execution>
                                </executions>
                        </plugin>
                        <plugin>
@@ -221,25 +239,6 @@
                                                </configuration>
                                        </execution>
                                        <execution>
-                                               <id>settings-js</id>
-                                               <goals>
-                                                       <goal>minify</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <charset>UTF-8</charset>
-                                                       
<jsSourceDir>../java/org/apache/openmeetings/web/room</jsSourceDir>
-                                                       <jsSourceFiles>
-                                                               
<jsSourceFile>raw-video-util.js</jsSourceFile>
-                                                               
<jsSourceFile>raw-settings.js</jsSourceFile>
-                                                               
<jsSourceFile>adapter-latest.js</jsSourceFile>
-                                                               
<jsSourceFile>kurento-utils.js</jsSourceFile>
-                                                       </jsSourceFiles>
-                                                       
<jsFinalFile>settings.js</jsFinalFile>
-                                                       
<jsEngine>CLOSURE</jsEngine>
-                                                       
<jsTargetDir>../generated-sources/js/org/apache/openmeetings/web/room</jsTargetDir>
-                                               </configuration>
-                                       </execution>
-                                       <execution>
                                                <id>nettest-js</id>
                                                <goals>
                                                        <goal>minify</goal>
@@ -285,8 +284,6 @@
                                                **/raw-*.js,
                                                **/fabric.js,
                                                **/MathJax*.js,
-                                               **/adapter-latest.js,
-                                               **/kurento-utils.js,
                                                **/NoSleep.js
                                        </packagingExcludes>
                                        <warSourceExcludes>
@@ -294,8 +291,6 @@
                                                **/raw-*.js,
                                                **/fabric.js,
                                                **/MathJax*.js,
-                                               **/adapter-latest.js,
-                                               **/kurento-utils.js,
                                                **/NoSleep.js
                                        </warSourceExcludes>
                                        
<filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
@@ -314,8 +309,6 @@
                                                                
<exclude>**/raw-*.js</exclude>
                                                                
<exclude>**/fabric.js</exclude>
                                                                
<exclude>**/MathJax*.js</exclude>
-                                                               
<exclude>**/adapter-latest.js</exclude>
-                                                               
<exclude>**/kurento-utils.js</exclude>
                                                                
<exclude>**/NoSleep.js</exclude>
                                                        </excludes>
                                                </webResource>
@@ -430,8 +423,6 @@
                                                                                
<exclude>**/raw-*.js</exclude>
                                                                                
<exclude>**/fabric.js</exclude>
                                                                                
<exclude>**/MathJax*.js</exclude>
-                                                                               
<exclude>**/adapter-latest.js</exclude>
-                                                                               
<exclude>**/kurento-utils.js</exclude>
                                                                                
<exclude>**/network.js</exclude>
                                                                        
</excludes>
                                                                        
<filtering>true</filtering>
@@ -463,8 +454,6 @@
                                                                                
<exclude>**/raw-*.js</exclude>
                                                                                
<exclude>**/fabric.js</exclude>
                                                                                
<exclude>**/MathJax*.js</exclude>
-                                                                               
<exclude>**/adapter-latest.js</exclude>
-                                                                               
<exclude>**/kurento-utils.js</exclude>
                                                                                
<exclude>**/network.js</exclude>
                                                                        
</excludes>
                                                                </resource>
diff --git a/openmeetings-web/src/main/front/room/src/volume.js 
b/openmeetings-web/src/main/front/room/src/volume.js
new file mode 100644
index 0000000..65fa822
--- /dev/null
+++ b/openmeetings-web/src/main/front/room/src/volume.js
@@ -0,0 +1,121 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+var Volume = (function() {
+       let video, vol, drop, slider, handleEl, hideTimer = null
+               , lastVolume = 50, muted = false;
+
+       function __cancelHide() {
+               if (hideTimer) {
+                       clearTimeout(hideTimer);
+                       hideTimer = null;
+               }
+       }
+       function __hideDrop() {
+               __cancelHide();
+               hideTimer = setTimeout(() => {
+                       drop.hide();
+                       hideTimer = null;
+               }, 3000);
+       }
+
+       function _create(_video) {
+               video = _video;
+               _destroy();
+               const uid = video.stream().uid
+                       , cuid = video.stream().cuid
+                       , volId = 'volume-' + uid;
+               vol = OmUtil.tmpl('#volume-control-stub', volId)
+               slider = vol.find('.slider');
+               drop = vol.find('.dropdown-menu');
+               vol.on('mouseenter', function(e) {
+                               e.stopImmediatePropagation();
+                               drop.show();
+                               __hideDrop()
+                       })
+                       .click(function(e) {
+                               e.stopImmediatePropagation();
+                               OmUtil.roomAction({action: 'mute', uid: cuid, 
mute: !muted});
+                               _mute(!muted);
+                               drop.hide();
+                               return false;
+                       }).dblclick(function(e) {
+                               e.stopImmediatePropagation();
+                               return false;
+                       });
+               drop.on('mouseenter', function() {
+                       __cancelHide();
+               });
+               drop.on('mouseleave', function() {
+                       __hideDrop();
+               });
+               handleEl = vol.find('.handle');
+               slider.slider({
+                       orientation: 'vertical'
+                       , range: 'min'
+                       , min: 0
+                       , max: 100
+                       , value: lastVolume
+                       , create: function() {
+                               handleEl.text($(this).slider('value'));
+                       }
+                       , slide: function(event, ui) {
+                               _handle(ui.value);
+                       }
+               });
+               _handle(lastVolume);
+               _mute(muted);
+               return vol;
+       }
+       function _handle(val) {
+               handleEl.text(val);
+               const vidEl = video.video()
+                       , data = vidEl.data();
+               if (video.stream().self) {
+                       if (data.gainNode) {
+                               data.gainNode.gain.value = val / 100;
+                       }
+               } else {
+                       vidEl[0].volume = val / 100;
+               }
+               const ico = vol.find('a');
+               if (val > 0 && ico.hasClass('volume-off')) {
+                       ico.toggleClass('volume-off volume-on');
+                       video.handleMicStatus(true);
+               } else if (val === 0 && ico.hasClass('volume-on')) {
+                       ico.toggleClass('volume-on volume-off');
+                       video.handleMicStatus(false);
+               }
+       }
+       function _mute(mute) {
+               if (!slider) {
+                       return;
+               }
+               muted = mute;
+               if (mute) {
+                       const val = slider.slider('option', 'value');
+                       if (val > 0) {
+                               lastVolume = val;
+                       }
+                       slider.slider('option', 'value', 0);
+                       _handle(0);
+               } else {
+                       slider.slider('option', 'value', lastVolume);
+                       _handle(lastVolume);
+               }
+       }
+       function _destroy() {
+               if (vol) {
+                       vol.remove();
+                       vol = null;
+               }
+       }
+
+       return {
+               create: _create
+               , handle: _handle
+               , mute: _mute
+               , muted: function() {
+                       return muted;
+               }
+               , destroy: _destroy
+       };
+});
diff --git a/openmeetings-web/src/main/front/settings/package.json 
b/openmeetings-web/src/main/front/settings/package.json
new file mode 100644
index 0000000..44150d8
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/package.json
@@ -0,0 +1,22 @@
+{
+  "name": "settings",
+  "version": "1.0.0",
+  "description": "Video Utilities and video Settings dialog",
+  "main": "src/index.js",
+  "scripts": {
+    "build-dev": "browserify src/index.js --transform-key=staging -o 
${outDir}${npm_package_name}.js",
+    "build-prod": "browserify src/index.js --transform-key=production -p 
tinyify -o ${outDir}${npm_package_name}.min.js",
+    "build": "npm run build-dev && npm run build-prod"
+  },
+  "author": "",
+  "license": "Apache-2.0",
+  "rat-license": "Licensed under the Apache License, Version 2.0 (the 
\"License\") http://www.apache.org/licenses/LICENSE-2.0";,
+  "devDependencies": {
+    "browserify": "^17.0.0",
+    "tinyify": "^3.0.0"
+  },
+  "dependencies": {
+    "adapterjs": "^0.15.5",
+    "kurento-utils": "^6.15.0"
+  }
+}
diff --git a/openmeetings-web/src/main/front/settings/src/index.js 
b/openmeetings-web/src/main/front/settings/src/index.js
new file mode 100644
index 0000000..a0f9250
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/src/index.js
@@ -0,0 +1,15 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const VideoUtil = require('./video-util');
+
+if (window.hasOwnProperty('isSecureContext') === false) {
+       window.isSecureContext = window.location.protocol == 'https:' || 
["localhost", "127.0.0.1"].indexOf(window.location.hostname) !== -1;
+}
+
+Object.assign(window, {
+       VideoUtil: VideoUtil
+       , VIDWIN_SEL: VideoUtil.VIDWIN_SEL
+       , VID_SEL: VideoUtil.VID_SEL
+       , MicLevel: require('./mic-level')
+       , kurentoUtils: require('kurento-utils')
+       , uuidv4: require('uuid/v4')
+});
diff --git a/openmeetings-web/src/main/front/settings/src/mic-level.js 
b/openmeetings-web/src/main/front/settings/src/mic-level.js
new file mode 100644
index 0000000..32668f6
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/src/mic-level.js
@@ -0,0 +1,91 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const RingBuffer = require('./ring-buffer');
+
+module.exports = class MicLevel {
+       constructor() {
+               let ctx, mic, analyser, vol = .0, vals = new RingBuffer(100);
+
+               this.meterPeer = (rtcPeer, cnvs, _micActivity, _error, 
connectAudio) => {
+                       if (!rtcPeer || ('function' !== 
typeof(rtcPeer.getLocalStream) && 'function' !== 
typeof(rtcPeer.getRemoteStream))) {
+                               return;
+                       }
+                       const stream = rtcPeer.getLocalStream() || 
rtcPeer.getRemoteStream();
+                       if (!stream || stream.getAudioTracks().length < 1) {
+                               return;
+                       }
+                       try {
+                               const AudioCtx = window.AudioContext || 
window.webkitAudioContext;
+                               if (!AudioCtx) {
+                                       _error("AudioContext is inaccessible");
+                                       return;
+                               }
+                               ctx = new AudioCtx();
+                               analyser = ctx.createAnalyser();
+                               mic = ctx.createMediaStreamSource(stream);
+                               mic.connect(analyser);
+                               if (connectAudio) {
+                                       analyser.connect(ctx.destination);
+                               }
+                               this.meter(analyser, cnvs, _micActivity, 
_error);
+                       } catch (err) {
+                               _error(err);
+                       }
+               };
+               this.meter = (_analyser, cnvs, _micActivity, _error) => {
+                       try {
+                               analyser = _analyser;
+                               analyser.minDecibels = -90;
+                               analyser.maxDecibels = -10;
+                               analyser.fftSize = 256;
+                               const canvas = cnvs[0]
+                                       , color = $('body').css('--level-color')
+                                       , canvasCtx = canvas.getContext('2d')
+                                       , al = analyser.frequencyBinCount
+                                       , arr = new Uint8Array(al)
+                                       , horiz = cnvs.data('orientation') === 
'horizontal';
+                               function update() {
+                                       const WIDTH = canvas.width
+                                               , HEIGHT = canvas.height;
+                                       canvasCtx.clearRect(0, 0, WIDTH, 
HEIGHT);
+                                       if (!!analyser && cnvs.length > 0) {
+                                               if (cnvs.is(':visible')) {
+                                                       
analyser.getByteFrequencyData(arr);
+                                                       let favg = 0.0;
+                                                       for (let i = 0; i < al; 
++i) {
+                                                               favg += arr[i] 
* arr[i];
+                                                       }
+                                                       vol = Math.sqrt(favg / 
al);
+                                                       vals.push(vol);
+                                                       const min = vals.min();
+                                                       _micActivity(vol > min 
+ 5); // magic number
+                                                       canvasCtx.fillStyle = 
color;
+                                                       if (horiz) {
+                                                               
canvasCtx.fillRect(0, 0, WIDTH * vol / 100, HEIGHT);
+                                                       } else {
+                                                               const h = 
HEIGHT * vol / 100;
+                                                               
canvasCtx.fillRect(0, HEIGHT - h, WIDTH, h);
+                                                       }
+                                               }
+                                               requestAnimationFrame(update);
+                                       }
+                               }
+                               update();
+                       } catch (err) {
+                               _error(err);
+                       }
+               };
+               this.dispose = () => {
+                       if (!!ctx) {
+                               VideoUtil.cleanStream(mic.mediaStream);
+                               VideoUtil.disconnect(mic);
+                               VideoUtil.disconnect(ctx.destination);
+                               ctx.close();
+                               ctx = null;
+                       }
+                       if (!!analyser) {
+                               VideoUtil.disconnect(analyser);
+                               analyser = null;
+                       }
+               };
+       }
+};
diff --git a/openmeetings-web/src/main/front/settings/src/ring-buffer.js 
b/openmeetings-web/src/main/front/settings/src/ring-buffer.js
new file mode 100644
index 0000000..5c10717
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/src/ring-buffer.js
@@ -0,0 +1,18 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+module.exports = class RingBuffer {
+       constructor(length) {
+               const buffer = [];
+               let pos = 0;
+
+               this.get = (key) => {
+                       return buffer[key];
+               };
+               this.push = (item) => {
+                       buffer[pos] = item;
+                       pos = (pos + 1) % length;
+               };
+               this.min = () => {
+                       return Math.min.apply(Math, buffer);
+               }
+       }
+};
diff --git a/openmeetings-web/src/main/front/settings/src/settings.js 
b/openmeetings-web/src/main/front/settings/src/settings.js
new file mode 100644
index 0000000..391ca00
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/src/settings.js
@@ -0,0 +1,497 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const MicLevel = require('./mic-level');
+const VideoUtil = require('./video-util');
+const kurentoUtils = require('kurento-utils');
+
+const DEV_AUDIO = 'audioinput', DEV_VIDEO = 'videoinput';
+let vs, lm, s, cam, mic, res, o, rtcPeer, timer
+       , vidScroll, vid, recBtn, playBtn, recAllowed = false
+       , level;
+const MsgBase = {type: 'kurento', mode: 'test'};
+function _load() {
+       s = Settings.load();
+       if (!s.video) {
+               const _res = $('#video-settings .cam-resolution 
option:selected').data();
+               s.video = {
+                       cam: 0
+                       , mic: 0
+                       , width: _res.width
+                       , height: _res.height
+               };
+       }
+       return s;
+}
+function _save() {
+       Settings.save(s);
+       OmUtil.sendMessage({
+               type: 'av'
+               , area: 'room'
+               , settings: s
+       });
+}
+function _clear(_ms) {
+       const ms = _ms || (vid && vid.length === 1 ? vid[0].srcObject : null);
+       VideoUtil.cleanStream(ms);
+       if (vid && vid.length === 1) {
+               vid[0].srcObject = null;
+       }
+       VideoUtil.cleanPeer(rtcPeer);
+       if (!!lm) {
+               lm.hide();
+       }
+       if (!!level) {
+               level.dispose();
+               level = null;
+       }
+}
+function _close() {
+       _clear();
+       Wicket.Event.unsubscribe('/websocket/message', _onWsMessage);
+}
+function _onIceCandidate(candidate) {
+       OmUtil.log('Local candidate' + JSON.stringify(candidate));
+       OmUtil.sendMessage({
+               id : 'iceCandidate'
+               , candidate: candidate
+       }, MsgBase);
+}
+function _init(options) {
+       o = JSON.parse(JSON.stringify(options));
+       if (!!o.infoMsg) {
+               OmUtil.alert('info', o.infoMsg, 0);
+       }
+       vs = $('#video-settings');
+       lm = vs.find('.level-meter');
+       cam = vs.find('select.cam').change(function() {
+               _readValues();
+       });
+       mic = vs.find('select.mic').change(function() {
+               _readValues();
+       });
+       res = vs.find('select.cam-resolution').change(function() {
+               _readValues();
+       });
+       vidScroll = vs.find('.vid-block .video-conainer');
+       timer = vs.find('.timer');
+       vid = vidScroll.find('video');
+       recBtn = vs.find('.rec-start')
+               .click(function() {
+                       recBtn.prop('disabled', true);
+                       _setEnabled(true);
+                       OmUtil.sendMessage({
+                               id : 'wannaRecord'
+                       }, MsgBase);
+               });
+       playBtn = vs.find('.play')
+               .click(function() {
+                       recBtn.prop('disabled', true);
+                       _setEnabled(true);
+                       OmUtil.sendMessage({
+                               id : 'wannaPlay'
+                       }, MsgBase);
+               });
+       vs.find('.btn-save').off().click(function() {
+               _save();
+               _close();
+               vs.modal("hide");
+       });
+       vs.find('.btn-cancel').off().click(function() {
+               _close();
+               vs.modal("hide");
+       });
+       vs.off().on('hidden.bs.modal', function () {
+               _close();
+       });
+       o.width = 300;
+       o.height = 200;
+       o.mode = 'settings';
+       o.rights = (o.rights || []).join();
+       delete o.keycode;
+       vs.find('.modal-body input, .modal-body button').prop('disabled', true);
+       const rr = vs.find('.cam-resolution').parents('.sett-row');
+       if (!o.interview) {
+               rr.show();
+       } else {
+               rr.hide();
+       }
+       _load();
+       _save(); // trigger settings update
+}
+function _updateRec() {
+       recBtn.prop('disabled', !recAllowed || (s.video.cam < 0 && s.video.mic 
< 0));
+}
+function _setCntsDimensions(cnts) {
+       const b = VideoUtil.browser;
+       if (b.name === 'Safari') {
+               let width = s.video.width;
+               //valid widths are 320, 640, 1280
+               [320, 640, 1280].some(function(w) {
+                       if (width < w + 1) {
+                               width = w;
+                               return true;
+                       }
+                       return false;
+               });
+               cnts.video.width = width < 1281 ? width : 1280;
+       } else {
+               cnts.video.width = o.interview ? 320 : s.video.width;
+               cnts.video.height = o.interview ? 260 : s.video.height;
+       }
+}
+//each bool OR 
https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
+// min/ideal/max/exact/mandatory can also be used
+function _constraints(sd, callback) {
+       _getDevConstraints(function(devCnts){
+               const cnts = {};
+               if (devCnts.video && false === o.audioOnly && 
VideoUtil.hasCam(sd) && s.video.cam > -1) {
+                       cnts.video = {
+                               frameRate: o.camera.fps
+                       };
+                       _setCntsDimensions(cnts)
+                       if (!!s.video.camDevice) {
+                               cnts.video.deviceId = {
+                                       ideal: s.video.camDevice
+                               };
+                       } else {
+                               cnts.video.facingMode = {
+                                       ideal: 'user'
+                               }
+                       }
+               } else {
+                       cnts.video = false;
+               }
+               if (devCnts.audio && VideoUtil.hasMic(sd) && s.video.mic > -1) {
+                       cnts.audio = {
+                               sampleRate: o.microphone.rate
+                               , echoCancellation: o.microphone.echo
+                               , noiseSuppression: o.microphone.noise
+                       };
+                       if (!!s.video.micDevice) {
+                               cnts.audio.deviceId = {
+                                       ideal: s.video.micDevice
+                               };
+                       }
+               } else {
+                       cnts.audio = false;
+               }
+               callback(cnts);
+       });
+}
+function _readValues(msg, func) {
+       const v = cam.find('option:selected')
+               , m = mic.find('option:selected')
+               , o = res.find('option:selected').data();
+       s.video.cam = 1 * cam.val();
+       s.video.camDevice = v.data('device-id');
+       s.video.mic = 1 * mic.val();
+       s.video.micDevice = m.data('device-id');
+       s.video.width = o.width;
+       s.video.height = o.height;
+       vid.width(o.width).height(o.height);
+       vidScroll.scrollLeft(Math.max(0, s.video.width / 2 - 150))
+               .scrollTop(Math.max(0, s.video.height / 2 - 110));
+       _clear();
+       _constraints(null, function(cnts) {
+               if (cnts.video !== false || cnts.audio !== false) {
+                       const options = VideoUtil.addIceServers({
+                               localVideo: vid[0]
+                               , mediaConstraints: cnts
+                       }, msg);
+                       rtcPeer = new 
kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(
+                               options
+                               , function(error) {
+                                       if (error) {
+                                               if (true === rtcPeer.cleaned) {
+                                                       return;
+                                               }
+                                               return OmUtil.error(error);
+                                       }
+                                       if (cnts.audio) {
+                                               lm.show();
+                                               level = new MicLevel();
+                                               level.meterPeer(rtcPeer, lm, 
function(){}, OmUtil.error, false);
+                                       } else {
+                                               lm.hide();
+                                       }
+                                       rtcPeer.generateOffer(function(error, 
_offerSdp) {
+                                               if (error) {
+                                                       if (true === 
rtcPeer.cleaned) {
+                                                               return;
+                                                       }
+                                                       return 
OmUtil.error('Error generating the offer');
+                                               }
+                                               if (typeof(func) === 
'function') {
+                                                       func(_offerSdp, cnts);
+                                               } else {
+                                                       _allowRec(true);
+                                               }
+                                       });
+                               });
+               }
+               if (!msg) {
+                       _updateRec();
+               }
+       });
+}
+
+function _allowRec(allow) {
+       recAllowed = allow;
+       _updateRec();
+}
+function _setLoading(el) {
+       el.find('option').remove();
+       el.append(OmUtil.tmpl('#settings-option-loading'));
+}
+function _setDisabled(els) {
+       els.forEach(function(el) {
+               el.find('option').remove();
+               el.append(OmUtil.tmpl('#settings-option-disabled'));
+       });
+}
+function _setSelectedDevice(dev, devIdx) {
+       let o = dev.find('option[value="' + devIdx + '"]');
+       if (o.length === 0 && devIdx !== -1) {
+               o = dev.find('option[value="0"]');
+       }
+       o.prop('selected', true);
+}
+function _getDevConstraints(callback) {
+       const devCnts = {audio: false, video: false, devices: []};
+       if (window.isSecureContext === false) {
+               OmUtil.error($('#settings-https-required').text());
+               return;
+       }
+       if (!navigator.mediaDevices || 
!navigator.mediaDevices.enumerateDevices) {
+               OmUtil.error('enumerateDevices() not supported.');
+               return;
+       }
+       navigator.mediaDevices.enumerateDevices()
+               .then(devices => devices.forEach(device => {
+                               if (DEV_AUDIO === device.kind || DEV_VIDEO === 
device.kind) {
+                                       devCnts.devices.push({
+                                               kind: device.kind
+                                               , label: device.label || 
(device.kind + ' ' + devCnts.devices.length)
+                                               , deviceId: device.deviceId
+                                       });
+                               }
+                               if (DEV_AUDIO === device.kind) {
+                                       devCnts.audio = true;
+                               } else if (DEV_VIDEO === device.kind) {
+                                       devCnts.video = true;
+                               }
+                       }))
+               .catch(() => OmUtil.error('Unable to get the list of multimedia 
devices'))
+               .finally(() => callback(devCnts));
+}
+function _initDevices() {
+       if (window.isSecureContext === false) {
+               OmUtil.error($('#settings-https-required').text());
+               return;
+       }
+       if (!navigator.mediaDevices || 
!navigator.mediaDevices.enumerateDevices) {
+               OmUtil.error('enumerateDevices() not supported.');
+               return;
+       }
+       _setLoading(cam);
+       _setLoading(mic);
+       _getDevConstraints(function(devCnts) {
+               if (!devCnts.audio && !devCnts.video) {
+                       _setDisabled([cam, mic]);
+                       return;
+               }
+               navigator.mediaDevices.getUserMedia(devCnts)
+                       .then(stream => {
+                               const devices = 
navigator.mediaDevices.enumerateDevices()
+                                       .catch(function(err) {
+                                               throw err;
+                                       })
+                                       .finally(() => _clear(stream));
+                               return devices || devCnts.devices;
+                       })
+                       .catch(function() {
+                               return devCnts.devices;
+                       })
+                       .then(devices => {
+                               let cCount = 0, mCount = 0;
+                               _load();
+                               _setDisabled([cam, mic]);
+                               devices.forEach(device => {
+                                       if (DEV_AUDIO === device.kind) {
+                                               const o = 
$('<option></option>').attr('value', mCount).text(device.label)
+                                                       .data('device-id', 
device.deviceId);
+                                               mic.append(o);
+                                               mCount++;
+                                       } else if (DEV_VIDEO === device.kind) {
+                                               const o = 
$('<option></option>').attr('value', cCount).text(device.label)
+                                                       .data('device-id', 
device.deviceId);
+                                               cam.append(o);
+                                               cCount++;
+                                       }
+                               });
+                               _setSelectedDevice(cam, s.video.cam);
+                               _setSelectedDevice(mic, s.video.mic);
+                               res.find('option').each(function() {
+                                       const o = $(this).data();
+                                       if (o.width === s.video.width && 
o.height === s.video.height) {
+                                               $(this).prop('selected', true);
+                                               return false;
+                                       }
+                               });
+                               _readValues();
+                       })
+                       .catch(function(err) {
+                               _setDisabled([cam, mic]);
+                               OmUtil.error(err);
+                       });
+       });
+}
+function _open() {
+       Wicket.Event.subscribe('/websocket/message', _onWsMessage);
+       recAllowed = false;
+       timer.hide();
+       playBtn.prop('disabled', true);
+       vs.modal('show');
+       _load();
+       _initDevices();
+}
+function _setEnabled(enabled) {
+       playBtn.prop('disabled', enabled);
+       cam.prop('disabled', enabled);
+       mic.prop('disabled', enabled);
+       res.prop('disabled', enabled);
+}
+function _onStop() {
+       _updateRec();
+       _setEnabled(false);
+}
+function _onKMessage(m) {
+       OmUtil.info('Received message: ', m);
+       switch (m.id) {
+               case 'canRecord':
+                       _readValues(m, function(_offerSdp, cnts) {
+                               OmUtil.info('Invoking SDP offer callback 
function');
+                               OmUtil.sendMessage({
+                                       id : 'record'
+                                       , sdpOffer: _offerSdp
+                                       , video: cnts.video !== false
+                                       , audio: cnts.audio !== false
+                               }, MsgBase);
+                               rtcPeer.on('icecandidate', _onIceCandidate);
+                       });
+                       break;
+               case 'canPlay':
+                       {
+                               const options = VideoUtil.addIceServers({
+                                       remoteVideo: vid[0]
+                                       , mediaConstraints: {audio: true, 
video: true}
+                                       , onicecandidate: _onIceCandidate
+                               }, m);
+                               _clear();
+                               rtcPeer = new 
kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(
+                                       options
+                                       , function(error) {
+                                               if (error) {
+                                                       if (true === 
rtcPeer.cleaned) {
+                                                               return;
+                                                       }
+                                                       return 
OmUtil.error(error);
+                                               }
+                                               
rtcPeer.generateOffer(function(error, offerSdp) {
+                                                       if (error) {
+                                                               if (true === 
rtcPeer.cleaned) {
+                                                                       return;
+                                                               }
+                                                               return 
OmUtil.error('Error generating the offer');
+                                                       }
+                                                       OmUtil.sendMessage({
+                                                               id : 'play'
+                                                               , sdpOffer: 
offerSdp
+                                                       }, MsgBase);
+                                               });
+                                       });
+                               }
+                       break;
+               case 'playResponse':
+                       OmUtil.log('Play SDP answer received from server. 
Processing ...');
+                       rtcPeer.processAnswer(m.sdpAnswer, function(error) {
+                               if (error) {
+                                       if (true === rtcPeer.cleaned) {
+                                               return;
+                                       }
+                                       return OmUtil.error(error);
+                               }
+                               lm.show();
+                               level = new MicLevel();
+                               level.meterPeer(rtcPeer, lm, function(){}, 
OmUtil.error, true);
+                       });
+                       break;
+               case 'startResponse':
+                       OmUtil.log('SDP answer received from server. Processing 
...');
+                       rtcPeer.processAnswer(m.sdpAnswer, function(error) {
+                               if (error) {
+                                       if (true === rtcPeer.cleaned) {
+                                               return;
+                                       }
+                                       return OmUtil.error(error);
+                               }
+                       });
+                       break;
+               case 'iceCandidate':
+                       rtcPeer.addIceCandidate(m.candidate, function(error) {
+                               if (error) {
+                                       if (true === rtcPeer.cleaned) {
+                                               return;
+                                       }
+                                       return OmUtil.error('Error adding 
candidate: ' + error);
+                               }
+                       });
+                       break;
+               case 'recording':
+                       timer.show().find('.time').text(m.time);
+                       break;
+               case 'recStopped':
+                       timer.hide();
+                       _onStop();
+                       break;
+               case 'playStopped':
+                       _onStop();
+                       _readValues();
+                       break;
+               default:
+                       // no-op
+       }
+}
+function _onWsMessage(jqEvent, msg) {
+       try {
+               if (msg instanceof Blob) {
+                       return; //ping
+               }
+               const m = JSON.parse(msg);
+               if (m && 'kurento' === m.type) {
+                       if ('test' === m.mode) {
+                               _onKMessage(m);
+                       }
+                       switch (m.id) {
+                               case 'error':
+                                       OmUtil.error(m.message);
+                                       break;
+                               default:
+                                       //no-op
+                       }
+               }
+       } catch (err) {
+               OmUtil.error(err);
+       }
+}
+
+module.exports = {
+       init: _init
+       , open: _open
+       , close: function() {
+               _close();
+               vs && vs.modal('hide');
+       }
+       , load: _load
+       , save: _save
+       , constraints: _constraints
+};
diff --git a/openmeetings-web/src/main/front/settings/src/video-util.js 
b/openmeetings-web/src/main/front/settings/src/video-util.js
new file mode 100644
index 0000000..c2f983a
--- /dev/null
+++ b/openmeetings-web/src/main/front/settings/src/video-util.js
@@ -0,0 +1,333 @@
+/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
+const WB_AREA_SEL = '.room-block .wb-block';
+const WBA_WB_SEL = '.room-block .wb-block .wb-tab-content';
+const VIDWIN_SEL = '.video.user-video';
+const VID_SEL = '.video-container[id!=user-video]';
+const CAM_ACTIVITY = 'VIDEO';
+const MIC_ACTIVITY = 'AUDIO';
+const SCREEN_ACTIVITY = 'SCREEN';
+const REC_ACTIVITY = 'RECORD';
+
+const UAParser = require('ua-parser-js')
+       , ua = (typeof window !== 'undefined' && window.navigator) ? 
window.navigator.userAgent : ''
+       , parser = new UAParser(ua)
+       , browser = parser.getBrowser();
+
+function _getVid(uid) {
+       return 'video' + uid;
+}
+function _isSharing(sd) {
+       return !!sd && 'SCREEN' === sd.type && 
sd.activities.includes(SCREEN_ACTIVITY);
+}
+function _isRecording(sd) {
+       return !!sd && 'SCREEN' === sd.type && 
sd.activities.includes(REC_ACTIVITY);
+}
+function _hasMic(sd) {
+       return !sd || sd.activities.includes(MIC_ACTIVITY);
+}
+function _hasCam(sd) {
+       return !sd || sd.activities.includes(CAM_ACTIVITY);
+}
+function _hasVideo(sd) {
+       return _hasCam(sd) || _isSharing(sd) || _isRecording(sd);
+}
+function _getRects(sel, excl) {
+       const list = [], elems = $(sel);
+       for (let i = 0; i < elems.length; ++i) {
+               if (excl !== $(elems[i]).attr('aria-describedby')) {
+                       list.push(_getRect(elems[i]));
+               }
+       }
+       return list;
+}
+function _getRect(e) {
+       const win = $(e), winoff = win.offset();
+       return {left: winoff.left
+               , top: winoff.top
+               , right: winoff.left + win.width()
+               , bottom: winoff.top + win.height()};
+}
+function _container() {
+       const a = $(WB_AREA_SEL);
+       const c = a.find('.wb-area .tabs .wb-tab-content');
+       return c.length > 0 ? $(WBA_WB_SEL) : a;
+}
+function __processTopToBottom(area, rectNew, list) {
+       const offsetX = 20
+               , offsetY = 10;
+
+       let minY = area.bottom, posFound;
+       do {
+               posFound = true;
+               for (let i = 0; i < list.length; ++i) {
+                       const rect = list[i];
+                       minY = Math.min(minY, rect.bottom);
+
+                       if (rectNew.left < rect.right && rectNew.right > 
rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
+                               rectNew.left = rect.right + offsetX;
+                               posFound = false;
+                       }
+                       if (rectNew.right >= area.right) {
+                               rectNew.left = area.left;
+                               rectNew.top = Math.max(minY, rectNew.top) + 
offsetY;
+                               posFound = false;
+                       }
+                       if (rectNew.bottom >= area.bottom) {
+                               rectNew.top = area.top;
+                               posFound = true;
+                               break;
+                       }
+               }
+       } while (!posFound);
+       return {left: rectNew.left, top: rectNew.top};
+}
+function __processEqualsBottomToTop(area, rectNew, list) {
+       const offsetX = 20
+               , offsetY = 10;
+
+       rectNew.bottom = area.bottom;
+       let minY = area.bottom, posFound;
+       do {
+               posFound = true;
+               for (let i = 0; i < list.length; ++i) {
+                       const rect = list[i];
+                       minY = Math.min(minY, rect.top);
+
+                       if (rectNew.left < rect.right && rectNew.right > 
rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
+                               rectNew.left = rect.right + offsetX;
+                               posFound = false;
+                       }
+                       if (rectNew.right >= area.right) {
+                               rectNew.left = area.left;
+                               rectNew.bottom = Math.min(minY, rectNew.top) - 
offsetY;
+                               posFound = false;
+                       }
+                       if (rectNew.top <= area.top) {
+                               rectNew.top = area.top;
+                               posFound = true;
+                               break;
+                       }
+               }
+       } while (!posFound);
+       return {left: rectNew.left, top: rectNew.top};
+}
+function _getPos(list, w, h, _processor) {
+       if (Room.getOptions().interview) {
+               return {left: 0, top: 0};
+       }
+       const wba = _container()
+               , woffset = wba.offset()
+               , area = {left: woffset.left, top: woffset.top, right: 
woffset.left + wba.width(), bottom: woffset.top + wba.height()}
+               , rectNew = {
+                       _left: area.left
+                       , _top: area.top
+                       , _right: area.left + w
+                       , _bottom: area.top + h
+                       , get left() {
+                               return this._left;
+                       }
+                       , set left(l) {
+                               this._left = l;
+                               this._right = l + w;
+                       }
+                       , get right() {
+                               return this._right;
+                       }
+                       , get top() {
+                               return this._top;
+                       }
+                       , set top(t) {
+                               this._top = t;
+                               this._bottom = t + h;
+                       }
+                       , set bottom(b) {
+                               this._bottom = b;
+                               this._top = b - h;
+                       }
+                       , get bottom() {
+                               return this._bottom;
+                       }
+               };
+       const processor = _processor || __processTopToBottom;
+       return processor(area, rectNew, list);
+}
+function _arrange() {
+       const list = [];
+       $(VIDWIN_SEL).each(function() {
+               const v = $(this);
+               v.css(_getPos(list, v.width(), v.height()));
+               list.push(_getRect(v));
+       });
+}
+function _arrangeResize() {
+       const list = [];
+       function __getDialog(_v) {
+               return $(_v).find('.video-container.ui-dialog-content');
+       }
+       $(VIDWIN_SEL).toArray().sort((v1, v2) => {
+               const c1 = __getDialog(v1).data().stream()
+                       , c2 = __getDialog(v2).data().stream();
+               return c2.level - c1.level || 
c1.user.displayName.localeCompare(c2.user.displayName);
+       }).forEach(_v => {
+               const v = $(_v);
+               __getDialog(v)
+                       .dialog('option', 'width', 120)
+                       .dialog('option', 'height', 90);
+               v.css(_getPos(list, v.width(), v.height(), 
__processEqualsBottomToTop));
+               list.push(_getRect(v));
+       });
+}
+function _cleanStream(stream) {
+       if (!!stream) {
+               stream.getTracks().forEach(track => track.stop());
+       }
+}
+function _cleanPeer(peer) {
+       if (!!peer) {
+               peer.cleaned = true;
+               try {
+                       const pc = peer.peerConnection;
+                       if (!!pc) {
+                               pc.getSenders().forEach(sender => {
+                                       try {
+                                               if (sender.track) {
+                                                       sender.track.stop();
+                                               }
+                                       } catch(e) {
+                                               OmUtil.log('Failed to clean 
sender' + e);
+                                       }
+                               });
+                               pc.getReceivers().forEach(receiver => {
+                                       try {
+                                               if (receiver.track) {
+                                                       receiver.track.stop();
+                                               }
+                                       } catch(e) {
+                                               OmUtil.log('Failed to clean 
receiver' + e);
+                                       }
+                               });
+                               pc.onconnectionstatechange = null;
+                               pc.ontrack = null;
+                               pc.onremovetrack = null;
+                               pc.onremovestream = null;
+                               pc.onicecandidate = null;
+                               pc.oniceconnectionstatechange = null;
+                               pc.onsignalingstatechange = null;
+                               pc.onicegatheringstatechange = null;
+                               pc.onnegotiationneeded = null;
+                       }
+                       peer.dispose();
+                       peer.removeAllListeners('icecandidate');
+                       delete peer.generateOffer;
+                       delete peer.processAnswer;
+                       delete peer.processOffer;
+                       delete peer.addIceCandidate;
+               } catch(e) {
+                       //no-op
+               }
+       }
+}
+function _isChrome(_b) {
+       const b = _b || browser;
+       return b.name === 'Chrome' || b.name === 'Chromium';
+}
+function _isEdge(_b) {
+       const b = _b || browser;
+       return b.name === 'Edge' && "MSGestureEvent" in window;
+}
+function _isEdgeChromium(_b) {
+       const b = _b || browser;
+       return b.name === 'Edge' && !("MSGestureEvent" in window);
+}
+function _setPos(v, pos) {
+       if (v.dialog('instance')) {
+               v.dialog('widget').css(pos);
+       }
+}
+function _askPermission(callback) {
+       const perm = $('#ask-permission');
+       if (undefined === perm.dialog('instance')) {
+               perm.data('callbacks', []).dialog({
+                       appendTo: '.room-block .room-container'
+                       , dialogClass: "ask-video-play-permission"
+                       , autoOpen: true
+                       , buttons: [
+                               {
+                                       text: perm.data('btn-ok')
+                                       , click: function() {
+                                               while 
(perm.data('callbacks').length > 0) {
+                                                       
perm.data('callbacks').pop()();
+                                               }
+                                               $(this).dialog('close');
+                                       }
+                               }
+                       ]
+               });
+       } else if (!perm.dialog('isOpen')) {
+               perm.dialog('open')
+       }
+       perm.data('callbacks').push(callback);
+}
+function _disconnect(node) {
+       try {
+               node.disconnect(); //this one can throw
+       } catch (e) {
+               //no-op
+       }
+}
+function _sharingSupported() {
+       const b = browser;
+       return (b.name === 'Edge' && b.major > 16)
+               || (b.name === 'Firefox')
+               || (b.name === 'Opera')
+               || (b.name === 'Yandex')
+               || _isChrome(b)
+               || _isEdgeChromium(b)
+               || (b.name === 'Mozilla' && b.major > 4);
+}
+function _highlight(el, clazz, count) {
+       if (!el || el.length < 1 || el.hasClass('disabled') || count < 0) {
+               return;
+       }
+       el.addClass(clazz).delay(2000).queue(function(next) {
+               el.removeClass(clazz).delay(2000).queue(function(next1) {
+                       _highlight(el, clazz, --count);
+                       next1();
+               });
+               next();
+       });
+}
+
+module.exports = {
+       VIDWIN_SEL: VIDWIN_SEL
+       , VID_SEL: VID_SEL
+
+       , browser: browser
+       , getVid: _getVid
+       , isSharing: _isSharing
+       , isRecording: _isRecording
+       , hasMic: _hasMic
+       , hasCam: _hasCam
+       , hasVideo: _hasVideo
+       , getRects: _getRects
+       , getPos: _getPos
+       , container: _container
+       , arrange: _arrange
+       , arrangeResize: _arrangeResize
+       , cleanStream: _cleanStream
+       , cleanPeer: _cleanPeer
+       , addIceServers: function(opts, m) {
+               if (m && m.iceServers && m.iceServers.length > 0) {
+                       opts.configuration = {iceServers: m.iceServers};
+               }
+               return opts;
+       }
+       , isEdge: _isEdge
+       , isEdgeChromium: _isEdgeChromium
+       , isChrome: _isChrome
+       , setPos: _setPos
+       , askPermission: _askPermission
+       , disconnect: _disconnect
+       , sharingSupported: _sharingSupported
+       , highlight: _highlight
+};
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-settings.js
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-settings.js
deleted file mode 100644
index 1019f4f..0000000
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-settings.js
+++ /dev/null
@@ -1,605 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-if (window.hasOwnProperty('isSecureContext') === false) {
-       window.isSecureContext = window.location.protocol == 'https:' || 
["localhost", "127.0.0.1"].indexOf(window.location.hostname) !== -1;
-}
-var RingBuffer = (function(length) {
-       const buffer = [];
-       let pos = 0;
-
-       return {
-               get: function(key){
-                       return buffer[key];
-               }
-               , push: function(item) {
-                       buffer[pos] = item;
-                       pos = (pos + 1) % length;
-               }
-               , min: function(){
-                       return Math.min.apply(Math, buffer);
-               }
-       };
-});
-var MicLevel = (function() {
-       let ctx, mic, analyser, vol = .0, vals = RingBuffer(100);
-
-       function _meterPeer(rtcPeer, cnvs, _micActivity, _error, connectAudio) {
-               if (!rtcPeer || ('function' !== typeof(rtcPeer.getLocalStream) 
&& 'function' !== typeof(rtcPeer.getRemoteStream))) {
-                       return;
-               }
-               const stream = rtcPeer.getLocalStream() || 
rtcPeer.getRemoteStream();
-               if (!stream || stream.getAudioTracks().length < 1) {
-                       return;
-               }
-               try {
-                       const AudioCtx = window.AudioContext || 
window.webkitAudioContext;
-                       if (!AudioCtx) {
-                               _error("AudioContext is inaccessible");
-                               return;
-                       }
-                       ctx = new AudioCtx();
-                       analyser = ctx.createAnalyser();
-                       mic = ctx.createMediaStreamSource(stream);
-                       mic.connect(analyser);
-                       if (connectAudio) {
-                               analyser.connect(ctx.destination);
-                       }
-                       _meter(analyser, cnvs, _micActivity, _error);
-               } catch (err) {
-                       _error(err);
-               }
-       }
-       function _meter(_analyser, cnvs, _micActivity, _error) {
-               try {
-                       analyser = _analyser;
-                       analyser.minDecibels = -90;
-                       analyser.maxDecibels = -10;
-                       analyser.fftSize = 256;
-                       const canvas = cnvs[0]
-                               , color = $('body').css('--level-color')
-                               , canvasCtx = canvas.getContext('2d')
-                               , al = analyser.frequencyBinCount
-                               , arr = new Uint8Array(al)
-                               , horiz = cnvs.data('orientation') === 
'horizontal';
-                       function update() {
-                               const WIDTH = canvas.width
-                                       , HEIGHT = canvas.height;
-                               canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
-                               if (!!analyser && cnvs.length > 0) {
-                                       if (cnvs.is(':visible')) {
-                                               
analyser.getByteFrequencyData(arr);
-                                               let favg = 0.0;
-                                               for (let i = 0; i < al; ++i) {
-                                                       favg += arr[i] * arr[i];
-                                               }
-                                               vol = Math.sqrt(favg / al);
-                                               vals.push(vol);
-                                               const min = vals.min();
-                                               _micActivity(vol > min + 5); // 
magic number
-                                               canvasCtx.fillStyle = color;
-                                               if (horiz) {
-                                                       canvasCtx.fillRect(0, 
0, WIDTH * vol / 100, HEIGHT);
-                                               } else {
-                                                       const h = HEIGHT * vol 
/ 100;
-                                                       canvasCtx.fillRect(0, 
HEIGHT - h, WIDTH, h);
-                                               }
-                                       }
-                                       requestAnimationFrame(update);
-                               }
-                       }
-                       update();
-               } catch (err) {
-                       _error(err);
-               }
-       }
-       function _dispose() {
-               if (!!ctx) {
-                       VideoUtil.cleanStream(mic.mediaStream);
-                       VideoUtil.disconnect(mic);
-                       VideoUtil.disconnect(ctx.destination);
-                       ctx.close();
-                       ctx = null;
-               }
-               if (!!analyser) {
-                       VideoUtil.disconnect(analyser);
-                       analyser = null;
-               }
-       }
-       return {
-               meter: _meter
-               , meterPeer: _meterPeer
-               , dispose: _dispose
-       };
-});
-var VideoSettings = (function() {
-       const DEV_AUDIO = 'audioinput', DEV_VIDEO = 'videoinput';
-       let vs, lm, s, cam, mic, res, o, rtcPeer, timer
-               , vidScroll, vid, recBtn, playBtn, recAllowed = false
-               , level;
-       const MsgBase = {type: 'kurento', mode: 'test'};
-       function _load() {
-               s = Settings.load();
-               if (!s.video) {
-                       const _res = $('#video-settings .cam-resolution 
option:selected').data();
-                       s.video = {
-                               cam: 0
-                               , mic: 0
-                               , width: _res.width
-                               , height: _res.height
-                       };
-               }
-               return s;
-       }
-       function _save() {
-               Settings.save(s);
-               OmUtil.sendMessage({
-                       type: 'av'
-                       , area: 'room'
-                       , settings: s
-               });
-       }
-       function _clear(_ms) {
-               const ms = _ms || (vid && vid.length === 1 ? vid[0].srcObject : 
null);
-               VideoUtil.cleanStream(ms);
-               if (vid && vid.length === 1) {
-                       vid[0].srcObject = null;
-               }
-               VideoUtil.cleanPeer(rtcPeer);
-               if (!!lm) {
-                       lm.hide();
-               }
-               if (!!level) {
-                       level.dispose();
-                       level = null;
-               }
-       }
-       function _close() {
-               _clear();
-               Wicket.Event.unsubscribe('/websocket/message', _onWsMessage);
-       }
-       function _onIceCandidate(candidate) {
-               OmUtil.log('Local candidate' + JSON.stringify(candidate));
-               OmUtil.sendMessage({
-                       id : 'iceCandidate'
-                       , candidate: candidate
-               }, MsgBase);
-       }
-       function _init(options) {
-               o = JSON.parse(JSON.stringify(options));
-               if (!!o.infoMsg) {
-                       OmUtil.alert('info', o.infoMsg, 0);
-               }
-               vs = $('#video-settings');
-               lm = vs.find('.level-meter');
-               cam = vs.find('select.cam').change(function() {
-                       _readValues();
-               });
-               mic = vs.find('select.mic').change(function() {
-                       _readValues();
-               });
-               res = vs.find('select.cam-resolution').change(function() {
-                       _readValues();
-               });
-               vidScroll = vs.find('.vid-block .video-conainer');
-               timer = vs.find('.timer');
-               vid = vidScroll.find('video');
-               recBtn = vs.find('.rec-start')
-                       .click(function() {
-                               recBtn.prop('disabled', true);
-                               _setEnabled(true);
-                               OmUtil.sendMessage({
-                                       id : 'wannaRecord'
-                               }, MsgBase);
-                       });
-               playBtn = vs.find('.play')
-                       .click(function() {
-                               recBtn.prop('disabled', true);
-                               _setEnabled(true);
-                               OmUtil.sendMessage({
-                                       id : 'wannaPlay'
-                               }, MsgBase);
-                       });
-               vs.find('.btn-save').off().click(function() {
-                       _save();
-                       _close();
-                       vs.modal("hide");
-               });
-               vs.find('.btn-cancel').off().click(function() {
-                       _close();
-                       vs.modal("hide");
-               });
-               vs.off().on('hidden.bs.modal', function () {
-                       _close();
-               });
-               o.width = 300;
-               o.height = 200;
-               o.mode = 'settings';
-               o.rights = (o.rights || []).join();
-               delete o.keycode;
-               vs.find('.modal-body input, .modal-body 
button').prop('disabled', true);
-               const rr = vs.find('.cam-resolution').parents('.sett-row');
-               if (!o.interview) {
-                       rr.show();
-               } else {
-                       rr.hide();
-               }
-               _load();
-               _save(); // trigger settings update
-       }
-       function _updateRec() {
-               recBtn.prop('disabled', !recAllowed || (s.video.cam < 0 && 
s.video.mic < 0));
-       }
-       function _setCntsDimensions(cnts) {
-               const b = kurentoUtils.WebRtcPeer.browser;
-               if (b.name === 'Safari') {
-                       let width = s.video.width;
-                       //valid widths are 320, 640, 1280
-                       [320, 640, 1280].some(function(w) {
-                               if (width < w + 1) {
-                                       width = w;
-                                       return true;
-                               }
-                               return false;
-                       });
-                       cnts.video.width = width < 1281 ? width : 1280;
-               } else {
-                       cnts.video.width = o.interview ? 320 : s.video.width;
-                       cnts.video.height = o.interview ? 260 : s.video.height;
-               }
-       }
-       //each bool OR 
https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
-       // min/ideal/max/exact/mandatory can also be used
-       function _constraints(sd, callback) {
-               _getDevConstraints(function(devCnts){
-                       const cnts = {};
-                       if (devCnts.video && false === o.audioOnly && 
VideoUtil.hasCam(sd) && s.video.cam > -1) {
-                               cnts.video = {
-                                       frameRate: o.camera.fps
-                               };
-                               _setCntsDimensions(cnts)
-                               if (!!s.video.camDevice) {
-                                       cnts.video.deviceId = {
-                                               ideal: s.video.camDevice
-                                       };
-                               } else {
-                                       cnts.video.facingMode = {
-                                               ideal: 'user'
-                                       }
-                               }
-                       } else {
-                               cnts.video = false;
-                       }
-                       if (devCnts.audio && VideoUtil.hasMic(sd) && 
s.video.mic > -1) {
-                               cnts.audio = {
-                                       sampleRate: o.microphone.rate
-                                       , echoCancellation: o.microphone.echo
-                                       , noiseSuppression: o.microphone.noise
-                               };
-                               if (!!s.video.micDevice) {
-                                       cnts.audio.deviceId = {
-                                               ideal: s.video.micDevice
-                                       };
-                               }
-                       } else {
-                               cnts.audio = false;
-                       }
-                       callback(cnts);
-               });
-       }
-       function _readValues(msg, func) {
-               const v = cam.find('option:selected')
-                       , m = mic.find('option:selected')
-                       , o = res.find('option:selected').data();
-               s.video.cam = 1 * cam.val();
-               s.video.camDevice = v.data('device-id');
-               s.video.mic = 1 * mic.val();
-               s.video.micDevice = m.data('device-id');
-               s.video.width = o.width;
-               s.video.height = o.height;
-               vid.width(o.width).height(o.height);
-               vidScroll.scrollLeft(Math.max(0, s.video.width / 2 - 150))
-                       .scrollTop(Math.max(0, s.video.height / 2 - 110));
-               _clear();
-               _constraints(null, function(cnts) {
-                       if (cnts.video !== false || cnts.audio !== false) {
-                               const options = VideoUtil.addIceServers({
-                                       localVideo: vid[0]
-                                       , mediaConstraints: cnts
-                               }, msg);
-                               rtcPeer = new 
kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(
-                                       options
-                                       , function(error) {
-                                               if (error) {
-                                                       if (true === 
rtcPeer.cleaned) {
-                                                               return;
-                                                       }
-                                                       return 
OmUtil.error(error);
-                                               }
-                                               if (cnts.audio) {
-                                                       lm.show();
-                                                       level = MicLevel();
-                                                       
level.meterPeer(rtcPeer, lm, function(){}, OmUtil.error, false);
-                                               } else {
-                                                       lm.hide();
-                                               }
-                                               
rtcPeer.generateOffer(function(error, _offerSdp) {
-                                                       if (error) {
-                                                               if (true === 
rtcPeer.cleaned) {
-                                                                       return;
-                                                               }
-                                                               return 
OmUtil.error('Error generating the offer');
-                                                       }
-                                                       if (typeof(func) === 
'function') {
-                                                               func(_offerSdp, 
cnts);
-                                                       } else {
-                                                               _allowRec(true);
-                                                       }
-                                               });
-                                       });
-                       }
-                       if (!msg) {
-                               _updateRec();
-                       }
-               });
-       }
-
-       function _allowRec(allow) {
-               recAllowed = allow;
-               _updateRec();
-       }
-       function _setLoading(el) {
-               el.find('option').remove();
-               el.append(OmUtil.tmpl('#settings-option-loading'));
-       }
-       function _setDisabled(els) {
-               els.forEach(function(el) {
-                       el.find('option').remove();
-                       el.append(OmUtil.tmpl('#settings-option-disabled'));
-               });
-       }
-       function _setSelectedDevice(dev, devIdx) {
-               let o = dev.find('option[value="' + devIdx + '"]');
-               if (o.length === 0 && devIdx !== -1) {
-                       o = dev.find('option[value="0"]');
-               }
-               o.prop('selected', true);
-       }
-       function _getDevConstraints(callback) {
-               const devCnts = {audio: false, video: false, devices: []};
-               if (window.isSecureContext === false) {
-                       OmUtil.error($('#settings-https-required').text());
-                       return;
-               }
-               if (!navigator.mediaDevices || 
!navigator.mediaDevices.enumerateDevices) {
-                       OmUtil.error('enumerateDevices() not supported.');
-                       return;
-               }
-               navigator.mediaDevices.enumerateDevices()
-                       .then(devices => devices.forEach(device => {
-                                       if (DEV_AUDIO === device.kind || 
DEV_VIDEO === device.kind) {
-                                               devCnts.devices.push({
-                                                       kind: device.kind
-                                                       , label: device.label 
|| (device.kind + ' ' + devCnts.devices.length)
-                                                       , deviceId: 
device.deviceId
-                                               });
-                                       }
-                                       if (DEV_AUDIO === device.kind) {
-                                               devCnts.audio = true;
-                                       } else if (DEV_VIDEO === device.kind) {
-                                               devCnts.video = true;
-                                       }
-                               }))
-                       .catch(() => OmUtil.error('Unable to get the list of 
multimedia devices'))
-                       .finally(() => callback(devCnts));
-       }
-       function _initDevices() {
-               if (window.isSecureContext === false) {
-                       OmUtil.error($('#settings-https-required').text());
-                       return;
-               }
-               if (!navigator.mediaDevices || 
!navigator.mediaDevices.enumerateDevices) {
-                       OmUtil.error('enumerateDevices() not supported.');
-                       return;
-               }
-               _setLoading(cam);
-               _setLoading(mic);
-               _getDevConstraints(function(devCnts) {
-                       if (!devCnts.audio && !devCnts.video) {
-                               _setDisabled([cam, mic]);
-                               return;
-                       }
-                       navigator.mediaDevices.getUserMedia(devCnts)
-                               .then(stream => {
-                                       const devices = 
navigator.mediaDevices.enumerateDevices()
-                                               .catch(function(err) {
-                                                       throw err;
-                                               })
-                                               .finally(() => _clear(stream));
-                                       return devices || devCnts.devices;
-                               })
-                               .catch(function() {
-                                       return devCnts.devices;
-                               })
-                               .then(devices => {
-                                       let cCount = 0, mCount = 0;
-                                       _load();
-                                       _setDisabled([cam, mic]);
-                                       devices.forEach(device => {
-                                               if (DEV_AUDIO === device.kind) {
-                                                       const o = 
$('<option></option>').attr('value', mCount).text(device.label)
-                                                               
.data('device-id', device.deviceId);
-                                                       mic.append(o);
-                                                       mCount++;
-                                               } else if (DEV_VIDEO === 
device.kind) {
-                                                       const o = 
$('<option></option>').attr('value', cCount).text(device.label)
-                                                               
.data('device-id', device.deviceId);
-                                                       cam.append(o);
-                                                       cCount++;
-                                               }
-                                       });
-                                       _setSelectedDevice(cam, s.video.cam);
-                                       _setSelectedDevice(mic, s.video.mic);
-                                       res.find('option').each(function() {
-                                               const o = $(this).data();
-                                               if (o.width === s.video.width 
&& o.height === s.video.height) {
-                                                       
$(this).prop('selected', true);
-                                                       return false;
-                                               }
-                                       });
-                                       _readValues();
-                               })
-                               .catch(function(err) {
-                                       _setDisabled([cam, mic]);
-                                       OmUtil.error(err);
-                               });
-               });
-       }
-       function _open() {
-               Wicket.Event.subscribe('/websocket/message', _onWsMessage);
-               recAllowed = false;
-               timer.hide();
-               playBtn.prop('disabled', true);
-               vs.modal('show');
-               _load();
-               _initDevices();
-       }
-       function _setEnabled(enabled) {
-               playBtn.prop('disabled', enabled);
-               cam.prop('disabled', enabled);
-               mic.prop('disabled', enabled);
-               res.prop('disabled', enabled);
-       }
-       function _onStop() {
-               _updateRec();
-               _setEnabled(false);
-       }
-       function _onKMessage(m) {
-               OmUtil.info('Received message: ', m);
-               switch (m.id) {
-                       case 'canRecord':
-                               _readValues(m, function(_offerSdp, cnts) {
-                                       OmUtil.info('Invoking SDP offer 
callback function');
-                                       OmUtil.sendMessage({
-                                               id : 'record'
-                                               , sdpOffer: _offerSdp
-                                               , video: cnts.video !== false
-                                               , audio: cnts.audio !== false
-                                       }, MsgBase);
-                                       rtcPeer.on('icecandidate', 
_onIceCandidate);
-                               });
-                               break;
-                       case 'canPlay':
-                               {
-                                       const options = 
VideoUtil.addIceServers({
-                                               remoteVideo: vid[0]
-                                               , mediaConstraints: {audio: 
true, video: true}
-                                               , onicecandidate: 
_onIceCandidate
-                                       }, m);
-                                       _clear();
-                                       rtcPeer = new 
kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(
-                                               options
-                                               , function(error) {
-                                                       if (error) {
-                                                               if (true === 
rtcPeer.cleaned) {
-                                                                       return;
-                                                               }
-                                                               return 
OmUtil.error(error);
-                                                       }
-                                                       
rtcPeer.generateOffer(function(error, offerSdp) {
-                                                               if (error) {
-                                                                       if 
(true === rtcPeer.cleaned) {
-                                                                               
return;
-                                                                       }
-                                                                       return 
OmUtil.error('Error generating the offer');
-                                                               }
-                                                               
OmUtil.sendMessage({
-                                                                       id : 
'play'
-                                                                       , 
sdpOffer: offerSdp
-                                                               }, MsgBase);
-                                                       });
-                                               });
-                                       }
-                               break;
-                       case 'playResponse':
-                               OmUtil.log('Play SDP answer received from 
server. Processing ...');
-                               rtcPeer.processAnswer(m.sdpAnswer, 
function(error) {
-                                       if (error) {
-                                               if (true === rtcPeer.cleaned) {
-                                                       return;
-                                               }
-                                               return OmUtil.error(error);
-                                       }
-                                       lm.show();
-                                       level = MicLevel();
-                                       level.meterPeer(rtcPeer, lm, 
function(){}, OmUtil.error, true);
-                               });
-                               break;
-                       case 'startResponse':
-                               OmUtil.log('SDP answer received from server. 
Processing ...');
-                               rtcPeer.processAnswer(m.sdpAnswer, 
function(error) {
-                                       if (error) {
-                                               if (true === rtcPeer.cleaned) {
-                                                       return;
-                                               }
-                                               return OmUtil.error(error);
-                                       }
-                               });
-                               break;
-                       case 'iceCandidate':
-                               rtcPeer.addIceCandidate(m.candidate, 
function(error) {
-                                       if (error) {
-                                               if (true === rtcPeer.cleaned) {
-                                                       return;
-                                               }
-                                               return OmUtil.error('Error 
adding candidate: ' + error);
-                                       }
-                               });
-                               break;
-                       case 'recording':
-                               timer.show().find('.time').text(m.time);
-                               break;
-                       case 'recStopped':
-                               timer.hide();
-                               _onStop();
-                               break;
-                       case 'playStopped':
-                               _onStop();
-                               _readValues();
-                               break;
-                       default:
-                               // no-op
-               }
-       }
-       function _onWsMessage(jqEvent, msg) {
-               try {
-                       if (msg instanceof Blob) {
-                               return; //ping
-                       }
-                       const m = JSON.parse(msg);
-                       if (m && 'kurento' === m.type) {
-                               if ('test' === m.mode) {
-                                       _onKMessage(m);
-                               }
-                               switch (m.id) {
-                                       case 'error':
-                                               OmUtil.error(m.message);
-                                               break;
-                                       default:
-                                               //no-op
-                               }
-                       }
-               } catch (err) {
-                       OmUtil.error(err);
-               }
-       }
-       return {
-               init: _init
-               , open: _open
-               , close: function() {
-                       _close();
-                       vs && vs.modal('hide');
-               }
-               , load: _load
-               , save: _save
-               , constraints: _constraints
-       };
-})();
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
deleted file mode 100644
index 524509f..0000000
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video-util.js
+++ /dev/null
@@ -1,445 +0,0 @@
-/* Licensed under the Apache License, Version 2.0 (the "License") 
http://www.apache.org/licenses/LICENSE-2.0 */
-const WB_AREA_SEL = '.room-block .wb-block';
-const WBA_WB_SEL = '.room-block .wb-block .wb-tab-content';
-const VIDWIN_SEL = '.video.user-video';
-const VID_SEL = '.video-container[id!=user-video]';
-const CAM_ACTIVITY = 'VIDEO';
-const MIC_ACTIVITY = 'AUDIO';
-const SCREEN_ACTIVITY = 'SCREEN';
-const REC_ACTIVITY = 'RECORD';
-var VideoUtil = (function() {
-       const self = {};
-       function _getVid(uid) {
-               return 'video' + uid;
-       }
-       function _isSharing(sd) {
-               return !!sd && 'SCREEN' === sd.type && 
sd.activities.includes(SCREEN_ACTIVITY);
-       }
-       function _isRecording(sd) {
-               return !!sd && 'SCREEN' === sd.type && 
sd.activities.includes(REC_ACTIVITY);
-       }
-       function _hasMic(sd) {
-               return !sd || sd.activities.includes(MIC_ACTIVITY);
-       }
-       function _hasCam(sd) {
-               return !sd || sd.activities.includes(CAM_ACTIVITY);
-       }
-       function _hasVideo(sd) {
-               return _hasCam(sd) || _isSharing(sd) || _isRecording(sd);
-       }
-       function _getRects(sel, excl) {
-               const list = [], elems = $(sel);
-               for (let i = 0; i < elems.length; ++i) {
-                       if (excl !== $(elems[i]).attr('aria-describedby')) {
-                               list.push(_getRect(elems[i]));
-                       }
-               }
-               return list;
-       }
-       function _getRect(e) {
-               const win = $(e), winoff = win.offset();
-               return {left: winoff.left
-                       , top: winoff.top
-                       , right: winoff.left + win.width()
-                       , bottom: winoff.top + win.height()};
-       }
-       function _container() {
-               const a = $(WB_AREA_SEL);
-               const c = a.find('.wb-area .tabs .wb-tab-content');
-               return c.length > 0 ? $(WBA_WB_SEL) : a;
-       }
-       function __processTopToBottom(area, rectNew, list) {
-               const offsetX = 20
-                       , offsetY = 10;
-
-               let minY = area.bottom, posFound;
-               do {
-                       posFound = true;
-                       for (let i = 0; i < list.length; ++i) {
-                               const rect = list[i];
-                               minY = Math.min(minY, rect.bottom);
-
-                               if (rectNew.left < rect.right && rectNew.right 
> rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
-                                       rectNew.left = rect.right + offsetX;
-                                       posFound = false;
-                               }
-                               if (rectNew.right >= area.right) {
-                                       rectNew.left = area.left;
-                                       rectNew.top = Math.max(minY, 
rectNew.top) + offsetY;
-                                       posFound = false;
-                               }
-                               if (rectNew.bottom >= area.bottom) {
-                                       rectNew.top = area.top;
-                                       posFound = true;
-                                       break;
-                               }
-                       }
-               } while (!posFound);
-               return {left: rectNew.left, top: rectNew.top};
-       }
-       function __processEqualsBottomToTop(area, rectNew, list) {
-               const offsetX = 20
-                       , offsetY = 10;
-
-               rectNew.bottom = area.bottom;
-               let minY = area.bottom, posFound;
-               do {
-                       posFound = true;
-                       for (let i = 0; i < list.length; ++i) {
-                               const rect = list[i];
-                               minY = Math.min(minY, rect.top);
-
-                               if (rectNew.left < rect.right && rectNew.right 
> rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
-                                       rectNew.left = rect.right + offsetX;
-                                       posFound = false;
-                               }
-                               if (rectNew.right >= area.right) {
-                                       rectNew.left = area.left;
-                                       rectNew.bottom = Math.min(minY, 
rectNew.top) - offsetY;
-                                       posFound = false;
-                               }
-                               if (rectNew.top <= area.top) {
-                                       rectNew.top = area.top;
-                                       posFound = true;
-                                       break;
-                               }
-                       }
-               } while (!posFound);
-               return {left: rectNew.left, top: rectNew.top};
-       }
-       function _getPos(list, w, h, _processor) {
-               if (Room.getOptions().interview) {
-                       return {left: 0, top: 0};
-               }
-               const wba = _container()
-                       , woffset = wba.offset()
-                       , area = {left: woffset.left, top: woffset.top, right: 
woffset.left + wba.width(), bottom: woffset.top + wba.height()}
-                       , rectNew = {
-                               _left: area.left
-                               , _top: area.top
-                               , _right: area.left + w
-                               , _bottom: area.top + h
-                               , get left() {
-                                       return this._left;
-                               }
-                               , set left(l) {
-                                       this._left = l;
-                                       this._right = l + w;
-                               }
-                               , get right() {
-                                       return this._right;
-                               }
-                               , get top() {
-                                       return this._top;
-                               }
-                               , set top(t) {
-                                       this._top = t;
-                                       this._bottom = t + h;
-                               }
-                               , set bottom(b) {
-                                       this._bottom = b;
-                                       this._top = b - h;
-                               }
-                               , get bottom() {
-                                       return this._bottom;
-                               }
-                       };
-               const processor = _processor || __processTopToBottom;
-               return processor(area, rectNew, list);
-       }
-       function _arrange() {
-               const list = [];
-               $(VIDWIN_SEL).each(function() {
-                       const v = $(this);
-                       v.css(_getPos(list, v.width(), v.height()));
-                       list.push(_getRect(v));
-               });
-       }
-       function _arrangeResize() {
-               const list = [];
-               function __getDialog(_v) {
-                       return $(_v).find('.video-container.ui-dialog-content');
-               }
-               $(VIDWIN_SEL).toArray().sort((v1, v2) => {
-                       const c1 = __getDialog(v1).data().stream()
-                               , c2 = __getDialog(v2).data().stream();
-                       return c2.level - c1.level || 
c1.user.displayName.localeCompare(c2.user.displayName);
-               }).forEach(_v => {
-                       const v = $(_v);
-                       __getDialog(v)
-                               .dialog('option', 'width', 120)
-                               .dialog('option', 'height', 90);
-                       v.css(_getPos(list, v.width(), v.height(), 
__processEqualsBottomToTop));
-                       list.push(_getRect(v));
-               });
-       }
-       function _cleanStream(stream) {
-               if (!!stream) {
-                       stream.getTracks().forEach(track => track.stop());
-               }
-       }
-       function _cleanPeer(peer) {
-               if (!!peer) {
-                       peer.cleaned = true;
-                       try {
-                               const pc = peer.peerConnection;
-                               if (!!pc) {
-                                       pc.getSenders().forEach(sender => {
-                                               try {
-                                                       if (sender.track) {
-                                                               
sender.track.stop();
-                                                       }
-                                               } catch(e) {
-                                                       OmUtil.log('Failed to 
clean sender' + e);
-                                               }
-                                       });
-                                       pc.getReceivers().forEach(receiver => {
-                                               try {
-                                                       if (receiver.track) {
-                                                               
receiver.track.stop();
-                                                       }
-                                               } catch(e) {
-                                                       OmUtil.log('Failed to 
clean receiver' + e);
-                                               }
-                                       });
-                                       pc.onconnectionstatechange = null;
-                                       pc.ontrack = null;
-                                       pc.onremovetrack = null;
-                                       pc.onremovestream = null;
-                                       pc.onicecandidate = null;
-                                       pc.oniceconnectionstatechange = null;
-                                       pc.onsignalingstatechange = null;
-                                       pc.onicegatheringstatechange = null;
-                                       pc.onnegotiationneeded = null;
-                               }
-                               peer.dispose();
-                               peer.removeAllListeners('icecandidate');
-                               delete peer.generateOffer;
-                               delete peer.processAnswer;
-                               delete peer.processOffer;
-                               delete peer.addIceCandidate;
-                       } catch(e) {
-                               //no-op
-                       }
-               }
-       }
-       function _isChrome(_b) {
-               const b = _b || kurentoUtils.WebRtcPeer.browser;
-               return b.name === 'Chrome' || b.name === 'Chromium';
-       }
-       function _isEdge(_b) {
-               const b = _b || kurentoUtils.WebRtcPeer.browser;
-               return b.name === 'Edge' && "MSGestureEvent" in window;
-       }
-       function _isEdgeChromium(_b) {
-               const b = _b || kurentoUtils.WebRtcPeer.browser;
-               return b.name === 'Edge' && !("MSGestureEvent" in window);
-       }
-       function _setPos(v, pos) {
-               if (v.dialog('instance')) {
-                       v.dialog('widget').css(pos);
-               }
-       }
-       function _askPermission(callback) {
-               const perm = $('#ask-permission');
-               if (undefined === perm.dialog('instance')) {
-                       perm.data('callbacks', []).dialog({
-                               appendTo: '.room-block .room-container'
-                               , dialogClass: "ask-video-play-permission"
-                               , autoOpen: true
-                               , buttons: [
-                                       {
-                                               text: perm.data('btn-ok')
-                                               , click: function() {
-                                                       while 
(perm.data('callbacks').length > 0) {
-                                                               
perm.data('callbacks').pop()();
-                                                       }
-                                                       $(this).dialog('close');
-                                               }
-                                       }
-                               ]
-                       });
-               } else if (!perm.dialog('isOpen')) {
-                       perm.dialog('open')
-               }
-               perm.data('callbacks').push(callback);
-       }
-       function _disconnect(node) {
-               try {
-                       node.disconnect(); //this one can throw
-               } catch (e) {
-                       //no-op
-               }
-       }
-       function _sharingSupported() {
-               const b = kurentoUtils.WebRtcPeer.browser;
-               return (b.name === 'Edge' && b.major > 16)
-                       || (b.name === 'Firefox')
-                       || (b.name === 'Opera')
-                       || (b.name === 'Yandex')
-                       || _isChrome(b)
-                       || _isEdgeChromium(b)
-                       || (b.name === 'Mozilla' && b.major > 4);
-       }
-       function _highlight(el, clazz, count) {
-               if (!el || el.length < 1 || el.hasClass('disabled') || count < 
0) {
-                       return;
-               }
-               el.addClass(clazz).delay(2000).queue(function(next) {
-                       el.removeClass(clazz).delay(2000).queue(function(next1) 
{
-                               _highlight(el, clazz, --count);
-                               next1();
-                       });
-                       next();
-               });
-       }
-
-       self.getVid = _getVid;
-       self.isSharing = _isSharing;
-       self.isRecording = _isRecording;
-       self.hasMic = _hasMic;
-       self.hasCam = _hasCam;
-       self.hasVideo = _hasVideo;
-       self.getRects = _getRects;
-       self.getPos = _getPos;
-       self.container = _container;
-       self.arrange = _arrange;
-       self.arrangeResize = _arrangeResize;
-       self.cleanStream = _cleanStream;
-       self.cleanPeer = _cleanPeer;
-       self.addIceServers = function(opts, m) {
-               if (m && m.iceServers && m.iceServers.length > 0) {
-                       opts.configuration = {iceServers: m.iceServers};
-               }
-               return opts;
-       };
-       self.isEdge = _isEdge;
-       self.isEdgeChromium = _isEdgeChromium;
-       self.isChrome = _isChrome;
-       self.setPos = _setPos;
-       self.askPermission = _askPermission;
-       self.disconnect = _disconnect;
-       self.sharingSupported = _sharingSupported;
-       self.highlight = _highlight;
-       return self;
-})();
-var Volume = (function() {
-       let video, vol, drop, slider, handleEl, hideTimer = null
-               , lastVolume = 50, muted = false;
-
-       function __cancelHide() {
-               if (hideTimer) {
-                       clearTimeout(hideTimer);
-                       hideTimer = null;
-               }
-       }
-       function __hideDrop() {
-               __cancelHide();
-               hideTimer = setTimeout(() => {
-                       drop.hide();
-                       hideTimer = null;
-               }, 3000);
-       }
-
-       function _create(_video) {
-               video = _video;
-               _destroy();
-               const uid = video.stream().uid
-                       , cuid = video.stream().cuid
-                       , volId = 'volume-' + uid;
-               vol = OmUtil.tmpl('#volume-control-stub', volId)
-               slider = vol.find('.slider');
-               drop = vol.find('.dropdown-menu');
-               vol.on('mouseenter', function(e) {
-                               e.stopImmediatePropagation();
-                               drop.show();
-                               __hideDrop()
-                       })
-                       .click(function(e) {
-                               e.stopImmediatePropagation();
-                               OmUtil.roomAction({action: 'mute', uid: cuid, 
mute: !muted});
-                               _mute(!muted);
-                               drop.hide();
-                               return false;
-                       }).dblclick(function(e) {
-                               e.stopImmediatePropagation();
-                               return false;
-                       });
-               drop.on('mouseenter', function() {
-                       __cancelHide();
-               });
-               drop.on('mouseleave', function() {
-                       __hideDrop();
-               });
-               handleEl = vol.find('.handle');
-               slider.slider({
-                       orientation: 'vertical'
-                       , range: 'min'
-                       , min: 0
-                       , max: 100
-                       , value: lastVolume
-                       , create: function() {
-                               handleEl.text($(this).slider('value'));
-                       }
-                       , slide: function(event, ui) {
-                               _handle(ui.value);
-                       }
-               });
-               _handle(lastVolume);
-               _mute(muted);
-               return vol;
-       }
-       function _handle(val) {
-               handleEl.text(val);
-               const vidEl = video.video()
-                       , data = vidEl.data();
-               if (video.stream().self) {
-                       if (data.gainNode) {
-                               data.gainNode.gain.value = val / 100;
-                       }
-               } else {
-                       vidEl[0].volume = val / 100;
-               }
-               const ico = vol.find('a');
-               if (val > 0 && ico.hasClass('volume-off')) {
-                       ico.toggleClass('volume-off volume-on');
-                       video.handleMicStatus(true);
-               } else if (val === 0 && ico.hasClass('volume-on')) {
-                       ico.toggleClass('volume-on volume-off');
-                       video.handleMicStatus(false);
-               }
-       }
-       function _mute(mute) {
-               if (!slider) {
-                       return;
-               }
-               muted = mute;
-               if (mute) {
-                       const val = slider.slider('option', 'value');
-                       if (val > 0) {
-                               lastVolume = val;
-                       }
-                       slider.slider('option', 'value', 0);
-                       _handle(0);
-               } else {
-                       slider.slider('option', 'value', lastVolume);
-                       _handle(lastVolume);
-               }
-       }
-       function _destroy() {
-               if (vol) {
-                       vol.remove();
-                       vol = null;
-               }
-       }
-
-       return {
-               create: _create
-               , handle: _handle
-               , mute: _mute
-               , muted: function() {
-                       return muted;
-               }
-               , destroy: _destroy
-       };
-});
diff --git 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
index b556209..88abee7 100644
--- 
a/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
+++ 
b/openmeetings-web/src/main/java/org/apache/openmeetings/web/room/raw-video.js
@@ -170,7 +170,7 @@ var Video = (function() {
                                        return OmUtil.error(error);
                                }
                                if (data.analyser) {
-                                       level = MicLevel();
+                                       level = new MicLevel();
                                        level.meter(data.analyser, lm, 
_micActivity, OmUtil.error);
                                }
                                data.rtcPeer.generateOffer(function(genErr, 
offerSdp) {
diff --git a/pom.xml b/pom.xml
index 3ec8417..b6ea9be 100644
--- a/pom.xml
+++ b/pom.xml
@@ -122,7 +122,7 @@
                <jain-sip.version>1.2.307</jain-sip.version><!-- other versions 
are broken! -->
                <jasny-bootstrap.version>3.1.3-2</jasny-bootstrap.version>
                <!--  Exclude all generated code  -->
-               <sonar.exclusions>file:**/generated-sources/**, 
file:**/jquery-ui.css, file:**/fabric.js, file:**/cssemoticons.js, 
file:**/adapter-latest.js, file:**/kurento-utils.js, file:**/NoSleep.js, 
file:**/MathJax.js</sonar.exclusions>
+               <sonar.exclusions>file:**/generated-sources/**, 
file:**/jquery-ui.css, file:**/fabric.js, file:**/cssemoticons.js, 
file:**/NoSleep.js, file:**/MathJax.js</sonar.exclusions>
                <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
                <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
                
<sonar.junit.reportPaths>target/surefire-reports</sonar.junit.reportPaths>

Reply via email to