Show remote audio levels
parent
f1658f1b3c
commit
2645dc619a
|
|
@ -28,6 +28,9 @@ export default class Client extends events.EventEmitter
|
|||
super();
|
||||
this.setMaxListeners(Infinity);
|
||||
|
||||
// TODO: TMP
|
||||
global.CLIENT = this;
|
||||
|
||||
let url = urlFactory.getProtooUrl(peerId, roomId);
|
||||
let transport = new protooClient.WebSocketTransport(url);
|
||||
|
||||
|
|
@ -708,6 +711,16 @@ export default class Client extends events.EventEmitter
|
|||
break;
|
||||
}
|
||||
|
||||
case 'activespeaker':
|
||||
{
|
||||
let data = request.data;
|
||||
|
||||
this.emit('activespeaker', data.peer, data.level);
|
||||
accept();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
logger.error('unknown method');
|
||||
|
|
@ -776,7 +789,6 @@ export default class Client extends events.EventEmitter
|
|||
this._peerconnection = new RTCPeerConnection({ iceServers: [] });
|
||||
|
||||
// TODO: TMP
|
||||
global.CLIENT = this;
|
||||
global.PC = this._peerconnection;
|
||||
|
||||
if (this._localStream)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import IconButton from 'material-ui/IconButton/IconButton';
|
|||
import MicOffIcon from 'material-ui/svg-icons/av/mic-off';
|
||||
import VideoCamOffIcon from 'material-ui/svg-icons/av/videocam-off';
|
||||
import ChangeVideoCamIcon from 'material-ui/svg-icons/av/repeat';
|
||||
import classnames from 'classnames';
|
||||
import Video from './Video';
|
||||
import Logger from '../Logger';
|
||||
|
||||
|
|
@ -30,7 +31,12 @@ export default class LocalVideo extends React.Component
|
|||
let state = this.state;
|
||||
|
||||
return (
|
||||
<div data-component='LocalVideo' className={`state-${props.connectionState}`}>
|
||||
<div
|
||||
data-component='LocalVideo'
|
||||
className={classnames(`state-${props.connectionState}`, {
|
||||
'active-speaker' : props.isActiveSpeaker
|
||||
})}
|
||||
>
|
||||
{props.stream ?
|
||||
<Video
|
||||
stream={props.stream}
|
||||
|
|
@ -141,6 +147,7 @@ LocalVideo.propTypes =
|
|||
multipleWebcams : React.PropTypes.bool.isRequired,
|
||||
webcamType : React.PropTypes.string,
|
||||
connectionState : React.PropTypes.string,
|
||||
isActiveSpeaker : React.PropTypes.bool.isRequired,
|
||||
onMicMute : React.PropTypes.func.isRequired,
|
||||
onWebcamToggle : React.PropTypes.func.isRequired,
|
||||
onWebcamChange : React.PropTypes.func.isRequired,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,10 @@ export default class RemoteVideo extends React.Component
|
|||
return (
|
||||
<div
|
||||
data-component='RemoteVideo'
|
||||
className={classnames({ fullsize: !!props.fullsize })}
|
||||
className={classnames({
|
||||
fullsize : !!props.fullsize,
|
||||
'active-speaker' : props.isActiveSpeaker
|
||||
})}
|
||||
>
|
||||
<Video
|
||||
stream={props.stream}
|
||||
|
|
@ -93,9 +96,10 @@ export default class RemoteVideo extends React.Component
|
|||
|
||||
RemoteVideo.propTypes =
|
||||
{
|
||||
peer : React.PropTypes.object.isRequired,
|
||||
stream : React.PropTypes.object.isRequired,
|
||||
fullsize : React.PropTypes.bool,
|
||||
onDisableVideo : React.PropTypes.func.isRequired,
|
||||
onEnableVideo : React.PropTypes.func.isRequired
|
||||
peer : React.PropTypes.object.isRequired,
|
||||
stream : React.PropTypes.object.isRequired,
|
||||
fullsize : React.PropTypes.bool,
|
||||
isActiveSpeaker : React.PropTypes.bool.isRequired,
|
||||
onDisableVideo : React.PropTypes.func.isRequired,
|
||||
onEnableVideo : React.PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ export default class Room extends React.Component
|
|||
connectionState : null,
|
||||
remoteStreams : {},
|
||||
showStats : false,
|
||||
stats : null
|
||||
stats : null,
|
||||
activeSpeakerId : null
|
||||
};
|
||||
|
||||
// Mounted flag
|
||||
|
|
@ -89,6 +90,7 @@ export default class Room extends React.Component
|
|||
peer={peer}
|
||||
stream={stream}
|
||||
fullsize={numPeers === 1}
|
||||
isActiveSpeaker={peer.id === state.activeSpeakerId}
|
||||
onDisableVideo={this.handleDisableRemoteVideo.bind(this)}
|
||||
onEnableVideo={this.handleEnableRemoteVideo.bind(this)}
|
||||
/>
|
||||
|
|
@ -107,6 +109,7 @@ export default class Room extends React.Component
|
|||
multipleWebcams={state.multipleWebcams}
|
||||
webcamType={state.webcamType}
|
||||
connectionState={state.connectionState}
|
||||
isActiveSpeaker={props.peerId === state.activeSpeakerId}
|
||||
onMicMute={this.handleLocalMute.bind(this)}
|
||||
onWebcamToggle={this.handleLocalWebcamToggle.bind(this)}
|
||||
onWebcamChange={this.handleLocalWebcamChange.bind(this)}
|
||||
|
|
@ -296,8 +299,12 @@ export default class Room extends React.Component
|
|||
|
||||
this._client.on('close', (error) =>
|
||||
{
|
||||
// Clear remote streams (for reconnections).
|
||||
this.setState({ remoteStreams: {} });
|
||||
// Clear remote streams (for reconnections) and more stuff.
|
||||
this.setState(
|
||||
{
|
||||
remoteStreams : {},
|
||||
activeSpeakerId : null
|
||||
});
|
||||
|
||||
if (error)
|
||||
{
|
||||
|
|
@ -431,6 +438,14 @@ export default class Room extends React.Component
|
|||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1347578
|
||||
this.forceUpdate();
|
||||
});
|
||||
|
||||
this._client.on('activespeaker', (peer) =>
|
||||
{
|
||||
this.setState(
|
||||
{
|
||||
activeSpeakerId : (peer ? peer.id : null)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_startStats()
|
||||
|
|
|
|||
|
|
@ -90,12 +90,12 @@ export default class Video extends React.Component
|
|||
|
||||
this._hark.on('speaking', () =>
|
||||
{
|
||||
logger.debug('hark "speaking" event');
|
||||
// logger.debug('hark "speaking" event');
|
||||
});
|
||||
|
||||
this._hark.on('stopped_speaking', () =>
|
||||
{
|
||||
logger.debug('hark "stopped_speaking" event');
|
||||
// logger.debug('hark "stopped_speaking" event');
|
||||
|
||||
this.setState({ volume: 0 });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
}
|
||||
|
||||
&.state-checking {
|
||||
animation: LocalVideo-state-checking .75s infinite linear;
|
||||
animation: LocalVideo-state-checking .5s infinite linear;
|
||||
}
|
||||
|
||||
&.state-connected,
|
||||
|
|
@ -35,6 +35,10 @@
|
|||
border-color: rgba(#ff2000, 0.75);
|
||||
}
|
||||
|
||||
&.active-speaker {
|
||||
border-color: rgba(#fff, 0.9);
|
||||
}
|
||||
|
||||
> .controls {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.active-speaker {
|
||||
border-color: rgba(#fff, 0.9);
|
||||
}
|
||||
|
||||
> .controls {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
z-index: 5;
|
||||
top: 0
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
right: 2px;
|
||||
width: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ const config = require('../config');
|
|||
const MAX_BITRATE = config.mediasoup.maxBitrate || 3000000;
|
||||
const MIN_BITRATE = Math.min(50000 || MAX_BITRATE);
|
||||
const BITRATE_FACTOR = 0.75;
|
||||
const MIN_AUDIO_LEVEL = -50;
|
||||
|
||||
class Room extends EventEmitter
|
||||
{
|
||||
|
|
@ -30,6 +31,8 @@ class Room extends EventEmitter
|
|||
this._pendingProtooPeers = [];
|
||||
// Current max bitrate for all the participants.
|
||||
this._maxBitrate = MAX_BITRATE;
|
||||
// Current active speaker mediasoup Peer.
|
||||
this._activeSpeaker = null;
|
||||
|
||||
// Create a mediasoup room.
|
||||
mediaServer.createRoom(
|
||||
|
|
@ -53,6 +56,60 @@ class Room extends EventEmitter
|
|||
this._updateMaxBitrate();
|
||||
});
|
||||
});
|
||||
|
||||
this._mediaRoom.on('audiolevels', (entries) =>
|
||||
{
|
||||
logger.debug('room "audiolevels" event');
|
||||
|
||||
for (let entry of entries)
|
||||
{
|
||||
logger.debug('- [peer name:%s, rtpReceiver.id:%s, audio level:%s]',
|
||||
entry.peer.name, entry.rtpReceiver.id, entry.audioLevel);
|
||||
}
|
||||
|
||||
let activeSpeaker;
|
||||
let activeLevel;
|
||||
|
||||
if (entries.length > 0)
|
||||
{
|
||||
activeSpeaker = entries[0].peer;
|
||||
activeLevel = entries[0].audioLevel;
|
||||
|
||||
if (activeLevel < MIN_AUDIO_LEVEL)
|
||||
{
|
||||
activeSpeaker = null;
|
||||
activeLevel = undefined;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
activeSpeaker = null;
|
||||
}
|
||||
|
||||
if (this._activeSpeaker !== activeSpeaker)
|
||||
{
|
||||
let data = {};
|
||||
|
||||
if (activeSpeaker)
|
||||
{
|
||||
logger.debug('active speaker [peer:"%s", volume:%s]',
|
||||
activeSpeaker.name, activeLevel);
|
||||
|
||||
data.peer = { id: activeSpeaker.name };
|
||||
data.level = activeLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug('no current speaker');
|
||||
|
||||
data.peer = null;
|
||||
}
|
||||
|
||||
this._protooRoom.spread('activespeaker', data);
|
||||
}
|
||||
|
||||
this._activeSpeaker = activeSpeaker;
|
||||
});
|
||||
});
|
||||
|
||||
// Run all the pending join requests.
|
||||
|
|
|
|||
Loading…
Reference in New Issue