Mostly working mediasoup v3
parent
e9b946ba93
commit
30f42d6ced
|
|
@ -1,7 +1,7 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
/app/build/
|
/app/build/
|
||||||
/app/public/config.js
|
/app/public/config/config.js
|
||||||
/app/public/images/logo.*
|
/app/public/images/logo.*
|
||||||
/server/config/
|
/server/config/
|
||||||
!/server/config/config.example.js
|
!/server/config/config.example.js
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,14 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material-ui/core": "^3.9.2",
|
"@material-ui/core": "^3.9.2",
|
||||||
"@material-ui/icons": "^3.0.2",
|
"@material-ui/icons": "^3.0.2",
|
||||||
|
"bowser": "^2.4.0",
|
||||||
"create-torrent": "^3.33.0",
|
"create-torrent": "^3.33.0",
|
||||||
"domready": "^1.0.8",
|
"domready": "^1.0.8",
|
||||||
"file-saver": "^2.0.1",
|
"file-saver": "^2.0.1",
|
||||||
"hark": "^1.2.3",
|
"hark": "^1.2.3",
|
||||||
"js-cookie": "^2.2.0",
|
"js-cookie": "^2.2.0",
|
||||||
"marked": "^0.6.1",
|
"marked": "^0.6.1",
|
||||||
"mediasoup-client": "^2.4.10",
|
"mediasoup-client": "^3.0.6",
|
||||||
"notistack": "^0.5.1",
|
"notistack": "^0.5.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"random-string": "^0.2.0",
|
"random-string": "^0.2.0",
|
||||||
|
|
@ -169,7 +170,12 @@
|
||||||
"no-case-declarations": 2,
|
"no-case-declarations": 2,
|
||||||
"no-catch-shadow": 2,
|
"no-catch-shadow": 2,
|
||||||
"no-class-assign": 2,
|
"no-class-assign": 2,
|
||||||
"no-confusing-arrow": ["error", {"allowParens": true}],
|
"no-confusing-arrow": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allowParens": true
|
||||||
|
}
|
||||||
|
],
|
||||||
"no-console": 2,
|
"no-console": 2,
|
||||||
"no-const-assign": 2,
|
"no-const-assign": 2,
|
||||||
"no-debugger": 2,
|
"no-debugger": 2,
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,11 +5,11 @@ const logger = new Logger('Spotlight');
|
||||||
|
|
||||||
export default class Spotlights extends EventEmitter
|
export default class Spotlights extends EventEmitter
|
||||||
{
|
{
|
||||||
constructor(maxSpotlights, room)
|
constructor(maxSpotlights, signalingSocket)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this._room = room;
|
this._signalingSocket = signalingSocket;
|
||||||
this._maxSpotlights = maxSpotlights;
|
this._maxSpotlights = maxSpotlights;
|
||||||
this._peerList = [];
|
this._peerList = [];
|
||||||
this._selectedSpotlights = [];
|
this._selectedSpotlights = [];
|
||||||
|
|
@ -19,24 +19,25 @@ export default class Spotlights extends EventEmitter
|
||||||
|
|
||||||
start()
|
start()
|
||||||
{
|
{
|
||||||
const peers = this._room.peers;
|
this._handleSignaling();
|
||||||
|
|
||||||
for (const peer of peers)
|
|
||||||
{
|
|
||||||
this._handlePeer(peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._handleRoom();
|
|
||||||
|
|
||||||
this._started = true;
|
this._started = true;
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
peerInSpotlights(peerName)
|
addPeers(peers)
|
||||||
|
{
|
||||||
|
for (const peer of peers)
|
||||||
|
{
|
||||||
|
this._newPeer(peer.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peerInSpotlights(peerId)
|
||||||
{
|
{
|
||||||
if (this._started)
|
if (this._started)
|
||||||
{
|
{
|
||||||
return this._currentSpotlights.indexOf(peerName) !== -1;
|
return this._currentSpotlights.indexOf(peerId) !== -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -44,11 +45,11 @@ export default class Spotlights extends EventEmitter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPeerSpotlight(peerName)
|
setPeerSpotlight(peerId)
|
||||||
{
|
{
|
||||||
logger.debug('setPeerSpotlight() [peerName:"%s"]', peerName);
|
logger.debug('setPeerSpotlight() [peerId:"%s"]', peerId);
|
||||||
|
|
||||||
const index = this._selectedSpotlights.indexOf(peerName);
|
const index = this._selectedSpotlights.indexOf(peerId);
|
||||||
|
|
||||||
if (index !== -1)
|
if (index !== -1)
|
||||||
{
|
{
|
||||||
|
|
@ -56,13 +57,13 @@ export default class Spotlights extends EventEmitter
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._selectedSpotlights = [ peerName ];
|
this._selectedSpotlights = [ peerId ];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (index === -1) // We don't have this peer in the list, adding
|
if (index === -1) // We don't have this peer in the list, adding
|
||||||
{
|
{
|
||||||
this._selectedSpotlights.push(peerName);
|
this._selectedSpotlights.push(peerId);
|
||||||
}
|
}
|
||||||
else // We have this peer, remove
|
else // We have this peer, remove
|
||||||
{
|
{
|
||||||
|
|
@ -74,14 +75,63 @@ export default class Spotlights extends EventEmitter
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleRoom()
|
_handleSignaling()
|
||||||
{
|
{
|
||||||
this._room.on('newpeer', (peer) =>
|
this._signalingSocket.on('notification', (notification) =>
|
||||||
|
{
|
||||||
|
if (notification.method === 'newPeer')
|
||||||
|
{
|
||||||
|
const { id } = notification.data;
|
||||||
|
|
||||||
|
this._newPeer(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification.method === 'peerClosed')
|
||||||
|
{
|
||||||
|
const { peerId } = notification.data;
|
||||||
|
|
||||||
|
this._closePeer(peerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_newPeer(id)
|
||||||
{
|
{
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'room "newpeer" event [name:"%s", peer:%o]', peer.name, peer);
|
'room "newpeer" event [id: "%s"]', id);
|
||||||
this._handlePeer(peer);
|
|
||||||
});
|
if (this._peerList.indexOf(id) === -1) // We don't have this peer in the list
|
||||||
|
{
|
||||||
|
logger.debug('_handlePeer() | adding peer [peerId: "%s"]', id);
|
||||||
|
|
||||||
|
this._peerList.push(id);
|
||||||
|
|
||||||
|
if (this._started)
|
||||||
|
this._spotlightsUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_closePeer(id)
|
||||||
|
{
|
||||||
|
logger.debug(
|
||||||
|
'room "peerClosed" event [peerId:%o]', id);
|
||||||
|
|
||||||
|
let index = this._peerList.indexOf(id);
|
||||||
|
|
||||||
|
if (index !== -1) // We have this peer in the list, remove
|
||||||
|
{
|
||||||
|
this._peerList.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = this._selectedSpotlights.indexOf(id);
|
||||||
|
|
||||||
|
if (index !== -1) // We have this peer in the list, remove
|
||||||
|
{
|
||||||
|
this._selectedSpotlights.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._started)
|
||||||
|
this._spotlightsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
addSpeakerList(speakerList)
|
addSpeakerList(speakerList)
|
||||||
|
|
@ -92,49 +142,16 @@ export default class Spotlights extends EventEmitter
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePeer(peer)
|
handleActiveSpeaker(peerId)
|
||||||
{
|
{
|
||||||
logger.debug('_handlePeer() [peerName:"%s"]', peer.name);
|
logger.debug('handleActiveSpeaker() [peerId:"%s"]', peerId);
|
||||||
|
|
||||||
if (this._peerList.indexOf(peer.name) === -1) // We don't have this peer in the list
|
const index = this._peerList.indexOf(peerId);
|
||||||
{
|
|
||||||
peer.on('close', () =>
|
|
||||||
{
|
|
||||||
let index = this._peerList.indexOf(peer.name);
|
|
||||||
|
|
||||||
if (index !== -1) // We have this peer in the list, remove
|
|
||||||
{
|
|
||||||
this._peerList.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
index = this._selectedSpotlights.indexOf(peer.name);
|
|
||||||
|
|
||||||
if (index !== -1) // We have this peer in the list, remove
|
|
||||||
{
|
|
||||||
this._selectedSpotlights.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)
|
if (index > -1)
|
||||||
{
|
{
|
||||||
this._peerList.splice(index, 1);
|
this._peerList.splice(index, 1);
|
||||||
this._peerList = [ peerName ].concat(this._peerList);
|
this._peerList = [ peerId ].concat(this._peerList);
|
||||||
|
|
||||||
this._spotlightsUpdated();
|
this._spotlightsUpdated();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,11 @@ export const setRoomState = (state) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setRoomActiveSpeaker = (peerName) =>
|
export const setRoomActiveSpeaker = (peerId) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_ROOM_ACTIVE_SPEAKER',
|
type : 'SET_ROOM_ACTIVE_SPEAKER',
|
||||||
payload : { peerName }
|
payload : { peerId }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -57,19 +57,25 @@ export const setSettingsOpen = ({ settingsOpen }) =>
|
||||||
payload : { settingsOpen }
|
payload : { settingsOpen }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setMe = ({ peerName, device, loginEnabled }) =>
|
export const setMe = ({ peerId, device, loginEnabled }) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_ME',
|
type : 'SET_ME',
|
||||||
payload : { peerName, device, loginEnabled }
|
payload : { peerId, device, loginEnabled }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setMediaCapabilities = ({ canSendMic, canSendWebcam }) =>
|
export const setMediaCapabilities = ({
|
||||||
|
canSendMic,
|
||||||
|
canSendWebcam,
|
||||||
|
canShareScreen,
|
||||||
|
needExtension,
|
||||||
|
canShareFiles
|
||||||
|
}) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_MEDIA_CAPABILITIES',
|
type : 'SET_MEDIA_CAPABILITIES',
|
||||||
payload : { canSendMic, canSendWebcam }
|
payload : { canSendMic, canSendWebcam, canShareScreen, needExtension, canShareFiles }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -150,27 +156,27 @@ export const setDisplayMode = (mode) =>
|
||||||
payload : { mode }
|
payload : { mode }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerVideoInProgress = (peerName, flag) =>
|
export const setPeerVideoInProgress = (peerId, flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_VIDEO_IN_PROGRESS',
|
type : 'SET_PEER_VIDEO_IN_PROGRESS',
|
||||||
payload : { peerName, flag }
|
payload : { peerId, flag }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setPeerAudioInProgress = (peerName, flag) =>
|
export const setPeerAudioInProgress = (peerId, flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_AUDIO_IN_PROGRESS',
|
type : 'SET_PEER_AUDIO_IN_PROGRESS',
|
||||||
payload : { peerName, flag }
|
payload : { peerId, flag }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setPeerScreenInProgress = (peerName, flag) =>
|
export const setPeerScreenInProgress = (peerId, flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_SCREEN_IN_PROGRESS',
|
type : 'SET_PEER_SCREEN_IN_PROGRESS',
|
||||||
payload : { peerName, flag }
|
payload : { peerId, flag }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -226,11 +232,11 @@ export const setMyRaiseHandStateInProgress = (flag) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setPeerRaiseHandState = (peerName, raiseHandState) =>
|
export const setPeerRaiseHandState = (peerId, raiseHandState) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_RAISE_HAND_STATE',
|
type : 'SET_PEER_RAISE_HAND_STATE',
|
||||||
payload : { peerName, raiseHandState }
|
payload : { peerId, raiseHandState }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -274,6 +280,14 @@ export const setProducerTrack = (producerId, track) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setProducerScore = (producerId, score) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_PRODUCER_SCORE',
|
||||||
|
payload : { producerId, score }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const setAudioInProgress = (flag) =>
|
export const setAudioInProgress = (flag) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
|
@ -306,35 +320,35 @@ export const addPeer = (peer) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removePeer = (peerName) =>
|
export const removePeer = (peerId) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'REMOVE_PEER',
|
type : 'REMOVE_PEER',
|
||||||
payload : { peerName }
|
payload : { peerId }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setPeerDisplayName = (displayName, peerName) =>
|
export const setPeerDisplayName = (displayName, peerId) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_DISPLAY_NAME',
|
type : 'SET_PEER_DISPLAY_NAME',
|
||||||
payload : { displayName, peerName }
|
payload : { displayName, peerId }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addConsumer = (consumer, peerName) =>
|
export const addConsumer = (consumer, peerId) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'ADD_CONSUMER',
|
type : 'ADD_CONSUMER',
|
||||||
payload : { consumer, peerName }
|
payload : { consumer, peerId }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const removeConsumer = (consumerId, peerName) =>
|
export const removeConsumer = (consumerId, peerId) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'REMOVE_CONSUMER',
|
type : 'REMOVE_CONSUMER',
|
||||||
payload : { consumerId, peerName }
|
payload : { consumerId, peerId }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -354,11 +368,19 @@ export const setConsumerResumed = (consumerId, originator) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setConsumerEffectiveProfile = (consumerId, profile) =>
|
export const setConsumerCurrentLayers = (consumerId, spatialLayer, temporalLayer) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_CONSUMER_EFFECTIVE_PROFILE',
|
type : 'SET_CONSUMER_CURRENT_LAYERS',
|
||||||
payload : { consumerId, profile }
|
payload : { consumerId, spatialLayer, temporalLayer }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setConsumerPreferredLayers = (consumerId, spatialLayer, temporalLayer) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_CONSUMER_PREFERRED_LAYERS',
|
||||||
|
payload : { consumerId, spatialLayer, temporalLayer }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -370,11 +392,19 @@ export const setConsumerTrack = (consumerId, track) =>
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setPeerVolume = (peerName, volume) =>
|
export const setConsumerScore = (consumerId, score) =>
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
type : 'SET_CONSUMER_SCORE',
|
||||||
|
payload : { consumerId, score }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setPeerVolume = (peerId, volume) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
type : 'SET_PEER_VOLUME',
|
type : 'SET_PEER_VOLUME',
|
||||||
payload : { peerName, volume }
|
payload : { peerId, volume }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -536,10 +566,10 @@ export const setPicture = (picture) =>
|
||||||
payload : { picture }
|
payload : { picture }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setPeerPicture = (peerName, picture) =>
|
export const setPeerPicture = (peerId, picture) =>
|
||||||
({
|
({
|
||||||
type : 'SET_PEER_PICTURE',
|
type : 'SET_PEER_PICTURE',
|
||||||
payload : { peerName, picture }
|
payload : { peerId, picture }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const loggedIn = () =>
|
export const loggedIn = () =>
|
||||||
|
|
@ -547,10 +577,10 @@ export const loggedIn = () =>
|
||||||
type : 'LOGGED_IN'
|
type : 'LOGGED_IN'
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setSelectedPeer = (selectedPeerName) =>
|
export const setSelectedPeer = (selectedpeerId) =>
|
||||||
({
|
({
|
||||||
type : 'SET_SELECTED_PEER',
|
type : 'SET_SELECTED_PEER',
|
||||||
payload : { selectedPeerName }
|
payload : { selectedpeerId }
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setSpotlights = (spotlights) =>
|
export const setSpotlights = (spotlights) =>
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ const Me = (props) =>
|
||||||
roomClient.changeDisplayName(displayName);
|
roomClient.changeDisplayName(displayName);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Volume name={me.name} />
|
<Volume id={me.id} />
|
||||||
</VideoView>
|
</VideoView>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -138,7 +138,7 @@ const mapStateToProps = (state) =>
|
||||||
me : state.me,
|
me : state.me,
|
||||||
...meProducersSelector(state),
|
...meProducersSelector(state),
|
||||||
settings : state.settings,
|
settings : state.settings,
|
||||||
activeSpeaker : state.me.name === state.room.activeSpeakerName
|
activeSpeaker : state.me.id === state.room.activeSpeakerId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ export default withRoomContext(connect(
|
||||||
prev.me === next.me &&
|
prev.me === next.me &&
|
||||||
prev.producers === next.producers &&
|
prev.producers === next.producers &&
|
||||||
prev.settings === next.settings &&
|
prev.settings === next.settings &&
|
||||||
prev.room.activeSpeakerName === next.room.activeSpeakerName
|
prev.room.activeSpeakerId === next.room.activeSpeakerId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,13 +196,6 @@ const Peer = (props) =>
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={classnames(classes.viewContainer)} style={style}>
|
<div className={classnames(classes.viewContainer)} style={style}>
|
||||||
{ videoVisible && !webcamConsumer.supported ?
|
|
||||||
<div className={classes.videoInfo}>
|
|
||||||
<p>incompatible video</p>
|
|
||||||
</div>
|
|
||||||
:null
|
|
||||||
}
|
|
||||||
|
|
||||||
{ !videoVisible ?
|
{ !videoVisible ?
|
||||||
<div className={classes.videoInfo}>
|
<div className={classes.videoInfo}>
|
||||||
<p>this video is paused</p>
|
<p>this video is paused</p>
|
||||||
|
|
@ -210,7 +203,7 @@ const Peer = (props) =>
|
||||||
:null
|
:null
|
||||||
}
|
}
|
||||||
|
|
||||||
{ videoVisible && webcamConsumer.supported ?
|
{ videoVisible ?
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.controls, webcamHover ? 'hover' : null)}
|
className={classnames(classes.controls, webcamHover ? 'hover' : null)}
|
||||||
onMouseOver={() => setWebcamHover(true)}
|
onMouseOver={() => setWebcamHover(true)}
|
||||||
|
|
@ -240,8 +233,8 @@ const Peer = (props) =>
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
micEnabled ?
|
micEnabled ?
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'mic', false);
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ micEnabled ?
|
{ micEnabled ?
|
||||||
|
|
@ -295,7 +288,7 @@ const Peer = (props) =>
|
||||||
audioCodec={micConsumer ? micConsumer.codec : null}
|
audioCodec={micConsumer ? micConsumer.codec : null}
|
||||||
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
|
videoCodec={webcamConsumer ? webcamConsumer.codec : null}
|
||||||
>
|
>
|
||||||
<Volume name={peer.name} />
|
<Volume id={peer.id} />
|
||||||
</VideoView>
|
</VideoView>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -323,13 +316,6 @@ const Peer = (props) =>
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ screenVisible && !screenConsumer.supported ?
|
|
||||||
<div className={classes.videoInfo} style={style}>
|
|
||||||
<p>incompatible video</p>
|
|
||||||
</div>
|
|
||||||
:null
|
|
||||||
}
|
|
||||||
|
|
||||||
{ !screenVisible ?
|
{ !screenVisible ?
|
||||||
<div className={classes.videoInfo} style={style}>
|
<div className={classes.videoInfo} style={style}>
|
||||||
<p>this video is paused</p>
|
<p>this video is paused</p>
|
||||||
|
|
@ -337,7 +323,7 @@ const Peer = (props) =>
|
||||||
:null
|
:null
|
||||||
}
|
}
|
||||||
|
|
||||||
{ screenVisible && screenConsumer.supported ?
|
{ screenVisible ?
|
||||||
<div className={classnames(classes.viewContainer)} style={style}>
|
<div className={classnames(classes.viewContainer)} style={style}>
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.controls, screenHover ? 'hover' : null)}
|
className={classnames(classes.controls, screenHover ? 'hover' : null)}
|
||||||
|
|
@ -418,7 +404,7 @@ Peer.propTypes =
|
||||||
micConsumer : appPropTypes.Consumer,
|
micConsumer : appPropTypes.Consumer,
|
||||||
webcamConsumer : appPropTypes.Consumer,
|
webcamConsumer : appPropTypes.Consumer,
|
||||||
screenConsumer : appPropTypes.Consumer,
|
screenConsumer : appPropTypes.Consumer,
|
||||||
windowConsumer : PropTypes.number,
|
windowConsumer : PropTypes.string,
|
||||||
activeSpeaker : PropTypes.bool,
|
activeSpeaker : PropTypes.bool,
|
||||||
style : PropTypes.object,
|
style : PropTypes.object,
|
||||||
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
toggleConsumerFullscreen : PropTypes.func.isRequired,
|
||||||
|
|
@ -434,10 +420,10 @@ const makeMapStateToProps = (initialState, props) =>
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
peer : state.peers[props.name],
|
peer : state.peers[props.id],
|
||||||
...getPeerConsumers(state, props),
|
...getPeerConsumers(state, props),
|
||||||
windowConsumer : state.room.windowConsumer,
|
windowConsumer : state.room.windowConsumer,
|
||||||
activeSpeaker : props.name === state.room.activeSpeakerName
|
activeSpeaker : props.id === state.room.activeSpeakerId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -470,7 +456,7 @@ export default withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.room.activeSpeakerName === next.room.activeSpeakerName &&
|
prev.room.activeSpeakerId === next.room.activeSpeakerId &&
|
||||||
prev.room.windowConsumer === next.room.windowConsumer
|
prev.room.windowConsumer === next.room.windowConsumer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ const makeMapStateToProps = (initialState, props) =>
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
volume : state.peerVolumes[props.name]
|
volume : state.peerVolumes[props.id]
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -158,8 +158,8 @@ const Sidebar = (props) =>
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
{
|
{
|
||||||
micState === 'on' ?
|
micState === 'on' ?
|
||||||
roomClient.muteMic() :
|
roomClient.disableMic() :
|
||||||
roomClient.unmuteMic();
|
roomClient.enableMic();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ micState === 'on' ?
|
{ micState === 'on' ?
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class File extends React.PureComponent
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
roomClient,
|
roomClient,
|
||||||
torrentSupport,
|
canShareFiles,
|
||||||
file,
|
file,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
@ -105,7 +105,7 @@ class File extends React.PureComponent
|
||||||
<Typography className={classes.text}>
|
<Typography className={classes.text}>
|
||||||
{magnet.decode(file.magnetUri).dn}
|
{magnet.decode(file.magnetUri).dn}
|
||||||
</Typography>
|
</Typography>
|
||||||
{ torrentSupport ?
|
{ canShareFiles ?
|
||||||
<Button
|
<Button
|
||||||
variant='contained'
|
variant='contained'
|
||||||
component='span'
|
component='span'
|
||||||
|
|
@ -146,7 +146,7 @@ class File extends React.PureComponent
|
||||||
|
|
||||||
File.propTypes = {
|
File.propTypes = {
|
||||||
roomClient : PropTypes.object.isRequired,
|
roomClient : PropTypes.object.isRequired,
|
||||||
torrentSupport : PropTypes.bool.isRequired,
|
canShareFiles : PropTypes.bool.isRequired,
|
||||||
file : PropTypes.object.isRequired,
|
file : PropTypes.object.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
@ -155,7 +155,7 @@ const mapStateToProps = (state, { magnetUri }) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
file : state.files[magnetUri],
|
file : state.files[magnetUri],
|
||||||
torrentSupport : state.room.torrentSupport
|
canShareFiles : state.me.canShareFiles
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,11 @@ class FileSharing extends React.PureComponent
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
torrentSupport,
|
canShareFiles,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const buttonDescription = torrentSupport ?
|
const buttonDescription = canShareFiles ?
|
||||||
'Share file' : 'File sharing not supported';
|
'Share file' : 'File sharing not supported';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -67,7 +67,7 @@ class FileSharing extends React.PureComponent
|
||||||
variant='contained'
|
variant='contained'
|
||||||
component='span'
|
component='span'
|
||||||
className={classes.button}
|
className={classes.button}
|
||||||
disabled={!torrentSupport}
|
disabled={!canShareFiles}
|
||||||
>
|
>
|
||||||
{buttonDescription}
|
{buttonDescription}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -81,7 +81,7 @@ class FileSharing extends React.PureComponent
|
||||||
|
|
||||||
FileSharing.propTypes = {
|
FileSharing.propTypes = {
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
torrentSupport : PropTypes.bool.isRequired,
|
canShareFiles : PropTypes.bool.isRequired,
|
||||||
tabOpen : PropTypes.bool.isRequired,
|
tabOpen : PropTypes.bool.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
@ -89,7 +89,7 @@ FileSharing.propTypes = {
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
torrentSupport : state.room.torrentSupport,
|
canShareFiles : state.me.canShareFiles,
|
||||||
tabOpen : state.toolarea.currentToolTab === 'files'
|
tabOpen : state.toolarea.currentToolTab === 'files'
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,8 @@ const ListPeer = (props) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
screenVisible ?
|
screenVisible ?
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'screen', true) :
|
roomClient.modifyPeerConsumer(peer.id, 'screen', true) :
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'screen', false);
|
roomClient.modifyPeerConsumer(peer.id, 'screen', false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ screenVisible ?
|
{ screenVisible ?
|
||||||
|
|
@ -207,8 +207,8 @@ const ListPeer = (props) =>
|
||||||
{
|
{
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
micEnabled ?
|
micEnabled ?
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'mic', true) :
|
roomClient.modifyPeerConsumer(peer.id, 'mic', true) :
|
||||||
roomClient.modifyPeerConsumer(peer.name, 'mic', false);
|
roomClient.modifyPeerConsumer(peer.id, 'mic', false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ micEnabled ?
|
{ micEnabled ?
|
||||||
|
|
@ -241,7 +241,7 @@ const makeMapStateToProps = (initialState, props) =>
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
peer : state.peers[props.name],
|
peer : state.peers[props.id],
|
||||||
...getPeerConsumers(state, props)
|
...getPeerConsumers(state, props)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class ParticipantList extends React.PureComponent
|
||||||
roomClient,
|
roomClient,
|
||||||
advancedMode,
|
advancedMode,
|
||||||
passivePeers,
|
passivePeers,
|
||||||
selectedPeerName,
|
selectedPeerId,
|
||||||
spotlightPeers,
|
spotlightPeers,
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
@ -92,14 +92,14 @@ class ParticipantList extends React.PureComponent
|
||||||
<li className={classes.listheader}>Participants in Spotlight:</li>
|
<li className={classes.listheader}>Participants in Spotlight:</li>
|
||||||
{ spotlightPeers.map((peer) => (
|
{ spotlightPeers.map((peer) => (
|
||||||
<li
|
<li
|
||||||
key={peer.name}
|
key={peer.id}
|
||||||
className={classNames(classes.listItem, {
|
className={classNames(classes.listItem, {
|
||||||
selected : peer.name === selectedPeerName
|
selected : peer.id === selectedPeerId
|
||||||
})}
|
})}
|
||||||
onClick={() => roomClient.setSelectedPeer(peer.name)}
|
onClick={() => roomClient.setSelectedPeer(peer.id)}
|
||||||
>
|
>
|
||||||
<ListPeer name={peer.name} advancedMode={advancedMode}>
|
<ListPeer id={peer.id} advancedMode={advancedMode}>
|
||||||
<Volume small name={peer.name} />
|
<Volume small id={peer.id} />
|
||||||
</ListPeer>
|
</ListPeer>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
@ -107,15 +107,15 @@ class ParticipantList extends React.PureComponent
|
||||||
<br />
|
<br />
|
||||||
<ul className={classes.list}>
|
<ul className={classes.list}>
|
||||||
<li className={classes.listheader}>Passive Participants:</li>
|
<li className={classes.listheader}>Passive Participants:</li>
|
||||||
{ passivePeers.map((peerName) => (
|
{ passivePeers.map((peerId) => (
|
||||||
<li
|
<li
|
||||||
key={peerName}
|
key={peerId}
|
||||||
className={classNames(classes.listItem, {
|
className={classNames(classes.listItem, {
|
||||||
selected : peerName === selectedPeerName
|
selected : peerId === selectedPeerId
|
||||||
})}
|
})}
|
||||||
onClick={() => roomClient.setSelectedPeer(peerName)}
|
onClick={() => roomClient.setSelectedPeer(peerId)}
|
||||||
>
|
>
|
||||||
<ListPeer name={peerName} advancedMode={advancedMode} />
|
<ListPeer id={peerId} advancedMode={advancedMode} />
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -129,7 +129,7 @@ ParticipantList.propTypes =
|
||||||
roomClient : PropTypes.any.isRequired,
|
roomClient : PropTypes.any.isRequired,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
passivePeers : PropTypes.array,
|
passivePeers : PropTypes.array,
|
||||||
selectedPeerName : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlightPeers : PropTypes.array,
|
spotlightPeers : PropTypes.array,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
@ -138,7 +138,7 @@ const mapStateToProps = (state) =>
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
passivePeers : passivePeersSelector(state),
|
passivePeers : passivePeersSelector(state),
|
||||||
selectedPeerName : state.room.selectedPeerName,
|
selectedPeerId : state.room.selectedPeerId,
|
||||||
spotlightPeers : spotlightPeersSelector(state)
|
spotlightPeers : spotlightPeersSelector(state)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -153,7 +153,7 @@ const ParticipantListContainer = withRoomContext(connect(
|
||||||
return (
|
return (
|
||||||
prev.peers === next.peers &&
|
prev.peers === next.peers &&
|
||||||
prev.room.spotlights === next.room.spotlights &&
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
prev.room.selectedPeerName === next.room.selectedPeerName
|
prev.room.selectedPeerId === next.room.selectedPeerId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,9 +139,9 @@ class Democratic extends React.PureComponent
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<Peer
|
<Peer
|
||||||
key={peer.name}
|
key={peer.id}
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
name={peer.name}
|
id={peer.id}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,11 @@ class Filmstrip extends React.PureComponent
|
||||||
// Find the name of the peer which is currently speaking. This is either
|
// Find the name of the peer which is currently speaking. This is either
|
||||||
// the latest active speaker, or the manually selected peer, or, if no
|
// the latest active speaker, or the manually selected peer, or, if no
|
||||||
// person has spoken yet, the first peer in the list of peers.
|
// person has spoken yet, the first peer in the list of peers.
|
||||||
getActivePeerName = () =>
|
getActivePeerId = () =>
|
||||||
{
|
{
|
||||||
if (this.props.selectedPeerName)
|
if (this.props.selectedPeerId)
|
||||||
{
|
{
|
||||||
return this.props.selectedPeerName;
|
return this.props.selectedPeerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.lastSpeaker)
|
if (this.state.lastSpeaker)
|
||||||
|
|
@ -116,23 +116,23 @@ class Filmstrip extends React.PureComponent
|
||||||
return this.state.lastSpeaker;
|
return this.state.lastSpeaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
const peerNames = Object.keys(this.props.peers);
|
const peerIds = Object.keys(this.props.peers);
|
||||||
|
|
||||||
if (peerNames.length > 0)
|
if (peerIds.length > 0)
|
||||||
{
|
{
|
||||||
return peerNames[0];
|
return peerIds[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
isSharingCamera = (peerName) => this.props.peers[peerName] &&
|
isSharingCamera = (peerId) => this.props.peers[peerId] &&
|
||||||
this.props.peers[peerName].consumers.some((consumer) =>
|
this.props.peers[peerId].consumers.some((consumer) =>
|
||||||
this.props.consumers[consumer].source === 'screen');
|
this.props.consumers[consumer].source === 'screen');
|
||||||
|
|
||||||
getRatio = () =>
|
getRatio = () =>
|
||||||
{
|
{
|
||||||
let ratio = 4 / 3;
|
let ratio = 4 / 3;
|
||||||
|
|
||||||
if (this.isSharingCamera(this.getActivePeerName()))
|
if (this.isSharingCamera(this.getActivePeerId()))
|
||||||
{
|
{
|
||||||
ratio *= 2;
|
ratio *= 2;
|
||||||
}
|
}
|
||||||
|
|
@ -202,12 +202,12 @@ class Filmstrip extends React.PureComponent
|
||||||
classes
|
classes
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const activePeerName = this.getActivePeerName();
|
const activePeerId = this.getActivePeerId();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
<div className={classes.activePeerContainer} ref={this.activePeerContainer}>
|
<div className={classes.activePeerContainer} ref={this.activePeerContainer}>
|
||||||
{ peers[activePeerName] ?
|
{ peers[activePeerId] ?
|
||||||
<div
|
<div
|
||||||
className={classes.activePeer}
|
className={classes.activePeer}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -217,7 +217,7 @@ class Filmstrip extends React.PureComponent
|
||||||
>
|
>
|
||||||
<Peer
|
<Peer
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
name={activePeerName}
|
name={activePeerId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
:null
|
:null
|
||||||
|
|
@ -226,23 +226,23 @@ class Filmstrip extends React.PureComponent
|
||||||
|
|
||||||
<div className={classes.filmStrip}>
|
<div className={classes.filmStrip}>
|
||||||
<div className={classes.filmStripContent}>
|
<div className={classes.filmStripContent}>
|
||||||
{ Object.keys(peers).map((peerName) =>
|
{ Object.keys(peers).map((peerId) =>
|
||||||
{
|
{
|
||||||
if (spotlights.find((spotlightsElement) => spotlightsElement === peerName))
|
if (spotlights.find((spotlightsElement) => spotlightsElement === peerId))
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={peerName}
|
key={peerId}
|
||||||
onClick={() => roomClient.setSelectedPeer(peerName)}
|
onClick={() => roomClient.setSelectedPeer(peerId)}
|
||||||
className={classnames(classes.film, {
|
className={classnames(classes.film, {
|
||||||
selected : this.props.selectedPeerName === peerName,
|
selected : this.props.selectedPeerId === peerId,
|
||||||
active : this.state.lastSpeaker === peerName
|
active : this.state.lastSpeaker === peerId
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className={classes.filmContent}>
|
<div className={classes.filmContent}>
|
||||||
<Peer
|
<Peer
|
||||||
advancedMode={advancedMode}
|
advancedMode={advancedMode}
|
||||||
name={peerName}
|
name={peerId}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -276,7 +276,7 @@ Filmstrip.propTypes = {
|
||||||
peers : PropTypes.object.isRequired,
|
peers : PropTypes.object.isRequired,
|
||||||
consumers : PropTypes.object.isRequired,
|
consumers : PropTypes.object.isRequired,
|
||||||
myName : PropTypes.string.isRequired,
|
myName : PropTypes.string.isRequired,
|
||||||
selectedPeerName : PropTypes.string,
|
selectedPeerId : PropTypes.string,
|
||||||
spotlightsLength : PropTypes.number,
|
spotlightsLength : PropTypes.number,
|
||||||
spotlights : PropTypes.array.isRequired,
|
spotlights : PropTypes.array.isRequired,
|
||||||
classes : PropTypes.object.isRequired
|
classes : PropTypes.object.isRequired
|
||||||
|
|
@ -288,7 +288,7 @@ const mapStateToProps = (state) =>
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeSpeakerName : state.room.activeSpeakerName,
|
activeSpeakerName : state.room.activeSpeakerName,
|
||||||
selectedPeerName : state.room.selectedPeerName,
|
selectedPeerId : state.room.selectedPeerId,
|
||||||
peers : state.peers,
|
peers : state.peers,
|
||||||
consumers : state.consumers,
|
consumers : state.consumers,
|
||||||
myName : state.me.name,
|
myName : state.me.name,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const consumersSelect = (state) => state.consumers;
|
||||||
const spotlightsSelector = (state) => state.room.spotlights;
|
const spotlightsSelector = (state) => state.room.spotlights;
|
||||||
const peersSelector = (state) => state.peers;
|
const peersSelector = (state) => state.peers;
|
||||||
const getPeerConsumers = (state, props) =>
|
const getPeerConsumers = (state, props) =>
|
||||||
(state.peers[props.name] ? state.peers[props.name].consumers : null);
|
(state.peers[props.id] ? state.peers[props.id].consumers : null);
|
||||||
const getAllConsumers = (state) => state.consumers;
|
const getAllConsumers = (state) => state.consumers;
|
||||||
const peersKeySelector = createSelector(
|
const peersKeySelector = createSelector(
|
||||||
peersSelector,
|
peersSelector,
|
||||||
|
|
@ -66,10 +66,10 @@ export const spotlightPeersSelector = createSelector(
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
peersSelector,
|
peersSelector,
|
||||||
(spotlights, peers) =>
|
(spotlights, peers) =>
|
||||||
spotlights.reduce((result, peerName) =>
|
spotlights.reduce((result, peerId) =>
|
||||||
{
|
{
|
||||||
if (peers[peerName])
|
if (peers[peerId])
|
||||||
result.push(peers[peerName]);
|
result.push(peers[peerId]);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}, [])
|
}, [])
|
||||||
|
|
@ -83,7 +83,7 @@ export const peersLengthSelector = createSelector(
|
||||||
export const passivePeersSelector = createSelector(
|
export const passivePeersSelector = createSelector(
|
||||||
peersKeySelector,
|
peersKeySelector,
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
(peers, spotlights) => peers.filter((peerName) => !spotlights.includes(peerName))
|
(peers, spotlights) => peers.filter((peerId) => !spotlights.includes(peerId))
|
||||||
);
|
);
|
||||||
|
|
||||||
export const videoBoxesSelector = createSelector(
|
export const videoBoxesSelector = createSelector(
|
||||||
|
|
|
||||||
|
|
@ -102,13 +102,6 @@ const FullScreenView = (props) =>
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
{ consumerVisible && !consumer.supported ?
|
|
||||||
<div className={classes.incompatibleVideo}>
|
|
||||||
<p>incompatible video</p>
|
|
||||||
</div>
|
|
||||||
:null
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className={classes.controls}>
|
<div className={classes.controls}>
|
||||||
<div
|
<div
|
||||||
className={classnames(classes.button, {
|
className={classnames(classes.button, {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export const Room = PropTypes.shape(
|
||||||
url : PropTypes.string.isRequired,
|
url : PropTypes.string.isRequired,
|
||||||
state : PropTypes.oneOf(
|
state : PropTypes.oneOf(
|
||||||
[ 'new', 'connecting', 'connected', 'closed' ]).isRequired,
|
[ 'new', 'connecting', 'connected', 'closed' ]).isRequired,
|
||||||
activeSpeakerName : PropTypes.string
|
activeSpeakerId : PropTypes.string
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Device = PropTypes.shape(
|
export const Device = PropTypes.shape(
|
||||||
|
|
@ -17,7 +17,7 @@ export const Device = PropTypes.shape(
|
||||||
|
|
||||||
export const Me = PropTypes.shape(
|
export const Me = PropTypes.shape(
|
||||||
{
|
{
|
||||||
name : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
device : Device.isRequired,
|
device : Device.isRequired,
|
||||||
canSendMic : PropTypes.bool.isRequired,
|
canSendMic : PropTypes.bool.isRequired,
|
||||||
canSendWebcam : PropTypes.bool.isRequired,
|
canSendWebcam : PropTypes.bool.isRequired,
|
||||||
|
|
@ -26,30 +26,28 @@ export const Me = PropTypes.shape(
|
||||||
|
|
||||||
export const Producer = PropTypes.shape(
|
export const Producer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.number.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
||||||
deviceLabel : PropTypes.string,
|
deviceLabel : PropTypes.string,
|
||||||
type : PropTypes.oneOf([ 'front', 'back', 'screen' ]),
|
type : PropTypes.oneOf([ 'front', 'back', 'screen' ]),
|
||||||
locallyPaused : PropTypes.bool.isRequired,
|
paused : PropTypes.bool.isRequired,
|
||||||
remotelyPaused : PropTypes.bool.isRequired,
|
|
||||||
track : PropTypes.any,
|
track : PropTypes.any,
|
||||||
codec : PropTypes.string.isRequired
|
codec : PropTypes.string.isRequired
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Peer = PropTypes.shape(
|
export const Peer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
name : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
displayName : PropTypes.string,
|
displayName : PropTypes.string,
|
||||||
device : Device.isRequired,
|
device : Device.isRequired,
|
||||||
consumers : PropTypes.arrayOf(PropTypes.number).isRequired
|
consumers : PropTypes.arrayOf(PropTypes.string).isRequired
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Consumer = PropTypes.shape(
|
export const Consumer = PropTypes.shape(
|
||||||
{
|
{
|
||||||
id : PropTypes.number.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
peerName : PropTypes.string.isRequired,
|
peerId : PropTypes.string.isRequired,
|
||||||
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
source : PropTypes.oneOf([ 'mic', 'webcam', 'screen' ]).isRequired,
|
||||||
supported : PropTypes.bool.isRequired,
|
|
||||||
locallyPaused : PropTypes.bool.isRequired,
|
locallyPaused : PropTypes.bool.isRequired,
|
||||||
remotelyPaused : PropTypes.bool.isRequired,
|
remotelyPaused : PropTypes.bool.isRequired,
|
||||||
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
profile : PropTypes.oneOf([ 'none', 'default', 'low', 'medium', 'high' ]),
|
||||||
|
|
@ -75,7 +73,7 @@ export const Message = PropTypes.shape(
|
||||||
export const FileEntryProps = PropTypes.shape(
|
export const FileEntryProps = PropTypes.shape(
|
||||||
{
|
{
|
||||||
data : PropTypes.shape({
|
data : PropTypes.shape({
|
||||||
name : PropTypes.string.isRequired,
|
id : PropTypes.string.isRequired,
|
||||||
picture : PropTypes.string,
|
picture : PropTypes.string,
|
||||||
file : PropTypes.shape({
|
file : PropTypes.shape({
|
||||||
magnet : PropTypes.string.isRequired
|
magnet : PropTypes.string.isRequired
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import bowser from 'bowser';
|
||||||
|
|
||||||
|
window.BB = bowser;
|
||||||
|
|
||||||
|
export default function()
|
||||||
|
{
|
||||||
|
const ua = navigator.userAgent;
|
||||||
|
const browser = bowser.getParser(ua);
|
||||||
|
|
||||||
|
let flag;
|
||||||
|
|
||||||
|
if (browser.satisfies({ chrome: '>=0', chromium: '>=0' }))
|
||||||
|
flag = 'chrome';
|
||||||
|
else if (browser.satisfies({ firefox: '>=0' }))
|
||||||
|
flag = 'firefox';
|
||||||
|
else if (browser.satisfies({ safari: '>=0' }))
|
||||||
|
flag = 'safari';
|
||||||
|
else if (browser.satisfies({ opera: '>=0' }))
|
||||||
|
flag = 'opera';
|
||||||
|
else if (browser.satisfies({ 'microsoft edge': '>=0' }))
|
||||||
|
flag = 'edge';
|
||||||
|
else
|
||||||
|
flag = 'unknown';
|
||||||
|
|
||||||
|
return {
|
||||||
|
flag,
|
||||||
|
name : browser.getBrowserName(),
|
||||||
|
version : browser.getBrowserVersion(),
|
||||||
|
bowser : browser
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,12 @@ import UrlParse from 'url-parse';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { getDeviceInfo } from 'mediasoup-client';
|
|
||||||
import randomString from 'random-string';
|
import randomString from 'random-string';
|
||||||
import Logger from './Logger';
|
import Logger from './Logger';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import RoomClient from './RoomClient';
|
import RoomClient from './RoomClient';
|
||||||
import RoomContext from './RoomContext';
|
import RoomContext from './RoomContext';
|
||||||
|
import deviceInfo from './deviceInfo';
|
||||||
import * as stateActions from './actions/stateActions';
|
import * as stateActions from './actions/stateActions';
|
||||||
import Room from './components/Room';
|
import Room from './components/Room';
|
||||||
import LoadingView from './components/LoadingView';
|
import LoadingView from './components/LoadingView';
|
||||||
|
|
@ -44,13 +44,15 @@ function run()
|
||||||
{
|
{
|
||||||
logger.debug('run() [environment:%s]', process.env.NODE_ENV);
|
logger.debug('run() [environment:%s]', process.env.NODE_ENV);
|
||||||
|
|
||||||
const peerName = randomString({ length: 8 }).toLowerCase();
|
const peerId = randomString({ length: 8 }).toLowerCase();
|
||||||
const urlParser = new UrlParse(window.location.href, true);
|
const urlParser = new UrlParse(window.location.href, true);
|
||||||
|
|
||||||
let roomId = (urlParser.pathname).substr(1)
|
let roomId = (urlParser.pathname).substr(1)
|
||||||
? (urlParser.pathname).substr(1).toLowerCase() : urlParser.query.roomId.toLowerCase();
|
? (urlParser.pathname).substr(1).toLowerCase() : urlParser.query.roomId.toLowerCase();
|
||||||
const produce = urlParser.query.produce !== 'false';
|
const produce = urlParser.query.produce !== 'false';
|
||||||
|
const consume = urlParser.query.consume !== 'false';
|
||||||
const useSimulcast = urlParser.query.simulcast === 'true';
|
const useSimulcast = urlParser.query.simulcast === 'true';
|
||||||
|
const forceTcp = urlParser.query.forceTcp === 'true';
|
||||||
|
|
||||||
if (!roomId)
|
if (!roomId)
|
||||||
{
|
{
|
||||||
|
|
@ -80,21 +82,21 @@ function run()
|
||||||
const roomUrl = roomUrlParser.toString();
|
const roomUrl = roomUrlParser.toString();
|
||||||
|
|
||||||
// Get current device.
|
// Get current device.
|
||||||
const device = getDeviceInfo();
|
const device = deviceInfo();
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
stateActions.setRoomUrl(roomUrl));
|
stateActions.setRoomUrl(roomUrl));
|
||||||
|
|
||||||
store.dispatch(
|
store.dispatch(
|
||||||
stateActions.setMe({
|
stateActions.setMe({
|
||||||
peerName,
|
peerId,
|
||||||
device,
|
device,
|
||||||
loginEnabled : window.config.loginEnabled
|
loginEnabled : window.config.loginEnabled
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
roomClient = new RoomClient(
|
roomClient = new RoomClient(
|
||||||
{ roomId, peerName, device, useSimulcast, produce });
|
{ roomId, peerId, device, useSimulcast, produce, consume, forceTcp });
|
||||||
|
|
||||||
global.CLIENT = roomClient;
|
global.CLIENT = roomClient;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,11 +51,30 @@ const consumers = (state = initialState, action) =>
|
||||||
return { ...state, [consumerId]: newConsumer };
|
return { ...state, [consumerId]: newConsumer };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_CONSUMER_EFFECTIVE_PROFILE':
|
case 'SET_CONSUMER_CURRENT_LAYERS':
|
||||||
{
|
{
|
||||||
const { consumerId, profile } = action.payload;
|
const { consumerId, spatialLayer, temporalLayer } = action.payload;
|
||||||
const consumer = state[consumerId];
|
const consumer = state[consumerId];
|
||||||
const newConsumer = { ...consumer, profile };
|
const newConsumer =
|
||||||
|
{
|
||||||
|
...consumer,
|
||||||
|
currentSpatialLayer : spatialLayer,
|
||||||
|
currentTemporalLayer : temporalLayer
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, [consumerId]: newConsumer };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_CONSUMER_PREFERRED_LAYERS':
|
||||||
|
{
|
||||||
|
const { consumerId, spatialLayer, temporalLayer } = action.payload;
|
||||||
|
const consumer = state[consumerId];
|
||||||
|
const newConsumer =
|
||||||
|
{
|
||||||
|
...consumer,
|
||||||
|
preferredSpatialLayer : spatialLayer,
|
||||||
|
preferredTemporalLayer : temporalLayer
|
||||||
|
};
|
||||||
|
|
||||||
return { ...state, [consumerId]: newConsumer };
|
return { ...state, [consumerId]: newConsumer };
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +88,19 @@ const consumers = (state = initialState, action) =>
|
||||||
return { ...state, [consumerId]: newConsumer };
|
return { ...state, [consumerId]: newConsumer };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_CONSUMER_SCORE':
|
||||||
|
{
|
||||||
|
const { consumerId, score } = action.payload;
|
||||||
|
const consumer = state[consumerId];
|
||||||
|
|
||||||
|
if (!consumer)
|
||||||
|
return state;
|
||||||
|
|
||||||
|
const newConsumer = { ...consumer, score };
|
||||||
|
|
||||||
|
return { ...state, [consumerId]: newConsumer };
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
const initialState =
|
const initialState =
|
||||||
{
|
{
|
||||||
name : null,
|
id : null,
|
||||||
device : null,
|
device : null,
|
||||||
canSendMic : false,
|
canSendMic : false,
|
||||||
canSendWebcam : false,
|
canSendWebcam : false,
|
||||||
canShareScreen : false,
|
canShareScreen : false,
|
||||||
needExtension : false,
|
needExtension : false,
|
||||||
|
canShareFiles : false,
|
||||||
audioDevices : null,
|
audioDevices : null,
|
||||||
webcamDevices : null,
|
webcamDevices : null,
|
||||||
webcamInProgress : false,
|
webcamInProgress : false,
|
||||||
|
|
@ -24,14 +25,14 @@ const me = (state = initialState, action) =>
|
||||||
case 'SET_ME':
|
case 'SET_ME':
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
peerName,
|
peerId,
|
||||||
device,
|
device,
|
||||||
loginEnabled
|
loginEnabled
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
name : peerName,
|
id : peerId,
|
||||||
device,
|
device,
|
||||||
loginEnabled
|
loginEnabled
|
||||||
};
|
};
|
||||||
|
|
@ -45,9 +46,22 @@ const me = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_MEDIA_CAPABILITIES':
|
case 'SET_MEDIA_CAPABILITIES':
|
||||||
{
|
{
|
||||||
const { canSendMic, canSendWebcam } = action.payload;
|
const {
|
||||||
|
canSendMic,
|
||||||
|
canSendWebcam,
|
||||||
|
canShareScreen,
|
||||||
|
needExtension,
|
||||||
|
canShareFiles
|
||||||
|
} = action.payload;
|
||||||
|
|
||||||
return { ...state, canSendMic, canSendWebcam };
|
return {
|
||||||
|
...state,
|
||||||
|
canSendMic,
|
||||||
|
canSendWebcam,
|
||||||
|
canShareScreen,
|
||||||
|
needExtension,
|
||||||
|
canShareFiles
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_SCREEN_CAPABILITIES':
|
case 'SET_SCREEN_CAPABILITIES':
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,33 @@ const peerVolumes = (state = initialState, action) =>
|
||||||
case 'SET_ME':
|
case 'SET_ME':
|
||||||
{
|
{
|
||||||
const {
|
const {
|
||||||
peerName
|
peerId
|
||||||
} = action.payload;
|
} = action.payload;
|
||||||
|
|
||||||
return { ...state, [peerName]: 0 };
|
return { ...state, [peerId]: 0 };
|
||||||
}
|
}
|
||||||
case 'ADD_PEER':
|
case 'ADD_PEER':
|
||||||
{
|
{
|
||||||
const { peer } = action.payload;
|
const { peer } = action.payload;
|
||||||
|
|
||||||
return { ...state, [peer.name]: 0 };
|
return { ...state, [peer.id]: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'REMOVE_PEER':
|
case 'REMOVE_PEER':
|
||||||
{
|
{
|
||||||
const { peerName } = action.payload;
|
const { peerId } = action.payload;
|
||||||
const newState = { ...state };
|
const newState = { ...state };
|
||||||
|
|
||||||
delete newState[peerName];
|
delete newState[peerId];
|
||||||
|
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_PEER_VOLUME':
|
case 'SET_PEER_VOLUME':
|
||||||
{
|
{
|
||||||
const { peerName, volume } = action.payload;
|
const { peerId, volume } = action.payload;
|
||||||
|
|
||||||
return { ...state, [peerName]: volume };
|
return { ...state, [peerId]: volume };
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,12 @@ const peers = (state = {}, action) =>
|
||||||
{
|
{
|
||||||
case 'ADD_PEER':
|
case 'ADD_PEER':
|
||||||
{
|
{
|
||||||
return { ...state, [action.payload.peer.name]: peer(undefined, action) };
|
return { ...state, [action.payload.peer.id]: peer(undefined, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'REMOVE_PEER':
|
case 'REMOVE_PEER':
|
||||||
{
|
{
|
||||||
return omit(state, [ action.payload.peerName ]);
|
return omit(state, [ action.payload.peerId ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_PEER_DISPLAY_NAME':
|
case 'SET_PEER_DISPLAY_NAME':
|
||||||
|
|
@ -69,25 +69,25 @@ const peers = (state = {}, action) =>
|
||||||
case 'SET_PEER_PICTURE':
|
case 'SET_PEER_PICTURE':
|
||||||
case 'ADD_CONSUMER':
|
case 'ADD_CONSUMER':
|
||||||
{
|
{
|
||||||
const oldPeer = state[action.payload.peerName];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
||||||
if (!oldPeer)
|
if (!oldPeer)
|
||||||
{
|
{
|
||||||
throw new Error('no Peer found');
|
throw new Error('no Peer found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...state, [oldPeer.name]: peer(oldPeer, action) };
|
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'REMOVE_CONSUMER':
|
case 'REMOVE_CONSUMER':
|
||||||
{
|
{
|
||||||
const oldPeer = state[action.payload.peerName];
|
const oldPeer = state[action.payload.peerId];
|
||||||
|
|
||||||
// NOTE: This means that the Peer was closed before, so it's ok.
|
// NOTE: This means that the Peer was closed before, so it's ok.
|
||||||
if (!oldPeer)
|
if (!oldPeer)
|
||||||
return state;
|
return state;
|
||||||
|
|
||||||
return { ...state, [oldPeer.name]: peer(oldPeer, action) };
|
return { ...state, [oldPeer.id]: peer(oldPeer, action) };
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ const initialState =
|
||||||
locked : false,
|
locked : false,
|
||||||
lockedOut : false,
|
lockedOut : false,
|
||||||
audioSuspended : false,
|
audioSuspended : false,
|
||||||
activeSpeakerName : null,
|
activeSpeakerId : null,
|
||||||
torrentSupport : false,
|
torrentSupport : false,
|
||||||
showSettings : false,
|
showSettings : false,
|
||||||
fullScreenConsumer : null, // ConsumerID
|
fullScreenConsumer : null, // ConsumerID
|
||||||
windowConsumer : null, // ConsumerID
|
windowConsumer : null, // ConsumerID
|
||||||
toolbarsVisible : true,
|
toolbarsVisible : true,
|
||||||
mode : 'democratic',
|
mode : 'democratic',
|
||||||
selectedPeerName : null,
|
selectedPeerId : null,
|
||||||
spotlights : [],
|
spotlights : [],
|
||||||
settingsOpen : false
|
settingsOpen : false
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +35,7 @@ const room = (state = initialState, action) =>
|
||||||
if (roomState === 'connected')
|
if (roomState === 'connected')
|
||||||
return { ...state, state: roomState };
|
return { ...state, state: roomState };
|
||||||
else
|
else
|
||||||
return { ...state, state: roomState, activeSpeakerName: null };
|
return { ...state, state: roomState, activeSpeakerId: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'SET_ROOM_LOCKED':
|
case 'SET_ROOM_LOCKED':
|
||||||
|
|
@ -69,9 +69,9 @@ const room = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_ROOM_ACTIVE_SPEAKER':
|
case 'SET_ROOM_ACTIVE_SPEAKER':
|
||||||
{
|
{
|
||||||
const { peerName } = action.payload;
|
const { peerId } = action.payload;
|
||||||
|
|
||||||
return { ...state, activeSpeakerName: peerName };
|
return { ...state, activeSpeakerId: peerId };
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'FILE_SHARING_SUPPORTED':
|
case 'FILE_SHARING_SUPPORTED':
|
||||||
|
|
@ -119,13 +119,13 @@ const room = (state = initialState, action) =>
|
||||||
|
|
||||||
case 'SET_SELECTED_PEER':
|
case 'SET_SELECTED_PEER':
|
||||||
{
|
{
|
||||||
const { selectedPeerName } = action.payload;
|
const { selectedPeerId } = action.payload;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
|
||||||
selectedPeerName : state.selectedPeerName === selectedPeerName ?
|
selectedPeerId : state.selectedPeerId === selectedPeerId ?
|
||||||
null : selectedPeerName
|
null : selectedPeerId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
export function getSignalingUrl(peerName, roomId)
|
export function getSignalingUrl(peerId, roomId)
|
||||||
{
|
{
|
||||||
const hostname = window.location.hostname;
|
const hostname = window.location.hostname;
|
||||||
|
|
||||||
const port = process.env.NODE_ENV !== 'production' ? window.config.developmentPort : window.location.port;
|
const port = process.env.NODE_ENV !== 'production' ? window.config.developmentPort : window.location.port;
|
||||||
|
|
||||||
const url = `wss://${hostname}:${port}/?peerName=${peerName}&roomId=${roomId}`;
|
const url = `wss://${hostname}:${port}/?peerId=${peerId}&roomId=${roomId}`;
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
{
|
{
|
||||||
// oAuth2 conf
|
// oAuth2 conf
|
||||||
|
|
@ -9,12 +11,12 @@ module.exports =
|
||||||
could be discovered on:
|
could be discovered on:
|
||||||
issuerURL + '/.well-known/openid-configuration'
|
issuerURL + '/.well-known/openid-configuration'
|
||||||
*/
|
*/
|
||||||
issuerURL : 'https://example.com'
|
issuerURL : 'https://example.com',
|
||||||
clientOptions :
|
clientOptions :
|
||||||
{
|
{
|
||||||
client_id : '',
|
client_id : '',
|
||||||
client_secret : '',
|
client_secret : '',
|
||||||
scope : 'openid email profile'
|
scope : 'openid email profile',
|
||||||
// where client.example.com is your multiparty meeting server
|
// where client.example.com is your multiparty meeting server
|
||||||
redirect_uri : 'https://client.example.com/auth/callback'
|
redirect_uri : 'https://client.example.com/auth/callback'
|
||||||
}
|
}
|
||||||
|
|
@ -33,10 +35,13 @@ module.exports =
|
||||||
// Any http request is redirected to https.
|
// Any http request is redirected to https.
|
||||||
// Listening port for http server.
|
// Listening port for http server.
|
||||||
listeningRedirectPort : 80,
|
listeningRedirectPort : 80,
|
||||||
// STUN/TURN
|
// Mediasoup settings
|
||||||
mediasoup :
|
mediasoup :
|
||||||
{
|
{
|
||||||
// mediasoup Server settings.
|
numWorkers : Object.keys(os.cpus()).length,
|
||||||
|
// mediasoup Worker settings.
|
||||||
|
worker :
|
||||||
|
{
|
||||||
logLevel : 'warn',
|
logLevel : 'warn',
|
||||||
logTags :
|
logTags :
|
||||||
[
|
[
|
||||||
|
|
@ -45,47 +50,46 @@ module.exports =
|
||||||
'dtls',
|
'dtls',
|
||||||
'rtp',
|
'rtp',
|
||||||
'srtp',
|
'srtp',
|
||||||
'rtcp',
|
'rtcp'
|
||||||
'rbe',
|
|
||||||
'rtx'
|
|
||||||
],
|
],
|
||||||
rtcIPv4 : true,
|
|
||||||
rtcIPv6 : true,
|
|
||||||
rtcAnnouncedIPv4 : null,
|
|
||||||
rtcAnnouncedIPv6 : null,
|
|
||||||
rtcMinPort : 40000,
|
rtcMinPort : 40000,
|
||||||
rtcMaxPort : 49999,
|
rtcMaxPort : 49999
|
||||||
// mediasoup Room codecs.
|
},
|
||||||
|
// mediasoup Router settings.
|
||||||
|
router :
|
||||||
|
{
|
||||||
|
// Router media codecs.
|
||||||
mediaCodecs :
|
mediaCodecs :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
kind : 'audio',
|
kind : 'audio',
|
||||||
name : 'opus',
|
mimeType : 'audio/opus',
|
||||||
clockRate : 48000,
|
clockRate : 48000,
|
||||||
channels : 2,
|
channels : 2
|
||||||
parameters :
|
|
||||||
{
|
|
||||||
useinbandfec : 1
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// kind : 'video',
|
|
||||||
// name : 'VP8',
|
|
||||||
// clockRate : 90000
|
|
||||||
// }
|
|
||||||
{
|
{
|
||||||
kind : 'video',
|
kind : 'video',
|
||||||
name : 'H264',
|
mimeType : 'video/h264',
|
||||||
clockRate : 90000,
|
clockRate : 90000,
|
||||||
parameters :
|
parameters :
|
||||||
{
|
{
|
||||||
'packetization-mode' : 1,
|
'packetization-mode' : 1,
|
||||||
'profile-level-id' : '42e01f',
|
'profile-level-id' : '42e01f',
|
||||||
'level-asymmetry-allowed' : 1
|
'level-asymmetry-allowed' : 1,
|
||||||
|
'x-google-start-bitrate' : 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// mediasoup WebRtcTransport settings.
|
||||||
|
webRtcTransport :
|
||||||
|
{
|
||||||
|
listenIps :
|
||||||
|
[
|
||||||
|
{ ip: '1.2.3.4', announcedIp: null }
|
||||||
],
|
],
|
||||||
// mediasoup per Peer max sending bitrate (in bps).
|
maxIncomingBitrate : 1500000,
|
||||||
maxBitrate : 500000
|
initialAvailableOutgoingBitrate : 1000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
1301
server/lib/Room.js
1301
server/lib/Room.js
File diff suppressed because it is too large
Load Diff
|
|
@ -54,7 +54,7 @@ function handleRoom(room, stream)
|
||||||
Object.assign({}, baseEvent,
|
Object.assign({}, baseEvent,
|
||||||
{
|
{
|
||||||
event : 'room.newpeer',
|
event : 'room.newpeer',
|
||||||
peerName : peer.name,
|
peerId : peer.id,
|
||||||
rtpCapabilities : peer.rtpCapabilities
|
rtpCapabilities : peer.rtpCapabilities
|
||||||
}),
|
}),
|
||||||
stream);
|
stream);
|
||||||
|
|
@ -67,7 +67,7 @@ function handlePeer(peer, baseEvent, stream)
|
||||||
{
|
{
|
||||||
baseEvent = Object.assign({}, baseEvent,
|
baseEvent = Object.assign({}, baseEvent,
|
||||||
{
|
{
|
||||||
peerName : peer.name
|
peerId : peer.id
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('close', (originator) =>
|
peer.on('close', (originator) =>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "multiparty-meeting-server",
|
"name": "multiparty-meeting-server",
|
||||||
"version": "2.0.0",
|
"version": "3.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "multiparty meeting server",
|
"description": "multiparty meeting server",
|
||||||
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
"author": "Håvar Aambø Fosstveit <h@fosstveit.net>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"awaitqueue": "^1.0.0",
|
||||||
"base-64": "^0.1.0",
|
"base-64": "^0.1.0",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"express-session": "^1.16.1",
|
"express-session": "^1.16.1",
|
||||||
"mediasoup": "^2.6.11",
|
"mediasoup": "^3.0.12",
|
||||||
"openid-client": "^2.5.0",
|
"openid-client": "^2.5.0",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"socket.io": "^2.1.1",
|
"socket.io": "^2.1.1",
|
||||||
|
|
|
||||||
317
server/server.js
317
server/server.js
|
|
@ -10,6 +10,8 @@ const http = require('http');
|
||||||
const spdy = require('spdy');
|
const spdy = require('spdy');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const compression = require('compression');
|
const compression = require('compression');
|
||||||
|
const mediasoup = require('mediasoup');
|
||||||
|
const AwaitQueue = require('awaitqueue');
|
||||||
const Logger = require('./lib/Logger');
|
const Logger = require('./lib/Logger');
|
||||||
const Room = require('./lib/Room');
|
const Room = require('./lib/Room');
|
||||||
const utils = require('./util');
|
const utils = require('./util');
|
||||||
|
|
@ -17,7 +19,7 @@ const base64 = require('base-64');
|
||||||
// auth
|
// auth
|
||||||
const passport = require('passport');
|
const passport = require('passport');
|
||||||
const { Issuer, Strategy } = require('openid-client');
|
const { Issuer, Strategy } = require('openid-client');
|
||||||
const session = require('express-session')
|
const session = require('express-session');
|
||||||
|
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.log('- process.env.DEBUG:', process.env.DEBUG);
|
console.log('- process.env.DEBUG:', process.env.DEBUG);
|
||||||
|
|
@ -25,11 +27,18 @@ console.log('- config.mediasoup.logLevel:', config.mediasoup.logLevel);
|
||||||
console.log('- config.mediasoup.logTags:', config.mediasoup.logTags);
|
console.log('- config.mediasoup.logTags:', config.mediasoup.logTags);
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
|
|
||||||
// Start the mediasoup server.
|
|
||||||
const mediaServer = require('./mediasoup');
|
|
||||||
|
|
||||||
const logger = new Logger();
|
const logger = new Logger();
|
||||||
|
|
||||||
|
const queue = new AwaitQueue();
|
||||||
|
|
||||||
|
// mediasoup Workers.
|
||||||
|
// @type {Array<mediasoup.Worker>}
|
||||||
|
const mediasoupWorkers = [];
|
||||||
|
|
||||||
|
// Index of next mediasoup Worker to use.
|
||||||
|
// @type {Number}
|
||||||
|
let nextMediasoupWorkerIdx = 0;
|
||||||
|
|
||||||
// Map of Room instances indexed by roomId.
|
// Map of Room instances indexed by roomId.
|
||||||
const rooms = new Map();
|
const rooms = new Map();
|
||||||
|
|
||||||
|
|
@ -40,35 +49,84 @@ const tls =
|
||||||
key : fs.readFileSync(config.tls.key)
|
key : fs.readFileSync(config.tls.key)
|
||||||
};
|
};
|
||||||
|
|
||||||
let app = express();
|
const app = express();
|
||||||
let httpsServer;
|
let httpsServer;
|
||||||
let oidcClient;
|
let oidcClient;
|
||||||
let oidcStrategy;
|
let oidcStrategy;
|
||||||
|
|
||||||
passport.serializeUser(function(user, done)
|
passport.serializeUser((user, done) =>
|
||||||
{
|
{
|
||||||
done(null, user);
|
done(null, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
passport.deserializeUser(function(user, done)
|
passport.deserializeUser((user, done) =>
|
||||||
{
|
{
|
||||||
done(null, user);
|
done(null, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
const auth=config.auth;
|
const auth = config.auth;
|
||||||
|
|
||||||
function setupAuth(oidcIssuer)
|
async function run()
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
typeof(auth) !== 'undefined' &&
|
||||||
|
typeof(auth.issuerURL) !== 'undefined' &&
|
||||||
|
typeof(auth.clientOptions) !== 'undefined'
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Issuer.discover(auth.issuerURL).then( async (oidcIssuer) =>
|
||||||
|
{
|
||||||
|
// Setup authentication
|
||||||
|
await setupAuth(oidcIssuer);
|
||||||
|
|
||||||
|
// Run a mediasoup Worker.
|
||||||
|
await runMediasoupWorkers();
|
||||||
|
|
||||||
|
// Run HTTPS server.
|
||||||
|
await runHttpsServer();
|
||||||
|
|
||||||
|
// Run WebSocketServer.
|
||||||
|
await runWebSocketServer();
|
||||||
|
})
|
||||||
|
.catch((err) =>
|
||||||
|
{
|
||||||
|
logger.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.error('Auth is not configure properly!');
|
||||||
|
|
||||||
|
// Run a mediasoup Worker.
|
||||||
|
await runMediasoupWorkers();
|
||||||
|
|
||||||
|
// Run HTTPS server.
|
||||||
|
await runHttpsServer();
|
||||||
|
|
||||||
|
// Run WebSocketServer.
|
||||||
|
await runWebSocketServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log rooms status every 30 seconds.
|
||||||
|
setInterval(() =>
|
||||||
|
{
|
||||||
|
for (const room of rooms.values())
|
||||||
|
{
|
||||||
|
room.logStatus();
|
||||||
|
}
|
||||||
|
}, 120000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupAuth(oidcIssuer)
|
||||||
{
|
{
|
||||||
oidcClient = new oidcIssuer.Client(auth.clientOptions);
|
oidcClient = new oidcIssuer.Client(auth.clientOptions);
|
||||||
const params =
|
|
||||||
{
|
|
||||||
...auth.clientOptions
|
|
||||||
// ... any authorization request parameters go here
|
// ... any authorization request parameters go here
|
||||||
// client_id defaults to client.client_id
|
// client_id defaults to client.client_id
|
||||||
// redirect_uri defaults to client.redirect_uris[0]
|
// redirect_uri defaults to client.redirect_uris[0]
|
||||||
// response type defaults to client.response_types[0], then 'code'
|
// response type defaults to client.response_types[0], then 'code'
|
||||||
// scope defaults to 'openid'
|
// scope defaults to 'openid'
|
||||||
};
|
const params = auth.clientOptions;
|
||||||
|
|
||||||
// optional, defaults to false, when true req is passed as a first
|
// optional, defaults to false, when true req is passed as a first
|
||||||
// argument to verify fn
|
// argument to verify fn
|
||||||
|
|
@ -78,63 +136,73 @@ function setupAuth(oidcIssuer)
|
||||||
// resolved from the issuer configuration, instead of true you may provide
|
// resolved from the issuer configuration, instead of true you may provide
|
||||||
// any of the supported values directly, i.e. "S256" (recommended) or "plain"
|
// any of the supported values directly, i.e. "S256" (recommended) or "plain"
|
||||||
const usePKCE = false;
|
const usePKCE = false;
|
||||||
const client=oidcClient;
|
const client = oidcClient;
|
||||||
|
|
||||||
oidcStrategy = new Strategy(
|
oidcStrategy = new Strategy(
|
||||||
{ client, params, passReqToCallback, usePKCE },
|
{ client, params, passReqToCallback, usePKCE },
|
||||||
(tokenset, userinfo, done) =>
|
(tokenset, userinfo, done) =>
|
||||||
{
|
{
|
||||||
let user = {
|
const user =
|
||||||
|
{
|
||||||
id : tokenset.claims.sub,
|
id : tokenset.claims.sub,
|
||||||
provider : tokenset.claims.iss,
|
provider : tokenset.claims.iss,
|
||||||
_userinfo : userinfo,
|
_userinfo : userinfo,
|
||||||
_claims : tokenset.claims,
|
_claims : tokenset.claims
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (typeof(userinfo.picture) !== 'undefined')
|
||||||
if ( typeof(userinfo.picture) !== 'undefined' ){
|
{
|
||||||
if ( ! userinfo.picture.match(/^http/g) ) {
|
if (!userinfo.picture.match(/^http/g))
|
||||||
|
{
|
||||||
user.Photos = [ { value: `data:image/jpeg;base64, ${userinfo.picture}` } ];
|
user.Photos = [ { value: `data:image/jpeg;base64, ${userinfo.picture}` } ];
|
||||||
} else {
|
}
|
||||||
user.Photos= [ { value: userinfo.picture } ];
|
else
|
||||||
|
{
|
||||||
|
user.Photos = [ { value: userinfo.picture } ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.nickname) !== 'undefined' ){
|
if (typeof(userinfo.nickname) !== 'undefined')
|
||||||
user.displayName=userinfo.nickname;
|
{
|
||||||
|
user.displayName = userinfo.nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.name) !== 'undefined' ){
|
if (typeof(userinfo.name) !== 'undefined')
|
||||||
user.displayName=userinfo.name;
|
{
|
||||||
|
user.displayName = userinfo.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.email) !== 'undefined' ){
|
if (typeof(userinfo.email) !== 'undefined')
|
||||||
user.emails=[{value: userinfo.email}];
|
{
|
||||||
|
user.emails = [ { value: userinfo.email } ];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.given_name) !== 'undefined' ){
|
if (typeof(userinfo.given_name) !== 'undefined')
|
||||||
user.name={givenName: userinfo.given_name};
|
{
|
||||||
|
user.name = { givenName: userinfo.given_name };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.family_name) !== 'undefined' ){
|
if (typeof(userinfo.family_name) !== 'undefined')
|
||||||
user.name={familyName: userinfo.family_name};
|
{
|
||||||
|
user.name = { familyName: userinfo.family_name };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( typeof(userinfo.middle_name) !== 'undefined' ){
|
if (typeof(userinfo.middle_name) !== 'undefined')
|
||||||
user.name={middleName: userinfo.middle_name};
|
{
|
||||||
|
user.name = { middleName: userinfo.middle_name };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return done(null, user);
|
return done(null, user);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
passport.use('oidc', oidcStrategy);
|
passport.use('oidc', oidcStrategy);
|
||||||
|
|
||||||
app.use(session({
|
app.use(session({
|
||||||
secret: config.cookieSecret,
|
secret : config.cookieSecret,
|
||||||
resave: true,
|
resave : true,
|
||||||
saveUninitialized: true,
|
saveUninitialized : true,
|
||||||
cookie: { secure: true }
|
cookie : { secure: true }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
|
|
@ -146,19 +214,19 @@ function setupAuth(oidcIssuer)
|
||||||
passport.authenticate('oidc', {
|
passport.authenticate('oidc', {
|
||||||
state : base64.encode(JSON.stringify({
|
state : base64.encode(JSON.stringify({
|
||||||
roomId : req.query.roomId,
|
roomId : req.query.roomId,
|
||||||
peerName : req.query.peerName,
|
peerId : req.query.peerId,
|
||||||
code : utils.random(10)
|
code : utils.random(10)
|
||||||
}))
|
}))
|
||||||
})(req, res, next);
|
})(req, res, next);
|
||||||
});
|
});
|
||||||
|
|
||||||
// logout
|
// logout
|
||||||
app.get('/auth/logout', function(req, res)
|
app.get('/auth/logout', (req, res) =>
|
||||||
{
|
{
|
||||||
req.logout();
|
req.logout();
|
||||||
res.redirect('/');
|
res.redirect('/');
|
||||||
}
|
});
|
||||||
);
|
|
||||||
// callback
|
// callback
|
||||||
app.get(
|
app.get(
|
||||||
'/auth/callback',
|
'/auth/callback',
|
||||||
|
|
@ -169,21 +237,29 @@ function setupAuth(oidcIssuer)
|
||||||
|
|
||||||
if (rooms.has(state.roomId))
|
if (rooms.has(state.roomId))
|
||||||
{
|
{
|
||||||
let displayName,photo
|
let displayName;
|
||||||
if (typeof(req.user) !== 'undefined'){
|
let photo;
|
||||||
if (typeof(req.user.displayName) !== 'undefined') displayName=req.user.displayName;
|
|
||||||
else displayName="";
|
if (typeof(req.user) !== 'undefined')
|
||||||
|
{
|
||||||
|
if (typeof(req.user.displayName) !== 'undefined')
|
||||||
|
displayName = req.user.displayName;
|
||||||
|
else
|
||||||
|
displayName = '';
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof(req.user.Photos) !== 'undefined' &&
|
typeof(req.user.Photos) !== 'undefined' &&
|
||||||
typeof(req.user.Photos[0]) !== 'undefined' &&
|
typeof(req.user.Photos[0]) !== 'undefined' &&
|
||||||
typeof(req.user.Photos[0].value) !== 'undefined'
|
typeof(req.user.Photos[0].value) !== 'undefined'
|
||||||
) photo=req.user.Photos[0].value;
|
)
|
||||||
else photo="/static/media/buddy.403cb9f6.svg";
|
photo = req.user.Photos[0].value;
|
||||||
|
else
|
||||||
|
photo = '/static/media/buddy.403cb9f6.svg';
|
||||||
}
|
}
|
||||||
|
|
||||||
const data =
|
const data =
|
||||||
{
|
{
|
||||||
peerName : state.peerName,
|
peerId : state.peerId,
|
||||||
name : displayName,
|
name : displayName,
|
||||||
picture : photo
|
picture : photo
|
||||||
};
|
};
|
||||||
|
|
@ -198,9 +274,12 @@ function setupAuth(oidcIssuer)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupWebServer() {
|
async function runHttpsServer()
|
||||||
|
{
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
|
|
||||||
|
app.use('/.well-known/acme-challenge', express.static('public/.well-known/acme-challenge'));
|
||||||
|
|
||||||
app.all('*', (req, res, next) =>
|
app.all('*', (req, res, next) =>
|
||||||
{
|
{
|
||||||
if (req.secure)
|
if (req.secure)
|
||||||
|
|
@ -234,19 +313,23 @@ function setupWebServer() {
|
||||||
{
|
{
|
||||||
logger.info('Server redirecting port: ', config.listeningRedirectPort);
|
logger.info('Server redirecting port: ', config.listeningRedirectPort);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function setupSocketIO(){
|
/**
|
||||||
|
* Create a protoo WebSocketServer to allow WebSocket connections from browsers.
|
||||||
|
*/
|
||||||
|
async function runWebSocketServer()
|
||||||
|
{
|
||||||
const io = require('socket.io')(httpsServer);
|
const io = require('socket.io')(httpsServer);
|
||||||
|
|
||||||
// Handle connections from clients.
|
// Handle connections from clients.
|
||||||
io.on('connection', (socket) =>
|
io.on('connection', (socket) =>
|
||||||
{
|
{
|
||||||
const { roomId, peerName } = socket.handshake.query;
|
const { roomId, peerId } = socket.handshake.query;
|
||||||
|
|
||||||
if (!roomId || !peerName)
|
if (!roomId || !peerId)
|
||||||
{
|
{
|
||||||
logger.warn('connection request without roomId and/or peerName');
|
logger.warn('connection request without roomId and/or peerId');
|
||||||
|
|
||||||
socket.disconnect(true);
|
socket.disconnect(true);
|
||||||
|
|
||||||
|
|
@ -254,72 +337,90 @@ function setupSocketIO(){
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'connection request [roomId:"%s", peerName:"%s"]', roomId, peerName);
|
'connection request [roomId:"%s", peerId:"%s"]', roomId, peerId);
|
||||||
|
|
||||||
let room;
|
queue.push(async () =>
|
||||||
|
|
||||||
// If an unknown roomId, create a new Room.
|
|
||||||
if (!rooms.has(roomId))
|
|
||||||
{
|
{
|
||||||
logger.info('creating a new Room [roomId:"%s"]', roomId);
|
const room = await getOrCreateRoom({ roomId });
|
||||||
|
|
||||||
try
|
room.handleConnection({ peerId, socket });
|
||||||
|
})
|
||||||
|
.catch((error) =>
|
||||||
{
|
{
|
||||||
room = new Room(roomId, mediaServer, io);
|
logger.error('room creation or room joining failed:%o', error);
|
||||||
|
|
||||||
global.APP_ROOM = room;
|
|
||||||
}
|
|
||||||
catch (error)
|
|
||||||
{
|
|
||||||
logger.error('error creating a new Room: %s', error);
|
|
||||||
|
|
||||||
socket.disconnect(true);
|
socket.disconnect(true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const logStatusTimer = setInterval(() =>
|
/**
|
||||||
|
* Launch as many mediasoup Workers as given in the configuration file.
|
||||||
|
*/
|
||||||
|
async function runMediasoupWorkers()
|
||||||
|
{
|
||||||
|
const { numWorkers } = config.mediasoup;
|
||||||
|
|
||||||
|
logger.info('running %d mediasoup Workers...', numWorkers);
|
||||||
|
|
||||||
|
for (let i = 0; i < numWorkers; ++i)
|
||||||
{
|
{
|
||||||
room.logStatus();
|
const worker = await mediasoup.createWorker(
|
||||||
}, 30000);
|
{
|
||||||
|
logLevel : config.mediasoup.worker.logLevel,
|
||||||
|
logTags : config.mediasoup.worker.logTags,
|
||||||
|
rtcMinPort : config.mediasoup.worker.rtcMinPort,
|
||||||
|
rtcMaxPort : config.mediasoup.worker.rtcMaxPort
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.on('died', () =>
|
||||||
|
{
|
||||||
|
logger.error(
|
||||||
|
'mediasoup Worker died, exiting in 2 seconds... [pid:%d]', worker.pid);
|
||||||
|
|
||||||
|
setTimeout(() => process.exit(1), 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
mediasoupWorkers.push(worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next mediasoup Worker.
|
||||||
|
*/
|
||||||
|
function getMediasoupWorker()
|
||||||
|
{
|
||||||
|
const worker = mediasoupWorkers[nextMediasoupWorkerIdx];
|
||||||
|
|
||||||
|
if (++nextMediasoupWorkerIdx === mediasoupWorkers.length)
|
||||||
|
nextMediasoupWorkerIdx = 0;
|
||||||
|
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Room instance (or create one if it does not exist).
|
||||||
|
*/
|
||||||
|
async function getOrCreateRoom({ roomId })
|
||||||
|
{
|
||||||
|
let room = rooms.get(roomId);
|
||||||
|
|
||||||
|
// If the Room does not exist create a new one.
|
||||||
|
if (!room)
|
||||||
|
{
|
||||||
|
logger.info('creating a new Room [roomId:%s]', roomId);
|
||||||
|
|
||||||
|
const mediasoupWorker = getMediasoupWorker();
|
||||||
|
|
||||||
|
room = await Room.create({ mediasoupWorker, roomId });
|
||||||
|
|
||||||
rooms.set(roomId, room);
|
rooms.set(roomId, room);
|
||||||
|
room.on('close', () => rooms.delete(roomId));
|
||||||
room.on('close', () =>
|
|
||||||
{
|
|
||||||
rooms.delete(roomId);
|
|
||||||
clearInterval(logStatusTimer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
room = rooms.get(roomId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.room = roomId;
|
return room;
|
||||||
|
|
||||||
room.handleConnection(peerName, socket);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
typeof(auth) !== 'undefined' &&
|
|
||||||
typeof(auth.issuerURL) !== 'undefined' &&
|
|
||||||
typeof(auth.clientOptions) !== 'undefined'
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Issuer.discover(auth.issuerURL).then((oidcIssuer) =>
|
|
||||||
{
|
|
||||||
setupAuth(oidcIssuer);
|
|
||||||
setupWebServer();
|
|
||||||
setupSocketIO();
|
|
||||||
}).catch((err) => {
|
|
||||||
logger.error(err);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
logger.error('Auth is not configure properly!');
|
|
||||||
setupWebServer();
|
|
||||||
setupSocketIO();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Loading…
Reference in New Issue