add method for checking whether device has a camera

This commit is contained in:
Daniel committed 2019-02-05 09:32:48 -06:00
1 parent b3371bd2f1
commit 2144b169d1
5 files changed
+34 -15

No files matched your search

+6
View File
@@ -22,6 +22,9 @@
</style>
<h1>Scan from WebCam:</h1>
<div>
<b>Device has camera: </b>
<span id="cam-has-camera"></span>
<br>
<video muted playsinline id="qr-video"></video>
<canvas id="debug-canvas"></canvas>
</div>
@@ -50,6 +53,7 @@
const debugCheckbox = document.getElementById('debug-checkbox');
const debugCanvas = document.getElementById('debug-canvas');
const debugCanvasContext = debugCanvas.getContext('2d');
const camHasCamera = document.getElementById('cam-has-camera');
const camQrResult = document.getElementById('cam-qr-result');
const fileSelector = document.getElementById('file-selector');
const fileQrResult = document.getElementById('file-qr-result');
@@ -63,6 +67,8 @@
// ####### Web Cam Scanning #######
QrScanner.hasCamera().then(hasCamera => camHasCamera.textContent = hasCamera);
const scanner = new QrScanner(video, result => setResult(camQrResult, result));
scanner.start();
+15 -14
View File
@@ -1,18 +1,19 @@
/*
@asset(/libraries/qr-scanner/qr-scanner-worker.min.js) */
'use strict';export default class QrScanner{constructor(video,onDecode,canvasSize=QrScanner.DEFAULT_CANVAS_SIZE){this.$video=video;this.$canvas=document.createElement("canvas");this._onDecode=onDecode;this._active=false;this._paused=false;this.$canvas.width=canvasSize;this.$canvas.height=canvasSize;this._sourceRect={x:0,y:0,width:canvasSize,height:canvasSize};this._onCanPlay=this._onCanPlay.bind(this);this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);
this.$video.addEventListener("canplay",this._onCanPlay);this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(QrScanner.WORKER_PATH)}destroy(){this.$video.removeEventListener("canplay",this._onCanPlay);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&
!this._paused)return Promise.resolve();if(window.location.protocol!=="https:")console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=true;this._paused=false;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);this._offTimeout=null;if(this.$video.srcObject){this.$video.play();return Promise.resolve()}let facingMode="environment";return this._getCameraStream("environment",true).catch(()=>{facingMode="user";return this._getCameraStream()}).then((stream)=>
{this.$video.srcObject=stream;this._setVideoMirror(facingMode)}).catch((e)=>{this._active=false;throw e;})}stop(){this.pause();this._active=false}pause(){this._paused=true;if(!this._active)return;this.$video.pause();if(this._offTimeout)return;this._offTimeout=setTimeout(()=>{const track=this.$video.srcObject&&this.$video.srcObject.getTracks()[0];if(!track)return;track.stop();this.$video.srcObject=null;this._offTimeout=null},300)}static scanImage(imageOrFileOrUrl,sourceRect=null,worker=null,canvas=
null,fixedCanvasSize=false,alsoTryWithoutSourceRect=false){const promise=new Promise((resolve,reject)=>{if(!worker){worker=new Worker(QrScanner.WORKER_PATH);worker.postMessage({type:"inversionMode",data:"both"})}let timeout,onMessage,onError;onMessage=(event)=>{if(event.data.type!=="qrResult")return;worker.removeEventListener("message",onMessage);worker.removeEventListener("error",onError);clearTimeout(timeout);if(event.data.data!==null)resolve(event.data.data);else reject("QR code not found.")};
onError=(e)=>{worker.removeEventListener("message",onMessage);worker.removeEventListener("error",onError);clearTimeout(timeout);var errorMessage=!e?"Unknown Error":e.message||e;reject("Scanner error: "+errorMessage)};worker.addEventListener("message",onMessage);worker.addEventListener("error",onError);timeout=setTimeout(()=>onError("timeout"),3E3);QrScanner._loadImage(imageOrFileOrUrl).then((image)=>{const imageData=QrScanner._getImageData(image,sourceRect,canvas,fixedCanvasSize);worker.postMessage({type:"decode",
data:imageData},[imageData.data.buffer])}).catch(reject)});if(sourceRect&&alsoTryWithoutSourceRect)return promise.catch(()=>QrScanner.scanImage(imageOrFileOrUrl,null,worker,canvas,fixedCanvasSize));else return promise}setGrayscaleWeights(red,green,blue){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red,green,blue}})}setInversionMode(inversionMode){this._qrWorker.postMessage({type:"inversionMode",data:inversionMode})}_onCanPlay(){this._updateSourceRect();this.$video.play()}_onPlay(){this._updateSourceRect();
this._scanFrame()}_onVisibilityChange(){if(document.hidden)this.pause();else if(this._active)this.start()}_updateSourceRect(){const smallestDimension=Math.min(this.$video.videoWidth,this.$video.videoHeight);const sourceRectSize=Math.round(2/3*smallestDimension);this._sourceRect.width=this._sourceRect.height=sourceRectSize;this._sourceRect.x=(this.$video.videoWidth-sourceRectSize)/2;this._sourceRect.y=(this.$video.videoHeight-sourceRectSize)/2}_scanFrame(){if(!this._active||this.$video.paused||this.$video.ended)return false;
requestAnimationFrame(()=>{QrScanner.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,true).then(this._onDecode,(error)=>{if(this._active&&error!=="QR code not found.")console.error(error)}).then(()=>this._scanFrame())})}_getCameraStream(facingMode,exact=false){const constraintsToTry=[{width:{min:1024}},{width:{min:768}},{}];if(facingMode){if(exact)facingMode={exact:facingMode};constraintsToTry.forEach((constraint)=>constraint.facingMode=facingMode)}return this._getMatchingCameraStream(constraintsToTry)}_getMatchingCameraStream(constraintsToTry){if(constraintsToTry.length===
0)return Promise.reject("Camera not found.");return navigator.mediaDevices.getUserMedia({video:constraintsToTry.shift()}).catch(()=>this._getMatchingCameraStream(constraintsToTry))}_setVideoMirror(facingMode){const scaleFactor=facingMode==="user"?-1:1;this.$video.style.transform="scaleX("+scaleFactor+")"}static _getImageData(image,sourceRect=null,canvas=null,fixedCanvasSize=false){canvas=canvas||document.createElement("canvas");const sourceRectX=sourceRect&&sourceRect.x?sourceRect.x:0;const sourceRectY=
sourceRect&&sourceRect.y?sourceRect.y:0;const sourceRectWidth=sourceRect&&sourceRect.width?sourceRect.width:image.width||image.videoWidth;const sourceRectHeight=sourceRect&&sourceRect.height?sourceRect.height:image.height||image.videoHeight;if(!fixedCanvasSize&&(canvas.width!==sourceRectWidth||canvas.height!==sourceRectHeight)){canvas.width=sourceRectWidth;canvas.height=sourceRectHeight}const context=canvas.getContext("2d",{alpha:false});context.imageSmoothingEnabled=false;context.drawImage(image,
sourceRectX,sourceRectY,sourceRectWidth,sourceRectHeight,0,0,canvas.width,canvas.height);return context.getImageData(0,0,canvas.width,canvas.height)}static _loadImage(imageOrFileOrUrl){if(imageOrFileOrUrl instanceof HTMLCanvasElement||imageOrFileOrUrl instanceof HTMLVideoElement||window.ImageBitmap&&imageOrFileOrUrl instanceof window.ImageBitmap||window.OffscreenCanvas&&imageOrFileOrUrl instanceof window.OffscreenCanvas)return Promise.resolve(imageOrFileOrUrl);else if(imageOrFileOrUrl instanceof Image)return QrScanner._awaitImageLoad(imageOrFileOrUrl).then(()=>
imageOrFileOrUrl);else if(imageOrFileOrUrl instanceof File||imageOrFileOrUrl instanceof URL||typeof imageOrFileOrUrl==="string"){const image=new Image;if(imageOrFileOrUrl instanceof File)image.src=URL.createObjectURL(imageOrFileOrUrl);else image.src=imageOrFileOrUrl;return QrScanner._awaitImageLoad(image).then(()=>{if(imageOrFileOrUrl instanceof File)URL.revokeObjectURL(image.src);return image})}else return Promise.reject("Unsupported image type.")}static _awaitImageLoad(image){return new Promise((resolve,
reject)=>{if(image.complete&&image.naturalWidth!==0)resolve();else{let onLoad,onError;onLoad=()=>{image.removeEventListener("load",onLoad);image.removeEventListener("error",onError);resolve()};onError=()=>{image.removeEventListener("load",onLoad);image.removeEventListener("error",onError);reject("Image load error")};image.addEventListener("load",onLoad);image.addEventListener("error",onError)}})}}QrScanner.DEFAULT_CANVAS_SIZE=400;QrScanner.WORKER_PATH="/libraries/qr-scanner/qr-scanner-worker.min.js";
'use strict';export default class QrScanner{static hasCamera(){return navigator.mediaDevices.enumerateDevices().then((devices)=>devices.some((device)=>device.kind==="videoinput")).catch(()=>false)}constructor(video,onDecode,canvasSize=QrScanner.DEFAULT_CANVAS_SIZE){this.$video=video;this.$canvas=document.createElement("canvas");this._onDecode=onDecode;this._active=false;this._paused=false;this.$canvas.width=canvasSize;this.$canvas.height=canvasSize;this._sourceRect={x:0,y:0,width:canvasSize,height:canvasSize};
this._onCanPlay=this._onCanPlay.bind(this);this._onPlay=this._onPlay.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this.$video.addEventListener("canplay",this._onCanPlay);this.$video.addEventListener("play",this._onPlay);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrWorker=new Worker(QrScanner.WORKER_PATH)}destroy(){this.$video.removeEventListener("canplay",this._onCanPlay);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",
this._onVisibilityChange);this.stop();this._qrWorker.postMessage({type:"close"})}start(){if(this._active&&!this._paused)return Promise.resolve();if(window.location.protocol!=="https:")console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=true;this._paused=false;if(document.hidden)return Promise.resolve();clearTimeout(this._offTimeout);this._offTimeout=null;if(this.$video.srcObject){this.$video.play();return Promise.resolve()}let facingMode="environment";
return this._getCameraStream("environment",true).catch(()=>{facingMode="user";return this._getCameraStream()}).then((stream)=>{this.$video.srcObject=stream;this._setVideoMirror(facingMode)}).catch((e)=>{this._active=false;throw e;})}stop(){this.pause();this._active=false}pause(){this._paused=true;if(!this._active)return;this.$video.pause();if(this._offTimeout)return;this._offTimeout=setTimeout(()=>{const track=this.$video.srcObject&&this.$video.srcObject.getTracks()[0];if(!track)return;track.stop();
this.$video.srcObject=null;this._offTimeout=null},300)}static scanImage(imageOrFileOrUrl,sourceRect=null,worker=null,canvas=null,fixedCanvasSize=false,alsoTryWithoutSourceRect=false){const promise=new Promise((resolve,reject)=>{if(!worker){worker=new Worker(QrScanner.WORKER_PATH);worker.postMessage({type:"inversionMode",data:"both"})}let timeout,onMessage,onError;onMessage=(event)=>{if(event.data.type!=="qrResult")return;worker.removeEventListener("message",onMessage);worker.removeEventListener("error",
onError);clearTimeout(timeout);if(event.data.data!==null)resolve(event.data.data);else reject("QR code not found.")};onError=(e)=>{worker.removeEventListener("message",onMessage);worker.removeEventListener("error",onError);clearTimeout(timeout);var errorMessage=!e?"Unknown Error":e.message||e;reject("Scanner error: "+errorMessage)};worker.addEventListener("message",onMessage);worker.addEventListener("error",onError);timeout=setTimeout(()=>onError("timeout"),3E3);QrScanner._loadImage(imageOrFileOrUrl).then((image)=>
{const imageData=QrScanner._getImageData(image,sourceRect,canvas,fixedCanvasSize);worker.postMessage({type:"decode",data:imageData},[imageData.data.buffer])}).catch(reject)});if(sourceRect&&alsoTryWithoutSourceRect)return promise.catch(()=>QrScanner.scanImage(imageOrFileOrUrl,null,worker,canvas,fixedCanvasSize));else return promise}setGrayscaleWeights(red,green,blue){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red,green,blue}})}setInversionMode(inversionMode){this._qrWorker.postMessage({type:"inversionMode",
data:inversionMode})}_onCanPlay(){this._updateSourceRect();this.$video.play()}_onPlay(){this._updateSourceRect();this._scanFrame()}_onVisibilityChange(){if(document.hidden)this.pause();else if(this._active)this.start()}_updateSourceRect(){const smallestDimension=Math.min(this.$video.videoWidth,this.$video.videoHeight);const sourceRectSize=Math.round(2/3*smallestDimension);this._sourceRect.width=this._sourceRect.height=sourceRectSize;this._sourceRect.x=(this.$video.videoWidth-sourceRectSize)/2;this._sourceRect.y=
(this.$video.videoHeight-sourceRectSize)/2}_scanFrame(){if(!this._active||this.$video.paused||this.$video.ended)return false;requestAnimationFrame(()=>{QrScanner.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,true).then(this._onDecode,(error)=>{if(this._active&&error!=="QR code not found.")console.error(error)}).then(()=>this._scanFrame())})}_getCameraStream(facingMode,exact=false){const constraintsToTry=[{width:{min:1024}},{width:{min:768}},{}];if(facingMode){if(exact)facingMode=
{exact:facingMode};constraintsToTry.forEach((constraint)=>constraint.facingMode=facingMode)}return this._getMatchingCameraStream(constraintsToTry)}_getMatchingCameraStream(constraintsToTry){if(constraintsToTry.length===0)return Promise.reject("Camera not found.");return navigator.mediaDevices.getUserMedia({video:constraintsToTry.shift()}).catch(()=>this._getMatchingCameraStream(constraintsToTry))}_setVideoMirror(facingMode){const scaleFactor=facingMode==="user"?-1:1;this.$video.style.transform="scaleX("+
scaleFactor+")"}static _getImageData(image,sourceRect=null,canvas=null,fixedCanvasSize=false){canvas=canvas||document.createElement("canvas");const sourceRectX=sourceRect&&sourceRect.x?sourceRect.x:0;const sourceRectY=sourceRect&&sourceRect.y?sourceRect.y:0;const sourceRectWidth=sourceRect&&sourceRect.width?sourceRect.width:image.width||image.videoWidth;const sourceRectHeight=sourceRect&&sourceRect.height?sourceRect.height:image.height||image.videoHeight;if(!fixedCanvasSize&&(canvas.width!==sourceRectWidth||
canvas.height!==sourceRectHeight)){canvas.width=sourceRectWidth;canvas.height=sourceRectHeight}const context=canvas.getContext("2d",{alpha:false});context.imageSmoothingEnabled=false;context.drawImage(image,sourceRectX,sourceRectY,sourceRectWidth,sourceRectHeight,0,0,canvas.width,canvas.height);return context.getImageData(0,0,canvas.width,canvas.height)}static _loadImage(imageOrFileOrUrl){if(imageOrFileOrUrl instanceof HTMLCanvasElement||imageOrFileOrUrl instanceof HTMLVideoElement||window.ImageBitmap&&
imageOrFileOrUrl instanceof window.ImageBitmap||window.OffscreenCanvas&&imageOrFileOrUrl instanceof window.OffscreenCanvas)return Promise.resolve(imageOrFileOrUrl);else if(imageOrFileOrUrl instanceof Image)return QrScanner._awaitImageLoad(imageOrFileOrUrl).then(()=>imageOrFileOrUrl);else if(imageOrFileOrUrl instanceof File||imageOrFileOrUrl instanceof URL||typeof imageOrFileOrUrl==="string"){const image=new Image;if(imageOrFileOrUrl instanceof File)image.src=URL.createObjectURL(imageOrFileOrUrl);
else image.src=imageOrFileOrUrl;return QrScanner._awaitImageLoad(image).then(()=>{if(imageOrFileOrUrl instanceof File)URL.revokeObjectURL(image.src);return image})}else return Promise.reject("Unsupported image type.")}static _awaitImageLoad(image){return new Promise((resolve,reject)=>{if(image.complete&&image.naturalWidth!==0)resolve();else{let onLoad,onError;onLoad=()=>{image.removeEventListener("load",onLoad);image.removeEventListener("error",onError);resolve()};onError=()=>{image.removeEventListener("load",
onLoad);image.removeEventListener("error",onError);reject("Image load error")};image.addEventListener("load",onLoad);image.addEventListener("error",onError)}})}}QrScanner.DEFAULT_CANVAS_SIZE=400;QrScanner.WORKER_PATH="/libraries/qr-scanner/qr-scanner-worker.min.js";
//# sourceMappingURL=qr-scanner.min.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"sources":["qr-scanner.js"],"names":["QrScanner","video","onDecode","canvasSize","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","_active","_paused","width","height","_sourceRect","x","y","_onCanPlay","bind","_onPlay","_onVisibilityChange","addEventListener","_qrWorker","Worker","WORKER_PATH","removeEventListener","stop","postMessage","type","Promise","resolve","window","location","protocol","console","warn","hidden","clearTimeout","_offTimeout","srcObject","play","facingMode","_getCameraStream","catch","then","stream","_setVideoMirror","e","pause","setTimeout","track","getTracks","imageOrFileOrUrl","sourceRect","worker","canvas","fixedCanvasSize","alsoTryWithoutSourceRect","promise","reject","data","timeout","onMessage","onError","event","errorMessage","message","_loadImage","image","imageData","_getImageData","buffer","scanImage","red","green","blue","inversionMode","_updateSourceRect","_scanFrame","start","smallestDimension","Math","min","videoWidth","videoHeight","sourceRectSize","round","paused","ended","requestAnimationFrame","error","exact","constraintsToTry","forEach","constraint","_getMatchingCameraStream","length","navigator","mediaDevices","getUserMedia","shift","scaleFactor","style","transform","sourceRectX","sourceRectY","sourceRectWidth","sourceRectHeight","context","getContext","alpha","imageSmoothingEnabled","drawImage","getImageData","HTMLCanvasElement","HTMLVideoElement","ImageBitmap","OffscreenCanvas","Image","_awaitImageLoad","File","URL","src","createObjectURL","revokeObjectURL","complete","naturalWidth","onLoad"],"mappings":"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 truncated
{"version":3,"sources":["qr-scanner.js"],"names":["QrScanner","navigator","mediaDevices","enumerateDevices","then","devices","some","device","kind","catch","video","onDecode","canvasSize","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","_active","_paused","width","height","_sourceRect","x","y","_onCanPlay","bind","_onPlay","_onVisibilityChange","addEventListener","_qrWorker","Worker","WORKER_PATH","removeEventListener","stop","postMessage","type","Promise","resolve","window","location","protocol","console","warn","hidden","clearTimeout","_offTimeout","srcObject","play","facingMode","_getCameraStream","stream","_setVideoMirror","e","pause","setTimeout","track","getTracks","imageOrFileOrUrl","sourceRect","worker","canvas","fixedCanvasSize","alsoTryWithoutSourceRect","promise","reject","data","timeout","onMessage","onError","event","errorMessage","message","_loadImage","image","imageData","_getImageData","buffer","scanImage","red","green","blue","inversionMode","_updateSourceRect","_scanFrame","start","smallestDimension","Math","min","videoWidth","videoHeight","sourceRectSize","round","paused","ended","requestAnimationFrame","error","exact","constraintsToTry","forEach","constraint","_getMatchingCameraStream","length","getUserMedia","shift","scaleFactor","style","transform","sourceRectX","sourceRectY","sourceRectWidth","sourceRectHeight","context","getContext","alpha","imageSmoothingEnabled","drawImage","getImageData","HTMLCanvasElement","HTMLVideoElement","ImageBitmap","OffscreenCanvas","Image","_awaitImageLoad","File","URL","src","createObjectURL","revokeObjectURL","complete","naturalWidth","onLoad"],"mappings":"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 truncated
+10
View File
@@ -1,4 +1,14 @@
export default class QrScanner {
/* async */
static hasCamera() {
// note that enumerateDevices can always be called and does not prompt the user for permission. However, device
// labels are only readable if served via https and an active media stream exists or permanent permission is
// given. That doesn't matter for us though as we don't require labels.
return navigator.mediaDevices.enumerateDevices()
.then(devices => devices.some(device => device.kind === 'videoinput'))
.catch(() => false);
}
constructor(video, onDecode, canvasSize = QrScanner.DEFAULT_CANVAS_SIZE) {
this.$video = video;
this.$canvas = document.createElement('canvas');
+2
View File
@@ -6,6 +6,8 @@ declare class QrScanner {
static DEFAULT_CANVAS_SIZE: number;
static WORKER_PATH: string;
static hasCamera(): Promise<boolean>;
constructor(video: HTMLVideoElement, onDecode: Function, canvasSize?: number);
destroy(): void;