浏览器调用摄像头getUserMedia

浏览器调用摄像头getUserMedia

2018年启动QQ浏览器会启动摄像头的新闻闹得沸沸扬扬。
前段时间公司让研究一下这个浏览器启用摄像头的功能,希望能够实现浏览器网页扫描二维码的功能。现在写下来记录一下。
浏览器启用摄像头是使用getUserMedia这个api实现的,调用后会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。。此流可以包含一个视频轨道(来自硬件或者虚拟视频源,比如相机、视频采集设备和屏幕共享服务等等)、一个音频轨道(同样来自硬件或虚拟音频源,比如麦克风、A/D转换器等等),也可能是其它轨道类型。
有了这个api,使浏览器调用摄像头成为可能。但兼容性较差
浏览器调用摄像头getUserMedia
自己测试华为自带浏览器,以及安卓微信浏览器测试通过。图上说支持的UC在12.0.8.988版本测试过发现支持这个getUserMedia方法,也成功回调了却不能成像,而小米自带浏览器测试发现不能打开摄像,应该是小米自带浏览器有安全性检测,浏览器不允许调用摄像头。
废话不多说,上代码:
html

<select id="select"></select>
<input type="button" title="开启摄像头" value="开启摄像头" "getMedia();" />
<div>
        <video id="video" style="background:#000;" width="400" height="400"></video>
        <canvas id='canvas' style="display: none;" width='400' height='400'></canvas>
</div>

js

var video = document.getElementById('video'),       //摄像头
    canvas = document.getElementById('canvas'),     //截取图像
    select = document.getElementById("select"),     //切换摄像头
    exArray = [];                                   //存储设备源ID

//获取设备源ID navigator.getUserMedia需要
if (typeof MediaStreamTrack != 'undefined' && typeof MediaStreamTrack.getSources != 'undefined') {
    MediaStreamTrack.getSources(function (sourceInfos) {
        for (var i = 0; i != sourceInfos.length; ++i) {
            var sourceInfo = sourceInfos[i];
            //这里会遍历audio,video,所以要加以区分  
            if (sourceInfo.kind === 'video') {
                exArray.push(sourceInfo.id);
            }
        }
    });
}

function getMedia() {
    if(window.stream) {
        window.stream.getTracks().forEach(function(track) {
            track.stop();
        });
    }
    if (typeof navigator.mediaDevices == 'undefined' && typeof navigator.mediaDevices.getUserMedia == 'undefined') {
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
        if (typeof navigator.getUserMedia == 'undefined') {
            return errorFunc({name: "ERROR", message: "navigator.getUserMedia is undefined!"});            
        }
        for(var i = 0; i < exArray.length; i++) {
            var option = document.createElement('option');
            option.text = 'camera ' + (i + 1);
            option.value = exArray[i];
            select.appendChild(option);
        }
        var val = select.value;        
        navigator.getUserMedia({
            'video': {
                'optional': [{
                        'sourceId': val ? val : exArray[0]//0为前置摄像头,1为后置  
                    }]
            },
//            'video': true,
            'audio': false
        }, successFunc, errorFunc)
    } 
    else {
        navigator.mediaDevices.enumerateDevices().then(getDevice).catch(errorFunc);
        var val = select.value;
        var constraints = {
            audio: false,
            video: {deviceId: val ? {exact: val} : undefined}
//            video: {facingMode: {exact : 'environment'}}          //不起效
//            video: true
        }
        navigator.mediaDevices.getUserMedia(constraints)
            .then(function (stream) {
                successFunc(stream);
                return navigator.mediaDevices.enumerateDevices();
            }).then(getDevice)
            .catch(function (err) {
                errorFunc(err);
            });
    }
}
function getDevice(deviceInfos) {
    var val = select.value;
    while(select.firstChild) {
        select.removeChild(select.firstChild);
    }
    for(var i = 0; i !== deviceInfos.length; ++i) {
        var deviceInfo = deviceInfos[i];
        var option = document.createElement('option');
        option.value = deviceInfo.deviceId;
         if (deviceInfo.kind === 'videoinput') {
            option.text = deviceInfo.label || 'camera ' + (select.length + 1);
            select.appendChild(option);
         }
    }
    if(select.value && select.value !== "") {
        select.value = val;
    }
    
}
//开启摄像头成功回调
function successFunc(stream) {
    window.stream = stream;
    if ("srcObject" in video) {
        video.srcObject = stream;
    } else if (video.mozSrcObject !== undefined) {
//Firefox中,video.mozSrcObject最初为null,而不是未定义的,我们可以靠这个来检测Firefox的支持  
        video.mozSrcObject = stream;
    } else {
        window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
        video.src = window.URL && window.URL.createObjectURL(stream) || stream;
    }
    video.onloadedmetadata = function (e) {
        video.play();
    };
}
//开启摄像头错误回调
function errorFunc(e) {
    alert(e.name + ": " + e.message);
}

//截取图像
function screenShot() {
    canvas.getContext('2d').drawImage(video, 0, 0, 400, 400);
    var imgData = canvas.toDataURL("image/png", 0.8);
}

这是通过使用getUserMedia调用摄像后成像在canvas里,然后用canvas成图来扫描识别图片。
总结
以上代码有做兼容处理,能成像但不能放大,且不能聚焦,浏览器的支持率不高