欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-04 11:06:18
这篇文章主要介绍了HTML5通过navigator.mediaDevices.getUserMedia调用手机摄像头问题,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 20-04-27...

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

如果只是一部手机可以这样,但是测试了多部手机发现摄像头数组毫无规律可循,这个方法慎用。
如果页面上添加选择摄像设备的按钮的话,这个方法还是不错的。查看设备能调用几个摄像头链接如下:
由于我们的项目页面不希望出现切换按钮,面对后置出现的众多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调用 摄像头内容请搜索以前的文章或继续浏览下面的相关文章,希望大家以后多多支持!