1
1
import 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' ;
3
3
import CancelPresentationIcon from '@mui/icons-material/CancelPresentation' ;
4
4
import PresentToAllIcon from '@mui/icons-material/PresentToAll' ;
5
5
import FullScreenIcon from '@mui/icons-material/Fullscreen' ;
6
6
import PeopleIcon from '@mui/icons-material/People' ;
7
+ import VolumeMuteIcon from '@mui/icons-material/VolumeOff' ;
8
+ import VolumeIcon from '@mui/icons-material/VolumeUp' ;
7
9
import SettingsIcon from '@mui/icons-material/Settings' ;
8
10
import { useHotkeys } from 'react-hotkeys-hook' ;
9
11
import { Video } from './Video' ;
@@ -97,7 +99,17 @@ export const Room = ({
97
99
React . useEffect ( ( ) => {
98
100
if ( videoElement && stream ) {
99
101
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
+ } ) ;
101
113
}
102
114
} , [ videoElement , stream ] ) ;
103
115
@@ -161,6 +173,15 @@ export const Room = ({
161
173
} ,
162
174
[ state . clientStreams , selectedStream ]
163
175
) ;
176
+ useHotkeys (
177
+ 'm' ,
178
+ ( ) => {
179
+ if ( videoElement ) {
180
+ videoElement . muted = ! videoElement . muted ;
181
+ }
182
+ } ,
183
+ [ videoElement ]
184
+ ) ;
164
185
165
186
const videoClasses = ( ) => {
166
187
switch ( settings . displayMode ) {
@@ -194,7 +215,6 @@ export const Room = ({
194
215
195
216
{ stream ? (
196
217
< video
197
- muted
198
218
ref = { setVideoElement }
199
219
className = { videoClasses ( ) }
200
220
onDoubleClick = { handleFullscreen }
@@ -217,53 +237,58 @@ export const Room = ({
217
237
218
238
{ controlVisible && (
219
239
< 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 } />
232
242
) }
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
+ ) }
233
257
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
257
271
>
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 >
261
285
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 >
267
292
</ Paper >
268
293
) }
269
294
@@ -353,6 +378,40 @@ const useShowOnMouseMovement = (doShow: (s: boolean) => void) => {
353
378
) ;
354
379
} ;
355
380
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 = { 1 } 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 = { ( e , newVolume ) => {
407
+ video . muted = false ;
408
+ video . volume = newVolume ;
409
+ } }
410
+ />
411
+ </ Stack >
412
+ ) ;
413
+ } ;
414
+
356
415
const useStyles = makeStyles ( ( ) => ( {
357
416
title : {
358
417
padding : 15 ,
0 commit comments