diff --git a/html5/verto/verto_communicator/js/3rd-party/attachSinkId.js b/html5/verto/verto_communicator/js/3rd-party/attachSinkId.js new file mode 100644 index 0000000000..9ca286d4a4 --- /dev/null +++ b/html5/verto/verto_communicator/js/3rd-party/attachSinkId.js @@ -0,0 +1,18 @@ +function attachSinkId(element, sinkId) { + if (typeof element.sinkId !== 'undefined') { + element.setSinkId(sinkId) + .then(function() { + console.log('Success, audio output device attached:', sinkId); + }) + .catch(function(error) { + var errorMessage = error; + if (error.name === 'SecurityError') { + errorMessage = 'You need to use HTTPS for selecting audio output ' + + 'device: ' + error; + } + console.error(errorMessage); + }); + } else { + console.warn('Browser does not support output device selection.'); + } +} diff --git a/html5/verto/verto_communicator/src/css/verto.css b/html5/verto/verto_communicator/src/css/verto.css index 2cd6622cbd..4e0175d583 100644 --- a/html5/verto/verto_communicator/src/css/verto.css +++ b/html5/verto/verto_communicator/src/css/verto.css @@ -605,7 +605,7 @@ body .modal-body .btn-group .btn.active { } #incall .video-hover-buttons .btn-group .dropdown-menu { - height: 200px; + max-height: 200px; overflow: auto; } diff --git a/html5/verto/verto_communicator/src/index.html b/html5/verto/verto_communicator/src/index.html index 47bdf740cd..d15c3d9af1 100644 --- a/html5/verto/verto_communicator/src/index.html +++ b/html5/verto/verto_communicator/src/index.html @@ -96,6 +96,7 @@ <script type="text/javascript" src="js/3rd-party/getScreenId.js"></script> <script type="text/javascript" src="js/3rd-party/md5.min.js"></script> <script type="text/javascript" src="js/3rd-party/volume-meter.js"></script> + <script type="text/javascript" src="js/3rd-party/attachSinkId.js"></script> <script type="text/javascript" src="src/vertoApp/vertoApp.module.js"></script> diff --git a/html5/verto/verto_communicator/src/partials/modal_settings.html b/html5/verto/verto_communicator/src/partials/modal_settings.html index bffdf5e6a1..f4775c0e36 100644 --- a/html5/verto/verto_communicator/src/partials/modal_settings.html +++ b/html5/verto/verto_communicator/src/partials/modal_settings.html @@ -22,10 +22,17 @@ <select name="microphone" id="settings-microphone" class="form-control" ng-model="mydata.selectedAudio" ng-options="item.id as item.label for item in verto.data.audioDevices"> </select> - - <a class="btn btn-primary" href="" ng-click="refreshDeviceList()">Refresh device list</a> </div> + <div class="form-group"> + <label for="settings-microphone">Speaker:</label> + <select name="microphone" id="settings-microphone" class="form-control" + ng-model="mydata.selectedSpeaker" ng-options="item.id as item.label for item in verto.data.speakerDevices"> + </select> + </div> + + <a class="btn btn-primary" href="" ng-click="refreshDeviceList()">Refresh device list</a> + <div class="form-group"> <label for="settings-microphone">General settings:</label> <div class="checkbox"> diff --git a/html5/verto/verto_communicator/src/partials/video_call.html b/html5/verto/verto_communicator/src/partials/video_call.html index b3d5f1894c..9633149c77 100644 --- a/html5/verto/verto_communicator/src/partials/video_call.html +++ b/html5/verto/verto_communicator/src/partials/video_call.html @@ -57,6 +57,17 @@ class="btn btn-material-blue-900" ng-click="toggleChat()" ng-show="fullscreenEnabled"> <i class="mdi-communication-chat"></i> </button> + <div class="btn-group"> + <button tooltip-placement="bottom" tooltip-title="Speaker" uib-tooltips="Speaker" type="button" class="btn btn-material-blue-900 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> + <i class="mdi-hardware-headset"></i> + <span class="caret"></span> + </button> + <ul class="dropdown-menu"> + <li ng-repeat="speaker in verto.data.speakerDevices"> + <a ng-click="confChangeSpeaker(speaker.id)">{{ speaker.label }}</a> + </li> + </ul> + </div> </div> </div> <div class="video-tag-wrapper" id="video-tag-wrapper" ng-dblclick="goFullscreen()" show-controls> diff --git a/html5/verto/verto_communicator/src/storageService/services/storage.js b/html5/verto/verto_communicator/src/storageService/services/storage.js index b344ea2324..da3a600589 100644 --- a/html5/verto/verto_communicator/src/storageService/services/storage.js +++ b/html5/verto/verto_communicator/src/storageService/services/storage.js @@ -25,6 +25,7 @@ selectedVideo: null, selectedAudio: null, selectedShare: null, + selectedSpeaker: null, useStereo: true, useSTUN: true, useDedenc: false, diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js index 7f8d7b0765..93e510d5e3 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/InCallController.js @@ -75,6 +75,10 @@ verto.data.conf.setVideoLayout(layout); }; + $scope.confChangeSpeaker = function(speakerId) { + storage.data.selectedSpeaker = speakerId; + $rootScope.$emit('changedSpeaker', speakerId); + }; $scope.screenshare = function() { if(verto.data.shareCall) { diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js index 4a4a3accfe..dd4f1cd64f 100644 --- a/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js +++ b/html5/verto/verto_communicator/src/vertoControllers/controllers/MainController.js @@ -34,6 +34,11 @@ $rootScope.$on('config.http.success', function(ev) { $scope.login(false); }); + + $rootScope.$on('changedSpeaker', function(event, speakerId) { + attachSinkId(myVideo, speakerId); + }); + /** * Login the user to verto server and * redirects him to dialpad page. diff --git a/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js b/html5/verto/verto_communicator/src/vertoControllers/controllers/ModalSettingsController.js index 7bc9aa7452..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,12 @@ $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(); } diff --git a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js index 9fc0d4fa04..9bf54c3cbd 100644 --- a/html5/verto/verto_communicator/src/vertoService/services/vertoService.js +++ b/html5/verto/verto_communicator/src/vertoService/services/vertoService.js @@ -214,6 +214,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora label: 'Screen' }]; data.audioDevices = []; + data.speakerDevices = []; if(!storage.data.selectedShare) { storage.data.selectedShare = data.shareDevices[0]['id']; @@ -271,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! @@ -595,6 +616,7 @@ vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'stora }); data.instance.deviceParams({ useCamera: storage.data.selectedVideo, + useSpeak: storage.data.selectedSpeaker, useMic: storage.data.selectedAudio, onResCheck: that.refreshVideoResolution }); @@ -664,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,