Showing
3 changed files
with
45 additions
and
30 deletions
| @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; | @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; | ||
| 6 | import 'package:flutter/services.dart'; | 6 | import 'package:flutter/services.dart'; |
| 7 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; | 7 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; |
| 8 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; | 8 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; |
| 9 | +import 'package:mobile_scanner/src/web/base.dart'; | ||
| 9 | import 'package:mobile_scanner/src/web/jsqr.dart'; | 10 | import 'package:mobile_scanner/src/web/jsqr.dart'; |
| 10 | import 'package:mobile_scanner/src/web/media.dart'; | 11 | import 'package:mobile_scanner/src/web/media.dart'; |
| 11 | 12 | ||
| @@ -42,8 +43,8 @@ class MobileScannerWebPlugin { | @@ -42,8 +43,8 @@ class MobileScannerWebPlugin { | ||
| 42 | // Determine wether device has flas | 43 | // Determine wether device has flas |
| 43 | bool hasFlash = false; | 44 | bool hasFlash = false; |
| 44 | 45 | ||
| 45 | - // Timer used to capture frames to be analyzed | ||
| 46 | - Timer? _frameInterval; | 46 | + final WebBarcodeReaderBase _barCodeReader = JsQrCodeReader(); |
| 47 | + StreamSubscription? _barCodeStreamSubscription; | ||
| 47 | 48 | ||
| 48 | html.DivElement vidDiv = html.DivElement(); | 49 | html.DivElement vidDiv = html.DivElement(); |
| 49 | 50 | ||
| @@ -135,13 +136,14 @@ class MobileScannerWebPlugin { | @@ -135,13 +136,14 @@ class MobileScannerWebPlugin { | ||
| 135 | // required to tell iOS safari we don't want fullscreen | 136 | // required to tell iOS safari we don't want fullscreen |
| 136 | video.setAttribute('playsinline', 'true'); | 137 | video.setAttribute('playsinline', 'true'); |
| 137 | 138 | ||
| 138 | - await video.play(); | ||
| 139 | - | ||
| 140 | - // Then capture a frame to be analyzed every 200 miliseconds | ||
| 141 | - _frameInterval = | ||
| 142 | - Timer.periodic(const Duration(milliseconds: 200), (timer) { | ||
| 143 | - _captureFrame(); | 139 | + _barCodeStreamSubscription = |
| 140 | + _barCodeReader.detectBarcodeContinuously(video).listen((code) { | ||
| 141 | + if (_localStream == null) return; | ||
| 142 | + if (code != null) { | ||
| 143 | + controller.add({'name': 'barcodeWeb', 'data': code}); | ||
| 144 | + } | ||
| 144 | }); | 145 | }); |
| 146 | + await video.play(); | ||
| 145 | 147 | ||
| 146 | return { | 148 | return { |
| 147 | 'ViewID': viewID, | 149 | 'ViewID': viewID, |
| @@ -183,27 +185,7 @@ class MobileScannerWebPlugin { | @@ -183,27 +185,7 @@ class MobileScannerWebPlugin { | ||
| 183 | 185 | ||
| 184 | video.srcObject = null; | 186 | video.srcObject = null; |
| 185 | _localStream = null; | 187 | _localStream = null; |
| 186 | - _frameInterval?.cancel(); | ||
| 187 | - _frameInterval = null; | ||
| 188 | - } | ||
| 189 | - | ||
| 190 | - /// Captures a frame and analyzes it for QR codes | ||
| 191 | - Future<dynamic> _captureFrame() async { | ||
| 192 | - if (_localStream == null) return null; | ||
| 193 | - final canvas = | ||
| 194 | - html.CanvasElement(width: video.videoWidth, height: video.videoHeight); | ||
| 195 | - final ctx = canvas.context2D; | ||
| 196 | - | ||
| 197 | - ctx.drawImage(video, 0, 0); | ||
| 198 | - final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!); | ||
| 199 | - | ||
| 200 | - final code = jsQR(imgData.data, canvas.width, canvas.height); | ||
| 201 | - if (code != null) { | ||
| 202 | - controller.add({ | ||
| 203 | - 'name': 'barcodeWeb', | ||
| 204 | - 'data': code.data, | ||
| 205 | - 'binaryData': code.binaryData, | ||
| 206 | - }); | ||
| 207 | - } | 188 | + await _barCodeStreamSubscription?.cancel(); |
| 189 | + _barCodeStreamSubscription = null; | ||
| 208 | } | 190 | } |
| 209 | } | 191 | } |
lib/src/web/base.dart
0 → 100644
| 1 | @JS() | 1 | @JS() |
| 2 | library jsqr; | 2 | library jsqr; |
| 3 | 3 | ||
| 4 | +import 'dart:async'; | ||
| 5 | +import 'dart:html'; | ||
| 4 | import 'dart:typed_data'; | 6 | import 'dart:typed_data'; |
| 5 | 7 | ||
| 6 | import 'package:js/js.dart'; | 8 | import 'package:js/js.dart'; |
| 9 | +import 'package:mobile_scanner/src/web/base.dart'; | ||
| 7 | 10 | ||
| 8 | @JS('jsQR') | 11 | @JS('jsQR') |
| 9 | external Code? jsQR(dynamic data, int? width, int? height); | 12 | external Code? jsQR(dynamic data, int? width, int? height); |
| @@ -14,3 +17,28 @@ class Code { | @@ -14,3 +17,28 @@ class Code { | ||
| 14 | 17 | ||
| 15 | external Uint8ClampedList get binaryData; | 18 | external Uint8ClampedList get binaryData; |
| 16 | } | 19 | } |
| 20 | + | ||
| 21 | + | ||
| 22 | +class JsQrCodeReader extends WebBarcodeReaderBase { | ||
| 23 | + // Timer used to capture frames to be analyzed | ||
| 24 | + final frameInterval = const Duration(milliseconds: 200); | ||
| 25 | + | ||
| 26 | + @override | ||
| 27 | + Stream<String?> detectBarcodeContinuously(VideoElement video) async* { | ||
| 28 | + yield* Stream.periodic(frameInterval, (_) { | ||
| 29 | + return _captureFrame(video); | ||
| 30 | + }).asyncMap((event) async => (await event)?.data); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + /// Captures a frame and analyzes it for QR codes | ||
| 34 | + Future<Code?> _captureFrame(VideoElement video) async { | ||
| 35 | + final canvas = CanvasElement(width: video.videoWidth, height: video.videoHeight); | ||
| 36 | + final ctx = canvas.context2D; | ||
| 37 | + | ||
| 38 | + ctx.drawImage(video, 0, 0); | ||
| 39 | + final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!); | ||
| 40 | + | ||
| 41 | + final code = jsQR(imgData.data, canvas.width, canvas.height); | ||
| 42 | + return code; | ||
| 43 | + } | ||
| 44 | +} |
-
Please register or login to post a comment