mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 13:14:58 +08:00
Add a speaking indicator
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
5a1633d53c
commit
abab31c33b
@ -74,9 +74,9 @@ limitations under the License.
|
|||||||
> .mx_VideoFeed {
|
> .mx_VideoFeed {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border-width: 0 !important; // Override mx_VideoFeed_speaking
|
||||||
|
|
||||||
&.mx_VideoFeed_voice {
|
&.mx_VideoFeed_voice {
|
||||||
background-color: $inverted-bg-color;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -17,12 +17,17 @@ limitations under the License.
|
|||||||
.mx_VideoFeed {
|
.mx_VideoFeed {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
&.mx_VideoFeed_voice {
|
&.mx_VideoFeed_voice {
|
||||||
background-color: $inverted-bg-color;
|
background-color: $inverted-bg-color;
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_VideoFeed_speaking {
|
||||||
|
border: $accent-color 2px solid;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_VideoFeed_video {
|
.mx_VideoFeed_video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -24,6 +24,8 @@ import MemberAvatar from "../avatars/MemberAvatar";
|
|||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { SDPStreamMetadataPurpose } from 'matrix-js-sdk/src/webrtc/callEventTypes';
|
import { SDPStreamMetadataPurpose } from 'matrix-js-sdk/src/webrtc/callEventTypes';
|
||||||
|
|
||||||
|
const SPEAKING_THRESHOLD = -60;
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
call: MatrixCall;
|
call: MatrixCall;
|
||||||
|
|
||||||
@ -45,6 +47,7 @@ interface IProps {
|
|||||||
interface IState {
|
interface IState {
|
||||||
audioMuted: boolean;
|
audioMuted: boolean;
|
||||||
videoMuted: boolean;
|
videoMuted: boolean;
|
||||||
|
speaking: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.voip.VideoFeed")
|
@replaceableComponent("views.voip.VideoFeed")
|
||||||
@ -57,6 +60,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
audioMuted: this.props.feed.isAudioMuted(),
|
audioMuted: this.props.feed.isAudioMuted(),
|
||||||
videoMuted: this.props.feed.isVideoMuted(),
|
videoMuted: this.props.feed.isVideoMuted(),
|
||||||
|
speaking: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,11 +107,19 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||||||
if (oldFeed) {
|
if (oldFeed) {
|
||||||
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
this.props.feed.removeListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||||
this.props.feed.removeListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
this.props.feed.removeListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||||
|
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||||
|
this.props.feed.removeListener(CallFeedEvent.VolumeChanged, this.onVolumeChanged);
|
||||||
|
this.props.feed.measureVolumeActivity(false);
|
||||||
|
}
|
||||||
this.stopMedia();
|
this.stopMedia();
|
||||||
}
|
}
|
||||||
if (newFeed) {
|
if (newFeed) {
|
||||||
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
this.props.feed.addListener(CallFeedEvent.NewStream, this.onNewStream);
|
||||||
this.props.feed.addListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
this.props.feed.addListener(CallFeedEvent.MuteStateChanged, this.onMuteStateChanged);
|
||||||
|
if (this.props.feed.purpose === SDPStreamMetadataPurpose.Usermedia) {
|
||||||
|
this.props.feed.addListener(CallFeedEvent.VolumeChanged, this.onVolumeChanged);
|
||||||
|
this.props.feed.measureVolumeActivity(true);
|
||||||
|
}
|
||||||
this.playMedia();
|
this.playMedia();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,6 +174,10 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private onVolumeChanged = (volume: number): void => {
|
||||||
|
this.setState({ speaking: volume > SPEAKING_THRESHOLD });
|
||||||
|
};
|
||||||
|
|
||||||
private onResize = (e) => {
|
private onResize = (e) => {
|
||||||
if (this.props.onResize && !this.props.feed.isLocal()) {
|
if (this.props.onResize && !this.props.feed.isLocal()) {
|
||||||
this.props.onResize(e);
|
this.props.onResize(e);
|
||||||
@ -173,6 +189,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||||||
|
|
||||||
const wrapperClasses = classnames("mx_VideoFeed", {
|
const wrapperClasses = classnames("mx_VideoFeed", {
|
||||||
mx_VideoFeed_voice: this.state.videoMuted,
|
mx_VideoFeed_voice: this.state.videoMuted,
|
||||||
|
mx_VideoFeed_speaking: this.state.speaking,
|
||||||
});
|
});
|
||||||
const micIconClasses = classnames("mx_VideoFeed_mic", {
|
const micIconClasses = classnames("mx_VideoFeed_mic", {
|
||||||
mx_VideoFeed_mic_muted: this.state.audioMuted,
|
mx_VideoFeed_mic_muted: this.state.audioMuted,
|
||||||
|
Loading…
Reference in New Issue
Block a user