HTML5通过navigator.mediaDevices.getUserMedia调用手机摄像头问题


Posted in HTML / CSS onApril 27, 2020

navigator.mediaDevices.getUserMedia

应项目要求,需要实现移动端app嵌入H5页面完成实人认证的功能。打开getUserMedia文档,链接如下:
https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
看上去很简单,最终却写的怀疑人生。

API环境

问题一:(为什么不管怎么配置都显示前置摄像头)

想正常使用API必须在https环境下进行,否则你会发现不管怎么写,都只能调用默认的摄像头(大部分都是前置,只有少部分是后置)
前端开发者可以将文件上传至"码云"仓库,获取https链接然后在手机上预览
链接:码云仓库入口

问题二:(API在安卓和ios效果一样吗?)

根据官方文档,目前navigator.mediaDevices.getUserMedia在ios上只支持11版本以上,且只能在safari正常运行。安卓目前没有发现版本限制,需要兼容的代码如下

if (navigator.mediaDevices === undefined) {
    navigator.mediaDevices = {};
 }
if (navigator.mediaDevices.getUserMedia === undefined) {
    navigator.mediaDevices.getUserMedia = function (constraints) {
    var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
	if (!getUserMedia) {
	    return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
	}
	return new Promise(function (resolve, reject) {
		getUserMedia.call(navigator, constraints, resolve, reject);
	});
  }
}

问题三:(第一次启用成功调用前置摄像头,第二次需要调用后置却黑屏或者失败)

失败的原因很多,列举两个一开始我遇到的问题
1.前置摄像头调用后,摄像功能需要关闭后才能正常执行第二次调用,否则会报错:设备被占用。解决方法,在每次执行调用方法前,先关闭摄像设备。

if (window.stream) {
    window.stream.getTracks().forEach(track => {
          track.stop();
    });
}

亲测有用,别的找了很多停止的方法都没有效果。
2.调用后置API的方法还是无法唤醒后置摄像头,于是我找到另外一个方法,通过查看手机摄像头ID,来直接唤醒后置。

var deviceInfoId="", //摄像头ID
    num = 0, //摄像头数量
    carema = []; //摄像头ID数组
    //在页面加载完成后获得设备ID数组
window.onload = navigator.mediaDevices.enumerateDevices().then(gotDevices);
function gotDevices(deviceInfos) {
        for (let i = 0; i < deviceInfos.length; ++i) {
            if (deviceInfos[i].kind === 'videoinput') {
                carema.push(deviceInfos[i].deviceId)
              }
        }
        deviceInfoId = carema[后置位置];
}
            var constraints = {
                audio: false,
                video: {
                    deviceId: deviceInfoId,
                    //放在app里面需要下面配置一下
                    "permissions": {
                        "audio-capture": {
                            "description": "Required to capture audio using getUserMedia()"
                        },
                        "video-capture": {
                            "description": "Required to capture video using getUserMedia()"
                        }
                    }
                }
            };
            navigator.mediaDevices.getUserMedia(constraints)
                .then(function (stream) {
                    var video = document.getElementById('video');
                    try {
                        window.stream = stream;
                        video.srcObject = stream;
                    } catch (error) {
                        video.src = window.URL.createObjectURL(stream);
                    }
                    this.localMediaStream = stream;
                    // video.play();   这个加不加好像没有影响
                })
                .catch(function (err) {
                    console.log(err.name + ": " + err.message);
                });

如果只是一部手机可以这样,但是测试了多部手机发现摄像头数组毫无规律可循,这个方法慎用。
如果页面上添加选择摄像设备的按钮的话,这个方法还是不错的。查看设备能调用几个摄像头链接如下:https://webrtc.github.io/samples/src/content/devices/input-output/
由于我们的项目页面不希望出现切换按钮,面对后置出现的众多BUG,最终选择放弃,使用input调用摄像头。

<input class="card_input" v-on:change="appCapture($event)" type="file" accept="image/*" capture="camera" />

成功调用后用canvas实现成像并适应屏幕大小

我这里的代码是取video的宽高然后复制给canvas,这样可以让canvas和video保持一致,只用给video设置宽度100%,高度调节成合适的值,就实现了适应手机屏幕。

var video = document.getElementById('video');
            var canvas = document.getElementById('canvas'),
                ctx = canvas.getContext('2d'),
                CHeight = video.clientHeight, //获取屏幕大小让canvas自适应
                CWidth = video.clientWidth;
            canvas.width = CWidth;
            canvas.height = CHeight;
            //localMediaStream 在data里定义一个{}
            if (localMediaStream) {
                ctx.drawImage(video, 0, 0, CWidth, CHeight);
                var dataURL = canvas.toDataURL('image/jpeg'); //dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA'
                img.src = dataURL;

video成像镜像问题

API唤醒的前置摄像头是相反的,很不舒服很不舒服。
之后用css处理一下给video添加 transform: rotate(180deg),可以实现反转,但是还是没有达到和手机一样的效果。
这时候可以选择通过设备ID调用前置摄像头,前置摄像头的laval一直都是“default”,也有的是空值,但是也能实现。
配置代码如下:

var constraints = window.constraints = {
                audio: false,
                video: {
                    sourceId: 'default',
                    facingMode:  { exact: "user" } 
                }
              };

完美调用自己手机的前置摄像头!!!

完整代码如下:
页面代码:

<div @click='moveToCameraAVG()' v-cloak>
     <img v-if="imginfo!==''" :src="imginfo" />
     <div class="warm_title2">点击自拍一张头像</div>
</div>
<video id="video" class="pic_video" playsinline autoplay x5-video-player-type="h5" style='object-fit:fill'></video>
<canvas id="canvas" class="canvas_pic" style='margin: 0;padding: 0;'></canvas>
<div class="bottom_div">
    <div>拍照</div>
    <img src='images/pic_btn.png' class="capture-btn" @click='captureAvg' />
</div>
// 头像相机
        moveToCameraAVG() {
            var self = this;
            if (navigator.mediaDevices === undefined) {
                navigator.mediaDevices = {};
            }
            if (navigator.mediaDevices.getUserMedia === undefined) {
                navigator.mediaDevices.getUserMedia = function (constraints) {
                    var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || navigator.oGetUserMedia;
                    if (!getUserMedia) {
                        return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
                    }
                    return new Promise(function (resolve, reject) {
                        getUserMedia.call(navigator, constraints, resolve, reject);
                    });
                }
            }
            if (window.stream) {
                window.stream.getTracks().forEach(track => {
                    track.stop();
                });
            }
            var constraints = window.constraints = {
                audio: false,
                video: {
                    sourceId: 'default',
                    facingMode:  { exact: "user" } 
                }
              };
            navigator.mediaDevices.getUserMedia(constraints)
                .then(function (stream) {
                    var video = document.getElementById('video');
                    try {
                        window.stream = stream;
                        video.srcObject = stream;
                    } catch (error) {
                        video.src = window.URL.createObjectURL(stream);
                    }
                    self.localMediaStream = stream;
                    video.play();
                })
                .catch(function (err) {
                    alert(err.name + ": " + err.message);
                });
        },
        //停止摄像机
        stopCapture: function () {
            var video = document.getElementById('video');
            if (!video.srcObject) return
            let stream = video.srcObject
            let tracks = stream.getTracks();
            tracks.forEach(track => {
                track.stop()
            })
        },
        // 头像照片
        captureAvg() {
            var vm = this;
            var video = document.getElementById('video');
            var canvas = document.getElementById('canvas'),
                ctx = canvas.getContext('2d'),
                CHeight = video.clientHeight, //获取屏幕大小让canvas自适应
                CWidth = video.clientWidth;
            canvas.width = CWidth;
            canvas.height = CHeight;
            if (vm.localMediaStream) {
                ctx.drawImage(video, 0, 0, CWidth, CHeight);
                var dataURL = canvas.toDataURL('image/jpeg'); //dataURL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA'
                vm.imginfo = dataURL;
                // 停止摄像机
                video.pause();
                this.stopCapture();
            }
        },

到此这篇关于HTML5通过navigator.mediaDevices.getUserMedia调用手机摄像头问题的文章就介绍到这了,更多相关HTML5调用 摄像头内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章,希望大家以后多多支持三水点靠木!

HTML / CSS 相关文章推荐
纯css3无js实现的Android Logo(有简单动画)
Jan 21 HTML / CSS
css 省略号 css3让多余的字符串消失并附加省略号的实现代码
Feb 07 HTML / CSS
一款纯css3实现的竖形二级导航的实例教程
Dec 11 HTML / CSS
css3实现3d旋转动画特效
Mar 10 HTML / CSS
基于css3 animate制作绚丽的动画效果
Nov 24 HTML / CSS
CSS3常用的几种颜色渐变模式总结
Nov 18 HTML / CSS
CSS 说明横向进度条最后显示文字的实现代码
Nov 10 HTML / CSS
HTML5之SVG 2D入门9—蒙板及mask元素介绍与应用
Jan 30 HTML / CSS
html5+css3气泡组件的实现
Nov 21 HTML / CSS
LocalStorage记住用户和密码功能
Jul 24 HTML / CSS
利用html+css实现菜单栏缓慢下拉效果的示例代码
Mar 30 HTML / CSS
HTML页面滚动时部分内容位置固定不滚动的实现
Apr 14 HTML / CSS
遮罩层 + Iframe实现界面自动显示的示例代码
Apr 26 #HTML / CSS
html5中使用hotcss.js实现手机端自适配的方法
Apr 23 #HTML / CSS
html5 canvas 实现光线沿不规则路径运动
Apr 20 #HTML / CSS
基于HTML5+tracking.js实现刷脸支付功能
Apr 16 #HTML / CSS
HTML中meta标签及Keywords
Apr 15 #HTML / CSS
详解移动端h5页面根据屏幕适配的四种方案
Apr 15 #HTML / CSS
html5移动端自适应布局的实现
Apr 15 #HTML / CSS
You might like
PHP安全性漫谈
2012/06/28 PHP
Thinkphp5行为使用方法汇总
2017/12/21 PHP
浅析PHP7的多进程及实例源码
2019/04/14 PHP
关于Laravel-admin的基础用法总结和自定义model详解
2019/10/08 PHP
laravel实现上传图片,并且制作缩略图,按照日期存放的代码
2019/10/16 PHP
php提高脚本性能的4个技巧
2020/08/18 PHP
在Javascript中为String对象添加trim,ltrim,rtrim方法
2006/09/22 Javascript
javascript中万恶的function实例分析
2011/05/25 Javascript
jquery选择器-根据多个属性选择示例代码
2013/10/21 Javascript
js采用map取到id集合组并且实现点击一行选中一行
2013/12/16 Javascript
利用js(jquery)操作Cookie的方法说明
2013/12/19 Javascript
JavaScript基础教程之alert弹出提示框实例
2014/10/16 Javascript
JS判断图片是否加载完成方法汇总(最新版)
2016/05/13 Javascript
js仿淘宝和百度文库的评分功能
2016/05/15 Javascript
React BootStrap用户体验框架快速上手
2018/03/06 Javascript
微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】
2019/02/20 Javascript
详解django模板与vue.js冲突问题
2019/07/07 Javascript
javascript面向对象程序设计实践常用知识点总结
2019/07/29 Javascript
python自定义类并使用的方法
2015/05/07 Python
详解Python中的from..import绝对导入语句
2016/06/21 Python
python实现音乐下载的统计
2018/06/20 Python
Python多进程原理与用法分析
2018/08/21 Python
python实现列表的排序方法分享
2019/07/01 Python
Visual Studio Code搭建django项目的方法步骤
2020/09/17 Python
Python尾递归优化实现代码及原理详解
2020/10/09 Python
英国最专业的健身器材供应商之一:Best Gym Equipment
2017/12/22 全球购物
英国休闲奢华的缩影:Crew Clothing
2019/05/05 全球购物
英国Flybe航空官网:欧洲最大的独立支线廉价航空公司
2019/07/15 全球购物
LORAC官网:美国彩妆品牌
2019/08/27 全球购物
WebSphere面试题:在WebSphere里面如何部署一个应用
2015/08/02 面试题
资深财务管理人员自我评价
2013/09/22 职场文书
党校培训自我鉴定范文
2014/03/20 职场文书
物业管理委托协议(2篇)
2014/09/23 职场文书
幼儿园秋季开学通知
2015/07/16 职场文书
2016母亲节感恩话语
2015/12/09 职场文书
SQL注入的实现以及防范示例详解
2021/06/02 MySQL