I would like to find out if the user's device has an attached camera and microphone, and if so, has permissions been granted to get the audio and video stream using Javascript. I want to make this check to be made across Chrome and Firefox at the very least. What's a consistent API for this?
7 Answers
Live Demo:
If user didn't allow webcam and/or microphone, then media-devices will be having "NULL" value for the "label" attribute. Above page will show this message: "Please invoke getUserMedia once."
PS. You can type "DetectRTC.MediaDevices" in the Chrome Console developers tool.
Note: It works only in Chrome. Firefox isn't supporting similar API yet. (Updated: Firefox supports as well)
Updated at Dec 16, 2015
Note: Following code snippet works both in Chrome and Firefox.
if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
// Firefox 38+ seems having support of enumerateDevicesx
navigator.enumerateDevices = function(callback) {
navigator.mediaDevices.enumerateDevices().then(callback);
};
}
var MediaDevices = [];
var isHTTPs = location.protocol === 'https:';
var canEnumerate = false;
if (typeof MediaStreamTrack !== 'undefined' && 'getSources' in MediaStreamTrack) {
canEnumerate = true;
} else if (navigator.mediaDevices && !!navigator.mediaDevices.enumerateDevices) {
canEnumerate = true;
}
var hasMicrophone = false;
var hasSpeakers = false;
var hasWebcam = false;
var isMicrophoneAlreadyCaptured = false;
var isWebcamAlreadyCaptured = false;
function checkDeviceSupport(callback) {
if (!canEnumerate) {
return;
}
if (!navigator.enumerateDevices && window.MediaStreamTrack && window.MediaStreamTrack.getSources) {
navigator.enumerateDevices = window.MediaStreamTrack.getSources.bind(window.MediaStreamTrack);
}
if (!navigator.enumerateDevices && navigator.enumerateDevices) {
navigator.enumerateDevices = navigator.enumerateDevices.bind(navigator);
}
if (!navigator.enumerateDevices) {
if (callback) {
callback();
}
return;
}
MediaDevices = [];
navigator.enumerateDevices(function(devices) {
devices.forEach(function(_device) {
var device = {};
for (var d in _device) {
device[d] = _device[d];
}
if (device.kind === 'audio') {
device.kind = 'audioinput';
}
if (device.kind === 'video') {
device.kind = 'videoinput';
}
var skip;
MediaDevices.forEach(function(d) {
if (d.id === device.id && d.kind === device.kind) {
skip = true;
}
});
if (skip) {
return;
}
if (!device.deviceId) {
device.deviceId = device.id;
}
if (!device.id) {
device.id = device.deviceId;
}
if (!device.label) {
device.label = 'Please invoke getUserMedia once.';
if (!isHTTPs) {
device.label = 'HTTPs is required to get label of this ' + device.kind + ' device.';
}
} else {
if (device.kind === 'videoinput' && !isWebcamAlreadyCaptured) {
isWebcamAlreadyCaptured = true;
}
if (device.kind === 'audioinput' && !isMicrophoneAlreadyCaptured) {
isMicrophoneAlreadyCaptured = true;
}
}
if (device.kind === 'audioinput') {
hasMicrophone = true;
}
if (device.kind === 'audiooutput') {
hasSpeakers = true;
}
if (device.kind === 'videoinput') {
hasWebcam = true;
}
// there is no 'videoouput' in the spec.
MediaDevices.push(device);
});
if (callback) {
callback();
}
});
}
// check for microphone/camera support!
checkDeviceSupport(function() {
document.write('hasWebCam: ', hasWebcam, '<br>');
document.write('hasMicrophone: ', hasMicrophone, '<br>');
document.write('isMicrophoneAlreadyCaptured: ', isMicrophoneAlreadyCaptured, '<br>');
document.write('isWebcamAlreadyCaptured: ', isWebcamAlreadyCaptured, '<br>');
});

- 7,138
- 9
- 41
- 77
-
This looks interesting! How about browser compatibility though? – Amal Antony May 05 '15 at 08:20
-
2Firefox isn't supporting enumerateDevices or getMediaDevices or MediaStreamTrack.getSources API yet; which means that we can't detect if Firefox is having access to microphone/webcam without manually making getUserMedia requests. Whenever we'll make getUserMedia request in Firefox, it'll show permission popup/dropdown which isn't good for real life usecases. – Muaz Khan May 05 '15 at 08:25
-
1Above demo works both in desktop and Andriod ---- using Chrome. It even works in Opera as well. – Muaz Khan May 05 '15 at 08:26
-
3Firefox 38+ has an API for enumerating devices that works different, but still returns null values for device labels when permission aren't granted. See here for an example that uses my helper library to wrap up the different apis: https://www.xdumaine.com/enumerateDevices/test/ – xdumaine May 05 '15 at 14:18
-
1Just to expand on this - you don't have to use DetectRTC or my library enumerateDevices, you can just use `MediaStreamTrack.getSources` (chrome only) for listing devices and just checking to see if the results have labels or not. – xdumaine May 05 '15 at 14:25
-
1@xdumaine +1 to know that Firefox 38+ is supporting enumerateDevices API. I'll investigate further & implement soon. BTW, its harder for newcomers to learn and use tricky APIs. They prefer solution like your library or javascript-shims. – Muaz Khan May 05 '15 at 14:35
-
Can you please add the relevant code to your answer itself, instead of [only linking that demo page](http://meta.stackexchange.com/q/8231/183280)? And maybe additionally provide links to the documentation of the methods you used? – Bergi Dec 15 '15 at 11:43
-
This appears to be working Ffox too, at least on the latest version. I assume that there is still no "industry standard" way for checking this – Vitaliy Terziev Aug 08 '16 at 15:44
-
2@MuazKhan why checking `!navigator.enumerateDevices && navigator.enumerateDevices` at all? Doesn't it give false every time? – sertsedat Sep 03 '18 at 12:04
-
call your media permission in `DetectRTC.load(callback)` function, where callback will be `navigator.mediaDevices.getUserMedia` & you can pass `video: DetectRTC.hasWebcam` & `audio:DetectRTC.hasMicrophone` to avoid catch issue in case one of video or audio device is not available. – Hitesh Jangid Jun 28 '21 at 08:50
Now you can use navigator.permissions
also to check permissions already exist
navigator.permissions.query({ name: "camera" }).then(res => {
if(res.state == "granted"){
// has permission
}
});
But note that support is patchy as of Jan 2021:
- Chrome supports
navigator.permissions.query
as of Chrome 43+, and supports queryingcamera
andmicrophone
permissions, at least as of Chrome 87+. - Firefox supports
navigator.permissions.query
as of Firefox 46+, but does not support queryingcamera
ormicrophone
permissions as of Firefox 84. - Safari does not even support
navigator.permissions.query
.

- 34,029
- 31
- 121
- 167

- 1,603
- 2
- 15
- 21
Yes it is quite possible to detect whether a microphone and a camera is available after granting the permission.
Using the old API:
navigator.getUserMedia({ audio: true, video: true}, function (stream) {
if (stream.getVideoTracks().length > 0 && stream.getAudioTracks().length > 0) {
//code for when none of the devices are available
} else {
// code for when both devices are available
}
}, function (error) {
// code for when there is an error
});
Using the newer, promise-based API:
navigator.mediaDevices.getUserMedia({ audio: true, video: true})
.then(function (stream) {
if (stream.getVideoTracks().length > 0 && stream.getAudioTracks().length > 0){
//code for when none of the devices are available
} else {
// code for when both devices are available
}
})
.catch(function (error) {
// code for when there is an error
});

- 3,464
- 2
- 33
- 38

- 9,683
- 3
- 51
- 41
-
I see this error ``Uncaught TypeError: Failed to execute 'getUserMedia' on 'Navigator': 3 arguments required, but only 2 present.``. What's the problem? – Miron Jun 12 '18 at 08:33
-
2
1)You should be using Media Recorder and understand promise
2)Check if browser support the API enumerateDevices
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log("This browser does not support the API yet");
}
let checking=["audioinput","videoinput"];
let onlyHas=[];
navigator.mediaDevices.enumerateDevices()
.then((devices)=> {
let haveAllDevices=true;
devices.forEach((device)=>{
onlyHas.push(device.kind);
if(!(device.kind==checking[0] || device.kind==checking[1])){
haveAllDevices=false;
}
});
//do something about ...
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
NotAllowedError
, so for now we are only interested in this error.
If you read DOMException you can see you can acces DOMException.name
, this is the one that you should be compared, so:
let constraints={audio:true,video:true};
navigator.mediaDevices.getUserMedia(constraints)
.then((stream)=>{.....})
.catch((err)=>
{if(err.name=="NotAllowedError"){console.log("User has denied accessed")}
});
PS: About cross browser compatibility MediaRecorder as for today 09/06/2018 it is only supported in chrome and firefox, and the brothers IE and IOS don't https://caniuse.com/#search=MediaRecorder

- 2,632
- 3
- 26
- 41
-
-
because he wants to record audio and video, and one way to record its to use getUserMedia – John Balvin Arias May 03 '19 at 13:57
-
1Keep in mind that this will keep the camera/microphone stream running unless you close it. – Shahid Kamal Jan 21 '20 at 09:55
This function checks if the user have audio and video access:
checkMediaAccess = async() => {
navigator.mediaDevices.enumerateDevices().then( devices =>
devices.forEach( device => {
if(device.kind == 'audioinput' && device.label) console.log('Has Audio Access');
if(device.kind == 'videoinput' && device.label) console.log('Has Video Access');
}
));
}

- 774
- 9
- 19
-
1just a comment: `device.label` will only have a value if it is currently streaming _or_ has persistent permission granted, thus this code may incorrectly return false – Alejandro B. Sep 13 '21 at 00:11
You can use the MediaStreamTrack which represent a media stream, then you can use its getSources method as explained here: html5rocks
If you don't get any media sources then your client hasn't a webcam. It's not supported by firefox.

- 96
- 4
Please try my simple cross browser code.
Attention!!! Use https protocol for open web page with my code! Please go to demo
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<h1>Web camera</h1>
<video autoplay></video>
<script>
function errorMessage(message, e) {
console.error(message, typeof e == 'undefined' ? '' : e);
//alert(message);
}
if (location.protocol === 'https:') {
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({ audio: true, video: true }, function (stream) {
document.querySelector('video').src = window.URL.createObjectURL(stream);
var mediaStreamTrack = stream.getVideoTracks()[0];
if (typeof mediaStreamTrack != "undefined") {
mediaStreamTrack.onended = function () {//for Chrome.
errorMessage('Your webcam is busy!')
}
} else errorMessage('Permission denied!');
}, function (e) {
var message;
switch (e.name) {
case 'NotFoundError':
case 'DevicesNotFoundError':
message = 'Please setup your webcam first.';
break;
case 'SourceUnavailableError':
message = 'Your webcam is busy';
break;
case 'PermissionDeniedError':
case 'SecurityError':
message = 'Permission denied!';
break;
default: errorMessage('Reeeejected!', e);
return;
}
errorMessage(message);
});
} else errorMessage('Uncompatible browser!');
} else errorMessage('Use https protocol for open this page.')
</script>
</body>
</html>
I have tested it into follow browsers:
Windows 10
- Chrome 52
- Edge 25
- Firefox 47
- IE11 Uncompatible browser!
- Opera 39
- Safari 5 Uncompatible browser!
Android
- Chrome 52
- Firefox 48
- Opera 37

- 679
- 5
- 14
-
Technically you can use this also on localhost in developer testing if you want to check for that make this change `if (location.protocol === 'https:' || location.hostname === 'localhost') {` – gimp3695 Dec 29 '20 at 00:17