是否可以检查用户是否有摄像头和麦克风以及是否已授予 Javascript 权限?

Is it possible to check if the user has a camera and microphone and if the permissions have been granted with Javascript?

我想了解用户的设备是否连接了摄像头和麦克风,如果有,是否已授予使用 Javascript 获取音频和视频流的权限。我想至少在 Chrome 和 Firefox 中进行此检查。什么是一致的 API?

现场演示:

如果用户不允许网络摄像头 and/or 麦克风,则媒体设备将具有 "NULL""label"属性。上面的页面将显示此消息:"Please invoke getUserMedia once."

PS。您可以在 Chrome 控制台开发者工具中输入 "DetectRTC.MediaDevices"

注意:它仅适用于 Chrome。 Firefox 尚不支持类似 API。 (更新: Firefox 也支持)

更新于 2015 年 12 月 16 日

注意:以下代码片段在 Chrome 和 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>');
});

您可以使用代表媒体流的 MediaStreamTrack,然后您可以使用其 getSources 方法,如下所述:html5rocks

如果您没有获得任何媒体资源,那么您的客户没有网络摄像头。 Firefox 不支持。

请尝试我的简单跨浏览器代码。

注意!!!使用我的代码使用 https 协议打开网页!请前往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>

我已经在以下浏览器中对其进行了测试:

Windows 10

  • Chrome 52
  • 边 25
  • 火狐 47
  • IE11不兼容浏览器!
  • 歌剧 39
  • Safari 5 浏览器不兼容!

Android

  • Chrome 52
  • 火狐 48
  • 歌剧 37

是的,授予权限后可以检测麦克风和摄像头是否可用。

使用旧的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
});

使用更新的、基于 promise 的 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
   });

1)您应该使用 Media Recorder and understand promise

2)检查浏览器是否支持API enumerateDevices

if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
  console.log("This browser does not support the API yet");
}
3) 检查用户是否连接了音频和摄像头,只有"videoinput"、"audioinput"或"audiooutput" DeviceInfo.kind

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);
});
4)权限被重用,这意味着如果用户已经拒绝了权限那么当你调用getUserMedia the bowser won't prompt anything and will reject the promise promise throwing an error of type DOMException时,否则它会解决承诺。 当承诺被拒绝时,可能有多种原因 read, one of then is when user has denied access, when this happens it will throw an DOMException NotAllowedError,所以现在我们只对这个错误感兴趣。

如果你阅读DOMException你可以看到你可以访问DOMException.name,这是你应该比较的,所以:

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: 关于跨浏览器兼容性 MediaRecorder 至于今天09/06/2018 只支持chrome和firefox,兄弟IE和IOS 不 https://caniuse.com/#search=MediaRecorder

现在您还可以使用navigator.permissions来检查权限是否已经存在

navigator.permissions.query({ name: "camera" }).then(res => {
    if(res.state == "granted"){
        // has permission
    }
});

See MDN for more info.

但请注意,截至 2021 年 1 月,支持是不完整的:

  • Chrome 从 Chrome 43+ 开始支持 navigator.permissions.query,并且至少从 [=34= 开始支持查询 cameramicrophone 权限] 87+.
  • Firefox 从 Firefox 46+ 开始支持 navigator.permissions.query,但从 Firefox 84 开始不支持查询 cameramicrophone 权限。
  • Safari 甚至不支持 navigator.permissions.query

此函数检查用户是否具有音频和视频访问权限:

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');
        }
    ));
}