1
- import { useState } from "react" ;
1
+ import React , { useState } from "react" ;
2
2
import { VideoDevicePanel } from "./VideoDevicePanel" ;
3
3
import { AudioDevicePanel } from "./AudioDevicePanel" ;
4
4
import { showToastError } from "./Toasts" ;
@@ -7,20 +7,28 @@ import { EnumerateDevices, enumerateDevices } from "../utils/browser-media-utils
7
7
import { MockVideoPanel } from "./MockVideoPanel" ;
8
8
import { DeviceInfo } from "../containers/StreamingSettingsCard" ;
9
9
import { ScreensharingPanel } from "./ScreensharingPanel" ;
10
+ import { atomWithStorage } from "jotai/utils" ;
11
+ import { useAtom } from "jotai" ;
12
+ import { TbArrowBack } from "react-icons/tb" ;
13
+ import { TrackSource } from "../containers/Client" ;
10
14
11
15
export type StreamInfo = {
12
16
stream : MediaStream ;
13
17
id : string ;
14
18
} ;
15
- export type DeviceIdToStream = Record < string , StreamInfo > ;
16
19
17
20
type StreamingDeviceSelectorProps = {
18
21
id : string ;
19
22
selectedDeviceId : DeviceInfo | null ;
20
23
setSelectedDeviceId : ( info : DeviceInfo | null ) => void ;
21
- addLocalStream : ( stream : MediaStream , id : string ) => void ;
24
+ addLocalStream : ( stream : MediaStream , id : string , source : TrackSource , stop ?: ( ) => void ) => void ;
22
25
} ;
23
26
27
+ const widthAtom = atomWithStorage ( "width-constraint" , 1280 ) ;
28
+ const heightAtom = atomWithStorage ( "height-constraint" , 720 ) ;
29
+ const frameRateAtom = atomWithStorage ( "frame-rate-constraint" , 24 ) ;
30
+ const forceConstraintsAtom = atomWithStorage ( "force-constraint" , true ) ;
31
+
24
32
export const StreamingDeviceSelector = ( {
25
33
id,
26
34
selectedDeviceId,
@@ -29,6 +37,11 @@ export const StreamingDeviceSelector = ({
29
37
} : StreamingDeviceSelectorProps ) => {
30
38
const [ enumerateDevicesState , setEnumerateDevicesState ] = useState < EnumerateDevices | null > ( null ) ;
31
39
40
+ const [ width , setWidth ] = useAtom ( widthAtom ) ;
41
+ const [ height , setHeight ] = useAtom ( heightAtom ) ;
42
+ const [ frameRate , setFrameRate ] = useAtom ( frameRateAtom ) ;
43
+ const [ forceConstraints , setForceConstraints ] = useAtom ( forceConstraintsAtom ) ;
44
+
32
45
return (
33
46
< div className = "flex flex-col gap-2" >
34
47
{ enumerateDevicesState ?. video ?. type !== "OK" && (
@@ -52,20 +65,93 @@ export const StreamingDeviceSelector = ({
52
65
) }
53
66
54
67
< div className = "flex place-content-center align-baseline flex-col flex-wrap w-full gap-3" >
55
- { enumerateDevicesState ?. video . type === "OK" &&
56
- enumerateDevicesState . video . devices . map ( ( { deviceId, label } ) => (
57
- < div key = { deviceId } className = "join-item w-full" >
58
- < VideoDevicePanel
59
- key = { deviceId }
60
- deviceId = { deviceId }
61
- label = { label }
62
- addLocalVideoStream = { addLocalStream }
63
- setSelectedVideoId = { setSelectedDeviceId }
64
- selected = { selectedDeviceId ?. id === deviceId }
65
- />
66
- </ div >
67
- ) ) }
68
+ { enumerateDevicesState ?. video . type === "OK" && (
69
+ < div className = "flex flex-col gap-2" >
70
+ < div className = "flex flex-col" >
71
+ < div className = "flex flex-row flex-nowrap justify-between" >
72
+ < div className = "flex flex-row items-center gap-2" >
73
+ < label htmlFor = "force-video-constraints" > Force video constraints</ label >
74
+ < input
75
+ className = "checkbox"
76
+ type = "checkbox"
77
+ name = "force-video-constraints"
78
+ checked = { forceConstraints }
79
+ onChange = { ( ) => setForceConstraints ( ( prev ) => ! prev ) }
80
+ />
81
+ </ div >
68
82
83
+ < button
84
+ className = "btn btn-neutral btn-sm p-1 ml-1 tooltip tooltip-info tooltip-right"
85
+ data-tip = "Restore default"
86
+ onClick = { ( ) => {
87
+ setWidth ( 1280 ) ;
88
+ setHeight ( 720 ) ;
89
+ setFrameRate ( 24 ) ;
90
+ } }
91
+ >
92
+ < TbArrowBack size = { "1.5em" } />
93
+ </ button >
94
+ </ div >
95
+ < div className = "flex flex-row flex-nowrap items-center" >
96
+ < label className = "label flex-row gap-2" >
97
+ < span className = "label-text" > width</ span >
98
+ < input
99
+ disabled = { ! forceConstraints }
100
+ type = "number"
101
+ value = { width }
102
+ className = "input w-full input-sm max-w-xs"
103
+ onChange = { ( e ) => {
104
+ const parsed = Number . parseInt ( e . target . value ) ;
105
+ if ( isNaN ( parsed ) ) return ;
106
+ setWidth ( ( ) => parsed ) ;
107
+ } }
108
+ />
109
+ </ label >
110
+ < label className = "label flex-row gap-2" >
111
+ < span className = "label-text" > height</ span >
112
+ < input
113
+ disabled = { ! forceConstraints }
114
+ type = "number"
115
+ value = { height }
116
+ className = "input w-full input-sm max-w-xs"
117
+ onChange = { ( e ) => {
118
+ const parsed = Number . parseInt ( e . target . value ) ;
119
+ if ( isNaN ( parsed ) ) return ;
120
+ setHeight ( parsed ) ;
121
+ } }
122
+ />
123
+ </ label >
124
+ < label className = "label flex-row gap-2" >
125
+ < span className = "label-text" > frame rate</ span >
126
+ < input
127
+ disabled = { ! forceConstraints }
128
+ type = "number"
129
+ value = { frameRate }
130
+ className = "input w-full input-sm max-w-xs"
131
+ onChange = { ( e ) => {
132
+ const parsed = Number . parseInt ( e . target . value ) ;
133
+ if ( isNaN ( parsed ) ) return ;
134
+ setFrameRate ( parsed ) ;
135
+ } }
136
+ />
137
+ </ label >
138
+ </ div >
139
+ </ div >
140
+ { enumerateDevicesState . video . devices . map ( ( { deviceId, label } ) => (
141
+ < div key = { deviceId } className = "join-item w-full" >
142
+ < VideoDevicePanel
143
+ key = { deviceId }
144
+ deviceId = { deviceId }
145
+ constraints = { forceConstraints ? { width, height, frameRate } : undefined }
146
+ label = { label }
147
+ addLocalVideoStream = { addLocalStream }
148
+ setSelectedVideoId = { setSelectedDeviceId }
149
+ selected = { selectedDeviceId ?. id === deviceId }
150
+ />
151
+ </ div >
152
+ ) ) }
153
+ </ div >
154
+ ) }
69
155
{ enumerateDevicesState ?. audio ?. type === "OK" &&
70
156
enumerateDevicesState . audio . devices
71
157
. filter ( ( { label } ) => ! label . startsWith ( "Default" ) )
@@ -81,13 +167,11 @@ export const StreamingDeviceSelector = ({
81
167
/>
82
168
</ div >
83
169
) ) }
84
-
85
170
< ScreensharingPanel
86
171
addLocalStream = { addLocalStream }
87
172
setSelectedDeviceId = { setSelectedDeviceId }
88
173
label = { "Screenshare" }
89
174
/>
90
-
91
175
< MockVideoPanel
92
176
id = { id }
93
177
addLocalVideoStream = { addLocalStream }
0 commit comments