Play audio from video elements, fixes #242
parent
5c4eeb121a
commit
7a3bd327f2
|
|
@ -340,6 +340,7 @@ const Peer = (props) =>
|
||||||
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
|
videoMultiLayer={webcamConsumer && webcamConsumer.type !== 'simple'}
|
||||||
videoTrack={webcamConsumer && webcamConsumer.track}
|
videoTrack={webcamConsumer && webcamConsumer.track}
|
||||||
videoVisible={videoVisible}
|
videoVisible={videoVisible}
|
||||||
|
audioTrack={micConsumer && micConsumer.track}
|
||||||
audioCodec={micConsumer && micConsumer.codec}
|
audioCodec={micConsumer && micConsumer.codec}
|
||||||
videoCodec={webcamConsumer && webcamConsumer.codec}
|
videoCodec={webcamConsumer && webcamConsumer.codec}
|
||||||
audioScore={micConsumer ? micConsumer.score : null}
|
audioScore={micConsumer ? micConsumer.score : null}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { micConsumerSelector } from '../Selectors';
|
import { passiveMicConsumerSelector } from '../Selectors';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import PeerAudio from './PeerAudio';
|
import PeerAudio from './PeerAudio';
|
||||||
|
|
||||||
|
|
@ -37,7 +37,7 @@ AudioPeers.propTypes =
|
||||||
|
|
||||||
const mapStateToProps = (state) =>
|
const mapStateToProps = (state) =>
|
||||||
({
|
({
|
||||||
micConsumers : micConsumerSelector(state),
|
micConsumers : passiveMicConsumerSelector(state),
|
||||||
audioOutputDevice : state.settings.selectedAudioOutputDevice
|
audioOutputDevice : state.settings.selectedAudioOutputDevice
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -50,7 +50,9 @@ const AudioPeersContainer = connect(
|
||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
prev.consumers === next.consumers &&
|
prev.consumers === next.consumers &&
|
||||||
prev.settings.selectedAudioOutputDevice === next.settings.selectedAudioOutputDevice
|
prev.room.spotlights === next.room.spotlights &&
|
||||||
|
prev.settings.selectedAudioOutputDevice ===
|
||||||
|
next.settings.selectedAudioOutputDevice
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,15 @@ export const screenConsumerSelector = createSelector(
|
||||||
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
|
(consumers) => Object.values(consumers).filter((consumer) => consumer.source === 'screen')
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const passiveMicConsumerSelector = createSelector(
|
||||||
|
spotlightsSelector,
|
||||||
|
consumersSelect,
|
||||||
|
(spotlights, consumers) =>
|
||||||
|
Object.values(consumers).filter(
|
||||||
|
(consumer) => consumer.source === 'mic' && !spotlights.includes(consumer.peerId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
export const spotlightsLengthSelector = createSelector(
|
export const spotlightsLengthSelector = createSelector(
|
||||||
spotlightsSelector,
|
spotlightsSelector,
|
||||||
(spotlights) => spotlights.length
|
(spotlights) => spotlights.length
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withStyles } from '@material-ui/core/styles';
|
import { withStyles } from '@material-ui/core/styles';
|
||||||
import EditableInput from '../Controls/EditableInput';
|
import EditableInput from '../Controls/EditableInput';
|
||||||
|
import Logger from '../../Logger';
|
||||||
import { green, yellow, orange, red } from '@material-ui/core/colors';
|
import { green, yellow, orange, red } from '@material-ui/core/colors';
|
||||||
import SignalCellularOffIcon from '@material-ui/icons/SignalCellularOff';
|
import SignalCellularOffIcon from '@material-ui/icons/SignalCellularOff';
|
||||||
import SignalCellular0BarIcon from '@material-ui/icons/SignalCellular0Bar';
|
import SignalCellular0BarIcon from '@material-ui/icons/SignalCellular0Bar';
|
||||||
|
|
@ -11,6 +12,8 @@ import SignalCellular2BarIcon from '@material-ui/icons/SignalCellular2Bar';
|
||||||
import SignalCellular3BarIcon from '@material-ui/icons/SignalCellular3Bar';
|
import SignalCellular3BarIcon from '@material-ui/icons/SignalCellular3Bar';
|
||||||
import SignalCellularAltIcon from '@material-ui/icons/SignalCellularAlt';
|
import SignalCellularAltIcon from '@material-ui/icons/SignalCellularAlt';
|
||||||
|
|
||||||
|
const logger = new Logger('VideoView');
|
||||||
|
|
||||||
const styles = (theme) =>
|
const styles = (theme) =>
|
||||||
({
|
({
|
||||||
root :
|
root :
|
||||||
|
|
@ -134,6 +137,10 @@ class VideoView extends React.PureComponent
|
||||||
videoHeight : null
|
videoHeight : null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Latest received audio track
|
||||||
|
// @type {MediaStreamTrack}
|
||||||
|
this._audioTrack = null;
|
||||||
|
|
||||||
// Latest received video track.
|
// Latest received video track.
|
||||||
// @type {MediaStreamTrack}
|
// @type {MediaStreamTrack}
|
||||||
this._videoTrack = null;
|
this._videoTrack = null;
|
||||||
|
|
@ -292,7 +299,7 @@ class VideoView extends React.PureComponent
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<video
|
<video
|
||||||
ref='video'
|
ref='videoElement'
|
||||||
className={classnames(classes.video, {
|
className={classnames(classes.video, {
|
||||||
hidden : !videoVisible,
|
hidden : !videoVisible,
|
||||||
'isMe' : isMe && !isScreen,
|
'isMe' : isMe && !isScreen,
|
||||||
|
|
@ -300,6 +307,16 @@ class VideoView extends React.PureComponent
|
||||||
})}
|
})}
|
||||||
autoPlay
|
autoPlay
|
||||||
playsInline
|
playsInline
|
||||||
|
muted
|
||||||
|
controls={false}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<audio
|
||||||
|
ref='audioElement'
|
||||||
|
autoPlay
|
||||||
|
playsInline
|
||||||
|
muted={isMe}
|
||||||
|
controls={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
@ -309,52 +326,84 @@ class VideoView extends React.PureComponent
|
||||||
|
|
||||||
componentDidMount()
|
componentDidMount()
|
||||||
{
|
{
|
||||||
const { videoTrack } = this.props;
|
const { videoTrack, audioTrack } = this.props;
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
this._setTracks(videoTrack, audioTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount()
|
componentWillUnmount()
|
||||||
{
|
{
|
||||||
clearInterval(this._videoResolutionTimer);
|
clearInterval(this._videoResolutionTimer);
|
||||||
|
|
||||||
|
const { videoElement } = this.refs;
|
||||||
|
|
||||||
|
if (videoElement)
|
||||||
|
{
|
||||||
|
videoElement.oncanplay = null;
|
||||||
|
videoElement.onplay = null;
|
||||||
|
videoElement.onpause = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line camelcase
|
componentWillUpdate()
|
||||||
UNSAFE_componentWillReceiveProps(nextProps)
|
|
||||||
{
|
{
|
||||||
const { videoTrack } = nextProps;
|
const { videoTrack, audioTrack } = this.props;
|
||||||
|
|
||||||
this._setTracks(videoTrack);
|
|
||||||
|
|
||||||
|
this._setTracks(videoTrack, audioTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
_setTracks(videoTrack)
|
_setTracks(videoTrack, audioTrack)
|
||||||
{
|
{
|
||||||
if (this._videoTrack === videoTrack)
|
if (this._videoTrack === videoTrack && this._audioTrack === audioTrack)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._videoTrack = videoTrack;
|
this._videoTrack = videoTrack;
|
||||||
|
this._audioTrack = audioTrack;
|
||||||
|
|
||||||
clearInterval(this._videoResolutionTimer);
|
clearInterval(this._videoResolutionTimer);
|
||||||
this._hideVideoResolution();
|
this._hideVideoResolution();
|
||||||
|
|
||||||
const { video } = this.refs;
|
const { videoElement, audioElement } = this.refs;
|
||||||
|
|
||||||
if (videoTrack)
|
if (videoTrack)
|
||||||
{
|
{
|
||||||
const stream = new MediaStream();
|
const stream = new MediaStream();
|
||||||
|
|
||||||
if (videoTrack)
|
|
||||||
stream.addTrack(videoTrack);
|
stream.addTrack(videoTrack);
|
||||||
|
|
||||||
video.srcObject = stream;
|
videoElement.srcObject = stream;
|
||||||
|
|
||||||
|
videoElement.oncanplay = () => this.setState({ videoCanPlay: true });
|
||||||
|
|
||||||
|
videoElement.onplay = () =>
|
||||||
|
{
|
||||||
|
audioElement.play()
|
||||||
|
.catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
|
||||||
|
};
|
||||||
|
|
||||||
|
videoElement.play()
|
||||||
|
.catch((error) => logger.warn('videoElement.play() [error:"%o]', error));
|
||||||
|
|
||||||
if (videoTrack)
|
|
||||||
this._showVideoResolution();
|
this._showVideoResolution();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
video.srcObject = null;
|
videoElement.srcObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioTrack)
|
||||||
|
{
|
||||||
|
const stream = new MediaStream();
|
||||||
|
|
||||||
|
stream.addTrack(audioTrack);
|
||||||
|
audioElement.srcObject = stream;
|
||||||
|
|
||||||
|
audioElement.play()
|
||||||
|
.catch((error) => logger.warn('audioElement.play() [error:"%o]', error));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioElement.srcObject = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,16 +412,19 @@ class VideoView extends React.PureComponent
|
||||||
this._videoResolutionTimer = setInterval(() =>
|
this._videoResolutionTimer = setInterval(() =>
|
||||||
{
|
{
|
||||||
const { videoWidth, videoHeight } = this.state;
|
const { videoWidth, videoHeight } = this.state;
|
||||||
const { video } = this.refs;
|
const { videoElement } = this.refs;
|
||||||
|
|
||||||
// Don't re-render if nothing changed.
|
// Don't re-render if nothing changed.
|
||||||
if (video.videoWidth === videoWidth && video.videoHeight === videoHeight)
|
if (
|
||||||
|
videoElement.videoWidth === videoWidth &&
|
||||||
|
videoElement.videoHeight === videoHeight
|
||||||
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
videoWidth : video.videoWidth,
|
videoWidth : videoElement.videoWidth,
|
||||||
videoHeight : video.videoHeight
|
videoHeight : videoElement.videoHeight
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
@ -392,6 +444,7 @@ VideoView.propTypes =
|
||||||
videoContain : PropTypes.bool,
|
videoContain : PropTypes.bool,
|
||||||
advancedMode : PropTypes.bool,
|
advancedMode : PropTypes.bool,
|
||||||
videoTrack : PropTypes.any,
|
videoTrack : PropTypes.any,
|
||||||
|
audioTrack : PropTypes.any,
|
||||||
videoVisible : PropTypes.bool.isRequired,
|
videoVisible : PropTypes.bool.isRequired,
|
||||||
consumerSpatialLayers : PropTypes.number,
|
consumerSpatialLayers : PropTypes.number,
|
||||||
consumerTemporalLayers : PropTypes.number,
|
consumerTemporalLayers : PropTypes.number,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue