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

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

【问题讨论】:

    标签: javascript html html5-video webrtc html5-audio


    【解决方案1】:

    现场演示:

    如果用户不允许使用网络摄像头和/或麦克风,则媒体设备的 "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
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多