);
};
Peer.propTypes =
{
- advancedMode : PropTypes.bool,
- peer : appPropTypes.Peer.isRequired,
- micConsumer : appPropTypes.Consumer,
- webcamConsumer : appPropTypes.Consumer,
- screenConsumer : appPropTypes.Consumer,
- onMuteMic : PropTypes.func.isRequired,
- onUnmuteMic : PropTypes.func.isRequired,
- onEnableWebcam : PropTypes.func.isRequired,
- onDisableWebcam : PropTypes.func.isRequired
+ advancedMode : PropTypes.bool,
+ peer : appPropTypes.Peer.isRequired,
+ micConsumer : appPropTypes.Consumer,
+ webcamConsumer : appPropTypes.Consumer,
+ screenConsumer : appPropTypes.Consumer,
+ onMuteMic : PropTypes.func.isRequired,
+ onUnmuteMic : PropTypes.func.isRequired,
+ onEnableWebcam : PropTypes.func.isRequired,
+ onDisableWebcam : PropTypes.func.isRequired,
+ streamDimensions : PropTypes.object,
+ style : PropTypes.object
};
const mapStateToProps = (state, { name }) =>
diff --git a/app/lib/components/PeerView.jsx b/app/lib/components/PeerView.jsx
index 8a6ef57..c258584 100644
--- a/app/lib/components/PeerView.jsx
+++ b/app/lib/components/PeerView.jsx
@@ -14,11 +14,9 @@ export default class PeerView extends React.Component
this.state =
{
- volume : 0, // Integer from 0 to 10.,
- videoWidth : null,
- videoHeight : null,
- screenWidth : null,
- screenHeight : null
+ volume : 0, // Integer from 0 to 10.,
+ videoWidth : null,
+ videoHeight : null
};
// Latest received video track.
@@ -29,10 +27,6 @@ export default class PeerView extends React.Component
// @type {MediaStreamTrack}
this._videoTrack = null;
- // Latest received screen track.
- // @type {MediaStreamTrack}
- this._screenTrack = null;
-
// Hark instance.
// @type {Object}
this._hark = null;
@@ -45,24 +39,19 @@ export default class PeerView extends React.Component
{
const {
isMe,
- advancedMode,
peer,
+ advancedMode,
videoVisible,
videoProfile,
- screenVisible,
- screenProfile,
audioCodec,
videoCodec,
- screenCodec,
onChangeDisplayName
} = this.props;
const {
volume,
videoWidth,
- videoHeight,
- screenWidth,
- screenHeight
+ videoHeight
} = this.state;
return (
@@ -70,40 +59,22 @@ export default class PeerView extends React.Component
{advancedMode ?
- {screenVisible ?
-
- {audioCodec ?
-
{audioCodec}
- :null
- }
+
+ {audioCodec ?
+
{audioCodec}
+ :null
+ }
- {screenCodec ?
-
{screenCodec} {screenProfile}
- :null
- }
+ {videoCodec ?
+
{videoCodec} {videoProfile}
+ :null
+ }
- {(screenVisible && screenWidth !== null) ?
-
{screenWidth}x{screenHeight}
- :null
- }
-
- :
- {audioCodec ?
-
{audioCodec}
- :null
- }
-
- {videoCodec ?
-
{videoCodec} {videoProfile}
- :null
- }
-
- {(videoVisible && videoWidth !== null) ?
-
{videoWidth}x{videoHeight}
- :null
- }
-
- }
+ {(videoVisible && videoWidth !== null) ?
+
{videoWidth}x{videoHeight}
+ :null
+ }
+
:null
}
@@ -147,35 +118,19 @@ export default class PeerView extends React.Component
- {screenVisible ?
-
-
-
- :null
- }
-
- {videoProfile === 'none' && screenProfile === 'none' ?
+ {videoProfile === 'none' ?
@@ -187,9 +142,9 @@ export default class PeerView extends React.Component
componentDidMount()
{
- const { audioTrack, videoTrack, screenTrack } = this.props;
+ const { audioTrack, videoTrack } = this.props;
- this._setTracks(audioTrack, videoTrack, screenTrack);
+ this._setTracks(audioTrack, videoTrack);
}
componentWillUnmount()
@@ -202,21 +157,18 @@ export default class PeerView extends React.Component
componentWillReceiveProps(nextProps)
{
- const { audioTrack, videoTrack, screenTrack } = nextProps;
+ const { audioTrack, videoTrack } = nextProps;
- this._setTracks(audioTrack, videoTrack, screenTrack);
+ this._setTracks(audioTrack, videoTrack);
}
- _setTracks(audioTrack, videoTrack, screenTrack)
+ _setTracks(audioTrack, videoTrack)
{
- if (this._audioTrack === audioTrack &&
- this._videoTrack === videoTrack &&
- this._screenTrack === screenTrack)
+ if (this._audioTrack === audioTrack && this._videoTrack === videoTrack)
return;
this._audioTrack = audioTrack;
this._videoTrack = videoTrack;
- this._screenTrack = screenTrack;
if (this._hark)
this._hark.stop();
@@ -224,9 +176,9 @@ export default class PeerView extends React.Component
clearInterval(this._videoResolutionTimer);
this._hideVideoResolution();
- const { video, minivideo } = this.refs;
+ const { video } = this.refs;
- if (audioTrack || videoTrack || screenTrack)
+ if (audioTrack || videoTrack)
{
const stream = new MediaStream;
@@ -236,19 +188,7 @@ export default class PeerView extends React.Component
if (videoTrack)
stream.addTrack(videoTrack);
- if (screenTrack)
- {
- const screenStream = new MediaStream;
-
- screenStream.addTrack(screenTrack);
-
- video.srcObject = screenStream;
- minivideo.srcObject = stream;
- }
- else
- {
- video.srcObject = stream;
- }
+ video.srcObject = stream;
if (audioTrack)
this._runHark(stream);
@@ -314,19 +254,15 @@ export default class PeerView extends React.Component
PeerView.propTypes =
{
- isMe : PropTypes.bool,
- advancedMode : PropTypes.bool,
- peer : PropTypes.oneOfType(
+ isMe : PropTypes.bool,
+ peer : PropTypes.oneOfType(
[ appPropTypes.Me, appPropTypes.Peer ]).isRequired,
+ advancedMode : PropTypes.bool,
audioTrack : PropTypes.any,
videoTrack : PropTypes.any,
- screenTrack : PropTypes.any,
videoVisible : PropTypes.bool.isRequired,
videoProfile : PropTypes.string,
- screenVisible : PropTypes.bool,
- screenProfile : PropTypes.string,
audioCodec : PropTypes.string,
videoCodec : PropTypes.string,
- screenCodec : PropTypes.string,
onChangeDisplayName : PropTypes.func
};
diff --git a/app/lib/components/Peers.jsx b/app/lib/components/Peers.jsx
index ae01ed1..6d93b12 100644
--- a/app/lib/components/Peers.jsx
+++ b/app/lib/components/Peers.jsx
@@ -13,7 +13,7 @@ class Peers extends React.Component
super(props);
this.state = {
peerWidth : 400,
- peerHeight : 300,
+ peerHeight : 300,
ratio : 1.334
};
}
@@ -25,7 +25,7 @@ class Peers extends React.Component
updateDimensions(props = this.props)
{
- const n = props.peers ? props.peers.length : 0;
+ const n = props.videoStreams ? props.videoStreams : 0;
if (n == 0)
{
@@ -74,7 +74,7 @@ class Peers extends React.Component
componentWillReceiveProps(nextProps)
{
- if (nextProps.peers)
+ if (nextProps.videoStreams)
this.updateDimensions(nextProps);
}
@@ -86,11 +86,11 @@ class Peers extends React.Component
peers
} = this.props;
- const style =
- {
- 'width' : this.state.peerWidth,
- 'height' : this.state.peerHeight
- };
+ const style =
+ {
+ 'width' : this.state.peerWidth,
+ 'height' : this.state.peerHeight
+ };
return (
@@ -102,11 +102,12 @@ class Peers extends React.Component
@@ -122,15 +123,23 @@ Peers.propTypes =
{
advancedMode : PropTypes.bool,
peers : PropTypes.arrayOf(appPropTypes.Peer).isRequired,
+ videoStreams : PropTypes.any,
activeSpeakerName : PropTypes.string
};
const mapStateToProps = (state) =>
{
const peersArray = Object.values(state.peers);
+ const videoStreamsArray = Object.values(state.consumers);
+ const videoStreams =
+ videoStreamsArray.filter((consumer) =>
+ {
+ return (consumer.source === 'webcam' || consumer.source === 'screen');
+ }).length;
return {
peers : peersArray,
+ videoStreams : videoStreams,
activeSpeakerName : state.room.activeSpeakerName
};
};
diff --git a/app/lib/components/ScreenView.jsx b/app/lib/components/ScreenView.jsx
new file mode 100644
index 0000000..b95be2b
--- /dev/null
+++ b/app/lib/components/ScreenView.jsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import Spinner from 'react-spinner';
+
+export default class PeerView extends React.Component
+{
+ constructor(props)
+ {
+ super(props);
+
+ this.state =
+ {
+ screenWidth : null,
+ screenHeight : null
+ };
+
+ // Latest received screen track.
+ // @type {MediaStreamTrack}
+ this._screenTrack = null;
+
+ // Periodic timer for showing video resolution.
+ this._screenResolutionTimer = null;
+ }
+
+ render()
+ {
+ const {
+ isMe,
+ advancedMode,
+ screenVisible,
+ screenProfile,
+ screenCodec
+ } = this.props;
+
+ const {
+ screenWidth,
+ screenHeight
+ } = this.state;
+
+ return (
+
+
+ {advancedMode ?
+
+ {screenVisible ?
+
+ {screenCodec ?
+
{screenCodec} {screenProfile}
+ :null
+ }
+
+ {(screenVisible && screenWidth !== null) ?
+
{screenWidth}x{screenHeight}
+ :null
+ }
+
+ :null
+ }
+
+ :null
+ }
+
+
+
+
+ {screenProfile === 'none' ?
+
+
+
+ :null
+ }
+
+ );
+ }
+
+ componentDidMount()
+ {
+ const { screenTrack } = this.props;
+
+ this._setTracks(screenTrack);
+ }
+
+ componentWillUnmount()
+ {
+ clearInterval(this._screenResolutionTimer);
+ }
+
+ componentWillReceiveProps(nextProps)
+ {
+ const { screenTrack } = nextProps;
+
+ this._setTracks(screenTrack);
+ }
+
+ _setTracks(screenTrack)
+ {
+ if (this._screenTrack === screenTrack)
+ return;
+
+ this._screenTrack = screenTrack;
+
+ clearInterval(this._screenResolutionTimer);
+ this._hideScreenResolution();
+
+ const { video } = this.refs;
+
+ if (screenTrack)
+ {
+ const stream = new MediaStream;
+
+ if (screenTrack)
+ stream.addTrack(screenTrack);
+
+ video.srcObject = stream;
+
+ if (screenTrack)
+ this._showScreenResolution();
+ }
+ else
+ {
+ video.srcObject = null;
+ }
+ }
+
+ _showScreenResolution()
+ {
+ this._screenResolutionTimer = setInterval(() =>
+ {
+ const { screenWidth, screenHeight } = this.state;
+ const { video } = this.refs;
+
+ // Don't re-render if nothing changed.
+ if (video.videoWidth === screenWidth && video.videoHeight === screenHeight)
+ return;
+
+ this.setState(
+ {
+ screenWidth : video.videoWidth,
+ screenHeight : video.videoHeight
+ });
+ }, 1000);
+ }
+
+ _hideScreenResolution()
+ {
+ this.setState({ screenWidth: null, screenHeight: null });
+ }
+}
+
+PeerView.propTypes =
+{
+ isMe : PropTypes.bool,
+ advancedMode : PropTypes.bool,
+ screenTrack : PropTypes.any,
+ screenVisible : PropTypes.bool,
+ screenProfile : PropTypes.string,
+ screenCodec : PropTypes.string
+};
diff --git a/app/stylus/components/Peer.styl b/app/stylus/components/Peer.styl
index 4dc3036..efeef3a 100644
--- a/app/stylus/components/Peer.styl
+++ b/app/stylus/components/Peer.styl
@@ -3,6 +3,15 @@
position: relative;
height: 100%;
width: 100%;
+ flex-direction: row;
+ display: flex;
+
+ &.screen {
+ border: 5px solid rgba(#fff, 0.4);
+ }
+
+ &:not(.screen) {
+ }
> .controls {
position: absolute;
@@ -94,6 +103,16 @@
align-items: center;
}
+ > .view-container {
+ &.webcam {
+ order: 2;
+ }
+
+ &.screen {
+ order: 1;
+ }
+ }
+
.incompatible-video {
position: absolute;
z-index: 2
diff --git a/app/stylus/components/PeerView.styl b/app/stylus/components/PeerView.styl
index 75c709f..7bfa57f 100644
--- a/app/stylus/components/PeerView.styl
+++ b/app/stylus/components/PeerView.styl
@@ -194,39 +194,6 @@
}
}
- > .minivideo {
- height: 15%;
- width: 15%;
- bottom: 1%;
- right: 1%;
- position: absolute;
- overflow: hidden;
-
- > video {
- flex: 100 100 auto;
- height: 100%;
- width: 100%;
- object-fit: cover;
- user-select: none;
- transition-property: opacity;
- transition-duration: .15s;
- background-color: rgba(#000, 0.75);
-
- &.is-me {
- transform: scaleX(-1);
- }
-
- &.hidden {
- opacity: 0;
- transition-duration: 0s;
- }
-
- &.loading {
- filter: blur(5px);
- }
- }
- }
-
> .volume-container {
position: absolute;
top: 0
diff --git a/app/stylus/components/ScreenView.styl b/app/stylus/components/ScreenView.styl
new file mode 100644
index 0000000..bee884f
--- /dev/null
+++ b/app/stylus/components/ScreenView.styl
@@ -0,0 +1,109 @@
+[data-component='ScreenView'] {
+ position: relative;
+ flex: 100 100 auto;
+ height: 100%;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background-color: rgba(#2a4b58, 0.9);
+ background-image: url('/resources/images/buddy.svg');
+ background-position: bottom;
+ background-size: auto 85%;
+ background-repeat: no-repeat;
+
+ > .info {
+ $backgroundTint = #000;
+
+ position: absolute;
+ z-index: 5
+ top: 0.6vmin;
+ left: 0.6vmin;
+ bottom: 0;
+ right: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+
+ > .media {
+ flex: 0 0 auto;
+ display: flex;
+ flex-direction: row;
+
+ > .box {
+ padding: 0.4vmin;
+ border-radius: 2px;
+ background-color: rgba(#000, 0.25);
+
+ > p {
+ user-select: none;
+ pointer-events: none;
+ margin-bottom: 2px;
+ color: rgba(#fff, 0.7);
+ font-size: 10px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+ }
+ }
+
+ > video {
+ flex: 100 100 auto;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ user-select: none;
+ transition-property: opacity;
+ transition-duration: .15s;
+ background-color: rgba(#000, 0.75);
+
+ &.is-me {
+ transform: scaleX(-1);
+ }
+
+ &.hidden {
+ opacity: 0;
+ transition-duration: 0s;
+ }
+
+ &.loading {
+ filter: blur(5px);
+ }
+ }
+
+ > .spinner-container {
+ position: absolute;
+ top: 0
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background-color: rgba(#000, 0.75);
+
+ .react-spinner {
+ position: relative;
+ width: 48px;
+ height: 48px;
+ top: 50%;
+ left: 50%;
+
+ .react-spinner_bar {
+ position: absolute;
+ width: 20%;
+ height: 7.8%;
+ top: -3.9%;
+ left: -10%;
+ animation: PeerView-spinner 1.2s linear infinite;
+ border-radius: 5px;
+ background-color: rgba(#fff, 0.5);
+ }
+ }
+ }
+}
+
+@keyframes ScreenView-spinner {
+ 0% { opacity: 1; }
+ 100% { opacity: 0.15; }
+}
diff --git a/app/stylus/index.styl b/app/stylus/index.styl
index 4d9561c..b70a957 100644
--- a/app/stylus/index.styl
+++ b/app/stylus/index.styl
@@ -40,6 +40,7 @@ body {
@import './components/Peers';
@import './components/Peer';
@import './components/PeerView';
+ @import './components/ScreenView';
@import './components/Notifications';
@import './components/Chat';
@import './components/Settings';