Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
mobile_scanner
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
p-mazhnik
2022-11-21 18:10:57 +0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
4710e24f72ad7573df95eb608871563e86fecf29
4710e24f
1 parent
d3cbaa85
refactor: abstract web scanner
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
125 additions
and
83 deletions
lib/mobile_scanner_web_plugin.dart
lib/src/web/base.dart
lib/src/web/jsqr.dart
lib/mobile_scanner_web_plugin.dart
View file @
4710e24
...
...
@@ -33,10 +33,6 @@ class MobileScannerWebPlugin {
// Controller to send events back to the framework
StreamController
controller
=
StreamController
.
broadcast
();
// The video stream. Will be initialized later to see which camera needs to be used.
html
.
MediaStream
?
_localStream
;
html
.
VideoElement
video
=
html
.
VideoElement
();
// ID of the video feed
String
viewID
=
'WebScanner-
${DateTime.now().millisecondsSinceEpoch}
'
;
...
...
@@ -46,8 +42,6 @@ class MobileScannerWebPlugin {
final
WebBarcodeReaderBase
_barCodeReader
=
JsQrCodeReader
();
StreamSubscription
?
_barCodeStreamSubscription
;
html
.
DivElement
vidDiv
=
html
.
DivElement
();
/// Handle incomming messages
Future
<
dynamic
>
handleMethodCall
(
MethodCall
call
)
async
{
switch
(
call
.
method
)
{
...
...
@@ -68,87 +62,48 @@ class MobileScannerWebPlugin {
/// Can enable or disable the flash if available
Future
<
void
>
_torch
(
arguments
)
async
{
if
(
hasFlash
)
{
final
track
=
_localStream
?.
getVideoTracks
();
await
track
!.
first
.
applyConstraints
({
'advanced'
:
{
'torch'
:
arguments
==
1
}
});
}
else
{
controller
.
addError
(
'Device has no flash'
);
}
// if (hasFlash) {
// final track = _localStream?.getVideoTracks();
// await track!.first.applyConstraints({
// 'advanced': {'torch': arguments == 1}
// });
// } else {
// controller.addError('Device has no flash');
// }
controller
.
addError
(
'Device has no flash'
);
}
/// Starts the video stream and the scanner
Future
<
Map
>
_start
(
Map
arguments
)
async
{
vidDiv
.
children
=
[
video
];
var
cameraFacing
=
CameraFacing
.
front
;
if
(
arguments
.
containsKey
(
'facing'
))
{
cameraFacing
=
CameraFacing
.
values
[
arguments
[
'facing'
]
as
int
];
}
// See https://github.com/flutter/flutter/issues/41563
// ignore: UNDEFINED_PREFIXED_NAME, avoid_dynamic_calls
ui
.
platformViewRegistry
.
registerViewFactory
(
viewID
,
(
int
id
)
=>
vidDiv
..
style
.
width
=
'100%'
..
style
.
height
=
'100%'
,
);
// Check if stream is running
if
(
_
localStream
!=
null
)
{
if
(
_
barCodeReader
.
isStarted
)
{
return
{
'ViewID'
:
viewID
,
'videoWidth'
:
video
.
videoWidth
,
'videoHeight'
:
video
.
videoHeight
'videoWidth'
:
_barCodeReader
.
videoWidth
,
'videoHeight'
:
_barCodeReader
.
videoHeight
};
}
try
{
// Check if browser supports multiple camera's and set if supported
final
Map
?
capabilities
=
html
.
window
.
navigator
.
mediaDevices
?.
getSupportedConstraints
();
if
(
capabilities
!=
null
&&
capabilities
[
'facingMode'
]
as
bool
)
{
final
constraints
=
{
'video'
:
VideoOptions
(
facingMode:
cameraFacing
==
CameraFacing
.
front
?
'user'
:
'environment'
,
)
};
_localStream
=
await
html
.
window
.
navigator
.
mediaDevices
?.
getUserMedia
(
constraints
);
}
else
{
_localStream
=
await
html
.
window
.
navigator
.
mediaDevices
?.
getUserMedia
({
'video'
:
true
});
}
await
_barCodeReader
.
start
(
viewID:
viewID
,
cameraFacing:
cameraFacing
,
);
video
.
srcObject
=
_localStream
;
// TODO: fix flash light. See https://github.com/dart-lang/sdk/issues/48533
// final track = _localStream?.getVideoTracks();
// if (track != null) {
// final imageCapture = html.ImageCapture(track.first);
// final photoCapabilities = await imageCapture.getPhotoCapabilities();
// }
// required to tell iOS safari we don't want fullscreen
video
.
setAttribute
(
'playsinline'
,
'true'
);
_barCodeStreamSubscription
=
_barCodeReader
.
detectBarcodeContinuously
(
video
).
listen
((
code
)
{
if
(
_localStream
==
null
)
return
;
_barCodeStreamSubscription
=
_barCodeReader
.
detectBarcodeContinuously
().
listen
((
code
)
{
if
(
code
!=
null
)
{
controller
.
add
({
'name'
:
'barcodeWeb'
,
'data'
:
code
});
}
});
await
video
.
play
();
return
{
'ViewID'
:
viewID
,
'videoWidth'
:
video
.
videoWidth
,
'videoHeight'
:
video
.
videoHeight
,
'videoWidth'
:
_barCodeReader
.
videoWidth
,
'videoHeight'
:
_barCodeReader
.
videoHeight
,
'torchable'
:
hasFlash
};
}
catch
(
e
)
{
...
...
@@ -172,19 +127,7 @@ class MobileScannerWebPlugin {
/// Stops the video feed and analyzer
Future
<
void
>
cancel
()
async
{
try
{
// Stop the camera stream
_localStream
?.
getTracks
().
forEach
((
track
)
{
if
(
track
.
readyState
==
'live'
)
{
track
.
stop
();
}
});
}
catch
(
e
)
{
debugPrint
(
'Failed to stop stream:
$e
'
);
}
video
.
srcObject
=
null
;
_localStream
=
null
;
_barCodeReader
.
stop
();
await
_barCodeStreamSubscription
?.
cancel
();
_barCodeStreamSubscription
=
null
;
}
...
...
lib/src/web/base.dart
View file @
4710e24
import
'
dart:html
'
;
import
'
package:mobile_scanner/src/enums/camera_facing.dart
'
;
abstract
class
WebBarcodeReaderBase
{
Stream
<
String
?>
detectBarcodeContinuously
(
VideoElement
video
);
/// Timer used to capture frames to be analyzed
final
frameInterval
=
const
Duration
(
milliseconds:
200
);
bool
get
isStarted
;
int
get
videoWidth
;
int
get
videoHeight
;
/// Starts streaming video
Future
<
void
>
start
({
required
String
viewID
,
required
CameraFacing
cameraFacing
,
});
/// Starts scanning QR codes or barcodes
Stream
<
String
?>
detectBarcodeContinuously
();
/// Stops streaming video
Future
<
void
>
stop
();
}
...
...
lib/src/web/jsqr.dart
View file @
4710e24
...
...
@@ -4,10 +4,15 @@ library jsqr;
import
'dart:async'
;
import
'dart:html'
;
import
'dart:typed_data'
;
import
'dart:ui'
as
ui
;
import
'package:flutter/widgets.dart'
;
import
'package:js/js.dart'
;
import
'package:mobile_scanner/src/enums/camera_facing.dart'
;
import
'package:mobile_scanner/src/web/base.dart'
;
import
'media.dart'
;
@JS
(
'jsQR'
)
external
Code
?
jsQR
(
dynamic
data
,
int
?
width
,
int
?
height
);
...
...
@@ -20,18 +25,77 @@ class Code {
class
JsQrCodeReader
extends
WebBarcodeReaderBase
{
// Timer used to capture frames to be analyzed
final
frameInterval
=
const
Duration
(
milliseconds:
200
);
// The video stream. Will be initialized later to see which camera needs to be used.
MediaStream
?
_localStream
;
VideoElement
video
=
VideoElement
();
DivElement
vidDiv
=
DivElement
();
@override
bool
get
isStarted
=>
_localStream
!=
null
;
@override
Stream
<
String
?>
detectBarcodeContinuously
(
VideoElement
video
)
async
*
{
int
get
videoWidth
=>
video
.
width
;
@override
int
get
videoHeight
=>
video
.
height
;
@override
Future
<
void
>
start
({
required
String
viewID
,
required
CameraFacing
cameraFacing
,
})
async
{
vidDiv
.
children
=
[
video
];
// See https://github.com/flutter/flutter/issues/41563
// ignore: UNDEFINED_PREFIXED_NAME, avoid_dynamic_calls
ui
.
platformViewRegistry
.
registerViewFactory
(
viewID
,
(
int
id
)
=>
vidDiv
..
style
.
width
=
'100%'
..
style
.
height
=
'100%'
,
);
// Check if browser supports multiple camera's and set if supported
final
Map
?
capabilities
=
window
.
navigator
.
mediaDevices
?.
getSupportedConstraints
();
if
(
capabilities
!=
null
&&
capabilities
[
'facingMode'
]
as
bool
)
{
final
constraints
=
{
'video'
:
VideoOptions
(
facingMode:
cameraFacing
==
CameraFacing
.
front
?
'user'
:
'environment'
,
)
};
_localStream
=
await
window
.
navigator
.
mediaDevices
?.
getUserMedia
(
constraints
);
}
else
{
_localStream
=
await
window
.
navigator
.
mediaDevices
?.
getUserMedia
({
'video'
:
true
});
}
video
.
srcObject
=
_localStream
;
// TODO: fix flash light. See https://github.com/dart-lang/sdk/issues/48533
// final track = _localStream?.getVideoTracks();
// if (track != null) {
// final imageCapture = html.ImageCapture(track.first);
// final photoCapabilities = await imageCapture.getPhotoCapabilities();
// }
// required to tell iOS safari we don't want fullscreen
video
.
setAttribute
(
'playsinline'
,
'true'
);
await
video
.
play
();
}
@override
Stream
<
String
?>
detectBarcodeContinuously
()
async
*
{
yield
*
Stream
.
periodic
(
frameInterval
,
(
_
)
{
return
_captureFrame
(
video
);
}).
asyncMap
((
e
vent
)
async
=>
(
await
event
)
?.
data
);
}).
asyncMap
((
e
)
=>
e
).
map
((
event
)
=>
event
?.
data
);
}
/// Captures a frame and analyzes it for QR codes
Future
<
Code
?>
_captureFrame
(
VideoElement
video
)
async
{
if
(
_localStream
==
null
)
return
null
;
final
canvas
=
CanvasElement
(
width:
video
.
videoWidth
,
height:
video
.
videoHeight
);
final
ctx
=
canvas
.
context2D
;
...
...
@@ -41,4 +105,21 @@ class JsQrCodeReader extends WebBarcodeReaderBase {
final
code
=
jsQR
(
imgData
.
data
,
canvas
.
width
,
canvas
.
height
);
return
code
;
}
@override
Future
<
void
>
stop
()
async
{
try
{
// Stop the camera stream
_localStream
?.
getTracks
().
forEach
((
track
)
{
if
(
track
.
readyState
==
'live'
)
{
track
.
stop
();
}
});
}
catch
(
e
)
{
debugPrint
(
'Failed to stop stream:
$e
'
);
}
video
.
srcObject
=
null
;
_localStream
=
null
;
}
}
...
...
Please
register
or
login
to post a comment