From 69c38c05acc182a0c871350bf961eccf53c3db13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 6 Jun 2018 10:44:21 +0200 Subject: [PATCH 1/3] Move code to mute and unmute remote videos to OCA.SpreedMe.videos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- js/webrtc.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/js/webrtc.js b/js/webrtc.js index acf9c5ab864..abee4484915 100644 --- a/js/webrtc.js +++ b/js/webrtc.js @@ -303,6 +303,28 @@ var spreedPeerConnectionTable = []; $(container).prependTo($('#videos')); return container; }, + muteRemoteVideo: function(id) { + if (!(typeof id === 'string' || id instanceof String)) { + return; + } + + var $container = $(OCA.SpreedMe.videos.getContainerId(id)); + + var avatarContainer = $container.find('.avatar-container'); + avatarContainer.removeClass('hidden'); + avatarContainer.show(); + $container.find('video').hide(); + }, + unmuteRemoteVideo: function(id) { + if (!(typeof id === 'string' || id instanceof String)) { + return; + } + + var $container = $(OCA.SpreedMe.videos.getContainerId(id)); + + $container.find('.avatar-container').hide(); + $container.find('video').show(); + }, remove: function(id) { if (!(typeof id === 'string' || id instanceof String)) { return; @@ -917,10 +939,7 @@ var spreedPeerConnectionTable = []; var $el = $(el); if (data.name === 'video') { - var avatarContainer = $el.find('.avatar-container'); - avatarContainer.removeClass('hidden'); - avatarContainer.show(); - $el.find('video').hide(); + OCA.SpreedMe.videos.muteRemoteVideo(data.id); } else { var muteIndicator = $el.find('.muteIndicator'); muteIndicator.removeClass('audio-on'); @@ -943,8 +962,7 @@ var spreedPeerConnectionTable = []; var $el = $(el); if (data.name === 'video') { - $el.find('.avatar-container').hide(); - $el.find('video').show(); + OCA.SpreedMe.videos.unmuteRemoteVideo(data.id); } else { var muteIndicator = $el.find('.muteIndicator'); muteIndicator.removeClass('audio-off'); From 1a3664a6d7b75f075cf06f28d5a4a8a9db2768a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 6 Jun 2018 11:10:37 +0200 Subject: [PATCH 2/3] Remove unneeded code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "hidden" CSS class is not used in the avatar container of remote participants (only for the local one), so there is no need to remove it. Signed-off-by: Daniel Calviño Sánchez --- js/webrtc.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/webrtc.js b/js/webrtc.js index abee4484915..9831bbb7a08 100644 --- a/js/webrtc.js +++ b/js/webrtc.js @@ -310,9 +310,7 @@ var spreedPeerConnectionTable = []; var $container = $(OCA.SpreedMe.videos.getContainerId(id)); - var avatarContainer = $container.find('.avatar-container'); - avatarContainer.removeClass('hidden'); - avatarContainer.show(); + $container.find('.avatar-container').show(); $container.find('video').hide(); }, unmuteRemoteVideo: function(id) { From 2dcaae4666f022e46794f64ec71b1ed32340e93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Wed, 6 Jun 2018 12:17:51 +0200 Subject: [PATCH 3/3] Add toggle to show and hide video from other participants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before a user could disable her own video, but she had no way to disable the videos from other participants. This could be needed, for example, to alleviate the load on low-end systems, as in that case playing the video from remote participants could be too much for the system. Now a toggle is provided to manually show or hide the video of each remote participant if needed. The toggle is shown only when the remote participant is sending video; if the remote participant has disabled her own video (or does not have a camera) the toggle is hidden. Note that the toggle just shows or hides the HTML video element; it does not notify the remote participant to mute her video or to fully stop sending it. It is purely a local change that does not affect the remote clients. In the future this could be extended to involve the remote clients too, but for now just hiding the HTML video element notably reduces the CPU load in most systems (although unfortunately in some cases it does not). Signed-off-by: Daniel Calviño Sánchez --- css/style.scss | 1 + js/webrtc.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/css/style.scss b/css/style.scss index cc34ac42d78..80f29ab6898 100644 --- a/css/style.scss +++ b/css/style.scss @@ -519,6 +519,7 @@ video { } .muteIndicator, +.hideRemoteVideo, .screensharingIndicator, .iceFailedIndicator { position: relative; diff --git a/js/webrtc.js b/js/webrtc.js index 9831bbb7a08..70ff3e4474d 100644 --- a/js/webrtc.js +++ b/js/webrtc.js @@ -275,6 +275,14 @@ var spreedPeerConnectionTable = []; muteIndicator.className = 'muteIndicator icon-white icon-shadow icon-audio-off audio-on'; muteIndicator.disabled = true; + var hideRemoteVideoButton = document.createElement('button'); + hideRemoteVideoButton.className = 'hideRemoteVideo icon-white icon-shadow icon-video'; + hideRemoteVideoButton.setAttribute('style', 'display: none;'); + hideRemoteVideoButton.setAttribute('data-original-title', t('spreed', 'Disable video')); + hideRemoteVideoButton.onclick = function() { + OCA.SpreedMe.videos._toggleRemoteVideo(id); + }; + var screenSharingIndicator = document.createElement('button'); screenSharingIndicator.className = 'screensharingIndicator icon-white icon-shadow icon-screen screen-off'; screenSharingIndicator.setAttribute('data-original-title', t('spreed', 'Show screen')); @@ -283,12 +291,18 @@ var spreedPeerConnectionTable = []; iceFailedIndicator.className = 'iceFailedIndicator icon-white icon-shadow icon-error not-failed'; iceFailedIndicator.disabled = true; + $(hideRemoteVideoButton).tooltip({ + placement: 'top', + trigger: 'hover' + }); + $(screenSharingIndicator).tooltip({ placement: 'top', trigger: 'hover' }); mediaIndicator.appendChild(muteIndicator); + mediaIndicator.appendChild(hideRemoteVideoButton); mediaIndicator.appendChild(screenSharingIndicator); mediaIndicator.appendChild(iceFailedIndicator); @@ -312,6 +326,7 @@ var spreedPeerConnectionTable = []; $container.find('.avatar-container').show(); $container.find('video').hide(); + $container.find('.hideRemoteVideo').hide(); }, unmuteRemoteVideo: function(id) { if (!(typeof id === 'string' || id instanceof String)) { @@ -320,8 +335,39 @@ var spreedPeerConnectionTable = []; var $container = $(OCA.SpreedMe.videos.getContainerId(id)); - $container.find('.avatar-container').hide(); - $container.find('video').show(); + var $hideRemoteVideoButton = $container.find('.hideRemoteVideo'); + $hideRemoteVideoButton.show(); + + if ($hideRemoteVideoButton.hasClass('icon-video')) { + $container.find('.avatar-container').hide(); + $container.find('video').show(); + } + }, + _toggleRemoteVideo: function(id) { + if (!(typeof id === 'string' || id instanceof String)) { + return; + } + + var $container = $(OCA.SpreedMe.videos.getContainerId(id)); + + var $hideRemoteVideoButton = $container.find('.hideRemoteVideo'); + if ($hideRemoteVideoButton.hasClass('icon-video')) { + $container.find('.avatar-container').show(); + $container.find('video').hide(); + $hideRemoteVideoButton.attr('data-original-title', t('spreed', 'Enable video')) + .removeClass('icon-video') + .addClass('icon-video-off'); + } else { + $container.find('.avatar-container').hide(); + $container.find('video').show(); + $hideRemoteVideoButton.attr('data-original-title', t('spreed', 'Disable video')) + .removeClass('icon-video-off') + .addClass('icon-video'); + } + + if (latestSpeakerId === id) { + OCA.SpreedMe.speakers.updateVideoContainerDummy(id); + } }, remove: function(id) { if (!(typeof id === 'string' || id instanceof String)) { @@ -470,6 +516,15 @@ var spreedPeerConnectionTable = []; .append(newContainer.find('.speakingIndicator').clone()) ); + // Cloning does not copy event handlers by default; it could be + // forced with a parameter, but the tooltip would have to be + // explicitly set on the new element anyway. Due to this the + // click handler is explicitly copied too. + $('.videoContainer-dummy').find('.hideRemoteVideo').get(0).onclick = newContainer.find('.hideRemoteVideo').get(0).onclick; + $('.videoContainer-dummy').find('.hideRemoteVideo').tooltip({ + placement: 'top', + trigger: 'hover' + }); }, add: function(id, notPromote) { if (!(typeof id === 'string' || id instanceof String)) {