js 如何调用摄像头拍照

问题:

//web rtc 调用摄像头(兼容性写法(谷歌、火狐、ie))

navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;

//调用成功会回调返回一个stream流 video:true 表示采集视频,audio:true 表示采集声音,反之就都不采集。

navigator.getUserMedia({video:true,audio:false},function(stream){

//将采集到的视频信息显示在video标签中

video.srcObject = stream;

},console.log)
原文链接:https://blog.csdn.net/xiehuanbin/article/details/131512316

 navigator.mediaDevices is undefined 会报错,得知因为加密的安全性问题:

目前查到的解决办法如下:

1. 使用https协议,需要域名

2. 手动修改本机浏览器配置【chrome://flags/ 】,多个地址用【,】隔开,修改后底部会提示重启浏览器

 3.localhost 域

本地文件以file 形式打开时
其他情况下你在浏览器里log这个API都是返回undefined.
如果想要 HTTP 环境下也能使用和调试 MediaDevices.getUserMedia(),通过开启 Chrome 的相应参数,也是可以实现的。

传递相应参数来启动 Chrome,-unsafely-treat-insecure-origin-as-secure="http://example.com"

链接:https://www.jianshu.com/p/b59ac832a00a

1.第一种 

业务逻辑需要人脸验证,需要通过调用摄像头获取人脸来调用接口做对比,所以学习了一下js关于调用摄像头拍照。主要通过video调用摄像头和canvas截取画面。

<!DOCTYPE html>
<html lang="en">
 
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
 
<body>
    <video width="500" height="500" autoplay class="video"></video>
    <canvas width="500" height="500"></canvas>
    <button onclick="openx()">调用摄像头</button>
    <button onclick="pho()">拍照</button>
    <button onclick="exit()">关闭摄像头</button>
</body>
 
</html>
<script>
    let video = document.querySelector('.video');
    let canvas = document.querySelector('canvas');
    function openx() {
        let constraints = {
            video: {            //这里是摄像头的信息
                height: 500,
                width: 500
            },
        // audio: true,  //是否开启麦克风
        }
        let isok = navigator.mediaDevices.getUserMedia(constraints); //这里主要是用于请求用户打开摄像头的权限
        isok.then(res => {     //可以看出是使用promise封装的 那么我们就可以使用then和catch
            video.srcObject = res;    //用户允许时 将摄像头对象的画面转移到video上面
            video.play();                //打开video的画面
        }).catch((err) => {
            console.log(err)            //拒绝时打印错误信息
        })
    }
    function pho() {
        canvas.getContext("2d").drawImage(video, 0, 0, 300, 300);   //第一个参数为要截取的dom对象,第二个和第三个为xy轴的偏移值    3-4为截取图像的大小
    }
    function exit() {
        video.srcObject.getTracks()[0].stop();   //这里如果打开了麦克风、getTracks是一个数组,我们同样需要获取下标[1]来关闭摄像头 打开麦克风[0]就是麦克风
    }
</script>
View Code

上述代码看起来并不多,包含了打开-截取图像-关闭 摄像头的功能,足以满足功能需求。

https://blog.csdn.net/m0_72436362/article/details/128321359

外接摄像头没错,笔记本的摄像头报错,放form标签里有问题,不显示

$("img").css("src", canvas.toDataURL("image/png"));

2.第二种,笔记本的摄像头打开没报错

 

<!DOCTYPE html>
<html lang="ZH-CN">
<head>
  <meta charset="utf-8">
  <title>web RTC 测试</title>
  <style>
    .booth {
      width:400px;
     
      background:#ccc;
      border: 10px solid #ddd;
      margin: 0 auto;
    }
  </style>
</head>
<body>
    <article>
      <section>
        <video id="video"></video>
      </section>
      <section>
        <audio id="audio"></audio>
      </section>
      <button id="btn">拍照</button>
      <section>
        <canvas id="canvas"></canvas>
      </section>
      <section><img src="" alt="" id="img"></section>
    </article>

    <article>
      <header>相关知识</header>
      <h2>获取用户媒体:</h2>
      <p>
        <code>navigator.mediaDevices.getUserMedia({video: true, audio: true})  // 异步操作</code>
      </p>

      <h2>枚举媒体数:video,audio输入输出设备</h2>
      <p><code>navigator.mediaDevices.enumerateDevices()  // 异步操作</code></p>
    </article>
  <script>
    let convas = document.querySelector("#canvas"); //
    let video = document.querySelector("#video");
    let audio = document.querySelector("audio");
    let img = document.querySelector("#img");
    let btn = document.querySelector("button");
    let context = canvas.getContext('2d');
    let width = 320; //视频和canvas的宽度
    let height = 0; //
    let streaming = false; // 是否开始捕获媒体
    
    // 获取用户媒体,包含视频和音频
    navigator.mediaDevices.getUserMedia({video: true, audio: true})
      .then(stream => {
      video.srcObject = stream; // 将捕获的视频流传递给video  放弃window.URL.createObjectURL(stream)的使用
      video.play(); //  播放视频
      audio.srcObject = stream;
      audio.play();
    });
    
    
    function tackcapture() {
      // 需要判断媒体流是否就绪
      if(streaming){
          context.drawImage(video, 0, 0, 350, 200);// 将视频画面捕捉后绘制到canvas里面
          img.src = canvas.toDataURL('image/png');// 将canvas的数据传送到img里
      }
    
    }
    
    btn.addEventListener('click',tackcapture,false); // 按钮点击事件
    
    // 监听视频流就位事件,即视频可以播放了
    video.addEventListener('canplay', function(ev){
          if (!streaming) {
            height = video.videoHeight / (video.videoWidth/width);
          
            video.setAttribute('width', width);
            video.setAttribute('height', height);
            canvas.setAttribute('width', width);
            canvas.setAttribute('height', height);
            streaming = true;
          }
        }, false);
  </script>
</body>
</html>
View Code
  1. 有些浏览器可能不支持此功能
  2. 必须通过服务器打开页面,通过files://打开无效
  3. 如果通过远程服务器打开则必须是https协议, http协议也无法使用

转:https://www.cnblogs.com/scarecrowlxb/p/6804747.html

3.第三种

<script src="https://cdn.bootcss.com/zepto/1.2.0/zepto.min.js"></script>

 

<!DOCTYPE html>
<html lang="zh">
  <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>使用js调用设备摄像头并实现拍照</title>
    <script src="https://cdn.bootcss.com/zepto/1.2.0/zepto.min.js"></script>
    <style type="text/css">
      * {
        margin: 0;
        padding: 0;
      }
      video {
        width: 200px;
      }
      button {
        width: 100px;
        height: 60px;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <video src=""></video> <button class="shot">拍照</button>
      <canvas id="canvas"></canvas> <img src="" />
    </div>
    <a href="https://codesandbox.io/s/811w1z2xwj">点击编辑</a>

    <script type="text/javascript">
      // 视频大小
      var constraints = { audio: true, video: { width: 200, height: 250 } };
      // 开启视频
      navigator.mediaDevices
        .getUserMedia(constraints)
        .then(function(mediaStream) {
          console.log("getUserMedia:", mediaStream);
          var video = document.querySelector("video");
          video.srcObject = mediaStream;
          video.onloadedmetadata = function(e) {
            video.play();
          };

          // 使用canvas进行拍照
          var canvas = document.getElementById("canvas");
          $("button").on("click", function() {
            canvas.getContext("2d").drawImage(video, 0, 0, 200, 250);
            $("img").css("src", canvas.toDataURL("image/png"));
          });
        })
        .catch(function(err) {
          console.log(err.name + ": " + err.message);
        });
    </script>
    
</body>
</html>
View Code

 

转:http://www.taodudu.cc/news/show-6195164.html?action=onClick

https://811w1z2xwj.codesandbox.io/

4.第四种

<!doctype html>
<html lang="en">

<head>
    <title>js调用摄像头拍照上传图片</title>
    <meta charset="utf-8">
</head>

<body>
    <button onclick="openMedia()">开启摄像头</button>
    <video id="video" width="500px" height="500px" autoplay="autoplay"></video>
    <canvas id="canvas" width="500px" height="500px"></canvas>
    <button onclick="takePhoto()">拍照</button>
    <img id="imgTag" src="" alt="imgTag">
    <button onclick="closeMedia()">关闭摄像头</button>
    <script>
        let mediaStreamTrack = null; // 视频对象(全局)
        function openMedia() {
            let constraints = {
                video: {
                    width: 500,
                    height: 500
                },
                audio: true
            };
            //获得video摄像头
            let video = document.getElementById('video');
            let promise = navigator.mediaDevices.getUserMedia(constraints);
            promise.then((mediaStream) => {
                mediaStreamTrack = typeof mediaStream.stop === 'function' ? mediaStream : mediaStream.getTracks()[1];
                video.srcObject = mediaStream;
                video.play();
            });
        }

        // 拍照
        function takePhoto() {
            //获得Canvas对象
            let video = document.getElementById('video');
            let canvas = document.getElementById('canvas');
            let ctx = canvas.getContext('2d');
            ctx.drawImage(video, 0, 0, 500, 500);


            // toDataURL  ---  可传入'image/png'---默认, 'image/jpeg'
            let img = document.getElementById('canvas').toDataURL();
            // 这里的img就是得到的图片
            console.log('img-----', img);
            document.getElementById('imgTag').src = img;
        }

        // 关闭摄像头
        function closeMedia() {
            mediaStreamTrack.stop();
        }
    </script>
</body>
View Code

 

5. 调用用户的摄像头

调用电脑摄像头功能,经过在网上查找资料有以下三种方案实现。

  1. 通过浏览器API来getUserMedia来实现调用用户的摄像头,但有两点限制:部署到生产服务的时候要使用htts协议访问该项目,第二点限制就是第一次获取摄像头时要点击允许弹窗。
  2. 通过web端发送websocket请求给客户端,然后客户端调用摄像头应用程序,但每次初始打开摄像头应用时都有个初始化2~3秒左右,用户体验不免友好。
  3. 通过客户端获取摄像头的每一帧数据,并通过websocket返回给web端,具体步骤下面有代码讲解。
getUserMedia方式实现代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>调用摄像头录像</title>
  </head>
  <body>
    <video id="video" width="640" height="480" autoplay></video>
    <button id="startRecord">开始录制</button>
    <button id="stopRecord">停止录制</button>
    <script>
      var video = document.querySelector('#video');
      var startRecord = document.querySelector('#startRecord');
      var stopRecord = document.querySelector('#stopRecord');
      var mediaRecorder;
      var chunks = [];

      navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then(function (stream) {
        video.srcObject = stream;
        mediaRecorder = new MediaRecorder(stream);
        mediaRecorder.ondataavailable = function (e) {
          chunks.push(e.data);
        };
        mediaRecorder.onstop = function (e) {
          var blob = new Blob(chunks, { type: 'video/mp4' });
          chunks = [];
          var videoURL = window.URL.createObjectURL(blob);
          video.src = videoURL;
        };
      });
      startRecord.onclick = function () {
        mediaRecorder.start();
      };
      stopRecord.onclick = function () {
        mediaRecorder.stop();
      };
    </script>
  </body>
</html>
View Code
客户端返回视频流方式实现代码

vue前端代码
前端接收到H264视频流,需要用JMuxer来转码才能在video标签里播放,需要执行npm install JMuxer来安装这个库。详细代码如下:

<template>
  <video id="player" :controls="false" :muted="false" :autoplay="true"></video>
</template>
<script setup>
  import JMuxer from 'jmuxer';
  import { onMounted, onBeforeUnmount} from 'vue';
  var ws = null;
  var jmuxer = null;
  const list = reactive([]);
  const isDisable = ref(false);

  onMounted(() => {
    jmuxer = new JMuxer({
      node: 'player',
      // 可用值是:video、audio
      mode: 'video',
      // 最大延迟时间(毫秒), 默认为值是500毫秒
      maxDelay: 100,
      // 缓冲区刷新时间,默认为值是500毫秒
      flushingTime: 0,
      // 是否会自动清除播放的媒体缓冲区。默认为true
      clearBuffer: true,
      // 可选值。视频的帧率,如果它是已知的或固定值。如果所提供的媒体数据中没有块持续时间,它将用于查找帧持续时间。
      fps: 30,
      // 将从MP4轨道数据读取FPS,而不是使用(以上)FPS值。默认为false。
      readFpsFromTrack: false,
      // 将在浏览器控制台打印调试日志。默认为false
      debug: false,
      // 遇到任何丢失的视频帧将会被触发
      onMissingVideoFrames: function (data) {
        console.log('丢失的视频帧');
      },
      onError: function (data) {
        if (/Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor)) {
          jmuxer.reset();
        }
      }
    });
    ws = new WebSocket('ws://127.0.0.1:12797');
    ws.binaryType = 'arraybuffer';
    ws.onmessage = function (event) {
      jmuxer.feed({ video: new Uint8Array(event.data) });
    };
    ws.onopen = function () {
      console.log('已连接');
      ws.send('{"interfaceId":"100002","command":"1"}');
    };
    ws.onerror = function (err) {
      console.log('出错--Socket Error', err);
    };
    ws.onclose = function () {
      console.log('断开');
    };
  });
// 实现拍照功能
  const toScan = () => {
      const videoEl = document.getElementById('player');
      const canvas = document.createElement('canvas');
      canvas.width = videoEl.videoWidth;
      canvas.height = videoEl.videoHeight;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(videoEl, 0, 0, videoEl.videoWidth, videoEl.videoHeight);
      console.log(canvas.toDataURL('image/png'));
  };

  onBeforeUnmount(() => {
    console.info('离开页面,关闭高拍仪');
    ws.close();
    jmuxer.destroy();
  });
</script>
View Code

 

链接:https://www.jianshu.com/p/e929fe5f8c35
6.这个里面比较多
https://blog.csdn.net/Nine_91/article/details/126173990?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0-126173990-blog-80924677.235^v38^pc_relevant_default_base3&spm=1001.2101.3001.4242.1&utm_relevant_index=3

 

 

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>html5拍照</title>
<style type="text/css">
body{overflow-y:auto;overflow-x:auto;margin:0;}
#cameraBtn,#cameraDiv{padding:5px;}
.big-btn-blue{ display:inline-block; min-width:80px; height:30px; margin:0 5px; padding:0 15px; vertical-align:top; line-height:30px; text-align:center; font-size:14px; font-family: "微软雅黑";
 color:#fff; border-radius:2px; box-sizing:border-box; -moz-box-sizing:border-box; -webkit-box-sizing:border-box; cursor:pointer; }
.big-btn-blue{ -webkit-transition: all 0.3s ease; -moz-transition: all 0.3s ease; transition: all 0.3s ease;}/*动画*/
.big-btn-blue{ border:1px solid #3194dd; background-color:#3194dd;}/*纯蓝色*/
</style>
<script type="text/javascript">
//访问用户媒体设备的兼容方法
function getUserMedia(constrains,success,error){
    if(navigator.mediaDevices.getUserMedia){
        //最新标准API
        navigator.mediaDevices.getUserMedia(constrains).then(success).catch(error);
    } else if (navigator.webkitGetUserMedia){
        //webkit内核浏览器
        navigator.webkitGetUserMedia(constrains).then(success).catch(error);
    } else if (navigator.mozGetUserMedia){
        //Firefox浏览器
        navagator.mozGetUserMedia(constrains).then(success).catch(error);
    } else if (navigator.getUserMedia){
        //旧版API
        navigator.getUserMedia(constrains).then(success).catch(error);
    }else{
        alert("不支持的浏览器");
    }
}
//成功的回调函数
function success(stream){
    //兼容webkit内核浏览器
    var CompatibleURL = window.URL || window.webkitURL;
    //将视频流设置为video元素的源
    try {
        video.srcObject = stream;
    } catch (e) {
        video.src = CompatibleURL.createObjectURL(stream);
    }
    //播放视频
    video.play();
}
//异常的回调函数
function error(error){
   alert("访问用户媒体设备失败,"+error.name+""+error.message);
}

/**
 * 获取当前静态页面的参数
 * 返回值和使用方法类似java request的getparamater
 * 不同: 当取得的为数组(length>1)时调用toString()返回(逗号分隔每个元素)
 * @param {Object} name
 * @return {TypeName} 
 */
function getPara(name,search){
    var p = getParas(name,search);
    if(p.length==0){
        return null;
    }else if(p.length==1){
        return p[0];
    }else{
        return p.toString();
    }
}

/**获取当前静态页面的参数
 * 返回值和使用方法类似java request的getparamaterValues
 * @param {Object} name 要取出的参数名,可以在参数字符串中重复出现
 * @param {Object} search 手工指定要解析的参数字符串,默认为当前页面后跟的参数
 * @return {TypeName} 
 */
function getParas(name,search){
    if(!search){
        search = window.location.search.substr(1);//1.html?a=1&b=2
    }
    var para = [];
    var pairs = search.split("&");//a=1&b=2&b=2&c=2&b=2
    for(var i=0;i<pairs.length;i=i+1){
        var sign = pairs[i].indexOf("="); 
        if(sign == -1){//如果没有找到=号,那么就跳过,跳到下一个字符串(下一个循环)。    
            continue; 
        }
        var aKey = pairs[i].substring(0,sign);
        if(aKey==name){
            para.push(unescape(pairs[i].substring(sign+1)));
        }
    }
    return para;
}
//开启摄像头
function captureInit(){
    if ((navigator.mediaDevices!=undefined && navigator.mediaDevices.getUserMedia!=undefined) 
            || navigator.getUserMedia!=undefined
            || navigator.webkitGetUserMedia!=undefined
            || navigator.mozGetUserMedia!=undefined){
        document.getElementById("help").style.display="none";
        //调用用户媒体设备,访问摄像头,改为触发事件
        getUserMedia({video:{width:imgWidth,height:imgHeight}},success,error);
        if(captureState==0){
            captureState=1;//标记此按钮已点击
        }
    } else {
        captureState=0;//异常标识按钮没点
        alert("你的浏览器不支持访问用户媒体设备或访问地址不是https开头,您可以点击右侧下载解决方案");
        document.getElementById("help").style.display="inline-block";
    }
}
//注册拍照按钮的单击事件
function capture(){
    //绘制画面
    if(captureState==0){
        alert("请先开启摄像头");
        return;
    }
    context.drawImage(video,0,0,imgWidth,imgHeight);//后面两个长宽
    //canvas.toDataURL("image/png");//即可得到base64编码
    captureState=2;
}
//确认按钮返回给父页面的函数
function queren(){
    if(captureState!=2){
        alert("请先开启摄像头并拍照");
        return;
    }
    var base64=canvas.toDataURL("image/jpeg");
    var pics={};
    pics.filetypeid=filetypeid;//返回给前端
    pics.base64=base64;
    if(window.opener){
        window.opener[cb](pics);// /FileUploadTmp/为项目临时文件夹相对路径
        window.close();
    }else if(window.parent){
        window.parent[cb](pics);
        window.parent.$("#dialog_ifr_html").dialog("close");//close会导致flash未执行完就销毁,页面JS报错
    }else{
        window.close();
    }
}
</script>
</head>

<body>
<div id="cameraBtn">
    <input type="button" id="init" onclick="captureInit()" value="开启摄像头"/>
    <input type="button" id="capture" onclick="capture()" value="拍照"/>
    <input type="button" id="queren" onclick="queren()" value="确定"/>
    <span id="help" style="display:none;"><a href="/static/ad/down/camera.doc">点此下载无法拍照的解决方案</a></span>
</div>
<div id="cameraDiv">
    <!-- 视频流 -->
      <video id="video" autoplay style="width: 300px;height: 200px"></video>
     <!--描绘video截图-->
    <canvas id="canvas" width="300" height="200"></canvas>
</div>
<script type="text/javascript">
var cb=getPara("cb")||"setImg";
var filetypeid=getPara("filetypeid")||"filetypeid";//附件类型id
var video=document.getElementById("video");
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
var imgWidth=getPara("width")||"300";//这个值div的宽一致
var imgHeight=getPara("height")||"200";//这个值div的高一致
var captureState=0;//未开启,1已开启,2已拍照,为2才可点击确认按钮
var style = getPara("style")||"big-btn-blue";
video.style.width=imgWidth;
video.style.height=imgHeight;
var st = style.split(",");
document.getElementById("init").className=st[0];
document.getElementById("capture").className=st[1]||st[0];
document.getElementById("queren").className=st[2]||st[0];
document.getElementById("help").className=st[3]||st[0];
</script>
</body>
</html>
View Code

 

posted @ 2023-08-15 13:37  bxzjzg  阅读(1468)  评论(0编辑  收藏  举报