diff --git a/build/Makefile.am b/build/Makefile.am index 61ac412d42..936ab56843 100644 --- a/build/Makefile.am +++ b/build/Makefile.am @@ -1,5 +1,6 @@ MK=`echo $(MAKE) | $(AWK) '{printf "%5s\n", $$0}' ` + all: @echo " +---------- FreeSWITCH Build Complete ----------+" @echo " + FreeSWITCH has been successfully built. +" @@ -54,6 +55,13 @@ install: @echo " + Install/Re-install default config: +" @echo " + ---------------------------------- +" @echo " + $(MK) samples +" +if SYSTEMD_INIT + @echo " + +" + @echo " + Install systemd startup scripts: +" + @echo " + -------------------------------- +" + @echo " + +" + @echo " + build/startup/install_systemd.sh +" +endif @echo " + +" @echo " + +" @echo " + Additional resources: +" diff --git a/build/freeswitch-tmpfiles.conf b/build/freeswitch-tmpfiles.conf deleted file mode 100644 index 881873fb29..0000000000 --- a/build/freeswitch-tmpfiles.conf +++ /dev/null @@ -1 +0,0 @@ -d /run/freeswitch 0750 freeswitch daemon - diff --git a/build/freeswitch.service b/build/freeswitch.service deleted file mode 100644 index ae6921b4df..0000000000 --- a/build/freeswitch.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=FreeSWITCH -After=syslog.target network.target -After=postgresql.service postgresql-9.3.service postgresql-9.4.service mysqld.service httpd.service - -[Service] -User=freeswitch -EnvironmentFile=-/etc/sysconfig/freeswitch -# RuntimeDirectory is not yet supported in CentOS 7. A workaround is to use /etc/tmpfiles.d/freeswitch.conf -#RuntimeDirectory=/run/freeswitch -#RuntimeDirectoryMode=0750 -WorkingDirectory=/run/freeswitch -ExecStart=/usr/bin/freeswitch -nc -nf $FREESWITCH_PARAMS -ExecReload=/usr/bin/kill -HUP $MAINPID - -[Install] -WantedBy=multi-user.target diff --git a/build/modules.conf.most b/build/modules.conf.most index 5c72ffa27c..408e9196a5 100644 --- a/build/modules.conf.most +++ b/build/modules.conf.most @@ -23,6 +23,7 @@ applications/mod_fifo applications/mod_fsk applications/mod_fsv applications/mod_hash +applications/mod_hiredis applications/mod_httapi applications/mod_http_cache #applications/mod_ladspa @@ -101,7 +102,6 @@ event_handlers/mod_erlang_event event_handlers/mod_event_multicast event_handlers/mod_event_socket event_handlers/mod_format_cdr -event_handlers/mod_hiredis event_handlers/mod_json_cdr #event_handlers/mod_radius_cdr event_handlers/mod_odbc_cdr diff --git a/build/startup/freeswitch.default b/build/startup/freeswitch.default new file mode 100644 index 0000000000..41cd0758c6 --- /dev/null +++ b/build/startup/freeswitch.default @@ -0,0 +1,2 @@ +# /etc/default/freeswitch +DAEMON_OPTS="-nonat" diff --git a/build/startup/freeswitch.service.in b/build/startup/freeswitch.service.in new file mode 100644 index 0000000000..f580ba2480 --- /dev/null +++ b/build/startup/freeswitch.service.in @@ -0,0 +1,32 @@ +[Unit] +Description=freeswitch +After=syslog.target network.target local-fs.target + +[Service] +; service +Type=forking +PIDFile=@runtimedir@/freeswitch.pid +PermissionsStartOnly=true +Environment="DAEMON_OPTS=-nonat" +EnvironmentFile=-/etc/sysconfig/freeswitch +EnvironmentFile=-/etc/default/freeswitch +ExecStart=@bindir_expanded@/freeswitch -u freeswitch -g freeswitch -ncwait -rp ${DAEMON_OPTS} +TimeoutSec=20s +Restart=on-failure +; exec +User=root +Group=daemon +LimitCORE=infinity +LimitNOFILE=100000 +LimitNPROC=60000 +;LimitSTACK=240 +LimitRTPRIO=infinity +LimitRTTIME=7000000 +IOSchedulingClass=realtime +IOSchedulingPriority=2 +CPUSchedulingPolicy=rr +CPUSchedulingPriority=89 +UMask=0007 + +[Install] +WantedBy=multi-user.target diff --git a/build/startup/freeswitch.tmpfile b/build/startup/freeswitch.tmpfile new file mode 100644 index 0000000000..baea7b8113 --- /dev/null +++ b/build/startup/freeswitch.tmpfile @@ -0,0 +1 @@ +d /var/run/freeswitch 0755 freeswitch freeswitch - - diff --git a/build/startup/install_systemd.sh.in b/build/startup/install_systemd.sh.in new file mode 100644 index 0000000000..3f77c4f8b3 --- /dev/null +++ b/build/startup/install_systemd.sh.in @@ -0,0 +1,59 @@ +#!/bin/bash +# Niek Vlessert + +USER=`whoami` +DISTRO=$(source /etc/os-release && echo $PRETTY_NAME) +if [ $USER != "root" ] ; then + SUDO=`which sudo | awk -F"/" '{print $NF}'` + if [ -z $SUDO ] ; then + echo "No root and no sudo... please run this as root or install sudo and make sure your user has permissions to use it." + exit + else + read -p "The currently active user is not root but sudo is available... do you want to install using sudo? (y/n) " -n 1 -r + if ! [[ $REPLY =~ ^[yY]$ ]] + then + echo + exit + fi + fi +fi + +echo +echo "This will do several things on your $DISTRO installation:" +echo "- Create user freeswitch and add it to group freeswitch" +FSPATH=@prefix@ +if [[ $FSPATH == *"freeswitch"* ]] +then + echo "- Set permissions on @prefix@ and files in @bindir_expanded@" +fi +echo "- Install systemd unit file and other required files" +echo +read -p "Do you want to continue? (y/n) " -n 1 -r +if [[ $REPLY =~ ^[yY]$ ]] +then + echo + echo "Installing..." + $SUDO useradd -d @confdir@ -r -U -s /bin/false -c "FreeSWITCH open source softswitch" freeswitch + if [[ $FSPATH == *"freeswitch"* ]] + then + $SUDO chown -R freeswitch:freeswitch @prefix@ + $SUDO chmod -R ug=rwX,o= @prefix@ + $SUDO chmod -R u=rwx,g=rx @bindir_expanded@/* + fi + $SUDO cp build/startup/freeswitch.service /etc/systemd/system/ + $SUDO cp build/startup/freeswitch.tmpfile /etc/tmpfiles.d/freeswitch.conf + if [ -d /etc/sysconfig ]; then + $SUDO cp build/startup/freeswitch.default /etc/sysconfig/freeswitch + else + $SUDO cp build/startup/freeswitch.default /etc/default/freeswitch + fi + $SUDO systemd-tmpfiles --clean --create + $SUDO systemctl daemon-reload + echo + if [ -f @confdir@/vars.xml ] ; then + echo "You may now start Freeswitch using 'systemctl start freeswitch'" + else + echo "Make sure your config files are in place in @confdir@, if they are you can start Freeswitch using 'systemctl start freeswitch'" + fi + echo "Then start fs_cli by running @bindir_expanded@/fs_cli" +fi diff --git a/conf/testing/autoload_configs/conference.conf.xml b/conf/testing/autoload_configs/conference.conf.xml index 4ce9ceceab..4e531ecfaf 100644 --- a/conf/testing/autoload_configs/conference.conf.xml +++ b/conf/testing/autoload_configs/conference.conf.xml @@ -281,6 +281,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conf/testing/dialplan/default/0019_conf.xml b/conf/testing/dialplan/default/0019_conf.xml index daacf3f419..54dcf8b0fd 100644 --- a/conf/testing/dialplan/default/0019_conf.xml +++ b/conf/testing/dialplan/default/0019_conf.xml @@ -7,6 +7,15 @@ + + + + + + + + + @@ -16,6 +25,13 @@ + + + + + + + diff --git a/conf/testing/vars.xml b/conf/testing/vars.xml index 0aa41240c8..405c7a7037 100644 --- a/conf/testing/vars.xml +++ b/conf/testing/vars.xml @@ -78,5 +78,6 @@ - + + diff --git a/conf/vanilla/autoload_configs/switch.conf.xml b/conf/vanilla/autoload_configs/switch.conf.xml index 4ffe878563..345a16c19e 100644 --- a/conf/vanilla/autoload_configs/switch.conf.xml +++ b/conf/vanilla/autoload_configs/switch.conf.xml @@ -152,6 +152,12 @@ + + + - + diff --git a/configure.ac b/configure.ac index 414c15caae..ba360dc865 100644 --- a/configure.ac +++ b/configure.ac @@ -83,6 +83,8 @@ default_certsdir="$prefix/certs" default_fontsdir="$prefix/fonts" default_imagesdir="$prefix/images" +eval bindir_expanded="${bindir}" + if test "${enable_fhs}" = "yes"; then eval full_datadir="${datadir}/freeswitch" eval datadir=$full_datadir @@ -771,6 +773,14 @@ case "$host" in ;; *linux*) APR_ADDTO([PLATFORM_CORE_LIBS], [-ldl -lcrypt -lrt]) + systemdinit=false + if test -d /run/systemd/system; then + systemdinit=true + AC_SUBST(bindir_expanded) + AC_CONFIG_FILES([build/startup/install_systemd.sh], [chmod +x build/startup/install_systemd.sh]) + AC_CONFIG_FILES([build/startup/freeswitch.service]) + fi + AM_CONDITIONAL([SYSTEMD_INIT], [test x$systemdinit = xtrue]) ;; esac diff --git a/debian/bootstrap.sh b/debian/bootstrap.sh index 43468800ae..2163c06947 100755 --- a/debian/bootstrap.sh +++ b/debian/bootstrap.sh @@ -70,13 +70,14 @@ avoid_mods_wheezy=( languages/mod_managed applications/mod_av applications/mod_cv - applications/mod_hiredis + applications/mod_hiredis formats/mod_shout applications/mod_sonar applications/mod_soundtouch formats/mod_vlc ) avoid_mods_trusty=( + event_handlers/mod_amqp ) avoid_mods_utopic=( directories/mod_ldap diff --git a/debian/freeswitch-mod-perl.install.tmpl b/debian/freeswitch-mod-perl.install.tmpl index 5b2fe712f3..c151beb6be 100644 --- a/debian/freeswitch-mod-perl.install.tmpl +++ b/debian/freeswitch-mod-perl.install.tmpl @@ -1,2 +1,2 @@ -debian/tmp/usr/perl/freeswitch.pm /usr/lib/perl5 -debian/tmp/usr/perl/freeswitch.so /usr/lib/perl5/auto/freeswitch +debian/tmp/usr/perl/freeswitch.pm /usr/share/perl5 +debian/tmp/usr/perl/freeswitch.so /usr/share/perl5/auto/freeswitch diff --git a/htdocs/portal/assets/bootstrap/css/bootstrap.css b/htdocs/portal/assets/bootstrap/css/bootstrap.css index 2f56af33f3..d246186fc4 100644 --- a/htdocs/portal/assets/bootstrap/css/bootstrap.css +++ b/htdocs/portal/assets/bootstrap/css/bootstrap.css @@ -223,7 +223,7 @@ textarea { body { margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px; color: #333333; @@ -1065,7 +1065,7 @@ input, button, select, textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif; } label { @@ -4510,7 +4510,7 @@ input[type="submit"].btn.btn-mini { .navbar-search .search-query { padding: 4px 14px; margin-bottom: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-family: verdana, "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: 1; @@ -5624,7 +5624,6 @@ a.thumbnail:focus { display: inline-block; padding: 2px 4px; font-size: 11.844px; - font-weight: bold; line-height: 14px; color: #ffffff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); diff --git a/html5/verto/js/src/jquery.FSRTC.js b/html5/verto/js/src/jquery.FSRTC.js index d0bf7bf24c..7d3e0ad2ec 100644 --- a/html5/verto/js/src/jquery.FSRTC.js +++ b/html5/verto/js/src/jquery.FSRTC.js @@ -90,7 +90,8 @@ }, }, options); - this.enabled = true; + this.audioEnabled = true; + this.videoEnabled = true; this.mediaData = { @@ -360,7 +361,7 @@ $.FSRTC.prototype.getMute = function() { var self = this; - return self.enabled; + return self.audioEnabled; } $.FSRTC.prototype.setMute = function(what) { @@ -381,10 +382,39 @@ break; } - self.enabled = audioTracks[i].enabled; + self.audioEnabled = audioTracks[i].enabled; } - return !self.enabled; + return !self.audioEnabled; + } + + $.FSRTC.prototype.getVideoMute = function() { + var self = this; + return self.videoEnabled; + } + + $.FSRTC.prototype.setVideoMute = function(what) { + var self = this; + var videoTracks = self.localStream.getVideoTracks(); + + for (var i = 0, len = videoTracks.length; i < len; i++ ) { + switch(what) { + case "on": + videoTracks[i].enabled = true; + break; + case "off": + videoTracks[i].enabled = false; + break; + case "toggle": + videoTracks[i].enabled = !videoTracks[i].enabled; + default: + break; + } + + self.videoEnabled = videoTracks[i].enabled; + } + + return !self.videoEnabled; } $.FSRTC.prototype.createAnswer = function(params) { diff --git a/html5/verto/js/src/jquery.jsonrpcclient.js b/html5/verto/js/src/jquery.jsonrpcclient.js index a02885e802..6f4f07c72b 100644 --- a/html5/verto/js/src/jquery.jsonrpcclient.js +++ b/html5/verto/js/src/jquery.jsonrpcclient.js @@ -91,6 +91,32 @@ /// The next JSON-RPC request id. $.JsonRpcClient.prototype._current_id = 1; + + $.JsonRpcClient.prototype.speedTest = function (bytes, cb) { + var socket = this.options.getSocket(this.wsOnMessage); + if (socket !== null) { + this.speedCB = cb; + this.speedBytes = bytes; + socket.send("#SPU " + bytes); + + var loops = bytes / 1024; + var rem = bytes % 1024; + var i; + var data = new Array(1024).join("."); + for (i = 0; i < loops; i++) { + socket.send("#SPB " + data); + } + + if (rem) { + socket.send("#SPB " + data); + } + + socket.send("#SPE"); + } + }; + + + /** * @fn call * @memberof $.JsonRpcClient @@ -382,6 +408,26 @@ $.JsonRpcClient.prototype._wsOnMessage = function(event) { // Check if this could be a JSON RPC message. var response; + + // Special sub proto + if (event.data[0] == "#" && event.data[1] == "S" && event.data[2] == "P") { + if (event.data[3] == "U") { + this.up_dur = parseInt(event.data.substring(4)); + } else if (this.speedCB && event.data[3] == "D") { + this.down_dur = parseInt(event.data.substring(4)); + + var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0); + var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0); + + console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps); + this.speedCB(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps }); + this.speedCB = null; + } + + return; + } + + try { response = $.parseJSON(event.data); diff --git a/html5/verto/js/src/jquery.verto.js b/html5/verto/js/src/jquery.verto.js index 779ba79125..0528652673 100644 --- a/html5/verto/js/src/jquery.verto.js +++ b/html5/verto/js/src/jquery.verto.js @@ -1075,6 +1075,27 @@ console.error("Error: ", obj, args); }; + /* back compat so jsonstatus can always be enabled */ + function genRow(data) { + if (typeof(data[4]) === "string" && data[4].indexOf("{") > -1) { + var tmp = $.parseJSON(data[4]); + data[4] = tmp.oldStatus; + data[5] = null; + } + return data; + } + + function genArray(obj) { + var data = obj.asArray(); + + for (var i in data) { + data[i] = genRow(data[i]); + } + + return data; + } + + la.onChange = function(obj, args) { var index = 0; var iserr = 0; @@ -1122,7 +1143,7 @@ return; } dt.fnClearTable(); - dt.fnAddData(obj.asArray()); + dt.fnAddData(genArray(obj)); dt.fnAdjustColumnSizing(); break; case "add": @@ -1133,9 +1154,9 @@ if (args.redraw > -1) { // specific position, more costly dt.fnClearTable(); - dt.fnAddData(obj.asArray()); + dt.fnAddData(genArray(obj)); } else { - dt.fnAddData(args.data); + dt.fnAddData(genRow(args.data)); } dt.fnAdjustColumnSizing(); break; @@ -1144,7 +1165,7 @@ return; } //console.debug(args, index); - dt.fnUpdate(args.data, index); + dt.fnUpdate(genRow(args.data), index); dt.fnAdjustColumnSizing(); break; case "del": @@ -1157,7 +1178,7 @@ case "reorder": // specific position, more costly dt.fnClearTable(); - dt.fnAddData(obj.asArray()); + dt.fnAddData(genArray(obj)); break; case "hide": jq.hide(); @@ -2022,6 +2043,53 @@ return false; } + + // Attach audio output device to video element using device/sink ID. + function find_name(id) { + for (var i in $.verto.audioOutDevices) { + var source = $.verto.audioOutDevices[i]; + if (source.id === id) { + return(source.label); + } + } + + return id; + } + + $.verto.dialog.prototype.setAudioPlaybackDevice = function(sinkId, callback, arg) { + var dialog = this; + var element = dialog.audioStream; + + if (typeof element.sinkId !== 'undefined') { + var devname = find_name(sinkId); + console.info("Dialog: " + dialog.callID + " Setting speaker:", element, devname); + + element.setSinkId(sinkId) + .then(function() { + console.log("Dialog: " + dialog.callID + ' Success, audio output device attached: ' + sinkId); + if (callback) { + callback(true, devname, arg); + } + }) + .catch(function(error) { + var errorMessage = error; + if (error.name === 'SecurityError') { + errorMessage = "Dialog: " + dialog.callID + ' You need to use HTTPS for selecting audio output ' + + 'device: ' + error; + } + if (callback) { + callback(false, null, arg); + } + console.error(errorMessage); + }); + } else { + console.warn("Dialog: " + dialog.callID + ' Browser does not support output device selection.'); + if (callback) { + callback(false, null, arg); + } + } + } + $.verto.dialog.prototype.setState = function(state) { var dialog = this; @@ -2061,11 +2129,9 @@ console.info("Using Speaker: ", speaker); if (speaker && speaker !== "any") { - var videoElement = dialog.audioStream; - setTimeout(function() { - console.info("Setting speaker:", videoElement, speaker); - attachSinkId(videoElement, speaker);}, 500); + dialog.setAudioPlaybackDevice(speaker); + }, 500); } break; @@ -2226,6 +2292,16 @@ return dialog.rtc.getMute(); }; + $.verto.dialog.prototype.setVideoMute = function(what) { + var dialog = this; + return dialog.rtc.setVideoMute(what); + }; + + $.verto.dialog.prototype.getVideoMute = function() { + var dialog = this; + return dialog.rtc.getVideoMute(); + }; + $.verto.dialog.prototype.useStereo = function(on) { var dialog = this; diff --git a/html5/verto/verto_communicator/bower.json b/html5/verto/verto_communicator/bower.json index 6a05c29a06..2bbb0bf6b0 100644 --- a/html5/verto/verto_communicator/bower.json +++ b/html5/verto/verto_communicator/bower.json @@ -42,7 +42,7 @@ "jquery-cookie": "~1.4.1", "jquery-json": "~2.5.1", "datatables": "~1.10.8", - "angular-bootstrap": "~0.13.3", + "angular-bootstrap": "~0.14.3", "bootstrap-material-design": "~0.3.0" }, "resolutions": { diff --git a/html5/verto/verto_communicator/js/3rd-party/getScreenId.js b/html5/verto/verto_communicator/js/3rd-party/getScreenId.js index b2cfc6d3c0..5c379b7e1a 100644 --- a/html5/verto/verto_communicator/js/3rd-party/getScreenId.js +++ b/html5/verto/verto_communicator/js/3rd-party/getScreenId.js @@ -1,10 +1,10 @@ -// Last time updated at Sep 07, 2014, 08:32:23 +// Last time updated at Oct 24, 2015, 08:32:23 // Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js // Muaz Khan - www.MuazKhan.com // MIT License - www.WebRTC-Experiment.com/licence -// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/getScreenId.js +// Documentation - https://github.com/muaz-khan/getScreenId. // ______________ // getScreenId.js @@ -13,7 +13,7 @@ getScreenId(function (error, sourceId, screen_constraints) { // error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome' // sourceId == null || 'string' || 'firefox' - + if(sourceId == 'firefox') { navigator.mozGetUserMedia(screen_constraints, onSuccess, onFailure); } @@ -79,6 +79,11 @@ getScreenId(function (error, sourceId, screen_constraints) { } function postMessage() { + if (!iframe) { + loadIFrame(postMessage); + return; + } + if (!iframe.isLoaded) { setTimeout(postMessage, 100); return; @@ -89,11 +94,137 @@ getScreenId(function (error, sourceId, screen_constraints) { }, '*'); } - var iframe = document.createElement('iframe'); - iframe.onload = function() { - iframe.isLoaded = true; + function loadIFrame(loadCallback) { + if (iframe) { + loadCallback(); + return; + } + + iframe = document.createElement('iframe'); + iframe.onload = function() { + iframe.isLoaded = true; + + loadCallback(); + }; + iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html + iframe.style.display = 'none'; + (document.body || document.documentElement).appendChild(iframe); + } + + var iframe; + + // this function is used in v3.0 + window.getScreenConstraints = function(callback) { + loadIFrame(function() { + getScreenId(function(error, sourceId, screen_constraints) { + callback(error, screen_constraints.video); + }); + }); + }; +})(); + +(function() { + if(document.domain.indexOf('webrtc-experiment.com') === -1) { + return; + } + + window.getScreenId = function(callback) { + // for Firefox: + // sourceId == 'firefox' + // screen_constraints = {...} + if (!!navigator.mozGetUserMedia) { + callback(null, 'firefox', { + video: { + mozMediaSource: 'window', + mediaSource: 'window' + } + }); + return; + } + + postMessage(); + + window.addEventListener('message', onIFrameCallback); + + function onIFrameCallback(event) { + if (!event.data) return; + + if (event.data.chromeMediaSourceId) { + if (event.data.chromeMediaSourceId === 'PermissionDeniedError') { + callback('permission-denied'); + } else callback(null, event.data.chromeMediaSourceId, getScreenConstraints(null, event.data.chromeMediaSourceId)); + } + + if (event.data.chromeExtensionStatus) { + callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus)); + } + + // this event listener is no more needed + window.removeEventListener('message', onIFrameCallback); + } + }; + + function getScreenConstraints(error, sourceId) { + var screen_constraints = { + audio: false, + video: { + mandatory: { + chromeMediaSource: error ? 'screen' : 'desktop', + maxWidth: window.screen.width > 1920 ? window.screen.width : 1920, + maxHeight: window.screen.height > 1080 ? window.screen.height : 1080 + }, + optional: [] + } + }; + + if (sourceId) { + screen_constraints.video.mandatory.chromeMediaSourceId = sourceId; + } + + return screen_constraints; + } + + function postMessage() { + if (!iframe) { + loadIFrame(postMessage); + return; + } + + if (!iframe.isLoaded) { + setTimeout(postMessage, 100); + return; + } + + iframe.contentWindow.postMessage({ + captureSourceId: true + }, '*'); + } + + function loadIFrame(loadCallback) { + if (iframe) { + loadCallback(); + return; + } + + iframe = document.createElement('iframe'); + iframe.onload = function() { + iframe.isLoaded = true; + + loadCallback(); + }; + iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; // https://wwww.yourdomain.com/getScreenId.html + iframe.style.display = 'none'; + (document.body || document.documentElement).appendChild(iframe); + } + + var iframe; + + // this function is used in v3.0 + window.getScreenConstraints = function(callback) { + loadIFrame(function() { + getScreenId(function(error, sourceId, screen_constraints) { + callback(error, screen_constraints.video); + }); + }); }; - iframe.src = 'https://www.webrtc-experiment.com/getSourceId/'; - iframe.style.display = 'none'; - (document.body || document.documentElement).appendChild(iframe); })(); diff --git a/html5/verto/verto_communicator/js/3rd-party/volume-meter.js b/html5/verto/verto_communicator/js/3rd-party/volume-meter.js new file mode 100644 index 0000000000..a4ac33a4a9 --- /dev/null +++ b/html5/verto/verto_communicator/js/3rd-party/volume-meter.js @@ -0,0 +1,96 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 Chris Wilson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/* + +Usage: +audioNode = createAudioMeter(audioContext,clipLevel,averaging,clipLag); + +audioContext: the AudioContext you're using. +clipLevel: the level (0 to 1) that you would consider "clipping". + Defaults to 0.98. +averaging: how "smoothed" you would like the meter to be over time. + Should be between 0 and less than 1. Defaults to 0.95. +clipLag: how long you would like the "clipping" indicator to show + after clipping has occured, in milliseconds. Defaults to 750ms. + +Access the clipping through node.checkClipping(); use node.shutdown to get rid of it. +*/ + +function createAudioMeter(audioContext,clipLevel,averaging,clipLag) { + var processor = audioContext.createScriptProcessor(512); + processor.onaudioprocess = volumeAudioProcess; + processor.clipping = false; + processor.lastClip = 0; + processor.volume = 0; + processor.clipLevel = clipLevel || 0.98; + processor.averaging = averaging || 0.95; + processor.clipLag = clipLag || 750; + + // this will have no effect, since we don't copy the input to the output, + // but works around a current Chrome bug. + processor.connect(audioContext.destination); + + processor.checkClipping = + function(){ + if (!this.clipping) + return false; + if ((this.lastClip + this.clipLag) < window.performance.now()) + this.clipping = false; + return this.clipping; + }; + + processor.shutdown = + function(){ + this.disconnect(); + this.onaudioprocess = null; + }; + + return processor; +} + +function volumeAudioProcess( event ) { + var buf = event.inputBuffer.getChannelData(0); + var bufLength = buf.length; + var sum = 0; + var x; + + // Do a root-mean-square on the samples: sum up the squares... + for (var i=0; i=this.clipLevel) { + this.clipping = true; + this.lastClip = window.performance.now(); + } + sum += x * x; + } + + // ... then take the square root of the sum. + var rms = Math.sqrt(sum / bufLength); + + // Now smooth this out with the averaging factor applied + // to the previous sample - take the max here because we + // want "fast attack, slow release." + this.volume = Math.max(rms, this.volume*this.averaging); +} diff --git a/html5/verto/verto_communicator/package.json b/html5/verto/verto_communicator/package.json index b2b0502a55..de23baa785 100644 --- a/html5/verto/verto_communicator/package.json +++ b/html5/verto/verto_communicator/package.json @@ -15,7 +15,7 @@ "grunt-contrib-copy": "^0.7.0", "grunt-contrib-cssmin": "^0.12.0", "grunt-contrib-htmlmin": "^0.4.0", - "grunt-contrib-imagemin": "^0.9.2", + "grunt-contrib-imagemin": "^1.0.0", "grunt-contrib-jshint": "^0.11.0", "grunt-contrib-uglify": "^0.7.0", "grunt-contrib-watch": "latest", diff --git a/html5/verto/verto_communicator/src/css/verto.css b/html5/verto/verto_communicator/src/css/verto.css index d307eecdaf..4e0175d583 100644 --- a/html5/verto/verto_communicator/src/css/verto.css +++ b/html5/verto/verto_communicator/src/css/verto.css @@ -8,6 +8,15 @@ body { padding-top: 60px; } +.panel.panel-material-blue-900 .panel-heading { + background-color: #0d47a1; +} + +.install { + color: white; + text-decoration: underline; +} + .ellipsis { text-overflow: ellipsis; overflow: hidden; @@ -595,12 +604,8 @@ body .modal-body .btn-group .btn.active { transition-delay:0s; } -#incall .video-hover-buttons .btn-group { - margin: 0; -} - #incall .video-hover-buttons .btn-group .dropdown-menu { - height: 200px; + max-height: 200px; overflow: auto; } @@ -766,6 +771,10 @@ body .modal-body .btn-group .btn.active { transition: all 0.5s ease; } +.tooltip-inner { + padding: 8px 8px; + background-color: #000; +} #sidebar-wrapper { right: 360px; @@ -909,7 +918,6 @@ body .modal-body .btn-group .btn.active { .members-badges { font-size: 10px; - text-transform: uppercase; margin-top: -2px; } @@ -1464,3 +1472,76 @@ body:-webkit-full-screen #incall .video-footer { } } + +.preview-wrapper { + position: relative; +} + +.preview-wrapper video { + transform: scaleX(-1); +} + +.drop-net-info { + padding-top: 0px; + cursor: default; +} + +.drop-net-info .title { + text-align: center; + font-size: 16px; + font-weight: bold; + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; +} + +.drop-net-info .title:hover { + background-color: #f7f7f7; +} + +.drop-net-info a:hover { + color: #333 !important; +} + +.net-info .yellow { + color: #e3d95b; +} + +.net-info .green { + color: #00ae00; +} + +.net-info .red { + color: #ae0000; +} + +#mic-meter { + position: absolute; + bottom: 5px; + left: 10px; +} +#mic-meter .icon { + margin-left: 3px; + color: #CCC; +} +#mic-meter .volumes { + width: 30px; +} +#mic-meter .volumes .volume-segment { + height: 10px; + width: 100%; + border-radius: 5px; + border: 2px solid #CCC; + display: block; + margin-top: 1.5px; +} + +#mic-meter .volumes .volume-segment.active { + background-color: #CCC; +} + +#preview .refresh { + margin: 15px 0px 0px 0px; +} diff --git a/html5/verto/verto_communicator/src/index.html b/html5/verto/verto_communicator/src/index.html index c450607c9f..47bdf740cd 100644 --- a/html5/verto/verto_communicator/src/index.html +++ b/html5/verto/verto_communicator/src/index.html @@ -95,6 +95,7 @@ + @@ -113,6 +114,7 @@ + diff --git a/html5/verto/verto_communicator/src/partials/chat.html b/html5/verto/verto_communicator/src/partials/chat.html index 7e10b05db3..23797d0b51 100644 --- a/html5/verto/verto_communicator/src/partials/chat.html +++ b/html5/verto/verto_communicator/src/partials/chat.html @@ -28,7 +28,8 @@
Floor
-
Presenter
+
Presenter
+
Screen Share
diff --git a/html5/verto/verto_communicator/src/partials/dialpad.html b/html5/verto/verto_communicator/src/partials/dialpad.html index f24e28331d..e5711d1ef2 100644 --- a/html5/verto/verto_communicator/src/partials/dialpad.html +++ b/html5/verto/verto_communicator/src/partials/dialpad.html @@ -1,4 +1,10 @@ -
+
+

Calling to {{ dialpadNumber }}...

+ + + +
+
diff --git a/html5/verto/verto_communicator/src/partials/menu.html b/html5/verto/verto_communicator/src/partials/menu.html index b14aad0061..55e0cee35b 100644 --- a/html5/verto/verto_communicator/src/partials/menu.html +++ b/html5/verto/verto_communicator/src/partials/menu.html @@ -19,9 +19,21 @@
+
+ + +
+ + Refresh device list +
@@ -84,35 +91,52 @@
- - -
+
+ - -

Dedicated Remote Encoder

-
Select a non default bandwidth to use a dedicated remote encoder.
+
+

Dedicated Remote Encoder enabled. +

-
-

Dedicated Remote Encoder enabled. -

+
+ +
-
- - -
+
+ +
-
- - + Check Network Speed + +
+ + +
+ +
+ + +
+ +
+ + +
diff --git a/html5/verto/verto_communicator/src/partials/preview.html b/html5/verto/verto_communicator/src/partials/preview.html new file mode 100644 index 0000000000..76e3b68744 --- /dev/null +++ b/html5/verto/verto_communicator/src/partials/preview.html @@ -0,0 +1,47 @@ +
+
+
+
+

Setup your camera and microphone settings

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+ + + + +
+ +
+
+
+
+
+
diff --git a/html5/verto/verto_communicator/src/partials/video_call.html b/html5/verto/verto_communicator/src/partials/video_call.html index da339e4878..9633149c77 100644 --- a/html5/verto/verto_communicator/src/partials/video_call.html +++ b/html5/verto/verto_communicator/src/partials/video_call.html @@ -2,23 +2,30 @@
- - - - -
- @@ -29,22 +36,39 @@
-
- - - - - +
+ + + + + +
+ + +
+
@@ -69,4 +93,3 @@
- diff --git a/html5/verto/verto_communicator/src/storageService/services/splash_screen.js b/html5/verto/verto_communicator/src/storageService/services/splash_screen.js index da7a156f63..40c2b6951b 100644 --- a/html5/verto/verto_communicator/src/storageService/services/splash_screen.js +++ b/html5/verto/verto_communicator/src/storageService/services/splash_screen.js @@ -4,7 +4,7 @@ .module('storageService') .service('splashscreen', ['$rootScope', '$q', 'storage', 'config', 'verto', function($rootScope, $q, storage, config, verto) { - + var checkBrowser = function() { return $q(function(resolve, reject) { var activity = 'browser-upgrade'; @@ -22,10 +22,10 @@ if (!navigator.getUserMedia) { result['status'] = 'error'; result['message'] = 'Error: browser doesn\'t support WebRTC.'; - reject(result); + reject(result); } - resolve(result); + resolve(result); }); }; @@ -48,7 +48,7 @@ reject(result); } verto.data.mediaPerm = true; - resolve(result); + resolve(result); }); }); }; @@ -62,7 +62,7 @@ 'activity': activity, 'message': 'Refresh Media Devices.' }; - + verto.refreshDevices(function(status) { verto.refreshDevicesCallback(function() { resolve(result); @@ -72,6 +72,28 @@ }); }; + var checkConnectionSpeed = function() { + return $q(function(resolve, reject) { + var activity = 'check-connection-speed'; + var result = { + 'status': 'success', + 'soft': true, + 'activity': activity, + 'message': 'Check Connection Speed.' + }; + + if (storage.data.autoBand && verto.data.instance) { + verto.testSpeed(cb); + } else { + resolve(result); + } + + function cb(data) { + resolve(result); + } + }); + }; + var provisionConfig = function() { return $q(function(resolve, reject) { var activity = 'provision-config'; @@ -100,7 +122,7 @@ }); result['promise'] = configPromise; - + resolve(result); }); }; @@ -136,13 +158,13 @@ verto.data.connecting = false; resolve(result); }); - }; + }; }; if(storage.data.ui_connected && storage.data.ws_connected) { - checkUserStored(); + checkUserStored(); } else { - resolve(result); + resolve(result); }; }); }; @@ -152,7 +174,8 @@ checkMediaPerm, refreshMediaDevices, provisionConfig, - checkLogin + checkLogin, + checkConnectionSpeed ]; var progress_message = [ @@ -160,12 +183,13 @@ 'Checking media permissions', 'Refresh Media Devices.', 'Provisioning configuration.', - 'Checking login.' + 'Checking login.', + 'Check Connection Speed.' ]; - + var getProgressMessage = function(current_progress) { if(progress_message[current_progress] != undefined) { - return progress_message[current_progress]; + return progress_message[current_progress]; } else { return 'Please wait...'; } @@ -176,7 +200,7 @@ var calculateProgress = function(index) { var _progress; - + _progress = index + 1; progress_percentage = (_progress / progress.length) * 100; return progress_percentage; @@ -186,12 +210,12 @@ var fn, fn_return, status, interrupt, activity, soft, message, promise; interrupt = false; current_progress++; - + if(current_progress >= progress.length) { $rootScope.$emit('progress.complete', current_progress); return; } - + fn = progress[current_progress]; fn_return = fn(); @@ -221,7 +245,7 @@ emitNextProgress(fn_return); } ); - + }; return { @@ -232,4 +256,3 @@ }; }]); - diff --git a/html5/verto/verto_communicator/src/storageService/services/storage.js b/html5/verto/verto_communicator/src/storageService/services/storage.js index f0b8bb1b80..da3a600589 100644 --- a/html5/verto/verto_communicator/src/storageService/services/storage.js +++ b/html5/verto/verto_communicator/src/storageService/services/storage.js @@ -21,9 +21,11 @@ userStatus: 'disconnected', mutedVideo: false, mutedMic: false, + preview: true, selectedVideo: null, selectedAudio: null, selectedShare: null, + selectedSpeaker: null, useStereo: true, useSTUN: true, useDedenc: false, @@ -34,7 +36,9 @@ askRecoverCall: false, googNoiseSuppression: true, googHighpassFilter: true, - googEchoCancellation: true + googEchoCancellation: true, + autoBand: true, + testSpeedJoin: true }; data.$default(defaultSettings); diff --git a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js index 966a7c1f7e..2edbad2d37 100644 --- a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js +++ b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js @@ -15,6 +15,7 @@ 'cgPrompt', '720kb.tooltips', 'ui.gravatar', + 'ui.bootstrap', 'directive.g+signin', ]); @@ -41,6 +42,11 @@ templateUrl: 'partials/incall.html', controller: 'InCallController' }). + when('/preview', { + title: 'Preview Video', + templateUrl: 'partials/preview.html', + controller: 'PreviewController' + }). when('/browser-upgrade', { title: '', templateUrl: 'partials/browser_upgrade.html', diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js index 256d1bb3e5..3129db87c7 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ChatController.js @@ -120,6 +120,8 @@ if (parseInt(member.id) == parseInt(verto.data.conferenceMemberID)) { verto.data.mutedMic = member.status.audio.muted; verto.data.mutedVideo = member.status.video.muted; + verto.data.call.setMute(member.status.audio.muted ? "off" : "on"); + verto.data.call.setVideoMute(member.status.video.muted ? "off" : "on"); } angular.extend($scope.members[memberIdx], member); }); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js index 2944cae46d..305befeea1 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js @@ -7,9 +7,9 @@ '$http', '$location', 'toastr', 'verto', 'storage', 'CallHistory', 'eventQueue', function($rootScope, $scope, $http, $location, toastr, verto, storage, CallHistory, eventQueue) { console.debug('Executing DialPadController.'); - + eventQueue.process(); - + $scope.call_history = CallHistory.all(); $scope.history_control = CallHistory.all_control(); $scope.has_history = Object.keys($scope.call_history).length; @@ -55,6 +55,10 @@ $rootScope.dialpadNumber = number; }; + $scope.preview = function() { + $location.path('/preview'); + }; + $rootScope.transfer = function() { if (!$rootScope.dialpadNumber) { return false; @@ -93,8 +97,17 @@ /** * Call to the number in the $rootScope.dialpadNumber. */ + $scope.loading = false; $rootScope.call = function(extension) { - return call(extension); + if (!storage.data.testSpeedJoin || !$rootScope.dialpadNumber) { + return call(extension); + } + $scope.loading = true; + + verto.testSpeed(function() { + $scope.loading = false; + call(extension); + }); } } ]); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js index 84d8894b28..93e510d5e3 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js @@ -5,7 +5,7 @@ .module('vertoControllers') .controller('InCallController', ['$rootScope', '$scope', '$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', - function($rootScope, $scope, $http, $location, $modal, $timeout, toatr, + function($rootScope, $scope, $http, $location, $modal, $timeout, toastr, verto, storage, prompt, Fullscreen) { console.debug('Executing InCallController.'); @@ -19,7 +19,7 @@ if (storage.data.videoCall) { $scope.callTemplate = 'partials/video_call.html'; } - + $rootScope.$on('call.conference', function(event, data) { $timeout(function() { if($scope.chatStatus) { @@ -75,9 +75,36 @@ verto.data.conf.setVideoLayout(layout); }; + $scope.confChangeSpeaker = function(speakerId) { + storage.data.selectedSpeaker = speakerId; + $rootScope.$emit('changedSpeaker', speakerId); + }; + + $scope.screenshare = function() { + if(verto.data.shareCall) { + verto.screenshareHangup(); + return false; + } + verto.screenshare(storage.data.called_number); + }; + $scope.muteMic = verto.muteMic; $scope.muteVideo = verto.muteVideo; + $rootScope.$on('ScreenShareExtensionStatus', function(event, error) { + var pluginUrl = 'https://chrome.google.com/webstore/detail/screen-capturing/ajhifddimkapgcifgcodmmfdlknahffk'; + switch(error) { + case 'permission-denied': + toastr.info('Please allow the plugin in order to use Screen Share', 'Error'); break; + case 'not-installed': + toastr.warning('Please install the plugin in order to use Screen Share', 'Warning', { allowHtml: true }); break; + case 'installed-disabled': + toastr.info('Please enable the plugin in order to use Screen Share', 'Error'); break; + // case 'not-chrome' + // toastr.info('Chrome', 'Error'); + } + }); + $timeout(function() { console.log('broadcast time-start incall'); $scope.$broadcast('timer-start'); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js index b34aad8631..3c245301a4 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js @@ -24,16 +24,24 @@ * @type {string} */ $rootScope.dialpadNumber = ''; - + // If verto is not connected, redirects to login page. if (!verto.data.connected) { console.debug('MainController: WebSocket not connected. Redirecting to login.'); $location.path('/'); } - + $rootScope.$on('config.http.success', function(ev) { $scope.login(false); }); + + $rootScope.$on('changedSpeaker', function(event, speakerId) { + // This should provide feedback + //setAudioPlaybackDevice([,[,]]); + // if callback is set it will be called as callback(, , ) + verto.data.call.setAudioPlaybackDevice(speakerId); + }); + /** * Login the user to verto server and * redirects him to dialpad page. @@ -52,13 +60,19 @@ storage.data.email = verto.data.email; storage.data.login = verto.data.login; storage.data.password = verto.data.password; - if (redirect) { + if (storage.data.autoBand) { + verto.testSpeed(); + } + + if (redirect && storage.data.preview) { + $location.path('/preview'); + } else if (redirect) { $location.path('/dialpad'); } } }); }; - + verto.data.connecting = true; verto.connect(connectCallback); }; @@ -135,7 +149,7 @@ templateUrl: templateUrl, controller: controller, }; - + angular.extend(options, _options); var modalInstance = $modal.open(options); @@ -154,7 +168,7 @@ jQuery.material.init(); } ); - + return modalInstance; }; @@ -177,6 +191,9 @@ }; function onWSLogin(ev, data) { + if(storage.data.autoBand) { + verto.testSpeed(); + } if(!ws_modalInstance) { return; }; @@ -454,14 +471,6 @@ $scope.incomingCall = false; }; - $scope.screenshare = function() { - if (verto.data.shareCall) { - verto.screenshareHangup(); - return false; - } - verto.screenshare(storage.data.called_number); - }; - $scope.play = function() { var file = $scope.promptInput('Please, enter filename', '', 'File', function(file) { diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/MenuController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/MenuController.js index 6d77567714..104b5c2b50 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/MenuController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/MenuController.js @@ -4,10 +4,43 @@ angular .module('vertoControllers') .controller('MenuController', ['$scope', '$http', '$location', - 'verto', 'storage', - function($scope, $http, $location, verto, storage) { + 'verto', 'storage', '$rootScope', + function($scope, $http, $location, verto, storage, $rootScope) { console.debug('Executing MenuController.'); - } + $scope.storage = storage; + + $rootScope.$on('testSpeed', function(e, data) { + var vidQual = storage.data.vidQual; + var bwp = 4; + + $scope.bandDown = data.downKPS; + $scope.bandUp = data.upKPS; + + if (data.downKPS < 2000) { + bwp--; + } + + if (data.upKPS < 2000) { + bwp--; + } + + $scope.iconClass = 'mdi-device-signal-wifi-4-bar green'; + + if (bwp < 4) { + $scope.iconClass = 'mdi-device-signal-wifi-3-bar yellow'; + } else if (bwp < 2) { + $scope.iconClass = 'mdi-device-signal-wifi-1-bar red'; + } + + verto.videoQuality.forEach(function(vid) { + if (vid.id == vidQual){ + $scope.vidRes = vid.label; + } + }); + + $scope.$apply(); + }); + } ]); -})(); \ No newline at end of file +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js index 2b70948bbe..20f8823878 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js @@ -4,8 +4,8 @@ angular .module('vertoControllers') .controller('ModalSettingsController', ['$scope', '$http', - '$location', '$modalInstance', 'storage', 'verto', - function($scope, $http, $location, $modalInstance, storage, verto) { + '$location', '$modalInstance', '$rootScope', 'storage', 'verto', + function($scope, $http, $location, $modalInstance, $rootScope, storage, verto) { console.debug('Executing ModalSettingsController.'); $scope.storage = storage; @@ -13,8 +13,15 @@ $scope.mydata = angular.copy(storage.data); $scope.ok = function() { + if ($scope.mydata.selectedSpeaker != storage.data.selectedSpeaker) { + $rootScope.$emit('changedSpeaker', $scope.mydata.selectedSpeaker); + } storage.changeData($scope.mydata); verto.data.instance.iceServers(storage.data.useSTUN); + + if (storage.data.autoBand) { + $scope.testSpeed(); + } $modalInstance.close('Ok.'); }; @@ -26,6 +33,16 @@ return verto.refreshDevices(); }; + $scope.testSpeed = function() { + return verto.testSpeed(cb); + + function cb(data) { + $scope.mydata.vidQual = storage.data.vidQual; + $scope.speedMsg = 'Up: ' + data.upKPS + ' Down: ' + data.downKPS; + $scope.$apply(); + } + }; + $scope.resetSettings = function() { if (confirm('Factory Reset Settings?')) { storage.factoryReset(); @@ -35,11 +52,22 @@ }; }; - $scope.checkUseDedRemoteEncoder = function(option) { - if ($scope.mydata.incomingBandwidth != 'default' || $scope.mydata.outgoingBandwidth != 'default') { - $scope.mydata.useDedenc = true; + $scope.checkAutoBand = function(option) { + $scope.mydata.useDedenc = false; + if (!option) { + $scope.mydata.outgoingBandwidth = 'default'; + $scope.mydata.incomingBandwidth = 'default'; + $scope.mydata.vidQual = 'hd'; } else { + $scope.mydata.testSpeedJoin = true; + } + }; + + $scope.checkUseDedRemoteEncoder = function(option) { + if (['0', 'default', '5120'].indexOf(option) != -1) { $scope.mydata.useDedenc = false; + } else { + $scope.mydata.useDedenc = true; } }; } diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/PreviewController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/PreviewController.js new file mode 100644 index 0000000000..6a1d649430 --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/PreviewController.js @@ -0,0 +1,122 @@ +(function() { + 'use strict'; + + angular + .module('vertoControllers') + .controller('PreviewController', ['$rootScope', '$scope', + '$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen', + function($rootScope, $scope, $http, $location, $modal, $timeout, toastr, + verto, storage, prompt, Fullscreen) { + + $scope.storage = storage; + console.debug('Executing PreviewController.'); + var localVideo = document.getElementById('videopreview'); + var volumes = document.querySelector('#mic-meter .volumes').children; + + $scope.localVideo = function() { + var constraints = { + mirrored: true, + audio: { + optional: [{ sourceId: storage.data.selectedAudio }] + } + }; + + if (storage.data.selectedVideo !== 'none') { + constraints.video = { + optional: [{ sourceId: storage.data.selectedVideo }] + }; + } + + navigator.getUserMedia(constraints, handleMedia, function(err, data) { + + }); + }; + + var audioContext = new AudioContext(); + var mediaStreamSource = null; + var meter; + var streamObj = {}; + + function stopMedia(stream) { + if (typeof stream == 'function') { + stream.stop(); + } else { + if (stream.active) { + var tracks = stream.getTracks(); + tracks.forEach(function(track, index) { + track.stop(); + }) + } + } + } + function handleMedia(stream) { + if (streamObj) { + stopMedia(streamObj); + } + + streamObj = stream; + localVideo.src = window.URL.createObjectURL(stream); + + mediaStreamSource = audioContext.createMediaStreamSource(stream); + meter = createAudioMeter(audioContext); + mediaStreamSource.connect(meter); + + renderMic(); + } + + function renderMic() { + // meter.volume; + var n = Math.round(meter.volume * 25); + for(var i = volumes.length -1, j = 0; i >= 0; i--, j++) { + var el = angular.element(volumes[j]); + if (i >= n) el.removeClass('active'); + else el.addClass('active'); + } + + if(!verto.data.call) { + window.requestAnimationFrame(renderMic); + } + } + /** + * TODO: useless? + */ + + $scope.refreshDeviceList = function() { + return verto.refreshDevices(); + }; + + $scope.videoCall = function() { + prompt({ + title: 'Would you like to activate video for this call?', + message: 'Video will be active during the next calls.' + }).then(function() { + storage.data.videoCall = true; + $scope.callTemplate = 'partials/video_call.html'; + }); + }; + + $scope.cbMuteVideo = function(event, data) { + storage.data.mutedVideo = !storage.data.mutedVideo; + } + + $scope.cbMuteMic = function(event, data) { + storage.data.mutedMic = !storage.data.mutedMic; + } + + $scope.confChangeVideoLayout = function(layout) { + verto.data.conf.setVideoLayout(layout); + }; + + $scope.endPreview = function() { + localVideo.src = null; + meter.shutdown(); + meter.onaudioprocess = null; + stopMedia(streamObj); + $location.path('/dialpad'); + storage.data.preview = false; + }; + + $scope.localVideo(); + } + ]); +})(); diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js index fdc96efd59..6fd32f9de7 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/SplashScreenController.js @@ -3,10 +3,10 @@ angular .module('vertoControllers') - .controller('SplashScreenController', ['$scope', '$rootScope', '$location', '$timeout', 'splashscreen', 'prompt', 'verto', - function($scope, $rootScope, $location, $timeout, splashscreen, prompt, verto) { + .controller('SplashScreenController', ['$scope', '$rootScope', '$location', '$timeout', 'storage', 'splashscreen', 'prompt', 'verto', + function($scope, $rootScope, $location, $timeout, storage, splashscreen, prompt, verto) { console.debug('Executing SplashScreenController.'); - + $scope.progress_percentage = splashscreen.progress_percentage; $scope.message = ''; $scope.interrupt_next = false; @@ -18,26 +18,26 @@ link = activity; } } - + $location.path(link); } var checkProgressState = function(current_progress, status, promise, activity, soft, interrupt, message) { - $scope.progress_percentage = splashscreen.calculate(current_progress); + $scope.progress_percentage = splashscreen.calculate(current_progress); $scope.message = message; if(interrupt && status == 'error') { $scope.errors.push(message); if(!soft) { - redirectTo('', activity); + redirectTo('', activity); return; } else { - message = message + '. Continue?'; + message = message + '. Continue?'; }; if(!confirm(message)) { - $scope.interrupt_next = true; - }; + $scope.interrupt_next = true; + }; }; if($scope.interrupt_next) { @@ -48,7 +48,7 @@ return true; }; - + $rootScope.$on('progress.next', function(ev, current_progress, status, promise, activity, soft, interrupt, message) { $timeout(function() { if(promise) { @@ -62,11 +62,11 @@ return; } - + if(!checkProgressState(current_progress, status, promise, activity, soft, interrupt, message)) { return; } - + splashscreen.next(); }, 400); }); @@ -74,7 +74,12 @@ $rootScope.$on('progress.complete', function(ev, current_progress) { $scope.message = 'Complete'; if(verto.data.connected) { - redirectTo('/dialpad'); + if (storage.data.preview) { + $location.path('/preview'); + } + else { + $location.path('/dialpad'); + } } else { redirectTo('/login'); $location.path('/login'); diff --git a/html5/verto/verto_communicator/src/vertoService/services/configService.js b/html5/verto/verto_communicator/src/vertoService/services/configService.js index 105a20b324..9fd789e81f 100644 --- a/html5/verto/verto_communicator/src/vertoService/services/configService.js +++ b/html5/verto/verto_communicator/src/vertoService/services/configService.js @@ -40,7 +40,7 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', ' verto.data.googlelogin = data.googlelogin; verto.data.googleclientid = data.googleclientid; } - + angular.extend(verto.data, data); /** @@ -62,13 +62,13 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', ' console.debug("auto login per config.json"); verto.data.autologin_done = true; } - + if(verto.data.autologin && storage.data.name.length && storage.data.email.length && storage.data.login.length && storage.data.password.length) { - $rootScope.$emit('config.http.success', data); + $rootScope.$emit('config.http.success', data); }; return response; }, function(response) { - $rootScope.$emit('config.http.error', response); + $rootScope.$emit('config.http.error', response); return response; }); @@ -79,4 +79,3 @@ vertoService.service('config', ['$rootScope', '$http', '$location', 'storage', ' 'configure': configure }; }]); - diff --git a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js index 26c13c6fc1..9bf54c3cbd 100644 --- a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js +++ b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js @@ -76,6 +76,12 @@ var bandwidth = [{ }, { id: '2048', label: '2mb' +}, { + id: '3196', + label: '3mb' +}, { + id: '4192', + label: '4mb' }, { id: '5120', label: '5mb' @@ -169,7 +175,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora var height = res[1]; if(resolution.width == width && resolution.height == height) { - videoQuality.push(resolution); + videoQuality.push(resolution); } }); }); @@ -200,14 +206,15 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora refreshDevicesCallback : function refreshDevicesCallback(callback) { data.videoDevices = [{ - id: 'none', - label: 'No Camera' - }]; + id: 'none', + label: 'No Camera' + }]; data.shareDevices = [{ id: 'screen', label: 'Screen' }]; data.audioDevices = []; + data.speakerDevices = []; if(!storage.data.selectedShare) { storage.data.selectedShare = data.shareDevices[0]['id']; @@ -265,6 +272,26 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora label: device.label || device.id }); } + + for (var i in jQuery.verto.audioOutDevices) { + var device = jQuery.verto.audioOutDevices[i]; + // Selecting the first source. + if (i == 0 && !storage.data.selectedSpeaker) { + storage.data.selectedSpeaker = device.id; + } + + if (!device.label) { + data.speakerDevices.push({ + id: 'Speaker ' + i, + label: 'Speaker ' + i + }); + continue; + } + data.speakerDevices.push({ + id: device.id, + label: device.label || device.id + }); + } console.debug('Devices were refreshed, checking that we have cameras.'); // This means that we cannot use video! @@ -465,13 +492,17 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora if (params.pvtData) { switch (params.pvtData.action) { case "conference-liveArray-join": - console.log("conference-liveArray-join"); - stopConference(); - startConference(v, dialog, params.pvtData); + if (!params.pvtData.screenShare && !params.pvtData.videoOnly) { + console.log("conference-liveArray-join"); + stopConference(); + startConference(v, dialog, params.pvtData); + } break; case "conference-liveArray-part": - console.log("conference-liveArray-part"); - stopConference(); + if (!params.pvtData.screenShare && !params.pvtData.videoOnly) { + console.log("conference-liveArray-part"); + stopConference(); + } break; } } @@ -556,10 +587,10 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora // Checking if we have a failed connection attempt before // connecting again. if (data.instance && !data.instance.rpcClient.socketReady()) { - clearTimeout(data.instance.rpcClient.to); - data.instance.logout(); - data.instance.login(); - return; + clearTimeout(data.instance.rpcClient.to); + data.instance.logout(); + data.instance.login(); + return; }; data.instance = new jQuery.verto({ login: data.login + '@' + data.hostname, @@ -583,22 +614,23 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora jQuery.verto.unloadJobs.push(function() { that.reloaded = true; }); - data.instance.deviceParams({ - useCamera: storage.data.selectedVideo, - useMic: storage.data.selectedAudio, - onResCheck: that.refreshVideoResolution - }); + data.instance.deviceParams({ + useCamera: storage.data.selectedVideo, + useSpeak: storage.data.selectedSpeaker, + useMic: storage.data.selectedAudio, + onResCheck: that.refreshVideoResolution + }); } if (data.mediaPerm) { ourBootstrap(); } else { - $.FSRTC.checkPerms(ourBootstrap, true, true); + $.FSRTC.checkPerms(ourBootstrap, true, true); } }, mediaPerm: function(callback) { - $.FSRTC.checkPerms(callback, true, true); + $.FSRTC.checkPerms(callback, true, true); }, /** @@ -654,6 +686,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora useVideo: storage.data.useVideo, useStereo: storage.data.useStereo, useCamera: storage.data.selectedVideo, + useSpeak: storage.data.selectedSpeaker, useMic: storage.data.selectedAudio, dedEnc: storage.data.useDedenc, mirrorInput: storage.data.mirrorInput, @@ -681,6 +714,12 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora var that = this; getScreenId(function(error, sourceId, screen_constraints) { + + if(error) { + $rootScope.$emit('ScreenShareExtensionStatus', error); + return; + } + var call = data.instance.newCall({ destination_number: destination + '-screen', caller_id_name: data.name + ' (Screen)', @@ -698,6 +737,24 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora } }); + // Override onStream callback in $.FSRTC instance + call.rtc.options.callbacks.onStream = function(rtc, stream) { + if(stream) { + var StreamTrack = stream.getVideoTracks()[0]; + StreamTrack.addEventListener('ended', stopSharing); + // (stream.getVideoTracks()[0]).onended = stopSharing; + } + + console.log("screenshare started"); + + function stopSharing() { + if(that.data.shareCall) { + that.screenshareHangup(); + console.log("screenshare ended"); + } + } + }; + data.shareCall = call; console.log('shareCall', data); @@ -773,6 +830,39 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora } }, + /** + * Do speed test. + * + * @param callback + */ + testSpeed: function(cb) { + + data.instance.rpcClient.speedTest(1024 * 256, function(e, data) { + var upBand = Math.ceil(data.upKPS * .75), + downBand = Math.ceil(data.downKPS * .75); + + + if (storage.data.autoBand) { + storage.data.incomingBandwidth = downBand; + storage.data.outgoingBandwidth = upBand; + storage.data.useDedenc = false; + storage.data.vidQual = 'hd'; + + if (upBand < 512) { + storage.data.vidQual = 'qvga'; + } + else if (upBand < 1024) { + storage.data.vidQual = 'vga'; + } + } + + if(cb) { + cb(data); + } + + $rootScope.$emit('testSpeed', data); + }); + }, /** * Mute the microphone for the current call. * diff --git a/html5/verto/video_demo/index.html b/html5/verto/video_demo/index.html index bd3d98fe4a..74992e5807 100644 --- a/html5/verto/video_demo/index.html +++ b/html5/verto/video_demo/index.html @@ -91,7 +91,7 @@ div#preload { display: none; }
@@ -108,6 +108,18 @@ div#preload { display: none; }
+
+ + +

Testing Network Connection

+ + + +
+ +