11import React , { useCallback } from 'react' ;
2- import { Badge , IconButton , Paper , Tooltip , Typography } from '@mui/material' ;
2+ import { Badge , Box , IconButton , Paper , Tooltip , Typography , Slider , Stack } from '@mui/material' ;
33import CancelPresentationIcon from '@mui/icons-material/CancelPresentation' ;
44import PresentToAllIcon from '@mui/icons-material/PresentToAll' ;
55import FullScreenIcon from '@mui/icons-material/Fullscreen' ;
66import PeopleIcon from '@mui/icons-material/People' ;
7+ import VolumeMuteIcon from '@mui/icons-material/VolumeOff' ;
8+ import VolumeIcon from '@mui/icons-material/VolumeUp' ;
79import SettingsIcon from '@mui/icons-material/Settings' ;
810import { useHotkeys } from 'react-hotkeys-hook' ;
911import { Video } from './Video' ;
@@ -97,7 +99,17 @@ export const Room = ({
9799 React . useEffect ( ( ) => {
98100 if ( videoElement && stream ) {
99101 videoElement . srcObject = stream ;
100- videoElement . play ( ) . catch ( ( e ) => console . log ( 'Could not play main video' , e ) ) ;
102+ videoElement . play ( ) . catch ( ( err ) => {
103+ console . log ( 'Could not play main video' , err ) ;
104+ if ( err . name === 'NotAllowedError' ) {
105+ videoElement . muted = true ;
106+ videoElement
107+ . play ( )
108+ . catch ( ( retryErr ) =>
109+ console . log ( 'Could not play main video with mute' , retryErr )
110+ ) ;
111+ }
112+ } ) ;
101113 }
102114 } , [ videoElement , stream ] ) ;
103115
@@ -161,6 +173,15 @@ export const Room = ({
161173 } ,
162174 [ state . clientStreams , selectedStream ]
163175 ) ;
176+ useHotkeys (
177+ 'm' ,
178+ ( ) => {
179+ if ( videoElement ) {
180+ videoElement . muted = ! videoElement . muted ;
181+ }
182+ } ,
183+ [ videoElement ]
184+ ) ;
164185
165186 const videoClasses = ( ) => {
166187 switch ( settings . displayMode ) {
@@ -194,7 +215,6 @@ export const Room = ({
194215
195216 { stream ? (
196217 < video
197- muted
198218 ref = { setVideoElement }
199219 className = { videoClasses ( ) }
200220 onDoubleClick = { handleFullscreen }
@@ -217,53 +237,58 @@ export const Room = ({
217237
218238 { controlVisible && (
219239 < Paper className = { classes . control } elevation = { 10 } { ...setHoverState } >
220- { state . hostStream ? (
221- < Tooltip title = "Cancel Presentation" arrow >
222- < IconButton onClick = { stopShare } size = "large" >
223- < CancelPresentationIcon fontSize = "large" />
224- </ IconButton >
225- </ Tooltip >
226- ) : (
227- < Tooltip title = "Start Presentation" arrow >
228- < IconButton onClick = { share } size = "large" >
229- < PresentToAllIcon fontSize = "large" />
230- </ IconButton >
231- </ Tooltip >
240+ { ( stream ?. getAudioTracks ( ) . length ?? 0 ) > 0 && videoElement && (
241+ < AudioControl video = { videoElement } />
232242 ) }
243+ < Box whiteSpace = "nowrap" >
244+ { state . hostStream ? (
245+ < Tooltip title = "Cancel Presentation" arrow >
246+ < IconButton onClick = { stopShare } size = "large" >
247+ < CancelPresentationIcon fontSize = "large" />
248+ </ IconButton >
249+ </ Tooltip >
250+ ) : (
251+ < Tooltip title = "Start Presentation" arrow >
252+ < IconButton onClick = { share } size = "large" >
253+ < PresentToAllIcon fontSize = "large" />
254+ </ IconButton >
255+ </ Tooltip >
256+ ) }
233257
234- < Tooltip
235- classes = { { tooltip : classes . noMaxWidth } }
236- title = {
237- < div >
238- < Typography variant = "h5" > Member List</ Typography >
239- { state . users . map ( ( user ) => (
240- < Typography key = { user . id } >
241- { user . name } { flags ( user ) }
242- </ Typography >
243- ) ) }
244- </ div >
245- }
246- arrow
247- >
248- < Badge badgeContent = { state . users . length } color = "primary" >
249- < PeopleIcon fontSize = "large" />
250- </ Badge >
251- </ Tooltip >
252- < Tooltip title = "Fullscreen" arrow >
253- < IconButton
254- onClick = { ( ) => handleFullscreen ( ) }
255- disabled = { ! selectedStream }
256- size = "large"
258+ < Tooltip
259+ classes = { { tooltip : classes . noMaxWidth } }
260+ title = {
261+ < div >
262+ < Typography variant = "h5" > Member List</ Typography >
263+ { state . users . map ( ( user ) => (
264+ < Typography key = { user . id } >
265+ { user . name } { flags ( user ) }
266+ </ Typography >
267+ ) ) }
268+ </ div >
269+ }
270+ arrow
257271 >
258- < FullScreenIcon fontSize = "large" />
259- </ IconButton >
260- </ Tooltip >
272+ < Badge badgeContent = { state . users . length } color = "primary" >
273+ < PeopleIcon fontSize = "large" />
274+ </ Badge >
275+ </ Tooltip >
276+ < Tooltip title = "Fullscreen" arrow >
277+ < IconButton
278+ onClick = { ( ) => handleFullscreen ( ) }
279+ disabled = { ! selectedStream }
280+ size = "large"
281+ >
282+ < FullScreenIcon fontSize = "large" />
283+ </ IconButton >
284+ </ Tooltip >
261285
262- < Tooltip title = "Settings" arrow >
263- < IconButton onClick = { ( ) => setOpen ( true ) } size = "large" >
264- < SettingsIcon fontSize = "large" />
265- </ IconButton >
266- </ Tooltip >
286+ < Tooltip title = "Settings" arrow >
287+ < IconButton onClick = { ( ) => setOpen ( true ) } size = "large" >
288+ < SettingsIcon fontSize = "large" />
289+ </ IconButton >
290+ </ Tooltip >
291+ </ Box >
267292 </ Paper >
268293 ) }
269294
@@ -353,6 +378,40 @@ const useShowOnMouseMovement = (doShow: (s: boolean) => void) => {
353378 ) ;
354379} ;
355380
381+ const AudioControl = ( { video} : { video : FullScreenHTMLVideoElement } ) => {
382+ // this is used to force a rerender
383+ const [ , setMuted ] = React . useState < boolean > ( ) ;
384+
385+ React . useEffect ( ( ) => {
386+ const handler = ( ) => setMuted ( video . muted ) ;
387+ video . addEventListener ( 'volumechange' , handler ) ;
388+ setMuted ( video . muted ) ;
389+ return ( ) => video . removeEventListener ( 'volumechange' , handler ) ;
390+ } ) ;
391+
392+ return (
393+ < Stack spacing = { 0.5 } pr = { 2 } direction = "row" sx = { { alignItems : 'center' , my : 1 , height : 35 } } >
394+ < IconButton size = "large" onClick = { ( ) => ( video . muted = ! video . muted ) } >
395+ { video . muted ? (
396+ < VolumeMuteIcon fontSize = "large" />
397+ ) : (
398+ < VolumeIcon fontSize = "large" />
399+ ) }
400+ </ IconButton >
401+ < Slider
402+ min = { 0 }
403+ max = { 1 }
404+ step = { 0.01 }
405+ defaultValue = { video . volume }
406+ onChange = { ( _ , newVolume ) => {
407+ video . muted = false ;
408+ video . volume = newVolume ;
409+ } }
410+ />
411+ </ Stack >
412+ ) ;
413+ } ;
414+
356415const useStyles = makeStyles ( ( ) => ( {
357416 title : {
358417 padding : 15 ,
0 commit comments