HTML5通過navigator.mediaDevices.getUserMedia調(diào)用手機(jī)攝像頭問題

navigator.mediaDevices.getUserMedia
應(yīng)項(xiàng)目要求,需要實(shí)現(xiàn)移動(dòng)端app嵌入H5頁(yè)面完成實(shí)人認(rèn)證的功能。打開getUserMedia文檔,鏈接如下:
https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia
看上去很簡(jiǎn)單,最終卻寫的懷疑人生。
API環(huán)境
問題一:(為什么不管怎么配置都顯示前置攝像頭)
想正常使用API必須在https環(huán)境下進(jìn)行,否則你會(huì)發(fā)現(xiàn)不管怎么寫,都只能調(diào)用默認(rèn)的攝像頭(大部分都是前置,只有少部分是后置)
前端開發(fā)者可以將文件上傳至"碼云"倉(cāng)庫(kù),獲取https鏈接然后在手機(jī)上預(yù)覽
鏈接:碼云倉(cāng)庫(kù)入口
問題二:(API在安卓和ios效果一樣嗎?)
根據(jù)官方文檔,目前navigator.mediaDevices.getUserMedia在ios上只支持11版本以上,且只能在safari正常運(yùn)行。安卓目前沒有發(fā)現(xiàn)版本限制,需要兼容的代碼如下
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); }); } }
問題三:(第一次啟用成功調(diào)用前置攝像頭,第二次需要調(diào)用后置卻黑屏或者失?。?/strong>
失敗的原因很多,列舉兩個(gè)一開始我遇到的問題
1.前置攝像頭調(diào)用后,攝像功能需要關(guān)閉后才能正常執(zhí)行第二次調(diào)用,否則會(huì)報(bào)錯(cuò):設(shè)備被占用。解決方法,在每次執(zhí)行調(diào)用方法前,先關(guān)閉攝像設(shè)備。
if (window.stream) { window.stream.getTracks().forEach(track => { track.stop(); }); }
親測(cè)有用,別的找了很多停止的方法都沒有效果。
2.調(diào)用后置API的方法還是無法喚醒后置攝像頭,于是我找到另外一個(gè)方法,通過查看手機(jī)攝像頭ID,來直接喚醒后置。
var deviceInfoId="", //攝像頭ID num = 0, //攝像頭數(shù)量 carema = []; //攝像頭ID數(shù)組 //在頁(yè)面加載完成后獲得設(shè)備ID數(shù)組 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(); 這個(gè)加不加好像沒有影響 }) .catch(function (err) { console.log(err.name + ": " + err.message); });
如果只是一部手機(jī)可以這樣,但是測(cè)試了多部手機(jī)發(fā)現(xiàn)攝像頭數(shù)組毫無規(guī)律可循,這個(gè)方法慎用。
如果頁(yè)面上添加選擇攝像設(shè)備的按鈕的話,這個(gè)方法還是不錯(cuò)的。查看設(shè)備能調(diào)用幾個(gè)攝像頭鏈接如下:https://webrtc.github.io/samples/src/content/devices/input-output/
由于我們的項(xiàng)目頁(yè)面不希望出現(xiàn)切換按鈕,面對(duì)后置出現(xiàn)的眾多BUG,最終選擇放棄,使用input調(diào)用攝像頭。
<input class="card_input" v-on:change="appCapture($event)" type="file" accept="image/*" capture="camera" />
成功調(diào)用后用canvas實(shí)現(xiàn)成像并適應(yīng)屏幕大小
我這里的代碼是取video的寬高然后復(fù)制給canvas,這樣可以讓canvas和video保持一致,只用給video設(shè)置寬度100%,高度調(diào)節(jié)成合適的值,就實(shí)現(xiàn)了適應(yīng)手機(jī)屏幕。
var video = document.getElementById('video'); var canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), CHeight = video.clientHeight, //獲取屏幕大小讓canvas自適應(yīng) CWidth = video.clientWidth; canvas.width = CWidth; canvas.height = CHeight; //localMediaStream 在data里定義一個(gè){} 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),可以實(shí)現(xiàn)反轉(zhuǎn),但是還是沒有達(dá)到和手機(jī)一樣的效果。
這時(shí)候可以選擇通過設(shè)備ID調(diào)用前置攝像頭,前置攝像頭的laval一直都是“default”,也有的是空值,但是也能實(shí)現(xiàn)。
配置代碼如下:
var constraints = window.constraints = { audio: false, video: { sourceId: 'default', facingMode: { exact: "user" } } };
完美調(diào)用自己手機(jī)的前置攝像頭!??!
完整代碼如下:
頁(yè)面代碼:
<div @click='moveToCameraAVG()' v-cloak> <img v-if="imginfo!==''" :src="imginfo" /> <div class="warm_title2">點(diǎn)擊自拍一張頭像</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>
// 頭像相機(jī) 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); }); }, //停止攝像機(jī) 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自適應(yīng) 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; // 停止攝像機(jī) video.pause(); this.stopCapture(); } },
到此這篇關(guān)于HTML5通過navigator.mediaDevices.getUserMedia調(diào)用手機(jī)攝像頭問題的文章就介紹到這了,更多相關(guān)HTML5調(diào)用 攝像頭內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
相關(guān)文章
- 本文給大家分享html5調(diào)用攝像頭實(shí)例代碼,代碼簡(jiǎn)單易懂,對(duì)前端html5的學(xué)習(xí)有很大幫助,如果對(duì)html5調(diào)用攝像頭相關(guān)知識(shí)感興趣的朋友一起看看吧2021-06-28
Html5調(diào)用手機(jī)攝像頭并實(shí)現(xiàn)人臉識(shí)別的實(shí)現(xiàn)
這篇文章主要介紹了Html5調(diào)用手機(jī)攝像頭并實(shí)現(xiàn)人臉識(shí)別的實(shí)現(xiàn),混合App開發(fā),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-21html5調(diào)用攝像頭功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了html5調(diào)用攝像頭功能的實(shí)現(xiàn)代碼的相關(guān)資料,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-07- 根據(jù)需求我們可以把圖片數(shù)據(jù)轉(zhuǎn)換為流或二進(jìn)制,我這里轉(zhuǎn)換為base64,拿到了數(shù)據(jù)就可以發(fā)揮想象了,tensorflow,機(jī)器學(xué)習(xí),模式識(shí)別,大把的應(yīng)用場(chǎng)景。下面給大家介紹下html2021-12-28