Reworked lastN

master
Håvar Aambø Fosstveit 2018-10-25 16:09:40 +02:00
parent a4b70ebaff
commit f5bb382ee3
3 changed files with 220 additions and 90 deletions

123
app/lib/LastN.js 100644
View File

@ -0,0 +1,123 @@
import { EventEmitter } from 'events';
import Logger from './Logger';
const logger = new Logger('LastN');
export default class LastN extends EventEmitter
{
constructor(lastNCount, room)
{
super();
this._room = room;
this._lastNCount = lastNCount;
this._peerList = [];
this._currentLastN = [];
this._started = false;
}
start()
{
const peers = this._room.peers;
for (const peer of peers)
{
this._handlePeer(peer);
}
this._handleRoom();
this._started = true;
this._lastNUpdated();
}
_handleRoom()
{
this._room.on('newpeer', (peer) =>
{
logger.debug(
'lastN 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._lastNUpdated();
}
_handlePeer(peer)
{
logger.debug('_lastN _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._lastNUpdated();
}
});
logger.debug('_handlePeer() | adding peer [peerName:"%s"]', peer.name);
this._peerList.push(peer.name);
this._lastNUpdated();
}
}
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._lastNUpdated();
}
}
_lastNUpdated()
{
if (
!this._arraysEqual(
this._currentLastN, this._peerList.slice(0, this._lastNCount)
)
)
{
logger.debug('_lastNUpdated() | lastN is updated, emitting');
this._currentLastN = this._peerList.slice(0, this._lastNCount);
this.emit('lastn-updated', this._currentLastN);
}
else
logger.debug('_lastNUpdated() | lastN 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;
}
}

View File

@ -3,6 +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 { getSignalingUrl } from './urlFactory';
import * as cookiesManager from './cookiesManager';
import * as requestActions from './redux/requestActions';
@ -75,7 +76,7 @@ export default class RoomClient
this._lastNSpeakers = ROOM_OPTIONS.lastN;
// Array of lastN speakers
this._lastN = [];
this._lastN = new LastN(this._lastNSpeakers, this._room);
// Transport for sending.
this._sendTransport = null;
@ -309,13 +310,12 @@ export default class RoomClient
{
logger.debug('Got lastN');
// Remove our self from list
const index = lastN.indexOf(this._peerName);
lastN.splice(index, 1);
this._lastN = lastN;
this.updateSpeakers();
this._lastN.addSpeakerList(lastN);
}
}
catch (error)
@ -340,22 +340,16 @@ export default class RoomClient
this._micProducer.resume();
}
// Resumes consumers based on lastN speakers
async updateSpeakers()
// Updated consumers based on lastN
async updateSpeakers(speakers)
{
logger.debug('updateSpeakers()');
try
{
const speakers = this._lastN.slice(0, this._lastNSpeakers);
this._dispatch(stateActions.setLastN(speakers));
speakers.forEach((peerName) =>
for (const peer of this._room.peers)
{
const peer = this._room.getPeerByName(peerName);
if (peer)
if (speakers.indexOf(peer.name) > -1) // Resume video for speaker
{
for (const consumer of peer.consumers)
{
@ -364,10 +358,22 @@ export default class RoomClient
!consumer.locallyPaused)
continue;
consumer.resume();
await consumer.resume();
}
}
});
else // Pause video for everybody else
{
for (const consumer of peer.consumers)
{
if (consumer.appData.source !== 'webcam' ||
!consumer.supported ||
consumer.locallyPaused)
continue;
await consumer.pause();
}
}
}
}
catch (error)
{
@ -375,41 +381,6 @@ export default class RoomClient
}
}
handleActiveSpeaker(peerName)
{
logger.debug('handleActiveSpeaker() [peerName:"%s"]', peerName);
const index = this._lastN.indexOf(peerName);
if (index > -1) // We have this speaker in the list, move to front
{
if (index >= this._lastNSpeakers) // We need to remove someone
{
const removePeer = this._lastN[this._lastNSpeakers - 1];
this.pausePeerVideo(removePeer);
}
this._lastN.splice(index, 1);
this._lastN = [ peerName ].concat(this._lastN);
this.updateSpeakers();
}
else // We don't have this speaker in the list, should not happen
{
if (this._lastN.length >= this._lastNSpeakers)
{
const removePeer = this._lastN[this._lastNSpeakers - 1];
this.pausePeerVideo(removePeer);
}
this._lastN = [ peerName ].concat(this._lastN);
this.updateSpeakers();
}
}
installExtension()
{
logger.debug('installExtension()');
@ -575,6 +546,8 @@ export default class RoomClient
this._dispatch(
stateActions.setProducerTrack(this._micProducer.id, newTrack));
cookiesManager.setAudioDevice({ audioDeviceId: deviceId });
await this._updateAudioDevices();
}
catch (error)
@ -629,6 +602,8 @@ export default class RoomClient
this._dispatch(
stateActions.setProducerTrack(this._webcamProducer.id, newTrack));
cookiesManager.setVideoDevice({ videoDeviceId: deviceId });
await this._updateWebcams();
}
catch (error)
@ -1065,7 +1040,7 @@ export default class RoomClient
stateActions.setRoomActiveSpeaker(peerName));
if (peerName && peerName !== this._peerName)
this.handleActiveSpeaker(peerName);
this._lastN.handleActiveSpeaker(peerName);
});
this._signalingSocket.on('display-name-changed', (data) =>
@ -1193,14 +1168,6 @@ export default class RoomClient
logger.debug(
'room "newpeer" event [name:"%s", peer:%o]', peer.name, peer);
const index = this._lastN.indexOf(peer.name);
if (index === -1) // We don't have this peer in the list, add
{
this._lastN.push(peer.name);
this.updateSpeakers();
}
this._handlePeer(peer);
});
@ -1262,15 +1229,23 @@ export default class RoomClient
this._dispatch(stateActions.removeAllNotifications());
this.getServerHistory();
this.notify('You are in the room');
this._lastN.on('lastn-updated', (lastN) =>
{
this._dispatch(stateActions.setLastN(lastN));
this.updateSpeakers(lastN);
});
const peers = this._room.peers;
for (const peer of peers)
{
this._handlePeer(peer, { notify: false });
}
this._lastN.start();
}
catch (error)
{
@ -1298,9 +1273,27 @@ export default class RoomClient
await this._updateAudioDevices();
const devicesCookie = cookiesManager.getDevices();
let audioDeviceId;
if (devicesCookie)
audioDeviceId = devicesCookie.audioDeviceId;
logger.debug('_setMicProducer() | calling getUserMedia()');
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
let stream;
if (this._audioDevices.has(audioDeviceId))
stream = await navigator.mediaDevices.getUserMedia(
{
audio :
{
deviceId : { exact: audioDeviceId }
}
});
else
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const track = stream.getAudioTracks()[0];
@ -1519,16 +1512,35 @@ export default class RoomClient
if (!device)
throw new Error('no webcam devices');
const devicesCookie = cookiesManager.getDevices();
let videoDeviceId;
if (devicesCookie)
videoDeviceId = devicesCookie.videoDeviceId;
logger.debug('_setWebcamProducer() | calling getUserMedia()');
const stream = await navigator.mediaDevices.getUserMedia(
{
video :
let stream;
if (this._webcams.has(videoDeviceId))
stream = await navigator.mediaDevices.getUserMedia(
{
deviceId : { exact: device.deviceId },
...VIDEO_CONSTRAINS
}
});
video :
{
deviceId : { exact: videoDeviceId },
...VIDEO_CONSTRAINS
}
});
else
stream = await navigator.mediaDevices.getUserMedia(
{
video :
{
deviceId : { exact: device.deviceId },
...VIDEO_CONSTRAINS
}
});
const track = stream.getVideoTracks()[0];
@ -1640,9 +1652,6 @@ export default class RoomClient
else if (!this._audioDevices.has(currentAudioDeviceId))
this._audioDevice.device = array[0];
this._dispatch(
stateActions.setCanChangeWebcam(this._webcams.size >= 2));
this._dispatch(
stateActions.setCanChangeAudioDevice(len >= 2));
if (len >= 1)
@ -1690,9 +1699,6 @@ export default class RoomClient
else if (!this._webcams.has(currentWebcamId))
this._webcam.device = array[0];
this._dispatch(
stateActions.setCanChangeWebcam(this._webcams.size >= 2));
this._dispatch(
stateActions.setCanChangeWebcam(len >= 2));
if (len >= 1)
@ -1752,14 +1758,6 @@ export default class RoomClient
this._dispatch(stateActions.removePeer(peer.name));
const index = this._lastN.indexOf(peer.name);
if (index > -1) // We have this peer in the list, remove
{
this._lastN.splice(index, 1);
this.updateSpeakers();
}
if (this._room.joined)
{
this.notify(`${peer.appData.displayName} left the room`);
@ -1871,14 +1869,13 @@ export default class RoomClient
// Receive the consumer (if we can).
if (consumer.supported)
{
// Pause it if video and we are in audio-only mode.
if (consumer.kind === 'video' && this._getState().me.audioOnly)
consumer.pause('audio-only-mode');
const index = this._lastN.indexOf(consumer.peer.name);
if (consumer.kind === 'video' && ((index >= this._lastNSpeakers) || (index === -1)))
if (consumer.kind === 'video' &&
Object.keys(this._room.peers).length > this._lastNSpeakers)
{ // Start paused
logger.debug(
'consumer paused by default');
consumer.pause('not-speaker');
}
consumer.receive(this._recvTransport)
.then((track) =>

View File

@ -22,3 +22,13 @@ export function setDevices({ webcamEnabled })
{
jsCookie.set(DEVICES_COOKIE, { webcamEnabled });
}
export function setAudioDevice({ audioDeviceId })
{
jsCookie.set(DEVICES_COOKIE, { audioDeviceId });
}
export function setVideoDevice({ videoDeviceId })
{
jsCookie.set(DEVICES_COOKIE, { videoDeviceId });
}