pause video if tab in background; start playback also on video that does not provide autoplay

This commit is contained in:
Daniel committed 2019-01-19 17:02:02 +01:00
1 parent f5771a8279
commit 27e40bf1ce
6 files changed
+62 -24

No files matched your search

+3 -3
View File
@@ -80,8 +80,8 @@ qrScanner.setGrayscaleWeights(red, green, blue);
Where `red`, `green` and `blue` must sum up to 256.
## Build the project
The project is prebuild in qr-scanner.min.js in combination with qr-scanner-worker.min.js. Building yourself is only neccessary if you want to change the code in
the /src folder. Nodejs and Java are required for building.
The project is prebuild in qr-scanner.min.js in combination with qr-scanner-worker.min.js. Building yourself is only necessary if you want to change the code in
the /src folder. NodeJs and Java are required for building.
Install required build packages:
```batch
@@ -90,7 +90,7 @@ npm install
Building:
```batch
gulp
npm run build
```
## Debug Mode
+1 -1
View File
@@ -22,7 +22,7 @@
</style>
<h1>Scan from WebCam:</h1>
<div>
<video muted autoplay playsinline id="qr-video"></video>
<video muted playsinline id="qr-video"></video>
<canvas id="debug-canvas"></canvas>
</div>
<div>
+2 -1
View File
@@ -4,7 +4,8 @@
"description": "A javascript QR scanner library",
"main": "qr-scanner.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"build": "gulp"
},
"repository": {
"type": "git",
+12 -11
View File
@@ -1,16 +1,17 @@
/*
@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.$canvas.width=canvasSize;this.$canvas.height=canvasSize;this._sourceRect={x:0,y:0,width:canvasSize,height:canvasSize};this.$video.addEventListener("canplay",()=>this._updateSourceRect());this.$video.addEventListener("play",()=>{this._updateSourceRect();this._scanFrame()},false);
this._qrWorker=new Worker(QrScanner.WORKER_PATH)}_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.$video.paused||this.$video.ended)return false;requestAnimationFrame(()=>{QrScanner.scanImage(this.$video,
this._sourceRect,this._qrWorker,this.$canvas,true).then(this._onDecode,(error)=>{if(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))}start(){if(this._active)return Promise.resolve();if(window.location.protocol!=="https:")console.warn("The camera stream is only readable if the page is transferred via https.");this._active=true;clearTimeout(this._offTimeout);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(){if(!this._active)return;this._active=false;this.$video.pause();this._offTimeout=setTimeout(()=>{this.$video.srcObject.getTracks()[0].stop();this.$video.srcObject=null},3E3)}_setVideoMirror(facingMode){const scaleFactor=facingMode==="user"?-1:1;this.$video.style.transform="scaleX("+scaleFactor+")"}setGrayscaleWeights(red,green,blue){this._qrWorker.postMessage({type:"grayscaleWeights",
data:{red,green,blue}})}setInversionMode(inversionMode){this._qrWorker.postMessage({type:"inversionMode",data:inversionMode})}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}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,
'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.$video.addEventListener("canplay",()=>{this._updateSourceRect();this.$video.play()});this.$video.addEventListener("play",()=>{this._updateSourceRect();
this._scanFrame()},false);this._qrWorker=new Worker(QrScanner.WORKER_PATH);document.addEventListener("visibilitychange",()=>{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.$video.paused||this.$video.ended)return false;requestAnimationFrame(()=>{QrScanner.scanImage(this.$video,this._sourceRect,this._qrWorker,this.$canvas,true).then(this._onDecode,(error)=>{if(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))}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},1E3)}_setVideoMirror(facingMode){const scaleFactor=facingMode==="user"?-1:1;this.$video.style.transform="scaleX("+scaleFactor+")"}setGrayscaleWeights(red,green,blue){this._qrWorker.postMessage({type:"grayscaleWeights",data:{red,green,blue}})}setInversionMode(inversionMode){this._qrWorker.postMessage({type:"inversionMode",
data:inversionMode})}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}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","width","height","_sourceRect","x","y","addEventListener","_updateSourceRect","_scanFrame","_qrWorker","Worker","WORKER_PATH","smallestDimension","Math","min","videoWidth","videoHeight","sourceRectSize","round","paused","ended","requestAnimationFrame","scanImage","then","error","console","facingMode","exact","constraintsToTry","forEach","constraint","_getMatchingCameraStream","length","Promise","reject","navigator","mediaDevices","getUserMedia","shift","catch","resolve","window","location","protocol","warn","clearTimeout","_offTimeout","_getCameraStream","stream","srcObject","_setVideoMirror","e","pause","setTimeout","getTracks","stop","scaleFactor","style","transform","red","green","blue","postMessage","type","data","inversionMode","imageOrFileOrUrl","sourceRect","worker","canvas","fixedCanvasSize","alsoTryWithoutSourceRect","promise","timeout","onMessage","onError","event","removeEventListener","errorMessage","message","_loadImage","image","imageData","_getImageData","buffer","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","video","onDecode","canvasSize","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","_active","_paused","width","height","_sourceRect","x","y","addEventListener","_updateSourceRect","play","_scanFrame","_qrWorker","Worker","WORKER_PATH","hidden","pause","start","smallestDimension","Math","min","videoWidth","videoHeight","sourceRectSize","round","paused","ended","requestAnimationFrame","scanImage","then","error","console","facingMode","exact","constraintsToTry","forEach","constraint","_getMatchingCameraStream","length","Promise","reject","navigator","mediaDevices","getUserMedia","shift","catch","resolve","window","location","protocol","warn","clearTimeout","_offTimeout","srcObject","_getCameraStream","stream","_setVideoMirror","e","setTimeout","track","getTracks","stop","scaleFactor","style","transform","red","green","blue","postMessage","type","data","inversionMode","imageOrFileOrUrl","sourceRect","worker","canvas","fixedCanvasSize","alsoTryWithoutSourceRect","promise","timeout","onMessage","onError","event","removeEventListener","errorMessage","message","_loadImage","image","imageData","_getImageData","buffer","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
+43 -7
View File
@@ -4,6 +4,7 @@ export default class QrScanner {
this.$canvas = document.createElement('canvas');
this._onDecode = onDecode;
this._active = false;
this._paused = false;
this.$canvas.width = canvasSize;
this.$canvas.height = canvasSize;
@@ -14,12 +15,23 @@ export default class QrScanner {
height: canvasSize
};
this.$video.addEventListener('canplay', () => this._updateSourceRect());
this.$video.addEventListener('canplay', () => {
this._updateSourceRect();
this.$video.play();
});
this.$video.addEventListener('play', () => {
this._updateSourceRect();
this._scanFrame();
}, false);
this._qrWorker = new Worker(QrScanner.WORKER_PATH);
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.pause();
} else if (this._active) {
this.start();
}
});
}
_updateSourceRect() {
@@ -32,6 +44,7 @@ export default class QrScanner {
_scanFrame() {
if (this.$video.paused || this.$video.ended) return false;
// using requestAnimationFrame to avoid scanning if tab is in background
requestAnimationFrame(() => {
QrScanner.scanImage(this.$video, this._sourceRect, this._qrWorker, this.$canvas, true)
.then(this._onDecode, error => {
@@ -69,20 +82,32 @@ export default class QrScanner {
}
start() {
if (this._active) {
if (this._active && !this._paused) {
return Promise.resolve();
}
if (window.location.protocol !== 'https:') {
console.warn('The camera stream is only readable if the page is transferred via 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) {
// camera will be started as soon as tab is in foreground
return Promise.resolve();
}
clearTimeout(this._offTimeout);
this._offTimeout = null;
if (this.$video.srcObject) {
// camera stream already/still set
this.$video.play();
return Promise.resolve();
}
let facingMode = 'environment';
return this._getCameraStream('environment', true)
.catch(() => {
// we (probably) don't have an environment camera
facingMode = 'user';
return this._getCameraStream(); // throws if we can't access the camera
return this._getCameraStream(); // throws if camera is not accessible (e.g. due to not https)
})
.then(stream => {
this.$video.srcObject = stream;
@@ -95,15 +120,26 @@ export default class QrScanner {
}
stop() {
this.pause();
this._active = false;
}
pause() {
this._paused = true;
if (!this._active) {
return;
}
this._active = false;
this.$video.pause();
if (this._offTimeout) {
return;
}
this._offTimeout = setTimeout(() => {
this.$video.srcObject.getTracks()[0].stop();
const track = this.$video.srcObject && this.$video.srcObject.getTracks()[0];
if (!track) return;
track.stop();
this.$video.srcObject = null;
}, 3000);
this._offTimeout = null;
}, 1000);
}
_setVideoMirror(facingMode) {