@@ -13,10 +13,23 @@ const audioInputSelect = document.querySelector('select#audioSource');
1313const audioOutputSelect = document . querySelector ( 'select#audioOutput' ) ;
1414const videoSelect = document . querySelector ( 'select#videoSource' ) ;
1515const selectors = [ audioInputSelect , audioOutputSelect , videoSelect ] ;
16+ let hasMic = false ;
17+ let hasCamera = false ;
18+ let openMic = undefined ;
19+ let openCamera = undefined ;
20+ let hasPermission = false ;
1621
1722audioOutputSelect . disabled = ! ( 'sinkId' in HTMLMediaElement . prototype ) ;
1823
24+ function getDevices ( ) {
25+ navigator . mediaDevices . enumerateDevices ( ) . then ( gotDevices ) . catch ( handleError ) ;
26+ }
27+
1928function gotDevices ( deviceInfos ) {
29+ console . log ( 'gotDevices' , deviceInfos ) ;
30+ hasMic = false ;
31+ hasCamera = false ;
32+ hasPermission = false ;
2033 // Handles being called several times to update labels. Preserve values.
2134 const values = selectors . map ( select => select . value ) ;
2235 selectors . forEach ( select => {
@@ -26,15 +39,23 @@ function gotDevices(deviceInfos) {
2639 } ) ;
2740 for ( let i = 0 ; i !== deviceInfos . length ; ++ i ) {
2841 const deviceInfo = deviceInfos [ i ] ;
42+ if ( deviceInfo . deviceId == '' ) {
43+ continue ;
44+ }
45+ // If we get at least one deviceId, that means user has granted user
46+ // media permissions.
47+ hasPermission = true ;
2948 const option = document . createElement ( 'option' ) ;
3049 option . value = deviceInfo . deviceId ;
3150 if ( deviceInfo . kind === 'audioinput' ) {
51+ hasMic = true ;
3252 option . text = deviceInfo . label || `microphone ${ audioInputSelect . length + 1 } ` ;
3353 audioInputSelect . appendChild ( option ) ;
3454 } else if ( deviceInfo . kind === 'audiooutput' ) {
3555 option . text = deviceInfo . label || `speaker ${ audioOutputSelect . length + 1 } ` ;
3656 audioOutputSelect . appendChild ( option ) ;
3757 } else if ( deviceInfo . kind === 'videoinput' ) {
58+ hasCamera = true ;
3859 option . text = deviceInfo . label || `camera ${ videoSelect . length + 1 } ` ;
3960 videoSelect . appendChild ( option ) ;
4061 } else {
@@ -46,10 +67,9 @@ function gotDevices(deviceInfos) {
4667 select . value = values [ selectorIndex ] ;
4768 }
4869 } ) ;
70+ start ( ) ;
4971}
5072
51- navigator . mediaDevices . enumerateDevices ( ) . then ( gotDevices ) . catch ( handleError ) ;
52-
5373// Attach audio output device to video element using device/sink ID.
5474function attachSinkId ( element , sinkId ) {
5575 if ( typeof element . sinkId !== 'undefined' ) {
@@ -79,32 +99,54 @@ function changeAudioDestination() {
7999function gotStream ( stream ) {
80100 window . stream = stream ; // make stream available to console
81101 videoElement . srcObject = stream ;
82- // Refresh button list in case labels have become available
83- return navigator . mediaDevices . enumerateDevices ( ) ;
102+ if ( stream . getVideoTracks ( ) [ 0 ] ) {
103+ openCamera = stream . getVideoTracks ( ) [ 0 ] . getSettings ( ) . deviceId ;
104+ }
105+ if ( stream . getAudioTracks ( ) [ 0 ] ) {
106+ openMic = stream . getAudioTracks ( ) [ 0 ] . getSettings ( ) . deviceId ;
107+ }
108+ // Refresh list in case labels have become available
109+ return getDevices ( ) ;
84110}
85111
86112function handleError ( error ) {
87113 console . log ( 'navigator.MediaDevices.getUserMedia error: ' , error . message , error . name ) ;
88114}
89115
90116function start ( ) {
117+ const audioSource = audioInputSelect . value || undefined ;
118+ const videoSource = videoSelect . value || undefined ;
119+ // Don't open the same devices again.
120+ if ( hasPermission && openMic == audioSource && openCamera == videoSource ) {
121+ return ;
122+ }
123+ // Close existng streams.
91124 if ( window . stream ) {
92125 window . stream . getTracks ( ) . forEach ( track => {
93126 track . stop ( ) ;
94127 } ) ;
128+ openCamera = undefined ;
129+ openMic = undefined ;
95130 }
96- const audioSource = audioInputSelect . value ;
97- const videoSource = videoSelect . value ;
98131 const constraints = {
99- audio : { deviceId : audioSource ? { exact : audioSource } : undefined } ,
100- video : { deviceId : videoSource ? { exact : videoSource } : undefined }
132+ audio : true ,
133+ video : true
101134 } ;
102- navigator . mediaDevices . getUserMedia ( constraints ) . then ( gotStream ) . then ( gotDevices ) . catch ( handleError ) ;
135+ if ( hasMic ) {
136+ constraints [ 'audio' ] = { deviceId : audioSource ? { exact : audioSource } : undefined } ;
137+ }
138+ if ( hasCamera ) {
139+ constraints [ 'video' ] = { deviceId : videoSource ? { exact : videoSource } : undefined } ;
140+ }
141+ console . log ( 'start' , constraints ) ;
142+ if ( ! hasPermission || hasCamera || hasMic ) {
143+ navigator . mediaDevices . getUserMedia ( constraints ) . then ( gotStream ) . catch ( handleError ) ;
144+ }
103145}
104146
105147audioInputSelect . onchange = start ;
106148audioOutputSelect . onchange = changeAudioDestination ;
107-
108149videoSelect . onchange = start ;
150+ navigator . mediaDevices . ondevicechange = getDevices ;
109151
110- start ( ) ;
152+ getDevices ( ) ;
0 commit comments