From 089557e6fc6bf48e1256e6b15dc7c6863d6ce784 Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 27 Feb 2021 01:17:26 -0500 Subject: [PATCH 1/2] testing bug fix for android 11 freezing and other chromes not releasing camera - also update several bits of the typescript spec that have not been updated when the relevant code changed - internal Quagga module stop() returns Promise instead of void, and external Quagga module returns same value -- this allows you to wait until the stop code has hopefully completely executed before continuing on with whatever you do next. You can also choose to ignore the return, just as we always have done. - internal Quagga module keeps track of the HTMLVideoElement that was last requested, and if there is one, it calls pause() on it, before calling stop() on the MediaStreamTrack - The stop() is called on MediaStreamTrack asynchronously, to give the browser a chance to actually pause, before disconnecting. - See https://github.com/ericblade/quagga2/issues/266 and all linked issues on it for an incredible amount of detail related to this problem - I have to commit this before I can fully test it, as I don't have access to Android 11 or a Pixel device. I also need to test it on my production server, so this is going straight to a new version, without actual testing. My apologies, but this is one of those bits that are very difficult to test outside of real life because of the limitations of using cameras and such :| - QuaggaJSCameraAccess::stop() now also returns Promise. - Quagga.init() is marked to return Promise - QuaggaJSCameraAccess::request() is correctly marked as having optional videoConstraints now --- package.json | 2 +- src/input/camera_access.ts | 24 ++++++++++++++------ src/input/test/browser/camera_access.spec.ts | 2 +- src/quagga.js | 6 ++--- src/quagga/quagga.ts | 4 ++-- type-definitions/quagga.d.ts | 11 +++++---- 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 732e5604..54ff5ce8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ericblade/quagga2", - "version": "1.2.1", + "version": "1.2.3", "description": "An advanced barcode-scanner written in JavaScript", "main": "lib/quagga.js", "types": "type-definitions/quagga.d.ts", diff --git a/src/input/camera_access.ts b/src/input/camera_access.ts index 8f583229..da15b5da 100644 --- a/src/input/camera_access.ts +++ b/src/input/camera_access.ts @@ -1,6 +1,6 @@ import pick from 'lodash/pick'; import { getUserMedia, enumerateDevices } from '../common/mediaDevices'; -import { MediaTrackConstraintsWithDeprecated } from '../../type-definitions/quagga.d'; +import { MediaTrackConstraintsWithDeprecated, QuaggaJSCameraAccess as CameraAccessType } from '../../type-definitions/quagga.d'; let streamRef: MediaStream | null; @@ -92,18 +92,28 @@ function getActiveTrack(): MediaStreamTrack | null { /** * Used for accessing information about the active stream track and available video devices. */ -const QuaggaJSCameraAccess = { +const QuaggaJSCameraAccess: CameraAccessType = { + requestedVideoElement: null, async request(video: HTMLVideoElement, videoConstraints?: MediaTrackConstraintsWithDeprecated): Promise { + QuaggaJSCameraAccess.requestedVideoElement = video; const newConstraints = await pickConstraints(videoConstraints); return initCamera(video, newConstraints); }, - release(): void { - // TODO: i wonder if telling the Video element to pause() before calling MediaStreamTrack.stop() would alleviate some of the issues with the camera appearing to stay open on Android even after stopping. + release(): Promise { const tracks = streamRef && streamRef.getVideoTracks(); - if (tracks && tracks.length) { - tracks[0].stop(); + if (QuaggaJSCameraAccess.requestedVideoElement !== null) { + QuaggaJSCameraAccess.requestedVideoElement.pause(); } - streamRef = null; + return new Promise((resolve) => { + setTimeout(() => { + if (tracks && tracks.length) { + tracks[0].stop(); + } + streamRef = null; + QuaggaJSCameraAccess.requestedVideoElement = null; + resolve(); + }); + }); }, enumerateVideoDevices, getActiveStreamLabel(): string { diff --git a/src/input/test/browser/camera_access.spec.ts b/src/input/test/browser/camera_access.spec.ts index bfe8e900..bc5d824b 100644 --- a/src/input/test/browser/camera_access.spec.ts +++ b/src/input/test/browser/camera_access.spec.ts @@ -112,7 +112,7 @@ describe('CameraAccess (browser)', () => { it('works', async () => { const video = document.createElement('video'); await Quagga.CameraAccess.request(video, {}); - Quagga.CameraAccess.release(); + await Quagga.CameraAccess.release(); expect(((video?.srcObject) as any)?.active).to.equal(false); }); }); diff --git a/src/quagga.js b/src/quagga.js index 694fc740..9ab9a353 100644 --- a/src/quagga.js +++ b/src/quagga.js @@ -1,3 +1,4 @@ +import merge from 'lodash/merge'; import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars import ImageWrapper from './common/image_wrapper'; import BarcodeDecoder from './decoder/barcode_decoder'; @@ -7,7 +8,6 @@ import CameraAccess from './input/camera_access'; import ImageDebug from './common/image_debug'; import ResultCollector from './analytics/result_collector'; import Config from './config/config'; -import merge from 'lodash/merge'; import Quagga from './quagga/quagga'; @@ -39,10 +39,10 @@ const QuaggaJSStaticInterface = { return promise; }, start: function () { - instance.start(); + return instance.start(); }, stop: function () { - instance.stop(); + return instance.stop(); }, pause: function () { _context.stopped = true; diff --git a/src/quagga/quagga.ts b/src/quagga/quagga.ts index 885c983c..5429e48c 100644 --- a/src/quagga/quagga.ts +++ b/src/quagga/quagga.ts @@ -256,11 +256,11 @@ export default class Quagga { } } - stop(): void { + async stop(): Promise { this.context.stopped = true; QWorkers.adjustWorkerPool(0); if (this.context.config?.inputStream && this.context.config.inputStream.type === 'LiveStream') { - CameraAccess.release(); + await CameraAccess.release(); this.context.inputStream.clearEventHandlers(); } } diff --git a/type-definitions/quagga.d.ts b/type-definitions/quagga.d.ts index 9e901e5b..6c2b1dfd 100644 --- a/type-definitions/quagga.d.ts +++ b/type-definitions/quagga.d.ts @@ -118,13 +118,13 @@ export interface QuaggaJSStatic { init( config: QuaggaJSConfigObject, callback?: (err: any) => void - ): void; + ): Promise; init( config: QuaggaJSConfigObject, callback: (err: any) => void, imageWrapper: ImageWrapper, - ): void; + ): Promise; /** * When the library is initialized, the start() @@ -139,7 +139,7 @@ export interface QuaggaJSStatic { * Additionally, if a camera-stream was requested upon initialization, * this operation also disconnects the camera. */ - stop(): void; + stop(): Promise; /** * Pauses processing, but does not release any handlers @@ -220,9 +220,10 @@ export interface QuaggaJSStatic { * Used for accessing information about the active stream track and available video devices. */ export interface QuaggaJSCameraAccess { - request(video: HTMLVideoElement, videoConstraints: MediaTrackConstraintsWithDeprecated): Promise | never; + requestedVideoElement: HTMLVideoElement | null, + request(video: HTMLVideoElement, videoConstraints?: MediaTrackConstraintsWithDeprecated): Promise | never; - release(): void; + release(): Promise; enumerateVideoDevices(): Promise | never; From 8552cce3fafcd986b936086326d3426fe21e5d7d Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 27 Feb 2021 01:46:04 -0500 Subject: [PATCH 2/2] fix import test after previous changes --- test/test-import.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-import.js b/test/test-import.js index bf2c65a3..89e17a4b 100644 --- a/test/test-import.js +++ b/test/test-import.js @@ -15,7 +15,7 @@ describe('testing node import', () => { const { CameraAccess: CA } = Q; expect(CA).to.be.an('object').with.keys([ 'request', 'release', 'enumerateVideoDevices', - 'getActiveStreamLabel', 'getActiveTrack', + 'getActiveStreamLabel', 'getActiveTrack', 'requestedVideoElement', ]); }); });