mirror of
https://github.com/screego/server.git
synced 2025-09-27 04:26:34 +08:00
feat: system/tab audio on supported systems
This commit is contained in:
151
ui/src/Room.tsx
151
ui/src/Room.tsx
@@ -1,9 +1,11 @@
|
|||||||
import React, {useCallback} from 'react';
|
import React, {useCallback} from 'react';
|
||||||
import {Badge, IconButton, Paper, Tooltip, Typography} from '@mui/material';
|
import {Badge, Box, IconButton, Paper, Tooltip, Typography, Slider, Stack} from '@mui/material';
|
||||||
import CancelPresentationIcon from '@mui/icons-material/CancelPresentation';
|
import CancelPresentationIcon from '@mui/icons-material/CancelPresentation';
|
||||||
import PresentToAllIcon from '@mui/icons-material/PresentToAll';
|
import PresentToAllIcon from '@mui/icons-material/PresentToAll';
|
||||||
import FullScreenIcon from '@mui/icons-material/Fullscreen';
|
import FullScreenIcon from '@mui/icons-material/Fullscreen';
|
||||||
import PeopleIcon from '@mui/icons-material/People';
|
import PeopleIcon from '@mui/icons-material/People';
|
||||||
|
import VolumeMuteIcon from '@mui/icons-material/VolumeOff';
|
||||||
|
import VolumeIcon from '@mui/icons-material/VolumeUp';
|
||||||
import SettingsIcon from '@mui/icons-material/Settings';
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
import {useHotkeys} from 'react-hotkeys-hook';
|
import {useHotkeys} from 'react-hotkeys-hook';
|
||||||
import {Video} from './Video';
|
import {Video} from './Video';
|
||||||
@@ -97,7 +99,17 @@ export const Room = ({
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (videoElement && stream) {
|
if (videoElement && stream) {
|
||||||
videoElement.srcObject = stream;
|
videoElement.srcObject = stream;
|
||||||
videoElement.play().catch((e) => console.log('Could not play main video', e));
|
videoElement.play().catch((err) => {
|
||||||
|
console.log('Could not play main video', err);
|
||||||
|
if (err.name === 'NotAllowedError') {
|
||||||
|
videoElement.muted = true;
|
||||||
|
videoElement
|
||||||
|
.play()
|
||||||
|
.catch((retryErr) =>
|
||||||
|
console.log('Could not play main video with mute', retryErr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}, [videoElement, stream]);
|
}, [videoElement, stream]);
|
||||||
|
|
||||||
@@ -161,6 +173,15 @@ export const Room = ({
|
|||||||
},
|
},
|
||||||
[state.clientStreams, selectedStream]
|
[state.clientStreams, selectedStream]
|
||||||
);
|
);
|
||||||
|
useHotkeys(
|
||||||
|
'm',
|
||||||
|
() => {
|
||||||
|
if (videoElement) {
|
||||||
|
videoElement.muted = !videoElement.muted;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[videoElement]
|
||||||
|
);
|
||||||
|
|
||||||
const videoClasses = () => {
|
const videoClasses = () => {
|
||||||
switch (settings.displayMode) {
|
switch (settings.displayMode) {
|
||||||
@@ -194,7 +215,6 @@ export const Room = ({
|
|||||||
|
|
||||||
{stream ? (
|
{stream ? (
|
||||||
<video
|
<video
|
||||||
muted
|
|
||||||
ref={setVideoElement}
|
ref={setVideoElement}
|
||||||
className={videoClasses()}
|
className={videoClasses()}
|
||||||
onDoubleClick={handleFullscreen}
|
onDoubleClick={handleFullscreen}
|
||||||
@@ -217,53 +237,58 @@ export const Room = ({
|
|||||||
|
|
||||||
{controlVisible && (
|
{controlVisible && (
|
||||||
<Paper className={classes.control} elevation={10} {...setHoverState}>
|
<Paper className={classes.control} elevation={10} {...setHoverState}>
|
||||||
{state.hostStream ? (
|
{(stream?.getAudioTracks().length ?? 0) > 0 && videoElement && (
|
||||||
<Tooltip title="Cancel Presentation" arrow>
|
<AudioControl video={videoElement} />
|
||||||
<IconButton onClick={stopShare} size="large">
|
|
||||||
<CancelPresentationIcon fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
) : (
|
|
||||||
<Tooltip title="Start Presentation" arrow>
|
|
||||||
<IconButton onClick={share} size="large">
|
|
||||||
<PresentToAllIcon fontSize="large" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
)}
|
||||||
|
<Box whiteSpace="nowrap">
|
||||||
|
{state.hostStream ? (
|
||||||
|
<Tooltip title="Cancel Presentation" arrow>
|
||||||
|
<IconButton onClick={stopShare} size="large">
|
||||||
|
<CancelPresentationIcon fontSize="large" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Tooltip title="Start Presentation" arrow>
|
||||||
|
<IconButton onClick={share} size="large">
|
||||||
|
<PresentToAllIcon fontSize="large" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
classes={{tooltip: classes.noMaxWidth}}
|
classes={{tooltip: classes.noMaxWidth}}
|
||||||
title={
|
title={
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h5">Member List</Typography>
|
<Typography variant="h5">Member List</Typography>
|
||||||
{state.users.map((user) => (
|
{state.users.map((user) => (
|
||||||
<Typography key={user.id}>
|
<Typography key={user.id}>
|
||||||
{user.name} {flags(user)}
|
{user.name} {flags(user)}
|
||||||
</Typography>
|
</Typography>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
arrow
|
arrow
|
||||||
>
|
|
||||||
<Badge badgeContent={state.users.length} color="primary">
|
|
||||||
<PeopleIcon fontSize="large" />
|
|
||||||
</Badge>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="Fullscreen" arrow>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => handleFullscreen()}
|
|
||||||
disabled={!selectedStream}
|
|
||||||
size="large"
|
|
||||||
>
|
>
|
||||||
<FullScreenIcon fontSize="large" />
|
<Badge badgeContent={state.users.length} color="primary">
|
||||||
</IconButton>
|
<PeopleIcon fontSize="large" />
|
||||||
</Tooltip>
|
</Badge>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Fullscreen" arrow>
|
||||||
|
<IconButton
|
||||||
|
onClick={() => handleFullscreen()}
|
||||||
|
disabled={!selectedStream}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<FullScreenIcon fontSize="large" />
|
||||||
|
</IconButton>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Tooltip title="Settings" arrow>
|
<Tooltip title="Settings" arrow>
|
||||||
<IconButton onClick={() => setOpen(true)} size="large">
|
<IconButton onClick={() => setOpen(true)} size="large">
|
||||||
<SettingsIcon fontSize="large" />
|
<SettingsIcon fontSize="large" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -353,6 +378,40 @@ const useShowOnMouseMovement = (doShow: (s: boolean) => void) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AudioControl = ({video}: {video: FullScreenHTMLVideoElement}) => {
|
||||||
|
// this is used to force a rerender
|
||||||
|
const [, setMuted] = React.useState<boolean>();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const handler = () => setMuted(video.muted);
|
||||||
|
video.addEventListener('volumechange', handler);
|
||||||
|
setMuted(video.muted);
|
||||||
|
return () => video.removeEventListener('volumechange', handler);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack spacing={0.5} pr={2} direction="row" sx={{alignItems: 'center', my: 1, height: 35}}>
|
||||||
|
<IconButton size="large" onClick={() => (video.muted = !video.muted)}>
|
||||||
|
{video.muted ? (
|
||||||
|
<VolumeMuteIcon fontSize="large" />
|
||||||
|
) : (
|
||||||
|
<VolumeIcon fontSize="large" />
|
||||||
|
)}
|
||||||
|
</IconButton>
|
||||||
|
<Slider
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
defaultValue={video.volume}
|
||||||
|
onChange={(_, newVolume) => {
|
||||||
|
video.muted = false;
|
||||||
|
video.volume = newVolume;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const useStyles = makeStyles(() => ({
|
const useStyles = makeStyles(() => ({
|
||||||
title: {
|
title: {
|
||||||
padding: 15,
|
padding: 15,
|
||||||
|
@@ -143,10 +143,15 @@ const clientSession = async ({
|
|||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let notified = false;
|
||||||
|
const stream = new MediaStream();
|
||||||
peer.ontrack = (event) => {
|
peer.ontrack = (event) => {
|
||||||
const stream = new MediaStream();
|
|
||||||
stream.addTrack(event.track);
|
stream.addTrack(event.track);
|
||||||
onTrack(stream);
|
if (!notified) {
|
||||||
|
notified = true;
|
||||||
|
onTrack(stream);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return peer;
|
return peer;
|
||||||
@@ -332,6 +337,14 @@ export const useRoom = (config: UIConfig): UseRoom => {
|
|||||||
try {
|
try {
|
||||||
stream.current = await navigator.mediaDevices.getDisplayMedia({
|
stream.current = await navigator.mediaDevices.getDisplayMedia({
|
||||||
video: {frameRate: loadSettings().framerate},
|
video: {frameRate: loadSettings().framerate},
|
||||||
|
audio: {
|
||||||
|
echoCancellation: false,
|
||||||
|
autoGainControl: false,
|
||||||
|
noiseSuppression: false,
|
||||||
|
// https://medium.com/@trystonperry/why-is-getdisplaymedias-audio-quality-so-bad-b49ba9cfaa83
|
||||||
|
// @ts-expect-error
|
||||||
|
googAutoGainControl: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Could not getDisplayMedia', e);
|
console.log('Could not getDisplayMedia', e);
|
||||||
|
Reference in New Issue
Block a user