Open chobo2 opened 3 years ago
Thank you for filing an issue! Please be patient. :-)
For the most part, it works on modern iOS devices, as far as I'm aware. In the current betas, it even works in Chrome on iOS, not just in Safari, now that they've got camera support for WebView.
Hi there,
I can confirm that this library works flawless on most likely all modern browsers (yes also on Android and iOS) that supports getusermedia etc etc.
Please bare in mind that you should use Safari on iOS to get the best experience out of this library... (since it's the most updated browser on apple devices)
And finally :) this library also works perfectly in progressive web apps as well :)... iOS had some issues before, but if I could recall they have fixed it in iOS 14 already.
https://bugs.webkit.org/show_bug.cgi?id=185448 <--- we have complained for almost 2/3 years to get this problem fixed.
this library also works perfectly in progressive web apps
This is a trick I need to learn! :-D
For the most part, it works on modern iOS devices, as far as I'm aware. In the current betas, it even works in Chrome on iOS, not just in Safari, now that they've got camera support for WebView.
When you say current betas, you mean of the iOS operating system or browser versions?
What you mean it does not work in Safari? So the newer devices like iphone 11 safari won't work with this library.
I don't know much about apple stuff, I have a contract to see if it is possible to implement a barcode reader into a responsive webpage.
I am just not sure how possible it will be apple and wanted to double check. I tried on a 6 year old ipad mini with the example and it worked fine, I don't have an iphone but the company said they would send me one but warned it might be older version so that does not help if new iphone versions may have problems.
Hi there,
I can confirm that this library works flawless on most likely all modern browsers (yes also on Android and iOS) that supports getusermedia etc etc.
Please bare in mind that you should use Safari on iOS to get the best experience out of this library... (since it's the most updated browser on apple devices)
And finally :) this library also works perfectly in progressive web apps as well :)... iOS had some issues before, but if I could recall they have fixed it in iOS 14 already.
https://bugs.webkit.org/show_bug.cgi?id=185448 <--- we have complained for almost 2/3 years to get this problem fixed.
So if I am reading this right, anything running iOS 10.3 or less can't use it?
I would hope most people using iphone and ipads would be using safari but since I am implementing this in a responsive website, I don't get the luxary of getting the users to use what I want them to use.
I guess best I can do is test for getusermedia and only show the icon to launch this library if present.
I am also not sure what "progressive web apps" are.
So if I am reading this right, anything running iOS 10.3 or less can't use it?
You are correct, this library will not work beneath iOS 11... You should always test if "mediadevices/getusermedia" is available for your specific device. Please bare in mind that devices with multiple camera's will not work correctly with getusermedia and it's constraints(specifically when choosing environment).
So try to use navigator.mediaDevices.enumerateDevices() instead and loop through your devices and it's label.
I am also not sure what "progressive web apps" are.
You can create an "app" experience by creating an progressive web app. It enables your users to install your website on their homescreen.
Please read this article : https://web.dev/what-are-pwas/
That's why I referred to iOS and it's problems in it's own webview and PWA technology. (luckily it's solved)
When you say current betas, you mean of the iOS operating system or browser versions?
Current betas of iOS allow getUserMedia to work in WebView, so people using those can use Chrome or whatever other 3rd party browser with the library. It's worked with Safari for a few years now, I believe. Older iThings that will not update beyond iOS 10 will not work with it (this makes me sad, as I have several old iPads that I hoped to use as testing devices, but that didn't end up working out)
So if I am reading this right, anything running iOS 10.3 or less can't use it?
You are correct, this library will not work beneath iOS 11... You should always test if "mediadevices/getusermedia" is available for your specific device. Please bare in mind that devices with multiple camera's will not work correctly with getusermedia and it's constraints(specifically when choosing environment).
So try to use navigator.mediaDevices.enumerateDevices() instead and loop through your devices and it's label.
I am also not sure what "progressive web apps" are.
You can create an "app" experience by creating an progressive web app. It enables your users to install your website on their homescreen.
Please read this article : https://web.dev/what-are-pwas/
That's why I referred to iOS and it's problems in it's own webview and PWA technology. (luckily it's solved)
When you say multiple cameras are you talking about phones that have front and back camera or are you talking about the phones that have like 5 cameras in the back?
Also could you give an example of code how to test for this?
So try to use navigator.mediaDevices.enumerateDevices() instead and loop through your devices and it's label.
Anyways I can emulate this situation as well? if you talking about multiple cameras in the back I won't be able to get my hands on something like that.
When you say current betas, you mean of the iOS operating system or browser versions?
Current betas of iOS allow getUserMedia to work in WebView, so people using those can use Chrome or whatever other 3rd party browser with the library. It's worked with Safari for a few years now, I believe. Older iThings that will not update beyond iOS 10 will not work with it (this makes me sad, as I have several old iPads that I hoped to use as testing devices, but that didn't end up working out)
I tried on an ipad mini my wife has had over for 6 years and it worked with the example app. I have to check what version it is running so at least older things to a certain point are supported.
There are a lot of old iPads out there that are limited to iOS 9 or 10, though :(
Quagga does offer a wrapper, Quagga.CameraAccess.enumerateDevices() i believe it's called, though it's been a while since i've looked at it, so i may have the name slightly off.
Most all phone devices have at least two logical cameras -- a front and a back, some have several, even if they only appear to have a single front and back lense. My LG G6, and many of the other top end phones report several back cameras, one with a wide-angle, one with normal proportions, etc ,as well as the front camera. Some phones, if you just ask for 'environment' camera, you'll get something wide-angle that fish-eyes the view, making it near impossible to scan the barcodes. Others, you'll get something that works well. My brand new Moto G Stylus works fantastically with the default camera device, and it even auto-focuses, which is not something we can control inside the browser currently.
Personally, I use React for my application that I use Quagga in, with a Redux data/state store, and I use this helper lib I wrote for determining available cameras: https://github.com/ericblade/quagga2-redux-middleware
I use that to get the camera devices, then I offer the user a choice of which device. Unfortunately, phones often do not provide any useful names for the devices, just "front" or "back", so the user has to use trial and error to figure out which one works best sometimes. Once the user has selected a device, you can pass it in with a "deviceId" in the constraints block.
You can do the same thing on a regular PC, by plugging in multiple cameras, or if you have a laptop with a built in camera, plugging in a single external USB camera. On PCs you almost always get the USB device name of the camera, though, which is a lot more useful.
Some phones, if you just ask for 'environment' camera, you'll get something wide-angle that fish-eyes the view, making it near impossible to scan the barcodes. Others, you'll get something that works well. My brand new Moto G Stylus works fantastically with the default camera device, and it even auto-focuses, which is not something we can control inside the browser currently.
Yes, I am currently using a Samsung Galaxy S20+. When I'm trying to set facingMode to environment (with getUserMedia) it returns "NotReadableError: Could not start video source".
But when trying enumerateDevices(). it shows a list of inputdevices :) where I can choose from.
There are a lot of old iPads out there that are limited to iOS 9 or 10, though :(
Quagga does offer a wrapper, Quagga.CameraAccess.enumerateDevices() i believe it's called, though it's been a while since i've looked at it, so i may have the name slightly off.
Most all phone devices have at least two logical cameras -- a front and a back, some have several, even if they only appear to have a single front and back lense. My LG G6, and many of the other top end phones report several back cameras, one with a wide-angle, one with normal proportions, etc ,as well as the front camera. Some phones, if you just ask for 'environment' camera, you'll get something wide-angle that fish-eyes the view, making it near impossible to scan the barcodes. Others, you'll get something that works well. My brand new Moto G Stylus works fantastically with the default camera device, and it even auto-focuses, which is not something we can control inside the browser currently.
Personally, I use React for my application that I use Quagga in, with a Redux data/state store, and I use this helper lib I wrote for determining available cameras: https://github.com/ericblade/quagga2-redux-middleware
I use that to get the camera devices, then I offer the user a choice of which device. Unfortunately, phones often do not provide any useful names for the devices, just "front" or "back", so the user has to use trial and error to figure out which one works best sometimes. Once the user has selected a device, you can pass it in with a "deviceId" in the constraints block.
You can do the same thing on a regular PC, by plugging in multiple cameras, or if you have a laptop with a built in camera, plugging in a single external USB camera. On PCs you almost always get the USB device name of the camera, though, which is a lot more useful.
would like to use reactjs as what I like, but the project I am doing apparently is some old version of angular (version 3 I think). I am planning to do it more in plan javascript anyways. The project is not hard in sense of programming but handling all thse different cameras and stuff as I have very limited amount of devices.
Basically project is, scan bar code and put in a textbox.
I will have to look for this "Quagga.CameraAccess.enumerateDevices()" and see what going on with it.
If a user has a old device, nothing I can do. Can't make things work if libraries are not supported.
Some phones, if you just ask for 'environment' camera, you'll get something wide-angle that fish-eyes the view, making it near impossible to scan the barcodes. Others, you'll get something that works well. My brand new Moto G Stylus works fantastically with the default camera device, and it even auto-focuses, which is not something we can control inside the browser currently.
Yes, I am currently using a Samsung Galaxy S20+. When I'm trying to set facingMode to environment (with getUserMedia) it returns "NotReadableError: Could not start video source".
But when trying enumerateDevices(). it shows a list of inputdevices :) where I can choose from.
How will you figure out which camera to choose?
I am still not clear on how to choose best camera. In my head all I want the user to do is hit a button to launch their camera and then scan a barcode. I don't really want to have another dropdown list that they have to choose camera but maybe I might have to do this?
it seems like the best one can do is either use the outdated facing environment constraint in your Quagga init, or filter the enumerate devices to pick one that appears to be facing back, and provide some way for the user to force a specific device if you pick wrong.
i'd hope for something better, but enumerateDevices doesn't support a constraint, and so.. there's not even a way to combine constraints with selection, there's no way to tell from anything but the device name, which might not even exist, anything about the camera. Once you've actually passed it through Quagga.init() you can start querying the media properties to find out what you actually got, resolution wise, but there's no way to reliable say "front" or "back" or "wide-angle" or whatever.
If you try that same call from a laptop with a built-in camera and an external camera, your labels will be "name/model number of built-in camera" and "name/model number of external camera" usually.
fwiw, i was just glancing at the quagga2-redux-middleware package, and realized it's dependency tree was a mess, and released an update just now that hopefully fixes it.
ok, well the redux stuff won't help me in this situation unless it can be easily extracted to be used in plan JS. If it is ok, I going to take a crack at this and post my code and maybe someone can help me to handle the multiple cameras and such.
Some phones, if you just ask for 'environment' camera, you'll get something wide-angle that fish-eyes the view, making it near impossible to scan the barcodes. Others, you'll get something that works well. My brand new Moto G Stylus works fantastically with the default camera device, and it even auto-focuses, which is not something we can control inside the browser currently.
Yes, I am currently using a Samsung Galaxy S20+. When I'm trying to set facingMode to environment (with getUserMedia) it returns "NotReadableError: Could not start video source".
But when trying enumerateDevices(). it shows a list of inputdevices :) where I can choose from.
how do I call enumerateDevices()?
@ericblade @tinnielam @seanhussey @UziTech
Hey
So I gone and made my first crack at it and I do have a few questions (not sure if I should start a new post?)
if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
// safely access `navigator.mediaDevices.getUserMedia`
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
<script src="bootstrap.js"></script>
<script src="all.js"></script>
<script src="main.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="all.css">
</head>
<body>
<section class="test">
<div id="container">
<select id="cameras-selection">
</select>
<div id="close-camera-container">
<i class="far fa-times-circle fa-2x"></i>
</div>
</div>
<div class="form-group has-feedback">
<input type="text" class="form-control search-box" />
<i class="fas fa-barcode form-control-feedback glyphicon fa-5x"></i>
</div>
</section>
</body>
</html>
js
$(function () {
$("#close-camera-container").click(function () {
hideCamera();
});
function hideCamera() {
$("#container").hide();
$(".form-group").show();
Quagga.stop();
}
function startCamera() {
Quagga.start();
$("#container").show();
$(".form-group").hide();
}
function setupCameraSelectList() {
navigator.mediaDevices.enumerateDevices().then(function (devices) {
var $cameras = $("#cameras-selection");
$cameras.empty();
var videoInputs = devices.filter(function (device) {
return device.kind === "videoinput";
});
if (videoInputs.length == 1) {
$cameras.append(
new Option(videoInputs[0].label, videoInputs[0].deviceId)
);
} else {
$cameras.append(new Option("Select a Camera", ""));
devices.forEach((device) => {
if(device.deviceId !== "default"){
$cameras.append(new Option(device.label, device.deviceId));
}
});
}
});
}
$(".fa-barcode").click(function () {
$("#container").hide();
Quagga.init(
{
inputStream: {
name: "Live",
type: "LiveStream",
target: "#container",
constraints: {
width: {min: 320},
height: {min: 240},
facing: "environment", // or user
},
},
decoder: {
readers: ["upc_reader", "ean_reader"],
},
},
function (err) {
if (err) {
console.log(err);
return;
}
startCamera();
setupCameraSelectList();
}
);
});
Quagga.onDetected(function (result) {
if (result.codeResult) {
$(".search-box").val(result.codeResult.code);
hideCamera();
}
});
Quagga.onProcessed(function (result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(
0,
0,
parseInt(drawingCanvas.getAttribute("width")),
parseInt(drawingCanvas.getAttribute("height"))
);
result.boxes
.filter(function (box) {
return box !== result.box;
})
.forEach(function (box) {
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, {
color: "green",
lineWidth: 2,
});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, {
color: "#00F",
lineWidth: 2,
});
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(
result.line,
{ x: "x", y: "y" },
drawingCtx,
{ color: "red", lineWidth: 3 }
);
}
}
});
});
css
@media only screen and (max-width: 600px) {
.fa-barcode {
right: 20px !important;
}
#cameras-selection {
position: absolute;
pointer-events: visible;
margin: 10px;
}
#close-camera-container {
position: absolute;
right: 0;
margin: 10px;
pointer-events: visible;
}
}
#container {
display: none;
pointer-events: none;
width: 320px;
height: 240px;
}
.test {
width: 100%;
}
In your constraints, you can specify the deviceId instead of the facing, to get a specific camera device. i think it's actually supposed to be something like deviceId: { exact: "...deviceId..." }
but just specifying a deviceId seems to work.
I honestly have not been able to figure out how to get my layout to do exactly what I want on mobile, Chrome on mobile acts differently than Chrome on desktop with video elements that are displaying cameras, and i'm not sure how it's different to try to adjust. I got my app mostly displaying something sort of what i want, via trial and error, and stopped fiddling with it at that point. :|
I noticed when I just switched from a LG G6 to a Moto G Stylus, though, that with the G Stylus, I get what appears to be the entire camera viewfinder in the page, whereas my G6 only gets about half. There's probably something that I don't understand, so I can't make sense out of it.
If you're not seeing drawing results, but your onProcessed is running the code, it's probably something in the html/css causing the canvas to not be visible, or to be under something.
In your constraints, you can specify the deviceId instead of the facing, to get a specific camera device. i think it's actually supposed to be something like
deviceId: { exact: "...deviceId..." }
but just specifying a deviceId seems to work.I honestly have not been able to figure out how to get my layout to do exactly what I want on mobile, Chrome on mobile acts differently than Chrome on desktop with video elements that are displaying cameras, and i'm not sure how it's different to try to adjust. I got my app mostly displaying something sort of what i want, via trial and error, and stopped fiddling with it at that point. :|
I noticed when I just switched from a LG G6 to a Moto G Stylus, though, that with the G Stylus, I get what appears to be the entire camera viewfinder in the page, whereas my G6 only gets about half. There's probably something that I don't understand, so I can't make sense out of it.
If you're not seeing drawing results, but your onProcessed is running the code, it's probably something in the html/css causing the canvas to not be visible, or to be under something.
Do I have to call the whole init function again when I want to set the deviceId? Or is there some other function I can call? I am still looking through the example code and see it has a dropdown that allows you to change the device but I have not really found what it actually changes.
So you have not been able to get it full screen on mobile either?
I know the processing is working as I do see the boxes that codes is drawing, the thing is it is drawing it like in the wrong areas. I don't know if I need more css styling as I saw what I think is alot of styling in the example css but since there is no comments on it I have no clue how it works.
oh, right, i misunderstood. That likely means that your drawing canvas is not positioned over the top of your video element. I get the same situation when I rotate my phone, because I think Chrome automatically rotates the video element, but doesn't do anything with the canvas, and I haven't investigated how to handle that yet.
If you have Quagga already running (Quagga.start()) then call Quagga.stop() before calling Quagga.init() again. That should work. Otherwise, if you haven't called start() already then just call init() again. I think. :-D
In the first version of my app, I tried to do a full-screen but I'd always end up with half of the video element off screen, OR it only taking up a quarter of the screen. Now I've got it inline in my page, in a little drawer that slides out, and imo, it looks quite nice like that. :)
oh, right, i misunderstood. That likely means that your drawing canvas is not positioned over the top of your video element. I get the same situation when I rotate my phone, because I think Chrome automatically rotates the video element, but doesn't do anything with the canvas, and I haven't investigated how to handle that yet.
If you have Quagga already running (Quagga.start()) then call Quagga.stop() before calling Quagga.init() again. That should work. Otherwise, if you haven't called start() already then just call init() again. I think. :-D
In the first version of my app, I tried to do a full-screen but I'd always end up with half of the video element off screen, OR it only taking up a quarter of the screen. Now I've got it inline in my page, in a little drawer that slides out, and imo, it looks quite nice like that. :)
what do you set your width and height too?
Is it possible to see your html and css? I am still trying to get the sizing right and the boxes correct.
Also anyone have tips on how to reduce false positives? I am doing UPC/EAN
This could really use some cleanup, I've hacked it together from posts on the original repo, as well as my own ideas
function getMedian(arr) {
const sorted = [...arr].sort((a, b) => a - b);
const half = Math.floor(sorted.length / 2);
if (arr.length % 2 === 1) {
return arr[half];
}
return (arr[half - 1] + arr[half]) / 2;
}
function getMedianOfCodeErrors(decodedCodes) {
const errors = decodedCodes.filter((x) => x.error !== undefined).map((y) => y.error); // TODO: use reduce
const median = getMedian(errors);
return { probablyValid: !(median > 0.10 || errors.some((err) => err > 0.1)), median };
}
const errorCheck = useCallback((result) => {
if (!onDetected) {
return;
}
const err = getMedianOfCodeErrors(result.codeResult.decodedCodes);
const validated = barcodeValidator(result.codeResult.code);
console.warn('* errorCheck', result.codeResult.code, err, validated);
if (err.probablyValid || (err.median < 0.25 && validated.valid === true && (validated.type === 'upc' || validated.type === 'isbn10' || validated.type === 'isbn13'))) {
const resultCopy = { ...result };
resultCopy.codeResult.code = validated.modifiedCode;
onDetected(result);
}
}, [onDetected]);
basically, if the median of errors is less than 0.1, accept it. if the median of errors is less than 0.25 and it appears to be a valid upc, isbn10, or isbn13, then accept it. otherwise, continue scanning
I believe this is a simplified version of what i'm using https://github.com/ericblade/quagga2-react-example though it has been a while since i put that together, and there's probably ways to improve it.
This could really use some cleanup, I've hacked it together from posts on the original repo, as well as my own ideas
function getMedian(arr) { const sorted = [...arr].sort((a, b) => a - b); const half = Math.floor(sorted.length / 2); if (arr.length % 2 === 1) { return arr[half]; } return (arr[half - 1] + arr[half]) / 2; } function getMedianOfCodeErrors(decodedCodes) { const errors = decodedCodes.filter((x) => x.error !== undefined).map((y) => y.error); // TODO: use reduce const median = getMedian(errors); return { probablyValid: !(median > 0.10 || errors.some((err) => err > 0.1)), median }; } const errorCheck = useCallback((result) => { if (!onDetected) { return; } const err = getMedianOfCodeErrors(result.codeResult.decodedCodes); const validated = barcodeValidator(result.codeResult.code); console.warn('* errorCheck', result.codeResult.code, err, validated); if (err.probablyValid || (err.median < 0.25 && validated.valid === true && (validated.type === 'upc' || validated.type === 'isbn10' || validated.type === 'isbn13'))) { const resultCopy = { ...result }; resultCopy.codeResult.code = validated.modifiedCode; onDetected(result); } }, [onDetected]);
basically, if the median of errors is less than 0.1, accept it. if the median of errors is less than 0.25 and it appears to be a valid upc, isbn10, or isbn13, then accept it. otherwise, continue scanning
I believe this is a simplified version of what i'm using https://github.com/ericblade/quagga2-react-example though it has been a while since i put that together, and there's probably ways to improve it.
ok, I will try this and see how it goes. I need EAN so I guess I would need to add that to the if statement?
Also where do I put this code? Is this on processes?
@tinnielam
Hey, can you please test my POC on your samsung s20 and let me know if it works(ie can you scan a barcode) and if it did not work what browser
https://barcodes.z4.web.core.windows.net/
I sent it out for testing and I heard on 1 s20 it works and on another it did not.
@chobo2 your POC works yes.
@chobo2 your POC works yes.
So it could load up and Scan a barcode (did the rectangles go over the right area?)
Could you please tell me what version of Android and which browser you used? Also is your s20 plus or s20?
Thanks
Hi
I want to make sure this library is right for me.
What I am trying to achieve is this. I want to go on an andriod device (think phone, maybe tablet), load up chrome or firefox, go to my site click a button and load up the devices camera and scan a bar code (mostly EAN-8/13).
I want to do the same thing on apple devices (iphone and ipad), load up safari (not sure if they got chrome and Firefox on these devices. I don't own apple), click a button and load up their devices cameras and scan a bar code.
I seen that there been some issues with iOS and wanted to double check that I will be able to do barcode scanning at least through safari and what versions of safari do they need.