Merge branch 'feature-lastn' of https://github.com/havfo/multiparty-meeting into feature-lastn
commit
997805eca0
|
|
@ -3,7 +3,7 @@ import * as mediasoupClient from 'mediasoup-client';
|
||||||
import Logger from './Logger';
|
import Logger from './Logger';
|
||||||
import hark from 'hark';
|
import hark from 'hark';
|
||||||
import ScreenShare from './ScreenShare';
|
import ScreenShare from './ScreenShare';
|
||||||
import LastN from './LastN';
|
import Spotlights from './Spotlights';
|
||||||
import { getSignalingUrl } from './urlFactory';
|
import { getSignalingUrl } from './urlFactory';
|
||||||
import * as cookiesManager from './cookiesManager';
|
import * as cookiesManager from './cookiesManager';
|
||||||
import * as requestActions from './redux/requestActions';
|
import * as requestActions from './redux/requestActions';
|
||||||
|
|
@ -21,7 +21,7 @@ const ROOM_OPTIONS =
|
||||||
requestTimeout : requestTimeout,
|
requestTimeout : requestTimeout,
|
||||||
transportOptions : transportOptions,
|
transportOptions : transportOptions,
|
||||||
turnServers : turnServers,
|
turnServers : turnServers,
|
||||||
lastN : 2
|
maxSpotlights : 4
|
||||||
};
|
};
|
||||||
|
|
||||||
const VIDEO_CONSTRAINS =
|
const VIDEO_CONSTRAINS =
|
||||||
|
|
@ -72,11 +72,11 @@ export default class RoomClient
|
||||||
this._room = new mediasoupClient.Room(ROOM_OPTIONS);
|
this._room = new mediasoupClient.Room(ROOM_OPTIONS);
|
||||||
this._room.roomId = roomId;
|
this._room.roomId = roomId;
|
||||||
|
|
||||||
// LastN speakers
|
// Max spotlights
|
||||||
this._lastNSpeakers = ROOM_OPTIONS.lastN;
|
this._maxSpotlights = ROOM_OPTIONS.maxSpotlights;
|
||||||
|
|
||||||
// Array of lastN speakers
|
// Manager of spotlight
|
||||||
this._lastN = new LastN(this._lastNSpeakers, this._room);
|
this._spotlights = new Spotlights(this._maxSpotlights, this._room);
|
||||||
|
|
||||||
// Transport for sending.
|
// Transport for sending.
|
||||||
this._sendTransport = null;
|
this._sendTransport = null;
|
||||||
|
|
@ -315,7 +315,7 @@ export default class RoomClient
|
||||||
|
|
||||||
lastN.splice(index, 1);
|
lastN.splice(index, 1);
|
||||||
|
|
||||||
this._lastN.addSpeakerList(lastN);
|
this._spotlights.addSpeakerList(lastN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error)
|
catch (error)
|
||||||
|
|
@ -340,16 +340,16 @@ export default class RoomClient
|
||||||
this._micProducer.resume();
|
this._micProducer.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updated consumers based on lastN
|
// Updated consumers based on spotlights
|
||||||
async updateSpeakers(speakers)
|
async updateSpotlights(spotlights)
|
||||||
{
|
{
|
||||||
logger.debug('updateSpeakers()');
|
logger.debug('updateSpotlights()');
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (const peer of this._room.peers)
|
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)
|
for (const consumer of peer.consumers)
|
||||||
{
|
{
|
||||||
|
|
@ -373,7 +373,7 @@ export default class RoomClient
|
||||||
}
|
}
|
||||||
catch (error)
|
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));
|
stateActions.setRoomActiveSpeaker(peerName));
|
||||||
|
|
||||||
if (peerName && peerName !== this._peerName)
|
if (peerName && peerName !== this._peerName)
|
||||||
this._lastN.handleActiveSpeaker(peerName);
|
this._spotlights.handleActiveSpeaker(peerName);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._signalingSocket.on('display-name-changed', (data) =>
|
this._signalingSocket.on('display-name-changed', (data) =>
|
||||||
|
|
@ -1228,10 +1228,10 @@ export default class RoomClient
|
||||||
|
|
||||||
this.notify('You are in the room');
|
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._dispatch(stateActions.setSpotlights(spotlights));
|
||||||
this.updateSpeakers(lastN);
|
this.updateSpotlights(spotlights);
|
||||||
});
|
});
|
||||||
|
|
||||||
const peers = this._room.peers;
|
const peers = this._room.peers;
|
||||||
|
|
@ -1241,7 +1241,7 @@ export default class RoomClient
|
||||||
this._handlePeer(peer, { notify: false });
|
this._handlePeer(peer, { notify: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lastN.start();
|
this._spotlights.start();
|
||||||
}
|
}
|
||||||
catch (error)
|
catch (error)
|
||||||
{
|
{
|
||||||
|
|
@ -1831,7 +1831,8 @@ export default class RoomClient
|
||||||
// Receive the consumer (if we can).
|
// Receive the consumer (if we can).
|
||||||
if (consumer.supported)
|
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
|
{ // Start paused
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'consumer paused by default');
|
'consumer paused by default');
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -114,7 +114,7 @@ class Filmstrip extends Component
|
||||||
|
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
const { peers, advancedMode, lastN, lastNLength } = this.props;
|
const { peers, advancedMode, spotlights, spotlightsLength } = this.props;
|
||||||
|
|
||||||
const activePeerName = this.getActivePeerName();
|
const activePeerName = this.getActivePeerName();
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ class Filmstrip extends Component
|
||||||
Object.keys(peers).map((peerName) =>
|
Object.keys(peers).map((peerName) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
lastN.find((lastNElement) => lastNElement === peerName)?
|
spotlights.find((spotlightsElement) => spotlightsElement === peerName)?
|
||||||
<div
|
<div
|
||||||
key={peerName}
|
key={peerName}
|
||||||
onClick={() => this.props.setSelectedPeer(peerName)}
|
onClick={() => this.props.setSelectedPeer(peerName)}
|
||||||
|
|
@ -166,9 +166,9 @@ class Filmstrip extends Component
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='hidden-peer-container'>
|
<div className='hidden-peer-container'>
|
||||||
{ (lastNLength<Object.keys(peers).length)?
|
{ (spotlightsLength<Object.keys(peers).length)?
|
||||||
<HiddenPeers
|
<HiddenPeers
|
||||||
lastNLength={Object.keys(peers).length-lastNLength}
|
spotlightsLength={Object.keys(peers).length-spotlightsLength}
|
||||||
/>:null
|
/>:null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -186,13 +186,13 @@ Filmstrip.propTypes = {
|
||||||
myName : PropTypes.string.isRequired,
|
myName : PropTypes.string.isRequired,
|
||||||
selectedPeerName : PropTypes.string,
|
selectedPeerName : PropTypes.string,
|
||||||
setSelectedPeer : PropTypes.func.isRequired,
|
setSelectedPeer : PropTypes.func.isRequired,
|
||||||
lastNLength : PropTypes.number,
|
spotlightsLength : PropTypes.number,
|
||||||
lastN : PropTypes.array.isRequired
|
spotlights : PropTypes.array.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
const lastNLength = state.room.lastN ? state.room.lastN.length : 0;
|
const spotlightsLength = state.room.spotlights ? state.room.spotlights.length : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeSpeakerName : state.room.activeSpeakerName,
|
activeSpeakerName : state.room.activeSpeakerName,
|
||||||
|
|
@ -200,8 +200,8 @@ const mapStateToProps = (state) =>
|
||||||
peers : state.peers,
|
peers : state.peers,
|
||||||
consumers : state.consumers,
|
consumers : state.consumers,
|
||||||
myName : state.me.name,
|
myName : state.me.name,
|
||||||
lastN : state.room.lastN,
|
spotlights : state.room.spotlights,
|
||||||
lastNLength
|
spotlightsLength
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,8 +93,8 @@ class Peers extends React.Component
|
||||||
advancedMode,
|
advancedMode,
|
||||||
activeSpeakerName,
|
activeSpeakerName,
|
||||||
peers,
|
peers,
|
||||||
lastN,
|
spotlights,
|
||||||
lastNLength
|
spotlightsLength
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const style =
|
const style =
|
||||||
|
|
@ -109,8 +109,8 @@ class Peers extends React.Component
|
||||||
peers.map((peer) =>
|
peers.map((peer) =>
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
(lastN.find(function(lastNElement)
|
(spotlights.find(function(spotlightsElement)
|
||||||
{ return lastNElement == peer.name; }))?
|
{ return spotlightsElement == peer.name; }))?
|
||||||
<Appear key={peer.name} duration={1000}>
|
<Appear key={peer.name} duration={1000}>
|
||||||
<div
|
<div
|
||||||
className={classnames('peer-container', {
|
className={classnames('peer-container', {
|
||||||
|
|
@ -129,9 +129,9 @@ class Peers extends React.Component
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
<div className='hidden-peer-container'>
|
<div className='hidden-peer-container'>
|
||||||
{ (lastNLength<peers.length)?
|
{ (spotlightsLength<peers.length)?
|
||||||
<HiddenPeers
|
<HiddenPeers
|
||||||
lastNLength={peers.length-lastNLength}
|
spotlightsLength={peers.length-spotlightsLength}
|
||||||
/>:null
|
/>:null
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -146,24 +146,24 @@ Peers.propTypes =
|
||||||
peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired,
|
peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired,
|
||||||
boxes : PropTypes.number,
|
boxes : PropTypes.number,
|
||||||
activeSpeakerName : PropTypes.string,
|
activeSpeakerName : PropTypes.string,
|
||||||
lastNLength : PropTypes.number,
|
spotlightsLength : PropTypes.number,
|
||||||
lastN : PropTypes.array.isRequired
|
spotlights : PropTypes.array.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
const peers = Object.values(state.peers);
|
const peers = Object.values(state.peers);
|
||||||
const lastN = state.room.lastN;
|
const spotlights = state.room.spotlights;
|
||||||
const lastNLength = lastN ? state.room.lastN.length : 0;
|
const spotlightsLength = spotlights ? state.room.spotlights.length : 0;
|
||||||
const boxes = lastNLength + Object.values(state.consumers)
|
const boxes = spotlightsLength + Object.values(state.consumers)
|
||||||
.filter((consumer) => consumer.source === 'screen').length;
|
.filter((consumer) => consumer.source === 'screen').length;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
peers,
|
peers,
|
||||||
boxes,
|
boxes,
|
||||||
activeSpeakerName : state.room.activeSpeakerName,
|
activeSpeakerName : state.room.activeSpeakerName,
|
||||||
lastN,
|
spotlights,
|
||||||
lastNLength
|
spotlightsLength
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const initialState =
|
||||||
toolbarsVisible : true,
|
toolbarsVisible : true,
|
||||||
mode : 'democratic',
|
mode : 'democratic',
|
||||||
selectedPeerName : null,
|
selectedPeerName : null,
|
||||||
lastN : []
|
spotlights : []
|
||||||
};
|
};
|
||||||
|
|
||||||
const room = (state = initialState, action) =>
|
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:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -494,8 +494,8 @@ export const setSelectedPeer = (selectedPeerName) =>
|
||||||
payload : { selectedPeerName }
|
payload : { selectedPeerName }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setLastN = (lastN) =>
|
export const setSpotlights = (spotlights) =>
|
||||||
({
|
({
|
||||||
type : 'SET_LASTN',
|
type : 'SET_SPOTLIGHTS',
|
||||||
payload : { lastN }
|
payload : { spotlights }
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue