jsqr.dart 2.24 KB
@JS()
library jsqr;

import 'dart:async';
import 'dart:html';
import 'dart:typed_data';

import 'package:js/js.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/web/base.dart';

@JS('jsQR')
external Code? jsQR(dynamic data, int? width, int? height);

@JS()
class Code {
  external String get data;

  external Uint8ClampedList get binaryData;
}


class JsQrCodeReader extends WebBarcodeReaderBase with InternalStreamCreation {
  JsQrCodeReader({required super.videoContainer});

  @override
  bool get isStarted => localMediaStream != null;

  @override
  Future<void> start({
    required CameraFacing cameraFacing,
  }) async {
    videoContainer.children = [video];

    final stream = await initMediaStream(cameraFacing);

    // 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();
    // }

    prepareVideoElement(video);
    if (stream != null) {
      await attachStreamToVideo(stream, video);
    }
  }

  @override
  void prepareVideoElement(VideoElement videoSource) {
    // required to tell iOS safari we don't want fullscreen
    videoSource.setAttribute('playsinline', 'true');
  }

  @override
  Future<void> attachStreamToVideo(
    MediaStream stream,
    VideoElement videoSource,
  ) async {
    localMediaStream = stream;
    videoSource.srcObject = stream;
    await videoSource.play();
  }

  @override
  Stream<String?> detectBarcodeContinuously() async* {
    yield* Stream.periodic(frameInterval, (_) {
      return _captureFrame(video);
    }).asyncMap((e) => e).map((event) => event?.data);
  }

  /// Captures a frame and analyzes it for QR codes
  Future<Code?> _captureFrame(VideoElement video) async {
    if (localMediaStream == null) return null;
    final canvas = CanvasElement(width: video.videoWidth, height: video.videoHeight);
    final ctx = canvas.context2D;

    ctx.drawImage(video, 0, 0);
    final imgData = ctx.getImageData(0, 0, canvas.width!, canvas.height!);

    final code = jsQR(imgData.data, canvas.width, canvas.height);
    return code;
  }
}