Use PeriodicRunner
MODIFY
WalletWasabi.Fluent/Models/WebcamQrReader.cs
WalletWasabi.Fluent/Models/WebcamQrReader.cs
@@ -13,17 +13,17 @@ using WalletWasabi.Fluent.Models.Windows;
using Avalonia.Controls;
using Microsoft.Extensions.Hosting;
using System.Threading;
+using ZXing.QrCode;
+using WalletWasabi.Bases;
namespace WalletWasabi.Fluent.Models;
-public class WebcamQrReader : BackgroundService
+public class WebcamQrReader : PeriodicRunner
{
private const byte DefaultCameraId = 0;
+ private WindowsCapture? Camera { get; set; }
- /// <summary>Whether user requested to stop webcamera to scan for QR codes.</summary>
- private volatile bool _requestEnd;
-
- public WebcamQrReader(Network network)
+ public WebcamQrReader(Network network) : base(TimeSpan.FromMilliseconds(100))
{
Network = network;
}
@@ -36,169 +36,54 @@ public class WebcamQrReader : BackgroundService
public event EventHandler<Exception>? ErrorOccurred;
- private AsyncLock ScanningTaskLock { get; } = new();
private Network Network { get; }
- private Task? ScanningTask { get; set; }
public static bool IsOsPlatformSupported => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
- public async Task StartScanningAsync()
- {
- using (await ScanningTaskLock.LockAsync().ConfigureAwait(false))
+ public override async Task StartAsync(CancellationToken cancellationToken)
{
- if (ScanningTask is { })
- {
- return;
- }
- _requestEnd = false;
- ScanningTask = Task.Run(() =>
- {
- WindowsCapture? camera = null;
+ QRCodeReader? decoder = null;
+
try
{
+ decoder = new QRCodeReader();
string[] devices = WindowsCapture.FindDevices();
if (devices.Length == 0)
{
- return;
- }
-
- WindowsCapture.VideoFormat[] formats = WindowsCapture.GetVideoFormat(DefaultCameraId);
-
- camera = new WindowsCapture(DefaultCameraId, formats[0]);
- camera.Start();
- //VideoCapture? camera = null;
- //try
- //{
- // if (!IsOsPlatformSupported)
- // {
- // throw new NotImplementedException("This operating system is not supported.");
- // }
- // camera = new();
- // camera.SetExceptionMode(true);
- // // Setting VideoCaptureAPI to DirectShow, to remove warning logs,
- // // might need to be changed in the future for other operating systems
- // if (!camera.Open(DefaultCameraId, VideoCaptureAPIs.DSHOW))
- // {
- // throw new InvalidOperationException("Could not open webcamera.");
- // }
- KeepScanning(camera);
- }
- catch (OpenCVException ex)
- {
- Logger.LogError("Could not open camera. Reason: " + ex);
ErrorOccurred?.Invoke(this, new NotSupportedException("Could not open camera."));
}
- catch (Exception ex)
- {
- Logger.LogError("QR scanning stopped. Reason:", ex);
- ErrorOccurred?.Invoke(this, ex);
- }
- finally
- {
- camera?.Release();
- }
- });
- }
- }
-
- public async Task StopScanningAsync()
- {
- using (await ScanningTaskLock.LockAsync().ConfigureAwait(false))
- {
- if (ScanningTask is { } task)
- {
- _requestEnd = true;
- await task;
-
- ScanningTask = null;
- }
- }
- }
- private void KeepScanning(WindowsCapture camera)
- {
- //PixelSize pixelSize = new((int)camera.Size.Width, (int)camera.Size.Height);
- //Vector dpi = new(96, 96);
- //WriteableBitmap writeableBitmap = new(pixelSize, dpi, PixelFormat.Rgba8888, AlphaFormat.Unpremul);
+ WindowsCapture.VideoFormat[] formats = WindowsCapture.GetVideoFormat(DefaultCameraId);
- //int dataSize = (int)(camera.Size.Width * camera.Size.Height);
- //int[] helperArray = new int[dataSize];
+ Camera = new WindowsCapture(DefaultCameraId, formats[0]);
+ Camera.Start();
// Immediately after starting the USB camera,
// GetBitmap() fails because image buffer is not prepared yet.
- var bmp = camera.GetBitmap();
- while (!_requestEnd)
- {
- try
- {
- //writeableBitmap = (WriteableBitmap)camera.GetBitmap();
-
- //if (!gotBackFrame || frame.Width == 0 || frame.Height == 0)
- //{
- // continue;
- //}
- NewImageArrived?.Invoke(this, camera.GetBitmap());
- //string decodedText = qRCodeDetector.Decode(frame, points, tmpMat);
- //if (string.IsNullOrWhiteSpace(decodedText))
- //{
- // continue;
- //}
- //if (AddressStringParser.TryParse(decodedText, Network, out _))
- //{
- // CorrectAddressFound?.Invoke(this, decodedText);
- // break;
- //}
- //else
- //{
- // InvalidAddressFound?.Invoke(this, decodedText);
- //}
+ _ = Camera.GetBitmap();
+ await base.StartAsync(cancellationToken);
}
catch (Exception)
{
- throw new InvalidOperationException("Could not read frames. Please make sure no other program uses your camera.");
- }
+ var ex = new InvalidOperationException("Could not read frames. Please make sure no other program uses your camera.");
+ ErrorOccurred?.Invoke(this, ex);
}
}
- protected override Task ExecuteAsync(CancellationToken stoppingToken)
+ public override async Task StopAsync(CancellationToken cancellationToken)
{
- ScanningTask = Task.Run(() =>
- {
- WindowsCapture? camera = null;
- try
- {
- string[] devices = WindowsCapture.FindDevices();
- if (devices.Length == 0)
- {
- ErrorOccurred?.Invoke(this, new NotSupportedException("Could not open camera."));
- }
-
- WindowsCapture.VideoFormat[] formats = WindowsCapture.GetVideoFormat(DefaultCameraId);
-
- camera = new WindowsCapture(DefaultCameraId, formats[0]);
- camera.Start();
+ Camera?.Release();
- var bmp = camera.GetBitmap();
- ulong cnt = 0;
- while (!stoppingToken.IsCancellationRequested)
- {
- cnt++;
- if (cnt % 1000 == 0)
- {
- Logger.LogInfo("Pic taken");
- NewImageArrived?.Invoke(this, camera.GetBitmap());
- }
+ await base.StopAsync(cancellationToken);
}
- }
- catch (Exception)
+
+ protected override Task ActionAsync(CancellationToken cancel)
{
- var ex = new InvalidOperationException("Could not read frames. Please make sure no other program uses your camera.");
- ErrorOccurred?.Invoke(this, ex);
- }
- finally
+ if (Camera is { })
{
- camera?.Release();
+ var bmp = Camera.GetBitmap();
+ NewImageArrived?.Invoke(this, bmp);
+ //decoder.DecodeBitmap(bmp);
}
- }, stoppingToken);
return Task.CompletedTask;
}
}

MODIFY
WalletWasabi.Fluent/Models/Windows/WindowsCapture.cs
WalletWasabi.Fluent/Models/Windows/WindowsCapture.cs
@@ -10,6 +10,7 @@ using Avalonia.Platform;
namespace WalletWasabi.Fluent.Models.Windows;
#pragma warning disable IDE0019 // Use pattern matching
#pragma warning disable IDE1006 // Naming Styles
+
public class WindowsCapture
{
private static Dictionary<long, byte[]> ArrayBuffer = new();

MODIFY
WalletWasabi.Fluent/ViewModels/Dialogs/ShowQrCameraDialogViewModel.cs
WalletWasabi.Fluent/ViewModels/Dialogs/ShowQrCameraDialogViewModel.cs
@@ -37,9 +37,7 @@ public partial class ShowQrCameraDialogViewModel : DialogViewModelBase<string?>
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(args =>
{
-
QrImage = args.EventArgs;
-
})
.DisposeWith(disposables);

MODIFY
WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
WalletWasabi.Fluent/WalletWasabi.Fluent.csproj
<RuntimeIdentifiers>win7-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<PathMap>$(MSBuildProjectDirectory)\=WalletWasabi.Fluent</PathMap>
<AvaloniaVersion>0.10.12</AvaloniaVersion>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>

MODIFY
WalletWasabi.Fluent/ZXing/QRCodeReader.cs
WalletWasabi.Fluent/ZXing/QRCodeReader.cs
@@ -266,5 +266,12 @@ namespace ZXing.QrCode
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
return decode(bitmap);
}
+
+ public Result DecodeBitmap(Bitmap image)
+ {
+ var source = new BitmapLuminanceSource(image);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ return decode(bitmap);
+ }
}
}

Use PeriodicRunner