diff --git a/app/lib/RoomClient.js b/app/lib/RoomClient.js index 45e0b8b..b6f1869 100644 --- a/app/lib/RoomClient.js +++ b/app/lib/RoomClient.js @@ -3,7 +3,7 @@ import * as mediasoupClient from 'mediasoup-client'; import Logger from './Logger'; import hark from 'hark'; import ScreenShare from './ScreenShare'; -import LastN from './LastN'; +import Spotlights from './Spotlights'; import { getSignalingUrl } from './urlFactory'; import * as cookiesManager from './cookiesManager'; import * as requestActions from './redux/requestActions'; @@ -21,7 +21,7 @@ const ROOM_OPTIONS = requestTimeout : requestTimeout, transportOptions : transportOptions, turnServers : turnServers, - lastN : 2 + maxSpotlights : 4 }; const VIDEO_CONSTRAINS = @@ -72,11 +72,11 @@ export default class RoomClient this._room = new mediasoupClient.Room(ROOM_OPTIONS); this._room.roomId = roomId; - // LastN speakers - this._lastNSpeakers = ROOM_OPTIONS.lastN; + // Max spotlights + this._maxSpotlights = ROOM_OPTIONS.maxSpotlights; - // Array of lastN speakers - this._lastN = new LastN(this._lastNSpeakers, this._room); + // Manager of spotlight + this._spotlights = new Spotlights(this._maxSpotlights, this._room); // Transport for sending. this._sendTransport = null; @@ -315,7 +315,7 @@ export default class RoomClient lastN.splice(index, 1); - this._lastN.addSpeakerList(lastN); + this._spotlights.addSpeakerList(lastN); } } catch (error) @@ -340,16 +340,16 @@ export default class RoomClient this._micProducer.resume(); } - // Updated consumers based on lastN - async updateSpeakers(speakers) + // Updated consumers based on spotlights + async updateSpotlights(spotlights) { - logger.debug('updateSpeakers()'); + logger.debug('updateSpotlights()'); try { for (const peer of this._room.peers) { - if (speakers.indexOf(peer.name) > -1) // Resume video for speaker + if (spotlights.indexOf(peer.name) > -1) // Resume video for speaker { for (const consumer of peer.consumers) { @@ -373,7 +373,7 @@ export default class RoomClient } catch (error) { - logger.error('updateSpeakers() failed: %o', error); + logger.error('updateSpotlights() failed: %o', error); } } @@ -1036,7 +1036,7 @@ export default class RoomClient stateActions.setRoomActiveSpeaker(peerName)); if (peerName && peerName !== this._peerName) - this._lastN.handleActiveSpeaker(peerName); + this._spotlights.handleActiveSpeaker(peerName); }); this._signalingSocket.on('display-name-changed', (data) => @@ -1228,10 +1228,10 @@ export default class RoomClient this.notify('You are in the room'); - this._lastN.on('lastn-updated', (lastN) => + this._spotlights.on('spotlights-updated', (spotlights) => { - this._dispatch(stateActions.setLastN(lastN)); - this.updateSpeakers(lastN); + this._dispatch(stateActions.setSpotlights(spotlights)); + this.updateSpotlights(spotlights); }); const peers = this._room.peers; @@ -1241,7 +1241,7 @@ export default class RoomClient this._handlePeer(peer, { notify: false }); } - this._lastN.start(); + this._spotlights.start(); } catch (error) { @@ -1831,7 +1831,8 @@ export default class RoomClient // Receive the consumer (if we can). if (consumer.supported) { - if (consumer.kind === 'video' && !this._lastN.peerInLastN(consumer.peer.name)) + if (consumer.kind === 'video' && + !this._spotlights.peerInSpotlights(consumer.peer.name)) { // Start paused logger.debug( 'consumer paused by default'); diff --git a/app/lib/Spotlights.js b/app/lib/Spotlights.js new file mode 100644 index 0000000..33f8015 --- /dev/null +++ b/app/lib/Spotlights.js @@ -0,0 +1,135 @@ +import { EventEmitter } from 'events'; +import Logger from './Logger'; + +const logger = new Logger('Spotlight'); + +export default class Spotlights extends EventEmitter +{ + constructor(maxSpotlights, room) + { + super(); + + this._room = room; + this._maxSpotlights = maxSpotlights; + this._peerList = []; + this._currentSpotlights = []; + this._started = false; + } + + start() + { + const peers = this._room.peers; + + for (const peer of peers) + { + this._handlePeer(peer); + } + + this._handleRoom(); + + this._started = true; + this._spotlightsUpdated(); + } + + peerInSpotlights(peerName) + { + if (this._started) + { + return this._currentSpotlights.indexOf(peerName) !== -1; + } + else + { + return false; + } + } + + _handleRoom() + { + this._room.on('newpeer', (peer) => + { + logger.debug( + 'room "newpeer" event [name:"%s", peer:%o]', peer.name, peer); + this._handlePeer(peer); + }); + } + + addSpeakerList(speakerList) + { + this._peerList = [ ...new Set([ ...speakerList, ...this._peerList ]) ]; + + if (this._started) + this._spotlightsUpdated(); + } + + _handlePeer(peer) + { + logger.debug('_handlePeer() [peerName:"%s"]', peer.name); + + if (this._peerList.indexOf(peer.name) === -1) // We don't have this peer in the list + { + peer.on('close', () => + { + const index = this._peerList.indexOf(peer.name); + + if (index > -1) // We have this peer in the list, remove + { + this._peerList.splice(index, 1); + + this._spotlightsUpdated(); + } + }); + + logger.debug('_handlePeer() | adding peer [peerName:"%s"]', peer.name); + + this._peerList.push(peer.name); + + this._spotlightsUpdated(); + } + } + + handleActiveSpeaker(peerName) + { + logger.debug('handleActiveSpeaker() [peerName:"%s"]', peerName); + + const index = this._peerList.indexOf(peerName); + + if (index > -1) + { + this._peerList.splice(index, 1); + this._peerList = [ peerName ].concat(this._peerList); + + this._spotlightsUpdated(); + } + } + + _spotlightsUpdated() + { + if ( + !this._arraysEqual( + this._currentSpotlights, this._peerList.slice(0, this._maxSpotlights) + ) + ) + { + logger.debug('_spotlightsUpdated() | spotlights updated, emitting'); + + this._currentSpotlights = this._peerList.slice(0, this._maxSpotlights); + this.emit('spotlights-updated', this._currentSpotlights); + } + else + logger.debug('_spotlightsUpdated() | spotlights not updated'); + } + + _arraysEqual(arr1, arr2) + { + if (arr1.length !== arr2.length) + return false; + + for (let i = arr1.length; i--;) + { + if (arr1[i] !== arr2[i]) + return false; + } + + return true; + } +} diff --git a/app/lib/components/Filmstrip.jsx b/app/lib/components/Filmstrip.jsx index 60c5ee6..ba5bf6a 100644 --- a/app/lib/components/Filmstrip.jsx +++ b/app/lib/components/Filmstrip.jsx @@ -114,7 +114,7 @@ class Filmstrip extends Component render() { - const { peers, advancedMode, lastN, lastNLength } = this.props; + const { peers, advancedMode, spotlights, spotlightsLength } = this.props; const activePeerName = this.getActivePeerName(); @@ -143,7 +143,7 @@ class Filmstrip extends Component Object.keys(peers).map((peerName) => { return ( - lastN.find((lastNElement) => lastNElement === peerName)? + spotlights.find((spotlightsElement) => spotlightsElement === peerName)?
this.props.setSelectedPeer(peerName)} @@ -166,9 +166,9 @@ class Filmstrip extends Component
- { (lastNLength:null }
@@ -186,13 +186,13 @@ Filmstrip.propTypes = { myName : PropTypes.string.isRequired, selectedPeerName : PropTypes.string, setSelectedPeer : PropTypes.func.isRequired, - lastNLength : PropTypes.number, - lastN : PropTypes.array.isRequired + spotlightsLength : PropTypes.number, + spotlights : PropTypes.array.isRequired }; const mapStateToProps = (state) => { - const lastNLength = state.room.lastN ? state.room.lastN.length : 0; + const spotlightsLength = state.room.spotlights ? state.room.spotlights.length : 0; return { activeSpeakerName : state.room.activeSpeakerName, @@ -200,8 +200,8 @@ const mapStateToProps = (state) => peers : state.peers, consumers : state.consumers, myName : state.me.name, - lastN : state.room.lastN, - lastNLength + spotlights : state.room.spotlights, + spotlightsLength }; }; diff --git a/app/lib/components/Peers.jsx b/app/lib/components/Peers.jsx index 8230976..662250b 100644 --- a/app/lib/components/Peers.jsx +++ b/app/lib/components/Peers.jsx @@ -93,8 +93,8 @@ class Peers extends React.Component advancedMode, activeSpeakerName, peers, - lastN, - lastNLength + spotlights, + spotlightsLength } = this.props; const style = @@ -109,8 +109,8 @@ class Peers extends React.Component peers.map((peer) => { return ( - (lastN.find(function(lastNElement) - { return lastNElement == peer.name; }))? + (spotlights.find(function(spotlightsElement) + { return spotlightsElement == peer.name; }))?
- { (lastNLength:null }
@@ -146,24 +146,24 @@ Peers.propTypes = peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired, boxes : PropTypes.number, activeSpeakerName : PropTypes.string, - lastNLength : PropTypes.number, - lastN : PropTypes.array.isRequired + spotlightsLength : PropTypes.number, + spotlights : PropTypes.array.isRequired }; const mapStateToProps = (state) => { const peers = Object.values(state.peers); - const lastN = state.room.lastN; - const lastNLength = lastN ? state.room.lastN.length : 0; - const boxes = lastNLength + Object.values(state.consumers) + const spotlights = state.room.spotlights; + const spotlightsLength = spotlights ? state.room.spotlights.length : 0; + const boxes = spotlightsLength + Object.values(state.consumers) .filter((consumer) => consumer.source === 'screen').length; return { peers, boxes, activeSpeakerName : state.room.activeSpeakerName, - lastN, - lastNLength + spotlights, + spotlightsLength }; }; diff --git a/app/lib/redux/reducers/room.js b/app/lib/redux/reducers/room.js index ad390f0..6a956fd 100644 --- a/app/lib/redux/reducers/room.js +++ b/app/lib/redux/reducers/room.js @@ -9,7 +9,7 @@ const initialState = toolbarsVisible : true, mode : 'democratic', selectedPeerName : null, - lastN : [] + spotlights : [] }; const room = (state = initialState, action) => @@ -84,11 +84,11 @@ const room = (state = initialState, action) => }; } - case 'SET_LASTN': + case 'SET_SPOTLIGHTS': { - const { lastN } = action.payload; + const { spotlights } = action.payload; - return { ...state, lastN }; + return { ...state, spotlights }; } default: diff --git a/app/lib/redux/stateActions.js b/app/lib/redux/stateActions.js index c4a7818..fc2c096 100644 --- a/app/lib/redux/stateActions.js +++ b/app/lib/redux/stateActions.js @@ -494,8 +494,8 @@ export const setSelectedPeer = (selectedPeerName) => payload : { selectedPeerName } }); -export const setLastN = (lastN) => +export const setSpotlights = (spotlights) => ({ - type : 'SET_LASTN', - payload : { lastN } + type : 'SET_SPOTLIGHTS', + payload : { spotlights } });