zoukankan      html  css  js  c++  java
  • 浏览器中打开摄像头

      本文是讲述如何在浏览器中打开摄像头,并且实时显示在页面上。想要实现这一功能,需要依赖WebRTC (Web Real-Time Communications) 这一实时通讯技术,它允许浏览器之间视频流和音频流或者其他任意数据的传输,当然其中包含了大量的API和协议,这些在这里都不做介绍,具体的标准还在完善之中,所以使用的方法有时候也需要考虑到兼容问题,那么回到主题,怎样使用webRTC获取视频流。(必须通过https打开)

      首先对于html,我们需要一个video标签来播放视频(JS中添加也可以),当然画布也是能够实现的。使用video有些属性必须要配置。

    <video id="video" autoplay playsinline></video>

      上面两个属性必须要添加,autoplay设置video自动播放,否则通信成功后页面将会保留第一张静止的画面。playsinline是由于有些浏览器默认会开启全屏播放,而全屏可能是画面变成黑屏。

      webRTC大部分浏览器支持,IE(Edge)15+, Safari 11+, IOS Safari 11.2+, Android 64+,  QQ、百度部分支持,对于IOS必须系统在11以上,并且只有Safari支持,UC 浏览器不支持。

      对于webRTC支持情况,可以采用以下代码判断。

    if (navigator.mediaDevices === undefined ||
      navigator.mediaDevices.enumerateDevices === undefined ||
      navigator.mediaDevices.getUserMedia === undefined) {
      if (navigator.mediaDevices === undefined) {
        var fctName = 'navigator.mediaDevices'
      } else if (navigator.mediaDevices.enumerateDevices === undefined) {
        var fctName = 'navigator.mediaDevices.enumerateDevices'
      } else if (navigator.mediaDevices.getUserMedia === undefined) {
        var fctName = 'navigator.mediaDevices.getUserMedia'
      } else {
        console.assert(false)
      }
      alert('WebRTC issue-! ' + fctName + ' not present in your browser')
    }

      获取video元素。

    let video = document.querySelector('#video');

      如果支持webRTC,那么就调用mediaDevice的方法来遍历媒体对象,该方法返回一个promise对象,在第一个then方法的回调函数里面默认接收返回的媒体信息。

    navigator.mediaDevices.enumerateDevices().then(function (sourceInfos) {
    })

      获取sourceInfos,要拿到后置摄像头的信息并不是通用的,PC、IOS、Android以及不同浏览器,处理方法都不同,这里只做IOS和Android区分。

    if(!navigator.userAgent.match(/(i[^;]+;( U;)? CPU.+Mac OS X/)) {
    }

      如果不是IOS,那么就需要遍历sourceinfos,获取后置摄像头再获取视频流。声明一个exArray用来存放不同媒体设备的id,再通过id获取摄像头信息。

    let exArray = [];  
    for (var i = 0; i < sourceInfos.length; ++i) {
      if (sourceInfos[i].kind == 'videoinput') {
        exArray.push(sourceInfos[i].deviceId);
      }
    }

      这样数组里面存放的都是摄像头的id,而我们需要的是后置摄像头,再调用获取媒体信息的方法。

    if (navigator.getUserMedia) {
      // 该方法可以传递3个参数,分别为获取媒体信息的配置,成功的回调函数和失败的回调函数
      navigator.getUserMedia({
        audio: false, // 表明是否获取音频
        video: {  // 对视频信息进行配置
          optional: [{
            'sourceId': exArray[1] // 下标为0是前置摄像头,1为后置摄像头,所以PC不能进入该判断,否则画面会保持在第一帧不动
          }]
        },
      }, function successFunc(stream) {
        // 对FireFox进行兼容,这里对返回流数据的处理不同
        if (video.mozSrcObject !== undefined) {
          //Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持  
          video.mozSrcObject = stream;
        } else {
          // 一般的浏览器需要使用createObjectURL对流数据进行处理,再交给video元素的src
          video.src = window.URL && window.URL.createObjectURL(stream) || stream;
        }
      }, function errorFunc(e) {
        alert('Error!' + e);
      }); //success是获取成功的回调函数  
    } else {
      alert('Native device media streaming (getUserMedia) not supported in this browser.');
    }

      <!--  URL.createObjectURL with media streams is deprecated and will be removed in M68, around July 2018. Please use HTMLMediaElement.srcObject instead.   -->

      在成功的回调函数中,将返回的视频流交给html中的video元素,这里src的方式实际是通过blob64的格式来传输的。

      // =============================  IOS

      在IOS中获取视频流现实的方式又不一样了,我们需要调用mediaDevice的获取媒体的方法,这也是最新的标准。首先需要些小小的配置:

    // 这里对生成视频进行配置
    var userMediaConstraints = {
      audio: false, // 是否获取音频
      video: {
        facingMode: 'environment',  // 环境表示后置摄像头,使用user表示采用前置
        // 宽高的配置比较灵活,由于video一般都会显示固定宽高比,所以使用ideal理想值即可
         {
          ideal: 1024,
          // min: 1024,
          // max: 1920
        },
        height: {
          ideal: 768,
          // min: 776,
          // max: 1080
        }
      }
    }

      再调用方法获取视频就很简单了,如下:

    navigator.mediaDevices.getUserMedia(userMediaConstraints).then(function success(stream) {
      video.srcObject = stream;
    }).catch(function (error) {
      alert(error.name + error.message)
    });

      整体如下:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <meta http-equiv="X-UA-Compatible" content="ie=edge">
      <title>web RTC</title>
    
    </head>
    
    <body>
      <!-- 这里必须设置autoplay,否则视频画面静止为第一张 -->
      <!-- 必须设置为playsinline,默认全屏播放可能会导致黑屏 -->
      <video id="video" autoplay playsinline></video>
      <script>
        // 习惯性写在函数中,控制变量
       ;(function(){
        //  由于IOS必须在版本11以上才能使用webrtc,并且只有Safari支持,所以做一个小小的判断,限定在
        if(/(iPhone|iPad|iPod|iOS)/i.test(window.navigator.userAgent) && navigator.vender.indexOf("apple") > -1) {
        return;
        }
        /**
         * =============实现在浏览器中打开摄像头,并且将摄像头内容显示在页面中
         * 想要实现这一功能,需要了解webRTC(Web Real-Time Communication)网络实时通话技术,它允许浏览器实现视频、音频、P2P文件分享等功能。
         */
        // 开启视频功能,依赖window的navigator对象,采用getUserMedia方法,有版本差异,所以需要判断区分
        // 需要IE(Edge)15+, Safari 11+, IOS Safari 11.2+, Android 64+, UC 不支持, QQ、百度部分支持
    
        // 所以首先需要对浏览器支持情况进行判断
        // 先判断浏览器是否支持
        if (navigator.mediaDevices === undefined ||
          navigator.mediaDevices.enumerateDevices === undefined ||
          navigator.mediaDevices.getUserMedia === undefined) {
          // 再判断具体是那个方法不支持,并向用户显示
          if (navigator.mediaDevices === undefined) {
            var fctName = 'navigator.mediaDevices'
          } else if (navigator.mediaDevices.enumerateDevices === undefined) {
            var fctName = 'navigator.mediaDevices.enumerateDevices'
          } else if (navigator.mediaDevices.getUserMedia === undefined) {
            var fctName = 'navigator.mediaDevices.getUserMedia'
          } else {
            console.assert(false)
          }
          alert('WebRTC issue-! ' + fctName + ' not present in your browser')
        }
        const video = document.querySelector('#video')
    
        // 如果浏览器支持,该方法的更新是向后兼容,新版将所有功能都使用navigator.mediaDevices进行了封装
        navigator.mediaDevices.enumerateDevices().then(function (sourceInfos) {
          // 如果支持新的方法,那么就使用新的方法来获取,当然这是一种比较主流的判断方法
          // 如果是想旧的方法兼容,可以使用下面作为判断条件,除IOS和PC以外,均使用旧的获取方式
          // !(navigator.userAgent.match(/(i[^;]+;( U;)? CPU.+Mac OS X/) || !/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
          
          /**
           * 无论是旧的写法还是新的标准,思路都是通过设备信息,获取摄像头的视频流,通过转换变成blob的格式交给video的src
           */
          if (!navigator.mediaDevices.getUserMedia) {
            // 声明一个数组,用于装载设备媒体设备的相关信息,由于回调中sourceInfos对象中携带有所有媒体对象的相关信息
            // 这里对信息进行遍历筛选,只选出摄像头的Id并保存在数组中
            var exArray = [];  
            for (var i = 0; i < sourceInfos.length; ++i) {
              if (sourceInfos[i].kind == 'videoinput') {
                exArray.push(sourceInfos[i].deviceId);
              }
            }
            // 通过navigator的getUserMedia获取摄像头的视频流,并在成功的回调中将视频流交给video
            getMedia();
    
            function getMedia() {
              if (navigator.getUserMedia) {
                // 该方法可以传递3个参数,分别为获取媒体信息的配置,成功的回调函数和失败的回调函数
                navigator.getUserMedia({
                  audio: false, // 表明是否获取音频
                  video: {  // 对视频信息进行配置
                    optional: [{
                      'sourceId': exArray[1] // 下标为0是前置摄像头,1为后置摄像头,所以PC不能进入该判断,否则画面会保持在第一帧不动
                    }]
                  },
                }, successFunc, errorFunc); //success是获取成功的回调函数  
              } else {
                alert('Native device media streaming (getUserMedia) not supported in this browser.');
              }
            }
            // 这里是获取媒体信息成功的回调函数
            function successFunc(stream) {
              // 对FireFox进行兼容,这里对返回流数据的处理不同
              if (video.mozSrcObject !== undefined) {
                //Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持  
                video.mozSrcObject = stream;
              } else {
                // 一般的浏览器需要使用createObjectURL对流数据进行处理,再交给video元素的src
                video.src = window.URL && window.URL.createObjectURL(stream) || stream;
              }
            }
            // 获取媒体信息失败的回调
            function errorFunc(e) {
              alert('Error!' + e);
            }
          } else {  // 当采用最新的标准方式获取视频时
            // 这里对生成视频进行配置
            var userMediaConstraints = {
              audio: false, // 是否获取音频
              video: {
                facingMode: 'environment'  // 环境表示后置摄像头,使用user表示采用前置
              }
            }
            // 这里就采用新的方法来获取视频
            navigator.mediaDevices.getUserMedia(userMediaConstraints).then(function success(stream) {
              video.srcObject = stream;
     
            }).catch(function (error) {
              alert(error.name + error.message)
            });
          }
        }).catch(function(error) {
          alert(error.name + error.message)
        })
       })();
      </script>
    </body>
    
    </html>

      最后提供一个将视频全屏显示的方法,对于PC和要求不高的手机已经可以实现全屏

    * {
        margin: 0;
    }
    body {
       overflow: hidden;
    }
    #video {
        min-width: 100%;
        min-height: 100%;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

      在手机上,这个还有一点点横向滚动条,要想完全去掉,需要改动html,让video自动适用全屏的div

      html

    <div id="wrapper">
      <video id="video" autoplay playsinline></video>
    </div>

      css

    * {
      margin: 0;
    }
    
    #wrapper {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      overflow: hidden;
    }
  • 相关阅读:
    遗传算法中适值函数的标定与大变异算法
    遗传算法中几种不同选择算子及Python实现
    BZOJ_4025_二分图_线段树按时间分治+并查集
    BZOJ_1818_[Cqoi2010]内部白点 _扫描线+树状数组
    BZOJ_3165_[Heoi2013]Segment_线段树
    UOJ_21_【UR #1】缩进优化_数学
    UOJ_14_【UER #1】DZY Loves Graph_并查集
    BZOJ_5359_[Lydsy1805月赛]寻宝游戏_DP
    BZOJ_2813_奇妙的Fibonacci_线性筛
    51nod_1236_序列求和 V3 _组合数学
  • 原文地址:https://www.cnblogs.com/zzmiaow/p/9033894.html
Copyright © 2011-2022 走看看