Bug 198148

Summary: mediaDevices.enumerateDevices() returns empty deviceId
Product: WebKit Reporter: aksuta
Component: WebRTCAssignee: Nobody <webkit-unassigned>
Status: RESOLVED WORKSFORME    
Severity: Normal CC: aksuta, eric.carlson, webkit-bug-importer, youennf
Priority: P2 Keywords: InRadar
Version: Safari 12   
Hardware: iPhone / iPad   
OS: iOS 12   

Description aksuta 2019-05-22 17:05:54 PDT
On Safari 12.3:

navigator.mediaDevices.enumerateDevices().then(function(devices) {devices.forEach(function(device) {alert("object: " + JSON.stringify(device));})})
produces following:
object: {"deviceId":"";"kind":"audioinput","label":"","groupId":""}
object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
Comment 1 Radar WebKit Bug Importer 2019-05-23 17:11:53 PDT
<rdar://problem/51089109>
Comment 2 youenn fablet 2019-05-24 08:32:43 PDT
Hi aksuta,
this is expected.
See related bug 195093 for more information.

*** This bug has been marked as a duplicate of bug 195093 ***
Comment 3 aksuta 2019-05-26 13:32:44 PDT
My bad, I did not include getusermedia call in bugreport, it is present in the code. 
This happens after getUserMedia is called and granted. e.g.
if (Modernizr.getusermedia) {
   if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {								 
     navigator.mediaDevices.enumerateDevices().then(function(devices) {devices.forEach(function(device) {alert("object: " + JSON.stringify(device));})}).catch(function(err) {}); }
} else {fallback();}
Comment 4 youenn fablet 2019-05-26 13:49:03 PDT
Can you try the following and verify this is working as expected?

let devices = await navigator.mediaDevices.enumerateDevices();
// device ids should be empty
alert(JSON.stringify(devices));

const stream = await navigator.mediaDevices.getUserMedia({video: true});

devices = await navigator.mediaDevices.enumerateDevices();
// device ids should not be empty
alert(JSON.stringify(devices));
Comment 5 aksuta 2019-05-26 15:10:33 PDT
Hi Youenn, thanks for quick response.

I've modified your code snippet due to my project setup, but, I think, the idea is still same:

navigator.mediaDevices.enumerateDevices().then(function(devices) {devices.forEach(function(device) {alert("before object: " + JSON.stringify(device));})}).catch(function(err) {alert('before catch',err)});
		// device ids should be empty
navigator.mediaDevices.getUserMedia();
// device ids should not be empty
navigator.mediaDevices.enumerateDevices().then(function(devices) {devices.forEach(function(device) {alert("after object: " + JSON.stringify(device));})}).catch(function(err) {alert('after catch',err)});

Output is as follows:
before object: {"deviceId":"";"kind":"audioinput","label":"","groupId":""}
before object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
before object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
after object: {"deviceId":"";"kind":"audioinput","label":"","groupId":""}
after object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
after object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}


PS 
navigator.mediaDevices.getUserMedia().then(function(ret){alert("after getUserMedia", ret)}).catch(function(err){alert('catch in getUserMedia', err)}); 
results in alerting "catch in getUserMedia". 

If I use Modernizr.getusermedia it returns true, but with same empty deviceIds.
Comment 6 youenn fablet 2019-05-26 22:18:20 PDT
This is expected.
You need to change your snippet to trigger a successful getUserMedia call: device ids will be exposed once the page got access to camera or microphone at least once.

Note though that device IDs previously exposed can be given to getUserMedia() even though the devices are not listed by enumerateDevices.

(In reply to aksuta from comment #5)
> Hi Youenn, thanks for quick response.
> 
> I've modified your code snippet due to my project setup, but, I think, the
> idea is still same:
> 
> navigator.mediaDevices.enumerateDevices().then(function(devices)
> {devices.forEach(function(device) {alert("before object: " +
> JSON.stringify(device));})}).catch(function(err) {alert('before
> catch',err)});
> 		// device ids should be empty
> navigator.mediaDevices.getUserMedia();
> // device ids should not be empty
> navigator.mediaDevices.enumerateDevices().then(function(devices)
> {devices.forEach(function(device) {alert("after object: " +
> JSON.stringify(device));})}).catch(function(err) {alert('after catch',err)});
> 
> Output is as follows:
> before object: {"deviceId":"";"kind":"audioinput","label":"","groupId":""}
> before object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
> before object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
> after object: {"deviceId":"";"kind":"audioinput","label":"","groupId":""}
> after object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
> after object: {"deviceId":"";"kind":"videoinput","label":"","groupId":""}
> 
> 
> PS 
> navigator.mediaDevices.getUserMedia().then(function(ret){alert("after
> getUserMedia", ret)}).catch(function(err){alert('catch in getUserMedia',
> err)}); 
> results in alerting "catch in getUserMedia". 
> 
> If I use Modernizr.getusermedia it returns true, but with same empty
> deviceIds.
Comment 7 Eric Carlson 2019-05-27 08:32:15 PDT
navigator.mediaDevices.getUserMedia() fails because you don’t have any constraints. The something like navigator.mediaDevices.getUserMedia({ video : true })

After you fix this, you will also have to modify your example to wait for the getUserMedia promise to resolve before calling enumerateMediaDevices()
Comment 8 aksuta 2019-05-27 15:36:12 PDT
Awesome, it worked for me, thanks a lot!