Skip to content

Commit

Permalink
feat(remotecontrol): Prevent multiple remote control sessions (jitsi#…
Browse files Browse the repository at this point in the history
  • Loading branch information
hristoterezov authored and virtuacoplenny committed Aug 17, 2017
1 parent 31dd3da commit 378a8d0
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 59 deletions.
22 changes: 21 additions & 1 deletion conference.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';

import { reload, reportError } from './modules/util/helpers';

import * as RemoteControlEvents
from './service/remotecontrol/RemoteControlEvents';
import UIEvents from './service/UI/UIEvents';
import UIUtil from './modules/UI/util/UIUtil';
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
Expand Down Expand Up @@ -1813,10 +1815,27 @@ export default {
ConferenceEvents.LOCK_STATE_CHANGED,
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));

APP.remoteControl.on(RemoteControlEvents.ACTIVE_CHANGED, isActive => {
room.setLocalParticipantProperty(
"remoteControlSessionStatus",
isActive
);
APP.UI.setLocalRemoteControlActiveChanged();
});

room.on(ConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,
(participant, name, oldValue, newValue) => {
if (name === "raisedHand") {
switch (name) {
case 'raisedHand':
APP.UI.setRaisedHandStatus(participant, newValue);
break;
case 'remoteControlSessionStatus':
APP.UI.setRemoteControlActiveStatus(
participant.getId(),
newValue);
break;
default:
// ignore
}
});

Expand Down Expand Up @@ -2361,6 +2380,7 @@ export default {
APP.UI.setLocalRaisedHandStatus(raisedHand);
}
},

/**
* Log event to callstats and analytics.
* @param {string} name the event name
Expand Down
20 changes: 20 additions & 0 deletions modules/UI/UI.js
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,26 @@ UI.getRemoteVideosCount = () => VideoLayout.getRemoteVideosCount();
UI.setRemoteThumbnailsVisibility
= shouldHide => Filmstrip.setRemoteVideoVisibility(shouldHide);

/**
* Sets the remote control active status for a remote participant.
*
* @param {string} participantID - The id of the remote participant.
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
UI.setRemoteControlActiveStatus = function(participantID, isActive) {
VideoLayout.setRemoteControlActiveStatus(participantID, isActive);
};

/**
* Sets the remote control active status for the local participant.
*
* @returns {void}
*/
UI.setLocalRemoteControlActiveChanged = function() {
VideoLayout.setLocalRemoteControlActiveChanged();
};

const UIListeners = new Map([
[
UIEvents.ETHERPAD_CLICKED,
Expand Down
55 changes: 30 additions & 25 deletions modules/UI/videolayout/RemoteVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
this.flipX = false;
this.isLocal = false;
this.popupMenuIsHovered = false;
this._isRemoteControlSessionActive = false;
/**
* The flag is set to <tt>true</tt> after the 'onplay' event has been
* triggered on the current video element. It goes back to <tt>false</tt>
Expand Down Expand Up @@ -87,9 +88,7 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {

this.initBrowserSpecificProperties();

if (APP.conference.isModerator || this._supportsRemoteControl) {
this.addRemoteVideoMenu();
}
this.addRemoteVideoMenu();

this.VideoLayout.resizeThumbnails(false, true);

Expand Down Expand Up @@ -135,7 +134,9 @@ RemoteVideo.prototype._generatePopupContent = function () {
let remoteControlState = null;
let onRemoteControlToggle;

if (this._supportsRemoteControl) {
if (this._supportsRemoteControl
&& ((!APP.remoteControl.active && !this._isRemoteControlSessionActive)
|| APP.remoteControl.controller.activeParticipant === this.id)) {
if (controller.getRequestedParticipant() === this.id) {
onRemoteControlToggle = () => {};
remoteControlState = REMOTE_CONTROL_MENU_STATES.REQUESTING;
Expand Down Expand Up @@ -179,7 +180,18 @@ RemoteVideo.prototype._generatePopupContent = function () {
};

RemoteVideo.prototype._onRemoteVideoMenuDisplay = function () {
this.updateRemoteVideoMenu(this.isAudioMuted, true);
this.updateRemoteVideoMenu();
};

/**
* Sets the remote control active status for the remote video.
*
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
RemoteVideo.prototype.setRemoteControlActiveStatus = function(isActive) {
this._isRemoteControlSessionActive = isActive;
this.updateRemoteVideoMenu();
};

/**
Expand All @@ -192,18 +204,7 @@ RemoteVideo.prototype.setRemoteControlSupport = function(isSupported = false) {
return;
}
this._supportsRemoteControl = isSupported;
if(!isSupported) {
return;
}

if(!this.hasRemoteVideoMenu) {
//create menu
this.addRemoteVideoMenu();
} else {
//update the content
this.updateRemoteVideoMenu(this.isAudioMuted, true);
}

this.updateRemoteVideoMenu();
};

/**
Expand All @@ -215,7 +216,7 @@ RemoteVideo.prototype._requestRemoteControlPermissions = function () {
if(result === null) {
return;
}
this.updateRemoteVideoMenu(this.isAudioMuted, true);
this.updateRemoteVideoMenu();
APP.UI.messageHandler.notify(
"dialog.remoteControlTitle",
(result === false) ? "dialog.remoteControlDeniedMessage"
Expand All @@ -232,15 +233,15 @@ RemoteVideo.prototype._requestRemoteControlPermissions = function () {
}
}, error => {
logger.error(error);
this.updateRemoteVideoMenu(this.isAudioMuted, true);
this.updateRemoteVideoMenu();
APP.UI.messageHandler.notify(
"dialog.remoteControlTitle",
"dialog.remoteControlErrorMessage",
{user: this.user.getDisplayName()
|| interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME}
);
});
this.updateRemoteVideoMenu(this.isAudioMuted, true);
this.updateRemoteVideoMenu();
};

/**
Expand All @@ -249,7 +250,7 @@ RemoteVideo.prototype._requestRemoteControlPermissions = function () {
RemoteVideo.prototype._stopRemoteControl = function () {
// send message about stopping
APP.remoteControl.controller.stop();
this.updateRemoteVideoMenu(this.isAudioMuted, true);
this.updateRemoteVideoMenu();
};

/**
Expand Down Expand Up @@ -286,9 +287,10 @@ RemoteVideo.prototype._setAudioVolume = function (newVal) {
* Updates the remote video menu.
*
* @param isMuted the new muted state to update to
* @param force to work even if popover is not visible
*/
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
RemoteVideo.prototype.updateRemoteVideoMenu = function (
isMuted = this.isAudioMuted
) {
this.isAudioMuted = isMuted;

this._generatePopupContent();
Expand Down Expand Up @@ -323,8 +325,6 @@ RemoteVideo.prototype._figureOutMutedWhileDisconnected = function() {
* Adds the remote video menu element for the given <tt>id</tt> in the
* given <tt>parentElement</tt>.
*
* @param id the id indicating the video for which we're adding a menu.
* @param parentElement the parent element where this menu will be added
*/
RemoteVideo.prototype.addRemoteVideoMenu = function () {
if (interfaceConfig.filmStripOnly) {
Expand Down Expand Up @@ -576,6 +576,11 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) {

if (!isVideo) {
this._audioStreamElement = streamElement;

// If the remote video menu was created before the audio stream was
// attached we need to update the menu in order to show the volume
// slider.
this.updateRemoteVideoMenu();
}
};

Expand Down
25 changes: 22 additions & 3 deletions modules/UI/videolayout/VideoLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,9 +642,7 @@ var VideoLayout = {
return;

remoteVideo.showAudioIndicator(isMuted);
if (APP.conference.isModerator) {
remoteVideo.updateRemoteVideoMenu(isMuted);
}
remoteVideo.updateRemoteVideoMenu(isMuted);
}
},

Expand Down Expand Up @@ -1165,6 +1163,27 @@ var VideoLayout = {
*/
getRemoteVideosCount() {
return Object.keys(remoteVideos).length;
},
/**
* Sets the remote control active status for a remote participant.
*
* @param {string} participantID - The id of the remote participant.
* @param {boolean} isActive - The new remote control active status.
* @returns {void}
*/
setRemoteControlActiveStatus(participantID, isActive) {
remoteVideos[participantID].setRemoteControlActiveStatus(isActive);
},

/**
* Sets the remote control active status for the local participant.
*
* @returns {void}
*/
setLocalRemoteControlActiveChanged() {
Object.values(remoteVideos).forEach(
remoteVideo => remoteVideo.updateRemoteVideoMenu()
);
}
};

Expand Down
19 changes: 18 additions & 1 deletion modules/remotecontrol/Controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
PERMISSIONS_ACTIONS,
REMOTE_CONTROL_MESSAGE_NAME
} from '../../service/remotecontrol/Constants';
import * as RemoteControlEvents
from '../../service/remotecontrol/RemoteControlEvents';
import UIEvents from '../../service/UI/UIEvents';

import RemoteControlParticipant from './RemoteControlParticipant';
Expand Down Expand Up @@ -86,6 +88,15 @@ export default class Controller extends RemoteControlParticipant {
= this._onLargeVideoIdChanged.bind(this);
}

/**
* Returns the current active participant's id.
*
* @returns {string|null} - The id of the current active participant.
*/
get activeParticipant(): string | null {
return this._requestedParticipant || this._controlledParticipant;
}

/**
* Requests permissions from the remote control receiver side.
*
Expand All @@ -100,7 +111,7 @@ export default class Controller extends RemoteControlParticipant {
if (!this._enabled) {
return Promise.reject(new Error('Remote control is disabled!'));
}

this.emit(RemoteControlEvents.ACTIVE_CHANGED, true);
this._area = eventCaptureArea;// $("#largeVideoWrapper")
logger.log(`Requsting remote control permissions from: ${userId}`);

Expand All @@ -125,16 +136,21 @@ export default class Controller extends RemoteControlParticipant {
result = this._handleReply(participant, event);
} catch (e) {
clearRequest();
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
reject(e);
}
if (result !== null) {
clearRequest();
if (result === false) {
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
}
resolve(result);
}
};
onUserLeft = id => {
if (id === this._requestedParticipant) {
clearRequest();
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
resolve(null);
}
};
Expand Down Expand Up @@ -316,6 +332,7 @@ export default class Controller extends RemoteControlParticipant {
this.pause();
this._controlledParticipant = null;
this._area = undefined;
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
'dialog.remoteControlStopMessage'
Expand Down
5 changes: 5 additions & 0 deletions modules/remotecontrol/Receiver.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
REMOTE_CONTROL_MESSAGE_NAME,
REQUESTS
} from '../../service/remotecontrol/Constants';
import * as RemoteControlEvents
from '../../service/remotecontrol/RemoteControlEvents';
import {
Transport,
PostMessageTransportBackend
Expand Down Expand Up @@ -132,6 +134,7 @@ export default class Receiver extends RemoteControlParticipant {
name: REMOTE_CONTROL_MESSAGE_NAME,
type: EVENTS.stop
});
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
if (!dontNotify) {
APP.UI.messageHandler.notify(
'dialog.remoteControlTitle',
Expand Down Expand Up @@ -177,6 +180,7 @@ export default class Receiver extends RemoteControlParticipant {
&& message.action === PERMISSIONS_ACTIONS.request) {
const userId = participant.getId();

this.emit(RemoteControlEvents.ACTIVE_CHANGED, true);
APP.store.dispatch(
openRemoteControlAuthorizationDialog(userId));
} else if (this._controller === participant.getId()) {
Expand All @@ -200,6 +204,7 @@ export default class Receiver extends RemoteControlParticipant {
* @returns {void}
*/
deny(userId: string) {
this.emit(RemoteControlEvents.ACTIVE_CHANGED, false);
this.sendRemoteControlEndpointMessage(userId, {
type: EVENTS.permissions,
action: PERMISSIONS_ACTIONS.deny
Expand Down
Loading

0 comments on commit 378a8d0

Please sign in to comment.