HTML5透過navigator.mediaDevices.getUserMedia呼叫手機攝像頭問題

行后知發表於2020-05-21

文章主要介紹了HTML5透過navigator.mediaDevices.getUserMedia呼叫手機攝像頭問題,本文透過例項程式碼給大家介紹的非常詳細,需要的朋友可以參考下

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呼叫攝像頭。

成功呼叫後用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呼叫 攝像頭內容請搜尋以前的文章或繼續瀏覽下面的相關文章

相關文章