nimiq / qr-scanner

Lightweight Javascript QR Code Scanner
https://nimiq.github.io/qr-scanner/demo
MIT License
2.53k stars 542 forks source link

(Question) preferredCamera='environment' selects "bad" rear camera (Galaxy S20) #146

Open willenjs opened 2 years ago

willenjs commented 2 years ago

Hey guys,

I'm developing something here that works perfectly on some devices like iPhone and others but for Galaxy S20, whenever I use the preferredCamera='environment' option it doesn't select a good camera for a short distance shot.

Galaxay S20 has these 3 rear cameras:

I couldn't confirm yet but it seems it's selecting this second camera, which is terrible for short-range shots (like a QR Core read).

Do you have any suggestions on how to do some generic that works well for this device and for others as well?

danimoh commented 2 years ago

Hello @willenjs, unfortunately I have no device at hand that shows this behavior. It would be great if you could help me to investigate whether there's something that could be done here to improve the automatic selection. Can you please open the demo page at https://nimiq.github.io/qr-scanner/demo/, grant the page the permission to access the camera (if you hadn't yet), run the following snippet in the dev tools, and then report the result here?:

const QrScanner = scanner.constructor;
const cameras = ['default', 'user', 'environment', ...(await QrScanner.listCameras()).map(({ id }) => id)];
const $video = document.getElementById('qr-video');

for (const camera of cameras) {
    if (camera !== 'default') {
        await scanner.setCamera(camera);
    }
    console.log(`Camera: ${camera}\n${JSON.stringify($video.srcObject.getVideoTracks()[0].getSettings(), null, 4)}\n`);
}
ilic-boris commented 2 years ago

Same thing is happening with Galaxy S22, will try to run snippet if I get time to connect the phone to the PC since this is would be the way to get the developer console running.

evellior commented 2 years ago

I'm getting the same issue, it uses the fisheye camera instead of the standard one. Running that script on my phone's browser console (Chrome on Samsung S10) gave the following:

Camera: default
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "3a61cb092fd9d966c8a5017256f7cb3053ed5b15a7cdd56749c3c17e1b0b3c2f",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "environment",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 60,
    "groupId": "9765e06c022b353064b41246db92d325a0da442cc856dc76a2d306970a67734f",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: user
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "886d4673daf3fb6ecffe5dd3407da1eabe2759af47ce465c58e534a501d28885",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "user",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 30,
    "groupId": "77344a31c6881082d175253933b5192ffb81b878bd9541fc4d57d4c8df034ee8",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: environment
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "3a61cb092fd9d966c8a5017256f7cb3053ed5b15a7cdd56749c3c17e1b0b3c2f",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "environment",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 60,
    "groupId": "9765e06c022b353064b41246db92d325a0da442cc856dc76a2d306970a67734f",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: 886d4673daf3fb6ecffe5dd3407da1eabe2759af47ce465c58e534a501d28885
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "886d4673daf3fb6ecffe5dd3407da1eabe2759af47ce465c58e534a501d28885",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "user",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 30,
    "groupId": "77344a31c6881082d175253933b5192ffb81b878bd9541fc4d57d4c8df034ee8",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: 9d77a124841c81098ed83de40839e671d4d7f4ec0a8061c795f778c358118b20
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "9d77a124841c81098ed83de40839e671d4d7f4ec0a8061c795f778c358118b20",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "user",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 30,
    "groupId": "128ce3f3b49200f6cab99cd2a8ef2cd5b1422360eb50c90e4fd3f360bb92d00a",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: 3a61cb092fd9d966c8a5017256f7cb3053ed5b15a7cdd56749c3c17e1b0b3c2f
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "3a61cb092fd9d966c8a5017256f7cb3053ed5b15a7cdd56749c3c17e1b0b3c2f",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "environment",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 60,
    "groupId": "9765e06c022b353064b41246db92d325a0da442cc856dc76a2d306970a67734f",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}

Camera: 8c337c5d190e170deff5623049cc5b406a0c8d52da58ca22e21f4d033e33693f
{
    "aspectRatio": 0.5625,
    "colorTemperature": 0,
    "deviceId": "8c337c5d190e170deff5623049cc5b406a0c8d52da58ca22e21f4d033e33693f",
    "exposureCompensation": 0,
    "exposureMode": "continuous",
    "exposureTime": 0,
    "facingMode": "environment",
    "focusDistance": 0,
    "focusMode": "continuous",
    "frameRate": 60,
    "groupId": "e55906ae79566238889a2442b428ee31ba877d1961fd938c15eceda5ce2caacb",
    "height": 1280,
    "iso": 0,
    "resizeMode": "none",
    "torch": false,
    "whiteBalanceMode": "continuous",
    "width": 720,
    "zoom": 1
}
evellior commented 2 years ago

I'm getting a few people to test, but so far I've found camera2 0 to pretty consistently be a good default. As a work-around I've replace the call to scanner.start() with:

      Nimiq.listCameras(true).then(list => {
        setList(list)
        let pref = list.find(cam => cam.label.startsWith("camera2 0"))?? {label: 'No camera2 0 found in list', list, id: 'environment'}
        //console.log("Using Camera: ", pref)

        if (pref) scanner.setCamera(pref.id).then(
          _ => {/*Camera Set*/},
          err => console.log("Failed to set camera", err)
        )

        scanner.start().then(
          _ => {/*Camera Started*/},
          err => console.log("Failed to start scanner", err)
        )
      })

or, a more generic version for anyone wanting to copy-paste this solution, replace scanner.start() with:

QrScanner.listCameras(true)
    .then(cameras => scanner.setCamera(cameras.find(camera => camera.label.startsWith("camera2 0"))?.id ?? 'environment')
    .then(() => scanner.start()))
hamzamac commented 1 year ago

I get the same problem on Samsung S10, it uses the wide camera.

PatrickGuthridge commented 3 months ago

I'm getting a few people to test, but so far I've found camera2 0 to pretty consistently be a good default. As a work-around I've replace the call to scanner.start() with:

      Nimiq.listCameras(true).then(list => {
        setList(list)
        let pref = list.find(cam => cam.label.startsWith("camera2 0"))?? {label: 'No camera2 0 found in list', list, id: 'environment'}
        //console.log("Using Camera: ", pref)

        if (pref) scanner.setCamera(pref.id).then(
          _ => {/*Camera Set*/},
          err => console.log("Failed to set camera", err)
        )

        scanner.start().then(
          _ => {/*Camera Started*/},
          err => console.log("Failed to start scanner", err)
        )
      })

or, a more generic version for anyone wanting to copy-paste this solution, replace scanner.start() with:

QrScanner.listCameras(true)
  .then(cameras => scanner.setCamera(cameras.find(camera => camera.label.startsWith("camera2 0"))?.id ?? 'environment')
  .then(() => scanner.start()))

This workaround seems to solve the issue on several phones I've tried