From 3ab603bbe7fdaec4d056a7ca3096ea42cf520c6a Mon Sep 17 00:00:00 2001 From: Stefan Otto Date: Tue, 17 Jul 2018 12:56:58 +0200 Subject: [PATCH] hark running now on producers and consumers directly instead on PeerView component, now we can send audiolevel events into more places ... --- app/lib/RoomClient.js | 67 ++++++++++++++++++++++++++++- app/lib/components/Me.jsx | 1 + app/lib/components/Peer.jsx | 37 ++++++++-------- app/lib/components/PeerView.jsx | 43 ++---------------- app/lib/redux/reducers/consumers.js | 9 ++++ app/lib/redux/reducers/producers.js | 9 ++++ app/lib/redux/stateActions.js | 16 +++++++ 7 files changed, 123 insertions(+), 59 deletions(-) diff --git a/app/lib/RoomClient.js b/app/lib/RoomClient.js index 93ac56a..5420adc 100644 --- a/app/lib/RoomClient.js +++ b/app/lib/RoomClient.js @@ -1,6 +1,7 @@ import protooClient from 'protoo-client'; import * as mediasoupClient from 'mediasoup-client'; import Logger from './Logger'; +import hark from 'hark'; import ScreenShare from './ScreenShare'; import { getProtooUrl } from './urlFactory'; import * as cookiesManager from './cookiesManager'; @@ -1380,7 +1381,33 @@ export default class RoomClient }) .then(() => { + const stream = new MediaStream; + logger.debug('_setMicProducer() succeeded'); + stream.addTrack(producer.track); + if (!stream.getAudioTracks()[0]) + throw new Error('_setMicProducer(): given stream has no audio track'); + producer.hark = hark(stream, { play: false }); + + // eslint-disable-next-line no-unused-vars + producer.hark.on('volume_change', (dBs, threshold) => + { + // The exact formula to convert from dBs (-100..0) to linear (0..1) is: + // Math.pow(10, dBs / 20) + // However it does not produce a visually useful output, so let exagerate + // it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to + // minimize component renderings. + let volume = Math.round(Math.pow(10, dBs / 85) * 10); + + if (volume === 1) + volume = 0; + + if (volume !== producer.volume) + { + producer.volume = volume; + this._dispatch(stateActions.setProducerVolume(producer.id, volume)); + } + }); }) .catch((error) => { @@ -1823,7 +1850,8 @@ export default class RoomClient track : null, codec : codec ? codec.name : null }, - consumer.peer.name)); + consumer.peer.name) + ); consumer.on('close', (originator) => { @@ -1835,6 +1863,43 @@ export default class RoomClient consumer.id, consumer.peer.name)); }); + consumer.on('handled', (originator) => + { + logger.debug( + 'consumer "handled" event [id:%s, originator:%s, consumer:%o]', + consumer.id, originator, consumer); + if (consumer.kind === 'audio') + { + const stream = new MediaStream; + + stream.addTrack(consumer.track); + if (!stream.getAudioTracks()[0]) + throw new Error('consumer.on("handled" | given stream has no audio track'); + + consumer.hark = hark(stream, { play: false }); + + // eslint-disable-next-line no-unused-vars + consumer.hark.on('volume_change', (dBs, threshold) => + { + // The exact formula to convert from dBs (-100..0) to linear (0..1) is: + // Math.pow(10, dBs / 20) + // However it does not produce a visually useful output, so let exagerate + // it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to + // minimize component renderings. + let volume = Math.round(Math.pow(10, dBs / 85) * 10); + + if (volume === 1) + volume = 0; + + if (volume !== consumer.volume) + { + consumer.volume = volume; + this._dispatch(stateActions.setConsumerVolume(consumer.id, volume)); + } + }); + } + }); + consumer.on('pause', (originator) => { logger.debug( diff --git a/app/lib/components/Me.jsx b/app/lib/components/Me.jsx index 38dc747..dcedb94 100644 --- a/app/lib/components/Me.jsx +++ b/app/lib/components/Me.jsx @@ -140,6 +140,7 @@ class Me extends React.Component advancedMode={advancedMode} peer={me} audioTrack={micProducer ? micProducer.track : null} + volume={micProducer ? micProducer.volume : null} videoTrack={webcamProducer ? webcamProducer.track : null} videoVisible={videoVisible} audioCodec={micProducer ? micProducer.codec : null} diff --git a/app/lib/components/Peer.jsx b/app/lib/components/Peer.jsx index d3961ae..2b06f13 100644 --- a/app/lib/components/Peer.jsx +++ b/app/lib/components/Peer.jsx @@ -8,27 +8,27 @@ import * as stateActions from '../redux/stateActions'; import PeerView from './PeerView'; import ScreenView from './ScreenView'; -class Peer extends Component +class Peer extends Component { state = { controlsVisible : false }; - handleMouseOver = () => + handleMouseOver = () => { this.setState({ controlsVisible : true }); }; - handleMouseOut = () => + handleMouseOut = () => { this.setState({ controlsVisible : false }); }; - render() + render() { const { advancedMode, @@ -45,35 +45,35 @@ class Peer extends Component toggleConsumerFullscreen, style } = this.props; - + const micEnabled = ( Boolean(micConsumer) && !micConsumer.locallyPaused && !micConsumer.remotelyPaused ); - + const videoVisible = ( Boolean(webcamConsumer) && !webcamConsumer.locallyPaused && !webcamConsumer.remotelyPaused ); - + const screenVisible = ( Boolean(screenConsumer) && !screenConsumer.locallyPaused && !screenConsumer.remotelyPaused ); - + let videoProfile; - + if (webcamConsumer) videoProfile = webcamConsumer.profile; - + let screenProfile; - + if (screenConsumer) screenProfile = screenConsumer.profile; - + return (
:null } - +
- +
- +
@@ -136,6 +136,7 @@ class Peer extends Component advancedMode={advancedMode} peer={peer} audioTrack={micConsumer ? micConsumer.track : null} + volume={micConsumer ? micConsumer.volume : null} videoTrack={webcamConsumer ? webcamConsumer.track : null} videoVisible={videoVisible} videoProfile={videoProfile} @@ -143,7 +144,7 @@ class Peer extends Component videoCodec={webcamConsumer ? webcamConsumer.codec : null} />
- + {screenConsumer ?
@@ -160,7 +161,7 @@ class Peer extends Component onDisableScreen(peer.name) : onEnableScreen(peer.name); }} /> - +
@@ -236,7 +237,7 @@ const mapDispatchToProps = (dispatch) => }, onEnableWebcam : (peerName) => { - + dispatch(requestActions.resumePeerVideo(peerName)); }, onDisableWebcam : (peerName) => diff --git a/app/lib/components/PeerView.jsx b/app/lib/components/PeerView.jsx index c258584..4a512a6 100644 --- a/app/lib/components/PeerView.jsx +++ b/app/lib/components/PeerView.jsx @@ -2,7 +2,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import Spinner from 'react-spinner'; -import hark from 'hark'; import * as appPropTypes from './appPropTypes'; import EditableInput from './EditableInput'; @@ -27,10 +26,6 @@ export default class PeerView extends React.Component // @type {MediaStreamTrack} this._videoTrack = null; - // Hark instance. - // @type {Object} - this._hark = null; - // Periodic timer for showing video resolution. this._videoResolutionTimer = null; } @@ -40,6 +35,7 @@ export default class PeerView extends React.Component const { isMe, peer, + volume, advancedMode, videoVisible, videoProfile, @@ -49,7 +45,6 @@ export default class PeerView extends React.Component } = this.props; const { - volume, videoWidth, videoHeight } = this.state; @@ -149,9 +144,6 @@ export default class PeerView extends React.Component componentWillUnmount() { - if (this._hark) - this._hark.stop(); - clearInterval(this._videoResolutionTimer); } @@ -160,6 +152,7 @@ export default class PeerView extends React.Component const { audioTrack, videoTrack } = nextProps; this._setTracks(audioTrack, videoTrack); + } _setTracks(audioTrack, videoTrack) @@ -170,9 +163,6 @@ export default class PeerView extends React.Component this._audioTrack = audioTrack; this._videoTrack = videoTrack; - if (this._hark) - this._hark.stop(); - clearInterval(this._videoResolutionTimer); this._hideVideoResolution(); @@ -190,9 +180,6 @@ export default class PeerView extends React.Component video.srcObject = stream; - if (audioTrack) - this._runHark(stream); - if (videoTrack) this._showVideoResolution(); } @@ -202,31 +189,6 @@ export default class PeerView extends React.Component } } - _runHark(stream) - { - if (!stream.getAudioTracks()[0]) - throw new Error('_runHark() | given stream has no audio track'); - - this._hark = hark(stream, { play: false }); - - // eslint-disable-next-line no-unused-vars - this._hark.on('volume_change', (dBs, threshold) => - { - // The exact formula to convert from dBs (-100..0) to linear (0..1) is: - // Math.pow(10, dBs / 20) - // However it does not produce a visually useful output, so let exagerate - // it a bit. Also, let convert it from 0..1 to 0..10 and avoid value 1 to - // minimize component renderings. - let volume = Math.round(Math.pow(10, dBs / 85) * 10); - - if (volume === 1) - volume = 0; - - if (volume !== this.state.volume) - this.setState({ volume: volume }); - }); - } - _showVideoResolution() { this._videoResolutionTimer = setInterval(() => @@ -259,6 +221,7 @@ PeerView.propTypes = [ appPropTypes.Me, appPropTypes.Peer ]).isRequired, advancedMode : PropTypes.bool, audioTrack : PropTypes.any, + volume : PropTypes.number, videoTrack : PropTypes.any, videoVisible : PropTypes.bool.isRequired, videoProfile : PropTypes.string, diff --git a/app/lib/redux/reducers/consumers.js b/app/lib/redux/reducers/consumers.js index 81f0e1c..117cdc5 100644 --- a/app/lib/redux/reducers/consumers.js +++ b/app/lib/redux/reducers/consumers.js @@ -35,6 +35,15 @@ const consumers = (state = initialState, action) => return { ...state, [consumerId]: newConsumer }; } + case 'SET_CONSUMER_VOLUME': + { + const { consumerId, volume } = action.payload; + const consumer = state[consumerId]; + const newConsumer = { ...consumer, volume }; + + return { ...state, [consumerId]: newConsumer }; + } + case 'SET_CONSUMER_RESUMED': { const { consumerId, originator } = action.payload; diff --git a/app/lib/redux/reducers/producers.js b/app/lib/redux/reducers/producers.js index 4562b8b..736f70b 100644 --- a/app/lib/redux/reducers/producers.js +++ b/app/lib/redux/reducers/producers.js @@ -35,6 +35,15 @@ const producers = (state = initialState, action) => return { ...state, [producerId]: newProducer }; } + case 'SET_PRODUCER_VOLUME': + { + const { producerId, volume } = action.payload; + const producer = state[producerId]; + const newProducer = { ...producer, volume }; + + return { ...state, [producerId]: newProducer }; + } + case 'SET_PRODUCER_RESUMED': { const { producerId, originator } = action.payload; diff --git a/app/lib/redux/stateActions.js b/app/lib/redux/stateActions.js index f9a88d4..62307a8 100644 --- a/app/lib/redux/stateActions.js +++ b/app/lib/redux/stateActions.js @@ -331,6 +331,22 @@ export const setConsumerTrack = (consumerId, track) => }; }; +export const setConsumerVolume = (consumerId, volume) => +{ + return { + type : 'SET_CONSUMER_VOLUME', + payload : { consumerId, volume } + }; +}; + +export const setProducerVolume = (producerId, volume) => +{ + return { + type : 'SET_PRODUCER_VOLUME', + payload : { producerId, volume } + }; +}; + export const addNotification = (notification) => { return {