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-22 13:41:22 +0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
27b4d0f5fdcc4b46c3643d888938e1c7fb0ad4b2
27b4d0f5
1 parent
176ec133
refactor: abstract web scanner
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
107 additions
and
71 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 @
27b4d0f
...
...
@@ -2,13 +2,11 @@ import 'dart:async';
import
'dart:html'
as
html
;
import
'dart:ui'
as
ui
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:flutter_web_plugins/flutter_web_plugins.dart'
;
import
'package:mobile_scanner/src/enums/camera_facing.dart'
;
import
'package:mobile_scanner/src/web/base.dart'
;
import
'package:mobile_scanner/src/web/jsqr.dart'
;
import
'package:mobile_scanner/src/web/media.dart'
;
/// This plugin is the web implementation of mobile_scanner.
/// It only supports QR codes.
...
...
@@ -39,7 +37,10 @@ class MobileScannerWebPlugin {
// Determine wether device has flas
bool
hasFlash
=
false
;
final
WebBarcodeReaderBase
_barCodeReader
=
JsQrCodeReader
();
final
html
.
DivElement
vidDiv
=
html
.
DivElement
();
late
final
WebBarcodeReaderBase
_barCodeReader
=
JsQrCodeReader
(
videoContainer:
vidDiv
);
StreamSubscription
?
_barCodeStreamSubscription
;
/// Handle incomming messages
...
...
@@ -80,6 +81,17 @@ class MobileScannerWebPlugin {
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
)
{
return
vidDiv
..
style
.
width
=
'100%'
..
style
.
height
=
'100%'
;
},
);
// Check if stream is running
if
(
_barCodeReader
.
isStarted
)
{
return
{
...
...
@@ -90,7 +102,6 @@ class MobileScannerWebPlugin {
}
try
{
await
_barCodeReader
.
start
(
viewID:
viewID
,
cameraFacing:
cameraFacing
,
);
...
...
lib/src/web/base.dart
View file @
27b4d0f
import
'dart:html'
;
import
'package:flutter/material.dart'
;
import
'package:mobile_scanner/src/enums/camera_facing.dart'
;
import
'package:mobile_scanner/src/web/media.dart'
;
abstract
class
WebBarcodeReaderBase
{
/// Timer used to capture frames to be analyzed
final
frameInterval
=
const
Duration
(
milliseconds:
200
);
final
Duration
frameInterval
;
final
DivElement
videoContainer
;
const
WebBarcodeReaderBase
({
required
this
.
videoContainer
,
this
.
frameInterval
=
const
Duration
(
milliseconds:
200
),
});
bool
get
isStarted
;
...
...
@@ -11,7 +21,6 @@ abstract class WebBarcodeReaderBase {
/// Starts streaming video
Future
<
void
>
start
({
required
String
viewID
,
required
CameraFacing
cameraFacing
,
});
...
...
@@ -21,3 +30,59 @@ abstract class WebBarcodeReaderBase {
/// Stops streaming video
Future
<
void
>
stop
();
}
mixin
InternalStreamCreation
on
WebBarcodeReaderBase
{
/// The video stream.
/// Will be initialized later to see which camera needs to be used.
MediaStream
?
localMediaStream
;
final
VideoElement
video
=
VideoElement
();
@override
int
get
videoWidth
=>
video
.
videoWidth
;
@override
int
get
videoHeight
=>
video
.
videoHeight
;
Future
<
MediaStream
?>
initMediaStream
(
CameraFacing
cameraFacing
)
async
{
// Check if browser supports multiple camera's and set if supported
final
Map
?
capabilities
=
window
.
navigator
.
mediaDevices
?.
getSupportedConstraints
();
final
Map
<
String
,
dynamic
>
constraints
;
if
(
capabilities
!=
null
&&
capabilities
[
'facingMode'
]
as
bool
)
{
constraints
=
{
'video'
:
VideoOptions
(
facingMode:
cameraFacing
==
CameraFacing
.
front
?
'user'
:
'environment'
,
)
};
}
else
{
constraints
=
{
'video'
:
true
};
}
final
stream
=
await
window
.
navigator
.
mediaDevices
?.
getUserMedia
(
constraints
);
return
stream
;
}
void
prepareVideoElement
(
VideoElement
videoSource
);
Future
<
void
>
attachStreamToVideo
(
MediaStream
stream
,
VideoElement
videoSource
,
);
@override
Future
<
void
>
stop
()
async
{
try
{
// Stop the camera stream
localMediaStream
?.
getTracks
().
forEach
((
track
)
{
if
(
track
.
readyState
==
'live'
)
{
track
.
stop
();
}
});
}
catch
(
e
)
{
debugPrint
(
'Failed to stop stream:
$e
'
);
}
video
.
srcObject
=
null
;
localMediaStream
=
null
;
videoContainer
.
children
=
[];
}
}
...
...
lib/src/web/jsqr.dart
View file @
27b4d0f
...
...
@@ -4,15 +4,11 @@ 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
);
...
...
@@ -24,55 +20,19 @@ class Code {
}
class
JsQrCodeReader
extends
WebBarcodeReaderBase
{
// 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
;
class
JsQrCodeReader
extends
WebBarcodeReaderBase
with
InternalStreamCreation
{
JsQrCodeReader
({
required
super
.
videoContainer
});
@override
int
get
videoWidth
=>
video
.
videoWidth
;
@override
int
get
videoHeight
=>
video
.
videoHeight
;
bool
get
isStarted
=>
localMediaStream
!=
null
;
@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
});
}
videoContainer
.
children
=
[
video
];
video
.
srcObject
=
_localStream
;
final
stream
=
await
initMediaStream
(
cameraFacing
)
;
// TODO: fix flash light. See https://github.com/dart-lang/sdk/issues/48533
// final track = _localStream?.getVideoTracks();
...
...
@@ -81,9 +41,26 @@ class JsQrCodeReader extends WebBarcodeReaderBase {
// final photoCapabilities = await imageCapture.getPhotoCapabilities();
// }
prepareVideoElement
(
video
);
if
(
stream
!=
null
)
{
await
attachStreamToVideo
(
stream
,
video
);
}
}
@override
void
prepareVideoElement
(
VideoElement
videoSource
)
{
// required to tell iOS safari we don't want fullscreen
video
.
setAttribute
(
'playsinline'
,
'true'
);
await
video
.
play
();
videoSource
.
setAttribute
(
'playsinline'
,
'true'
);
}
@override
Future
<
void
>
attachStreamToVideo
(
MediaStream
stream
,
VideoElement
videoSource
,
)
async
{
localMediaStream
=
stream
;
videoSource
.
srcObject
=
stream
;
await
videoSource
.
play
();
}
@override
...
...
@@ -95,7 +72,7 @@ class JsQrCodeReader extends WebBarcodeReaderBase {
/// Captures a frame and analyzes it for QR codes
Future
<
Code
?>
_captureFrame
(
VideoElement
video
)
async
{
if
(
_local
Stream
==
null
)
return
null
;
if
(
localMedia
Stream
==
null
)
return
null
;
final
canvas
=
CanvasElement
(
width:
video
.
videoWidth
,
height:
video
.
videoHeight
);
final
ctx
=
canvas
.
context2D
;
...
...
@@ -105,21 +82,4 @@ 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