zxing-js / ngx-scanner

Angular QR code, Barcode, DataMatrix, scanner component using ZXing.
https://zxing-js.github.io/ngx-scanner/
MIT License
640 stars 228 forks source link

v3.2.0: Camera does not get freed after route change #425

Open raketenwurm69 opened 3 years ago

raketenwurm69 commented 3 years ago

I am using Angular v12. ngx-scanner v3.2.0. Chrome browser under Android 9

Initialy the scanner works fine, but after a route change the camera does not get freed and therefore when entering the scanner page again it can not get a video stream resulting in an error.

When I switch back to v3.0.0 by using npm install @zxing/ngx-scanner@3.0.0 --force everything works fine again.

L96Github commented 3 years ago

I also have this bug in version 3.2.0. Last version I remember working properly is 3.0.1.

L96Github commented 3 years ago

I also have this bug in version 3.2.0. Last version I remember working properly is 3.0.1.

I can confirm that downgrading to 3.0.1 makes the bug go away.

LennartVandoorne commented 3 years ago

Sadly downgrading never makes bugs just go away. Version 3.0.1 doesn't work with my Angular 12 project. Is there any hope on fixing this issue any time soon?

rvalitov commented 3 years ago

I faced this issue with version 3.2.0. I have to use "@zxing/library": "^0.18.6" , see https://github.com/zxing-js/ngx-scanner/issues/428#issuecomment-937800191

The camera is not freed when I use enabled attribute, example:

<zxing-scanner
      [enable]="enabled"
      previewFitMode="cover"
    >
</zxing-scanner>

If I completely remove this attribute, then everything works fine. During the tests I used desktop Chrome browser. It indicates with a red dot if camera is in use: image

Using this indicator it's easy to tell when camera is freed or not.

lazoapostolovski commented 3 years ago

I think that i found the issue but maybe i am wrong. I am using Chrome and camera does not get freed after route change.

I have noticed that navigator.media.Devices.getUserMedia({video: true}) is used in multiple places (library, browser and ngx-scanner).

I did a simple experiment like this:

var streams = []; streams.push(navigator.mediaDevices.getUserMedia({ video: true })); streams.push(navigator.mediaDevices.getUserMedia({ video: true })); streams.push(navigator.mediaDevices.getUserMedia({ video: true }));

after this if i execute next line: streams[0].then(str => str.getTracks()[0].stop())

stream still stays open (i can see camera flash still in use and camera is still in use) after I execute same line for all items in streams:

streams[1].then(str => str.getTracks()[0].stop()) streams[2].then(str => str.getTracks()[0].stop())

camera is released.

I have noticed this behaviour on my machine, i wonder if someone else can notice it too. If this is the case, there should be only one place from where this camera source is provided (singleton use) and closed there before opening another stream.

lazoapostolovski commented 3 years ago

I had a better look in the problem. It is indeed creating multiple streams but it is not problem in browser library. Functions from browser library returnis scannerControls and zxing-component is responsible for stopping the stream. In zxing-component functions ngOnInit() and enable(boolean) are called one after another realy fast which is causing multiple streams to be opened and only last one will be used and closed while first one will be untrackable. Probably because async function calls and Promise.

Removing init() call from ngOnInit() on zxing-component solves this problem.

But if you change enable state (to true, false, true, false, true) multiple times in realy short period, it would also create multiple streams that are not trackable and only last one will be used and closed. This issue can be fixed in Browser library by introducing monitoring over created streams, and calling a function that will release all of them when ngOnClose() is called.

Here is the patch for browser library: [#425]_-_v3_2_0__Camera_does_not_get_freed_after_route_change.patch.txt

And here it is for zxing-scanner component: [#425]_-_v3_2_0__Camera_does_not_get_freed_after_route_change_zxing.patch.txt

werthdavid commented 3 years ago

Sorry guys I don't have time to maintain the component but if anybody can make a PR I'll be happy to merge it and create a new release

rvalitov commented 3 years ago

@lazoapostolovski Will make a PR?

DawidKuzminski commented 2 years ago

Hello :) any news, when publish will be done?

rvalitov commented 2 years ago

@Daskii I'm testing the new code now... Hopefully a new release will be published in the upcoming days.

DawidKuzminski commented 2 years ago

In version 3.3.0 not get freed camera can still occurred if we click like crazy (for example scan button which open scan modal and fast close this modal..and again click scan...and so on....) But from now on, after "normally" open and close scanner modal all existing streams will be closed. Probably we also can implement some checker which will call BrowserCodeReader.releaseAllStreams (); for such strange cases as the one described above :)

Thank a lot @rvalitov for the release!

rvalitov commented 2 years ago

Thank you for sharing your case. Can you please give some details?

  1. How do you check/debug that the streams are not closed?
  2. How do you use the ngx-scanner inside the modal? Only by putting <zxing-scanner></zxing-scanner> inside your HTML, or do you also communicate with the scanner as described here https://github.com/zxing-js/ngx-scanner/wiki/Advanced-Usage#logic-side-typescript ?
ca-milin15 commented 2 years ago

I also have this bug in version 3.2.0. Last version I remember working properly is 3.0.1.

I can confirm that downgrading to 3.0.1 makes the bug go away.

Could you show the configuration, for example, @zxing/browser and @zxing/library versions, please?

DawidKuzminski commented 2 years ago

@rvalitov sorry I had a lot of work :(

  1. Unfortunately I don't remember correctly but I can see that I implemented same logics as is in one of issues deviceId = mediaStream?.getTracks()[0]?.getSettings()?.deviceId; mediaStream.getTracks().forEach((t) => t.stop());

so...probably to check if streams are still open I used mediaStream.getTracks() https://developer.mozilla.org/en-US/docs/Web/API/MediaStream/getTracks

  1. I have additional communication with scanner.
harfyt commented 4 months ago

I had the same problem on the latest versions of Chrome and other browsers. I am using version 3.1.3 After trying all the above suggestions I ended up that the bug comes from [enabled] attribute. Removing this attributes the problem disappears.