class Webcam { constructor(webcamElement, facingMode = 'user', canvasElement = null, snapSoundElement = null) { this._webcamElement = webcamElement; // this._webcamElement.width = this._webcamElement.width || 640; // this._webcamElement.height = this._webcamElement.height || this._webcamElement.width * (3 / 4); this._facingMode = facingMode; this._webcamList = []; this._streamList = []; this._selectedDeviceId = ''; this._canvasElement = canvasElement; this._snapSoundElement = snapSoundElement; this._webcamIndex = 0; } get facingMode(){ return this._facingMode; } set facingMode(value){ this._facingMode = value; } get webcamList(){ return this._webcamList; } get webcamCount(){ return this._webcamList.length; } get selectedDeviceId(){ return this._selectedDeviceId; } /* Get all video input devices info */ getVideoInputs(mediaDevices){ this._webcamList = []; mediaDevices.forEach(mediaDevice => { if (mediaDevice.kind === 'videoinput') { this._webcamList.push(mediaDevice); } }); if(this._webcamList.length == 1){ this._facingMode = 'user'; } return this._webcamList; } /* Get media constraints */ getMediaConstraints() { var videoConstraints = {}; if (this._selectedDeviceId == '') { videoConstraints.facingMode = this._facingMode; } else { videoConstraints.deviceId = { exact: this._selectedDeviceId}; } var constraints = { video: videoConstraints, audio: false }; console.log('mediaConstraints:', constraints); console.log('selectedDeviceId:', this._selectedDeviceId); console.log('webcamIndex:', this._webcamIndex); return constraints; } /* Select camera based on facingMode */ selectCamera(){ for(let webcam of this._webcamList){ if( (this._facingMode=='user' && (webcam.label.toLowerCase().includes('front') || webcam.label.toLowerCase().includes('前置'))) || (this._facingMode=='enviroment' && (webcam.label.toLowerCase().includes('back') || webcam.label.toLowerCase().includes('後置'))) ) { this._selectedDeviceId = webcam.deviceId; break; } } } /* Change Facing mode and selected camera */ flip(){ const curremtDeviceId = this._selectedDeviceId; this._facingMode = (this._facingMode == 'user')? 'enviroment': 'user'; this._webcamElement.style.transform = ""; this.selectCamera(); if (curremtDeviceId !== this._selectedDeviceId) return; if (this._webcamList.filter(item => item.label === '').length === 0) return; console.log('webcam.label is empty!!!'); if (!this._selectedDeviceId) { this._selectedDeviceId = this._webcamList[0].deviceId; this._webcamIndex = 0; } let index = 0 if (this._webcamList.length >= 4 && this._webcamList.length % 2 === 0) { index = this._webcamIndex + 2; } else { index = this._webcamIndex + 1; } if (index > this._webcamList.length - 1) { index = 0; } this._selectedDeviceId = this._webcamList[index].deviceId; this._webcamIndex = index; } /* 1. Get permission from user 2. Get all video input devices info 3. Select camera based on facingMode 4. Start stream */ async start(startStream = true) { return new Promise((resolve, reject) => { this.stop(); if (navigator.mediaDevices === undefined || navigator.mediaDevices.enumerateDevices === undefined || navigator.mediaDevices.getUserMedia === undefined) { if (navigator.mediaDevices === undefined) { var fctName = 'navigator.mediaDevices'; } else if (navigator.mediaDevices.enumerateDevices === undefined) { var fctName = 'navigator.mediaDevices.enumerateDevices'; } else if (navigator.mediaDevices.getUserMedia === undefined) { var fctName = 'navigator.mediaDevices.getUserMedia'; } else { console.assert(false); } reject('WebRTC issue-! ' + fctName + ' not present in your browser'); console.log('navigator:', navigator); return; } console.log('webcam','getUserMedia'); navigator.mediaDevices.getUserMedia(this.getMediaConstraints()) //get permisson from user .then(stream => { this._streamList.push(stream); this.info() //get all video input devices info .then(webcams =>{ console.log("webcamList:", this._webcamList); this.selectCamera(); //select camera based on facingMode if(startStream){ console.log('webcam','startStream'); this.stream() .then(facingMode =>{ resolve(this._facingMode); }) .catch(error => { reject('Stream Error\nName: ' + error.name + '\nMessage: ' + error.message); }); }else{ resolve(this._selectedDeviceId); } }) .catch(error => { reject('EnumerateDevices Error\nName: ' + error.name + '\nMessage: ' + error.message); }); }) .catch(error => { const response = { 'PermissionDeniedError': '瀏覽器禁止本頁面使用攝像頭,請開啟相關的許可權', 'NotAllowedError': '瀏覽器禁止本頁面使用攝像頭,請開啟相關的許可權', 'ConstraintNotSatisfiedError': '您的設備不支持該分辨率。', } reject('getUserMedia Error\nName: ' + error.name + '\nMessage: ' + error.message); }); }); } /* Get all video input devices info */ async info(){ return new Promise((resolve, reject) => { navigator.mediaDevices.enumerateDevices() .then(devices =>{ this.getVideoInputs(devices); resolve(this._webcamList); }) .catch(error => { reject(error); }); }); } /* Start streaming webcam to video element */ async stream() { return new Promise((resolve, reject) => { navigator.mediaDevices.getUserMedia(this.getMediaConstraints()) .then(stream => { this._streamList.push(stream); this._webcamElement.srcObject = stream; if(this._facingMode == 'user'){ this._webcamElement.style.transform = "scale(-1,1)"; } // this._webcamElement.play(); resolve(this._facingMode); }) .catch(error => { reject(error); }); }); } /* Stop streaming webcam */ stop() { this._streamList.forEach(stream => { stream.getTracks().forEach(track => { track.stop(); }); }); } snap() { return new Promise((resolve,reject ) => { if(this._canvasElement!=null){ if(this._snapSoundElement!= null){ this._snapSoundElement.play(); } this._canvasElement.width = this._webcamElement.clientWidth; this._canvasElement.height = this._webcamElement.clientHeight; let context = this._canvasElement.getContext('2d'); if(this._facingMode == 'user'){ context.translate(this._canvasElement.width, 0); context.scale(-1, 1); } context.clearRect(0, 0, this._canvasElement.width, this._canvasElement.height); context.drawImage(this._webcamElement, 0, 0, this._canvasElement.width, this._canvasElement.height); let data = this._canvasElement.toDataURL('image/png', 1.0); resolve(data); } else{ reject("Snap Error\ncanvas element is missing"); } }); } }