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

Posted

技术标签:

【中文标题】是否可以检查用户是否有摄像头和麦克风以及是否已通过 Javascript 授予权限?【英文标题】:Is it possible to check if the user has a camera and microphone and if the permissions have been granted with Javascript? 【发布时间】:2015-07-14 20:06:33 【问题描述】:

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

【问题讨论】:

【参考方案1】:

现场演示:

https://www.webrtc-experiment.com/DetectRTC/

如果用户不允许使用网络摄像头和/或麦克风,则媒体设备的 "label" 属性将具有 "NULL" 值。以上页面将显示此消息:“请调用 getUserMedia 一次。”

附言。您可以在 Chrome 控制台开发者工具中输入“DetectRTC.MediaDevices”

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

2015 年 12 月 16 日更新

注意:以下代码 sn-p 适用于 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>');
);

【讨论】:

Firefox 还不支持 enumerateDevices 或 getMediaDevices 或 MediaStreamTrack.getSources API;这意味着如果不手动发出 getUserMedia 请求,我们无法检测 Firefox 是否可以访问麦克风/网络摄像头。每当我们在 Firefox 中发出 getUserMedia 请求时,它都会显示权限弹出/下拉菜单,这对于现实生活中的用例来说并不好。 以上演示适用于桌面和安卓----使用Chrome。它甚至可以在 Opera 中使用。 Firefox 38+ 有一个用于枚举设备的 API,这些设备的工作方式不同,但在未授予权限时仍会为设备标签返回空值。有关使用我的帮助程序库包装不同 api 的示例,请参见此处:xdumaine.com/enumerateDevices/test 只是为了扩展这一点 - 您不必使用 DetectRTC 或我的库 enumerateDevices,您可以使用 MediaStreamTrack.getSources(仅限 chrome)列出设备并检查结果是否有标签与否。 @xdumaine +1 知道 Firefox 38+ 支持 enumerateDevices API。我会进一步调查并尽快实施。顺便说一句,新手更难学习和使用棘手的 API。他们更喜欢您的库或 javascript-shims 之类的解决方案。【参考方案2】:

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

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

【讨论】:

我看到了这个错误Uncaught TypeError: Failed to execute 'getUserMedia' on 'Navigator': 3 arguments required, but only 2 present.。有什么问题? 替换为 navigator.mediaDevices.getUserMedia【参考方案3】:

1)你应该使用Media Recorder并理解promise

2)检查浏览器是否支持APIenumerateDevices

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时,Bowser不会提示任何内容,并且会拒绝promise的承诺,抛出DOMException类型的错误,否则它会解决承诺。 当 promise 拒绝时,可能有很多原因read,其中之一是用户拒绝访问,当这种情况发生时,它会抛出 NotAllowedError 类型的 DOMException,所以现在我们只对这个错误感兴趣。

如果您阅读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

【讨论】:

他究竟为什么要使用媒体流录制 API? 因为他要录制音频和视频,而录制它的一种方式是使用getUserMedia 请记住,除非您关闭它,否则这将使相机/麦克风流保持运行。【参考方案4】:

现在您也可以使用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,并支持查询 cameramicrophone 权限,至少从 Chrome 87+ 开始。 Firefox 从 Firefox 46+ 开始支持 navigator.permissions.query,但从 Firefox 84 开始不支持查询 cameramicrophone 权限。 Safari 甚至不支持navigator.permissions.query

【讨论】:

不幸的是,这也不适用于 safari【参考方案5】:

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

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

【讨论】:

【参考方案6】:

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

注意!!!用我的代码使用 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

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

安卓

铬 52 火狐48 歌剧 37

【讨论】:

从技术上讲,如果您想检查是否进行此更改if (location.protocol === 'https:' || location.hostname === 'localhost') ,您也可以在开发人员测试中的本地主机上使用它【参考方案7】:

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

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

【讨论】:

只是一个评论:device.label 只有在当前正在流式传输时才会有一个值已授予持久权限,因此此代码可能会错误地返回 false

以上是关于是否可以检查用户是否有摄像头和麦克风以及是否已通过 Javascript 授予权限?的主要内容,如果未能解决你的问题,请参考以下文章

单位办公电脑声音和摄像头被关闭,如何打开笔记本麦克风和摄像头。

前端多媒体-1.获取摄像头&麦克风

查看应用程序是不是正在使用 Windows 10 上的麦克风

是否可以确定麦克风类型?

001 WebRTC

001 WebRTC