gruhn / vue-qrcode-reader

A set of Vue.js components for detecting and decoding QR codes.
https://gruhn.github.io/vue-qrcode-reader
MIT License
2.13k stars 334 forks source link

Secret for such a low temperature? #374

Closed KaliaJS closed 1 year ago

KaliaJS commented 1 year ago

I would like to start by thanking you for your package, he helped me a lot.

I'm sorry to pollute the issues but I had a question. I am developing an electron.js app with vite.js and vue 3 and I don't want to use any external component (so do not use vue-qrcode-reader) but develop it myself.

First, I implemented jsQR in a web worker with an offscreen canvas and the temperature of my Macbook Air M2 rose to 100°C.

I then tried the native Web BarcodeDetector and the temperature drops to 70°C. A little more with the barcode-detector-polyfill live demo...

I then said to myself "Maybe it's electron.js", and so I created a fresh vite.js vue3 app and despite that the temperature is still 70°C.

How is it possible that with the vue-qrcode-reader live demo I have practically no impact on the temperatures of my mac (56°C) ? It's not a big deal but I'm really intrigued and would like to understand how you achieved this performance in your component.

Thank you in advance for your help.

const barcodeDetector = new BarcodeDetector({ formats: ['qr_code'] })

whenever(stream, () => {
  videoElement.value.srcObject = stream.value
  // I tried with requestAnimationFrame and it's even worse.
  // Only setTimeout 500 allows me  to have temperatures like your package.
  videoElement.value.requestVideoFrameCallback(scanCode)
})

const scanCode = async () => {
  if (videoElement.value) {
    const barcodes = await barcodeDetector.detect(videoElement.value)
    barcodes.forEach(barcode => console.log(barcode))
  }
  videoElement.value?.requestVideoFrameCallback(scanCode)
}
gruhn commented 1 year ago

By default we also scan with 500ms delay. But we don't do it using setTimeout. Instead we keep calling requestAnimationFrame without timeout but "skip" the expensive scanning part when the delay hasn't passed. See:

https://github.com/gruhn/vue-qrcode-reader/blob/8b639908d93c9a94b14d1a54fc66aa1b8c116577/src/misc/scanner.ts#L37-L44

Usually, 500ms delay is fine. But when visual tracking in enabled like in this demo we have to scan more often. We want to very roughly target a scanning frequency of 24fps, because that's the minimum frame rate where humans perceive an animation as continuous. 1000ms / 24fps = 41.66666666ms is not a nice number so we go with 1000ms / 25fps = 40ms delay.

Notice that this approach is different from setTimeout. If we just do 40ms timeouts, then scan-time and wait-time are sequential:

|----- 40 ms -----|--- scan ---|----- 40ms -----|--- scan ---|

Instead we make them concurrent:

|----- 40 ms -----|----- 40ms -----|----- 40ms -----|
|--- scan ---|    |--- scan ---|   |--- scan ---|

So only if scanning takes more than 40ms, we get a lower frame rate. This might happen on some or many devices. No idea. I have no metrics on that. But this is the reasoning behind this 40ms number.

KaliaJS commented 1 year ago

I found the problem thanks to your explanation. I use requestVideoFrameCallback which runs at 60 fps in this case of webcam stream. I'm going to take inspiration from your code and use requestAnimationFrame and also target 24fps or 25fps. The temperature is now low with visual tracking activated!

Thanks for the help.