Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot read property 'x' of null #218

Open
UziTech opened this issue Jul 17, 2020 · 8 comments
Open

Cannot read property 'x' of null #218

UziTech opened this issue Jul 17, 2020 · 8 comments

Comments

@UziTech
Copy link
Contributor

UziTech commented Jul 17, 2020

I get this error from this line:

_numPatches.x = _currentImageWrapper.size.x / _patchSize.x | 0;

It looks like _patchSize can be null. I would create a PR to fix this but I'm not sure what you want to happen in the case that _patchSize is null.

@github-actions
Copy link

Thank you for filing an issue! Please be patient. :-)

@ericblade
Copy link
Owner

Interesting. I'll have to take a look through there and see what happens. I think we had a similar error related to setting very large image sizes, and I increased the amount of array allocation to accommodate. Could you post the config that is causing the error?

@UziTech
Copy link
Contributor Author

UziTech commented Jul 17, 2020

I'm not exactly sure what config is causing this. It is part of an internal application for scanning equipment barcodes. It seems to happen sporadically and only on one device (an iPhone 8).

Interestingly, it doesn't actually crash the application so the user doesn't notice but goes into our logs as an error.

I'll try to see if I can extract more information into our logs when this happens.

@ericblade
Copy link
Owner

yeah, it is totally possible for calculatePatchSize to return null... so it should be guarded against in locator.. i'm not sure if i may have accidentally removed a guard while refactoring, or if it's just a really rare condition that doesn't usually get hit..

Since that is being hit in initBuffers(), which I believe is called in Quagga.init(), i'm not sure how it could be actually working. Very strange.

I'd like to see the config being passed to init(), i don't think I know specifically how to replicate it. So I'm not exactly sure how to guard it -- should it fail init() completely, or should it pass a bunch of 0's to the rest of the function?

@UziTech
Copy link
Contributor Author

UziTech commented Jul 20, 2020

Here is the config that made the call to init fail

await Quagga.init({
	"numOfWorkers": 4,
	"frequency": 10,
	"locator": {
		"patchSize": "large",
		"halfSample": false,
	},
	"decoder": {
		"readers": ["code_39_reader"],
	},
	"inputStream": {
		"type": "LiveStream",
		"target": stream,
		"constraints": {},
	},
	"locate": true,
	"src": null,
});

@UziTech
Copy link
Contributor Author

UziTech commented Jul 20, 2020

After quite a bit of debugging it looks like the issue happens when the inputStream.target doesn't have a width or height (because it is not attached to the dom).

This happens in our application when a button is clicked more than one time. the button detaches the last inputStream.target and attaches a new one. If the button is clicked quickly the first call to init doesn't finish before it is detached.

I fixed this in our code but it seems like there should still be a more relevant error thrown if the inputStream.target` doesn't have a width and height.

@ericblade
Copy link
Owner

Good find. I'm still not sure if that should throw an error or if it should just pass all 0's to the remainder of the function. I've been spending most of my programming time on other projects but I'll do some thinking / research on this one and see what i can find. i know there's already an issue to investigate spamming start/stop potentially causing problems, which this seems like it would be related to.

can you share what your fix in your code involved?

@UziTech
Copy link
Contributor Author

UziTech commented Jul 21, 2020

I queued the call to init so if the button is pressed three times in quick succession the first press will call init the second press will be queued to be called after the first finishes and the third press will replace the second in the queue since the first one hadn't finished yet and cancel the second init. Once the first init is done I run stop since there is another one queued and the third press will be executed.

Here is the code I used to do that:

	// this function called on button press to start the stream
	async startCapture() {
		if (this.capturing) {
			this.stopCapture();
		}

		this.codes = [];
		try {
			const success = await this.queueInit(this.mapState());
			if (!success) {
				// another call queued while this one was queued
				return;
			}
		} catch (ex) {
			// stop stream if `init` finished but there is another call queued
			Quagga.stop();
			if (ex) {
				throw ex;
			}
			return;
		}
		// `init` finished and not overridden by another call
		Quagga.start();
		this.capturing = true;
	}

	callInit(init) {
		let failed = false;
		this.currInit = Quagga.init(init.config)
			.catch((ex) => {
				// throw `ex` but still call `nextInit` if exists
				init.reject(ex);
				failed = true;
			})
			.then(() => {
				if (this.nextInit) {
					// start next call to `init`
					if (!failed) {
						init.reject();
					}
					const nextInit = this.nextInit;
					this.nextInit = null;
					this.callInit(nextInit);
				} else {
					// last call so continue `startCapture`
					this.currInit = null;
					if (!failed) {
						init.resolve(true);
					}
				}
			});
	}

	queueInit(config) {
		let init;
		const promise = new Promise((resolve, reject) => {
			init = {
				config,
				resolve,
				reject,
			};
		});
		if (this.currInit) {
			if (this.nextInit) {
				// cancel current queued call
				this.nextInit.resolve(false);
			}
			this.nextInit = init;
		} else {
			// no current calls
			this.callInit(init);
		}
		return promise;
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants