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 <[email protected]>
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>