Ngx-scanner: Cannot properly close videostream due to error

Created on 7 Aug 2018  路  28Comments  路  Source: zxing-js/ngx-scanner

Describe the bug
First of all, thanks for all the hard work you guys have put in the scanner! It works awesome.

If I scan something and then try to destroy the video element (to stop the stream) I'm ending up getting this error:

Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)

at BrowserQRCodeReader.BrowserCodeReader.createBinaryBitmap (zxing-ngx-scanner.js:175)
at BrowserQRCodeReader.BrowserCodeReader.decode (zxing-ngx-scanner.js:157)

I tried to put it into an ngIf to destroy the element and I've tried to stop it through scannerEnabled input.

To Reproduce
Steps to reproduce the behavior:
Open app an active scanner. Then just toggle the scanner of using a ngIf on the top element. Or toggle the scannerEnabled input to false.

Expected behavior
Just detroy the video and stop the stream.

Desktop (please complete the following information):

  • OS: osX
  • Browser Chrome
  • Version 67.0.3396.99
  • scanner version ^1.2.0
investigation

Most helpful comment

I took a little time to rest my mind and make a good job this time. I'll publish the updates until this month.

All 28 comments

Have you tryied to run the stop method from the scanner component before destroying the HTML element?

@odahcam how do I actually call that stop method? Since I can't seem to find it anywhere (or I'm looking in the wrong place :-))

Follow the example:

<zxing-scanner #scanner start="true" (scanSuccess)="handleQrCodeResult($event)"></zxing-scanner>
export class MyComponent implements OnInit {

    @ViewChild('scanner')
    scanner: ZXingScannerComponent;

    ngOnInit() {
        this.scanner.stop();
    }

}

馃

Well sounds reasonable, but It's telling me that the method doesn't exist. Could it be a versioning thing? I'm currently on version 1.2.0.

Oh damn! You're right, give a try to resetScan.

It's actually giving me the same error and it doesn't stop the stream (as you would expect).

core.js:1350 ERROR TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
at BrowserQRCodeReader.BrowserCodeReader.createBinaryBitmap (zxing-ngx-scanner.js:175)
at BrowserQRCodeReader.BrowserCodeReader.decode (zxing-ngx-scanner.js:157)

We may found a bug then. I'll try to debug this tonight.

I'm noticing that if I actually do get a scan result, everything seems to work fine. No errors whatsoever.

Getting the same error here? Any chance it'll be fixed soon or have someone found some workaround?
Thanks for the scanner guys, even with this error it works really fast.

That's probably because of the stream I've implemented in the latest releases, before that the scanning stream was recursive and I'm pretty sure I gonna have to reimplement something like that again. Oh, and thanks @mathudak .

I'm having he same error, when calling resetScan() as well. I've tied unsubscribing from all events, setting the scanner's device property to null, but to no avail. Any ideas? I'm hosting the scanner in a modal dialog. What I want to do is stop the scanner once it receives a code, close the dialog and return to the parent component.

Having the same problem.

Tried resetScan() as well.

I'm having the same problem. The issue appears to originate with the "decodeWithDelay" being called after the video/image container have been removed by Angular. Adding a simple check for the presence of these elements seems to remove the error. See my patch below:

 BrowserCodeReader.prototype.decodeWithDelay = function (delay) {
        var _this = this;
        if (delay === void 0) { delay = 500; }
        return Observable.create(function (observer) {
            var intervalId = setInterval(function () {
                try {
                  if (this.videoElement || this.imageElement)
                    observer.next(_this.decode());
                }
                catch (err) {
                    observer.error(err);
                }
            }, delay);
            return function () { return clearInterval(intervalId); };
        });
    };

In general, it would be nice if we could stop the interval in ngDestroy() to prevent the code from even being reached.

That makes sense, I'm rewriting the decode stream methods, so it should be fixed soon.

Hi @odahcam, any updates on this yet? Thanks! Appreciate all the effort you've put in so far!

I'm heavily busy at the moment, just doing superficial things. I think that by the start of December I will have some free time again to code the new version we need. Also I done some work on this, I think I have some pending changes to release, by this week everything pending will be released and I will post the updates in the release notes. Thanks and sorry for the long wait (even I am eating for this haha).

Haha @odahcam That's absolutely no problem, I can fully understand. If you need a hand on the project, please let me know. Perhaps I can chip in.

Hi @odahcam, I've just updated to new version(npm 1.3.0) and replaced function _resetScan()_ with resetCodeReader() and I'm not getting this error any more (Safari)

Thx @mathudak, I'll soon release some updates on that, hope to fix this very soon.

Hello @odahcam Still getting this error on iOS safari with both resetCodeReader() and resetScan().

I'm currently working on a big update, this is the last item to finish. 馃槃

@odahcam That's good news! How's the progress?

I took a little time to rest my mind and make a good job this time. I'll publish the updates until this month.

I have found that this problem is actually caused by listening to the video element 'playing' event instead of the 'play' event, at least in my Firefox browser.

The 'playing' event triggers twice while the component is initializing, setting up two observables that read from the video element. Only one of these (the last) is stored in this.decodingStream, and so only one is correctly stopped. The other one will try to read once after component is destroyed, and trigger the 'drawImage' error (after which it dies).

If I modify BrowserCodeReader.bindEvents to listen to the 'play' event instead, this problem goes away.

Also, the construction in BrowserCodeReader.decodeWithDelay with a setInterval-based observable that errors every time it tries and fails to read a code, seems a little strange. Provided there is not always a code in front of the camera, the timer will fire once, fail to read a code, call observer.error which results in an unsubscribe, stopping the timer. It will then be followed by another subscribe, setting up a new timer that also only fires once - until a code is read.

I've had the same problem now for the third time. I digged into the problem and could not really find an answer. The "solutions" from the top did not really help me out.

So I decided to quickly threw together a replacement to just _work_. Maybe it is useful for somebody else: https://www.npmjs.com/package/ngx-zxing-multicode

Edit: Never mind, just updated to latest version and the error is gone. Thanks!


Hi, are there any updates? I'm encountering the same issue, _resetCodeReader()_ doesn't fix it and the other functions are not available in the version I'm using (1.3.0)

Yeah, I finally did the coding I was talking about, half because I was in home and wanted to do it and half because I needed it in my work, hehe. Thanks for updating @Barruz .

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings