Merge branch 'feature-ptt' into develop

auto_join_3.3
Håvar Aambø Fosstveit 2020-03-27 10:46:14 +01:00
commit 9cd6efe761
4 changed files with 150 additions and 28 deletions

View File

@ -230,6 +230,9 @@ export default class RoomClient
// Local mic hark // Local mic hark
this._hark = null; this._hark = null;
// Local MediaStream for hark
this._harkStream = null
// Local webcam mediasoup Producer. // Local webcam mediasoup Producer.
this._webcamProducer = null; this._webcamProducer = null;
@ -278,8 +281,9 @@ export default class RoomClient
_startKeyListener() _startKeyListener()
{ {
// Add keypress event listner on document // Add keypress event listner on document
document.addEventListener('keypress', (event) => document.addEventListener('keydown', (event) =>
{ {
if (event.repeat) return;
const key = String.fromCharCode(event.which); const key = String.fromCharCode(event.which);
const source = event.target; const source = event.target;
@ -288,11 +292,11 @@ export default class RoomClient
if (exclude.indexOf(source.tagName.toLowerCase()) === -1) if (exclude.indexOf(source.tagName.toLowerCase()) === -1)
{ {
logger.debug('keyPress() [key:"%s"]', key); logger.debug('keyDown() [key:"%s"]', key);
switch (key) switch (key)
{ {
case 'a': // Activate advanced mode case 'A': // Activate advanced mode
{ {
store.dispatch(settingsActions.toggleAdvancedMode()); store.dispatch(settingsActions.toggleAdvancedMode());
store.dispatch(requestActions.notify( store.dispatch(requestActions.notify(
@ -331,8 +335,19 @@ export default class RoomClient
break; break;
} }
case ' ': case ' ': // Push To Talk start
case 'm': // Toggle microphone {
if (this._micProducer)
{
if (this._micProducer.paused)
{
this.unmuteMic();
}
}
break;
}
case 'M': // Toggle microphone
{ {
if (this._micProducer) if (this._micProducer)
{ {
@ -377,7 +392,7 @@ export default class RoomClient
break; break;
} }
case 'v': // Toggle video case 'V': // Toggle video
{ {
if (this._webcamProducer) if (this._webcamProducer)
this.disableWebcam(); this.disableWebcam();
@ -394,6 +409,41 @@ export default class RoomClient
} }
} }
}); });
document.addEventListener('keyup', (event) =>
{
const key = String.fromCharCode(event.which);
const source = event.target;
const exclude = [ 'input', 'textarea' ];
if (exclude.indexOf(source.tagName.toLowerCase()) === -1)
{
logger.debug('keyUp() [key:"%s"]', key);
switch (key)
{
case ' ': // Push To Talk stop
{
if (this._micProducer)
{
if (!this._micProducer.paused)
{
this.muteMic();
}
}
break;
}
default:
{
break;
}
}
}
event.preventDefault();
}, true);
} }
_startDevicesListener() _startDevicesListener()
@ -943,6 +993,16 @@ export default class RoomClient
'changeAudioDevice() | new selected webcam [device:%o]', 'changeAudioDevice() | new selected webcam [device:%o]',
device); device);
if (this._hark != null)
this._hark.stop();
if (this._harkStream != null)
{
logger.debug('Stopping hark.');
this._harkStream.getAudioTracks()[0].stop();
this._harkStream = null;
}
if (this._micProducer && this._micProducer.track) if (this._micProducer && this._micProducer.track)
this._micProducer.track.stop(); this._micProducer.track.stop();
@ -964,17 +1024,15 @@ export default class RoomClient
if (this._micProducer) if (this._micProducer)
this._micProducer.volume = 0; this._micProducer.volume = 0;
const harkStream = new MediaStream(); this._harkStream = new MediaStream();
harkStream.addTrack(track); this._harkStream.addTrack(track.clone());
this._harkStream.getAudioTracks()[0].enabled = true;
if (!harkStream.getAudioTracks()[0]) if (!this._harkStream.getAudioTracks()[0])
throw new Error('changeAudioDevice(): given stream has no audio track'); throw new Error('changeAudioDevice(): given stream has no audio track');
if (this._hark != null) this._hark = hark(this._harkStream, { play: false });
this._hark.stop();
this._hark = hark(harkStream, { play: false });
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this._hark.on('volume_change', (dBs, threshold) => this._hark.on('volume_change', (dBs, threshold) =>
@ -998,6 +1056,14 @@ export default class RoomClient
store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume)); store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume));
} }
}); });
this._hark.on('speaking', function()
{
store.dispatch(meActions.setIsSpeaking(true));
});
this._hark.on('stopped_speaking', function()
{
store.dispatch(meActions.setIsSpeaking(false));
});
if (this._micProducer && this._micProducer.id) if (this._micProducer && this._micProducer.id)
store.dispatch( store.dispatch(
producerActions.setProducerTrack(this._micProducer.id, track)); producerActions.setProducerTrack(this._micProducer.id, track));
@ -2620,8 +2686,11 @@ export default class RoomClient
track, track,
codecOptions : codecOptions :
{ {
opusStereo : 1, opusStereo : false,
opusDtx : 1 opusDtx : true,
opusFec : true,
opusPtime : '3',
opusMaxPlaybackRate : 48000
}, },
appData : appData :
{ source: 'mic' } { source: 'mic' }
@ -2663,17 +2732,20 @@ export default class RoomClient
this._micProducer.volume = 0; this._micProducer.volume = 0;
const harkStream = new MediaStream();
harkStream.addTrack(track);
if (!harkStream.getAudioTracks()[0])
throw new Error('enableMic(): given stream has no audio track');
if (this._hark != null) if (this._hark != null)
this._hark.stop(); this._hark.stop();
this._hark = hark(harkStream, { play: false }); if (this._harkStream != null)
this._harkStream.getAudioTracks()[0].stop();
this._harkStream = new MediaStream();
this._harkStream.addTrack(track.clone());
if (!this._harkStream.getAudioTracks()[0])
throw new Error('enableMic(): given stream has no audio track');
this._hark = hark(this._harkStream, { play: false });
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
this._hark.on('volume_change', (dBs, threshold) => this._hark.on('volume_change', (dBs, threshold) =>
@ -2697,6 +2769,14 @@ export default class RoomClient
store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume)); store.dispatch(peerVolumeActions.setPeerVolume(this._peerId, volume));
} }
}); });
this._hark.on('speaking', function()
{
store.dispatch(meActions.setIsSpeaking(true));
});
this._hark.on('stopped_speaking', function()
{
store.dispatch(meActions.setIsSpeaking(false));
});
} }
catch (error) catch (error)
{ {

View File

@ -91,3 +91,9 @@ export const setDisplayNameInProgress = (flag) =>
type : 'SET_DISPLAY_NAME_IN_PROGRESS', type : 'SET_DISPLAY_NAME_IN_PROGRESS',
payload : { flag } payload : { flag }
}); });
export const setIsSpeaking = (flag) =>
({
type : 'SET_IS_SPEAKING',
payload : { flag }
});

View File

@ -97,6 +97,26 @@ const styles = (theme) =>
fontSize : '7em', fontSize : '7em',
margin : 0 margin : 0
} }
},
ptt :
{
position : 'absolute',
float : 'left',
bottom : '10%',
left : '50%',
transform : 'translate(-50%, 0%)',
color : 'rgba(255, 255, 255, 0.7)',
fontSize : '2vs',
backgroundColor : 'rgba(255, 0, 0, 0.5)',
margin : '4px',
padding : '15px',
borderRadius : '20px',
textAlign : 'center',
opacity : 0,
'&.enabled' :
{
opacity : 1
}
} }
}); });
@ -272,7 +292,7 @@ const Me = (props) =>
> >
<div className={classnames(classes.viewContainer)} style={style}> <div className={classnames(classes.viewContainer)} style={style}>
<div <div
className={classnames(classes.controls, hover ? 'hover' : null)} className={classnames(classes.controls, 'hover')}
onMouseOver={() => setHover(true)} onMouseOver={() => setHover(true)}
onMouseOut={() => setHover(false)} onMouseOut={() => setHover(false)}
onTouchStart={() => onTouchStart={() =>
@ -293,12 +313,20 @@ const Me = (props) =>
}, 2000); }, 2000);
}} }}
> >
<p> <p className={classnames(hover ? 'hover' : null)}>
<FormattedMessage <FormattedMessage
id='room.me' id='room.me'
defaultMessage='ME' defaultMessage='ME'
/> />
</p> </p>
<div className={classnames(classes.ptt, (micState ==='muted' && me.isSpeaking) ? 'enabled' : null)} >
<FormattedMessage
id='me.mutedPTT'
defaultMessage='You are muted: hold SPACE-BAR to speak!'
/>
</div>
{ !me.isMobile && { !me.isMobile &&
<React.Fragment> <React.Fragment>
<Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}> <Tooltip title={micTip} placement={smallScreen ? 'top' : 'left'}>
@ -416,7 +444,7 @@ const Me = (props) =>
roomClient.changeDisplayName(displayName); roomClient.changeDisplayName(displayName);
}} }}
> >
<Volume id={me.id} /> { micState === 'muted' ? null : <Volume id={me.id} /> }
</VideoView> </VideoView>
</div> </div>
</div> </div>

View File

@ -19,7 +19,8 @@ const initialState =
loginEnabled : false, loginEnabled : false,
raiseHand : false, raiseHand : false,
raiseHandInProgress : false, raiseHandInProgress : false,
loggedIn : false loggedIn : false,
isSpeaking : false
}; };
const me = (state = initialState, action) => const me = (state = initialState, action) =>
@ -147,6 +148,13 @@ const me = (state = initialState, action) =>
return { ...state, displayNameInProgress: flag }; return { ...state, displayNameInProgress: flag };
} }
case 'SET_IS_SPEAKING':
{
const { flag } = action.payload;
return { ...state, isSpeaking: flag };
}
default: default:
return state; return state;
} }