From 522547f84e7a62588cc14f06fb80a1987bb40014 Mon Sep 17 00:00:00 2001 From: Jaon EarlWolf Date: Fri, 6 Nov 2015 15:50:51 -0300 Subject: [PATCH] FS-8400 [verto_communicator] Added Camera and microphone preview after the splash screen. --- .../js/3rd-party/volume-meter.js | 96 ++++++++++++ .../verto_communicator/src/css/verto.css | 40 +++++ html5/verto/verto_communicator/src/index.html | 2 + .../src/partials/preview.html | 70 +++++++++ .../src/storageService/services/storage.js | 1 + .../src/vertoApp/vertoApp.module.js | 5 + .../controllers/DialPadController.js | 8 +- .../controllers/MainController.js | 5 +- .../controllers/PreviewController.js | 142 ++++++++++++++++++ .../controllers/SplashScreenController.js | 31 ++-- 10 files changed, 384 insertions(+), 16 deletions(-) create mode 100644 html5/verto/verto_communicator/js/3rd-party/volume-meter.js create mode 100644 html5/verto/verto_communicator/src/partials/preview.html create mode 100644 html5/verto/verto_communicator/src/vertoControllers/controllers/PreviewController.js 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/src/css/verto.css b/html5/verto/verto_communicator/src/css/verto.css index 9d75a32037..c9694e15d3 100644 --- a/html5/verto/verto_communicator/src/css/verto.css +++ b/html5/verto/verto_communicator/src/css/verto.css @@ -8,6 +8,10 @@ body { padding-top: 60px; } +.panel.panel-material-blue-900 .panel-heading { + background-color: #0d47a1; +} + .install { color: white; text-decoration: underline; @@ -1469,3 +1473,39 @@ body:-webkit-full-screen #incall .video-footer { } } + +.preview-wrapper { + position: relative; +} + +.preview-wrapper video { + transform: scaleX(-1); +} +#mic-meter { + position: absolute; + bottom: 5px; + left: 10px; +} +#mic-meter .icon { + margin-left: 3px; + color: white; +} +#mic-meter .volumes { + width: 30px; +} +#mic-meter .volumes .volume-segment { + height: 10px; + width: 100%; + border-radius: 5px; + border: 2px solid white; + display: block; + margin-top: 1.5px; +} + +#mic-meter .volumes .volume-segment.active { + background-color: white; +} + +#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/preview.html b/html5/verto/verto_communicator/src/partials/preview.html new file mode 100644 index 0000000000..947b0afb04 --- /dev/null +++ b/html5/verto/verto_communicator/src/partials/preview.html @@ -0,0 +1,70 @@ + + +
+
+
+
+

Setup your camera and microphone settings

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+ + +
+ + + + +
+ +
+
+
+
+
+
diff --git a/html5/verto/verto_communicator/src/storageService/services/storage.js b/html5/verto/verto_communicator/src/storageService/services/storage.js index f0b8bb1b80..2ac76532f6 100644 --- a/html5/verto/verto_communicator/src/storageService/services/storage.js +++ b/html5/verto/verto_communicator/src/storageService/services/storage.js @@ -21,6 +21,7 @@ userStatus: 'disconnected', mutedVideo: false, mutedMic: false, + preview: true, selectedVideo: null, selectedAudio: null, selectedShare: null, diff --git a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js index 966a7c1f7e..2962301362 100644 --- a/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js +++ b/html5/verto/verto_communicator/src/vertoApp/vertoApp.module.js @@ -41,6 +41,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/DialPadController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/DialPadController.js index 2944cae46d..14a8846f45 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; diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js index 571558c780..7bd5cb6819 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js @@ -52,7 +52,10 @@ storage.data.email = verto.data.email; storage.data.login = verto.data.login; storage.data.password = verto.data.password; - if (redirect) { + if (redirect && storage.data.preview) { + $location.path('/preview'); + } + else if (redirect) { $location.path('/dialpad'); } } 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..61fbc5f2e2 --- /dev/null +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/PreviewController.js @@ -0,0 +1,142 @@ +(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 handleMedia(stream) { + streamObj.stop ? streamObj.stop() : streamObj.active = false; + + 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; + streamObj.stop(); + $location.path('/dialpad'); + storage.data.preview = false; + }; + + $scope.screenshare = function() { + if(verto.data.shareCall) { + verto.screenshareHangup(); + return false; + } + verto.screenshare(storage.data.called_number); + }; + + $scope.call = function() { + if($rootScope.dialpadNumber) { + localVideo.src = null; + meter.shutdown(); + meter.onaudioprocess = null; + streamObj.stop(); + } + $rootScope.call($rootScope.dialpadNumber); + }; + + $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'); + } + }); + $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');