This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
using Emgu.CV;
|
||||
using FaceRecognitionApp.Models;
|
||||
|
||||
namespace FaceRecognitionApp.Services;
|
||||
|
||||
public class CameraService : IDisposable
|
||||
{
|
||||
private VideoCapture? _capture;
|
||||
private readonly FaceRecognitionService _recognition;
|
||||
private readonly Action<DetectedFace> _onDetected;
|
||||
private CancellationTokenSource? _cts;
|
||||
private Task? _task;
|
||||
private DateTime _lastTime = DateTime.MinValue;
|
||||
private bool _isDisposed = false;
|
||||
|
||||
public CameraService(string url, FaceRecognitionService recognition, Action<DetectedFace> onDetected)
|
||||
{
|
||||
_recognition = recognition;
|
||||
_onDetected = onDetected;
|
||||
|
||||
Console.WriteLine($"Initializing camera with URL: {url}");
|
||||
_capture = new VideoCapture(url);
|
||||
|
||||
// Даем камере время на инициализацию потока
|
||||
Thread.Sleep(1000);
|
||||
|
||||
if (!_capture.IsOpened)
|
||||
throw new Exception("Camera failed to open. Check URL and network connection.");
|
||||
|
||||
Console.WriteLine("Camera hardware initialized.");
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
_task = Task.Run(() => Loop(_cts.Token));
|
||||
Console.WriteLine("Camera processing loop started.");
|
||||
}
|
||||
|
||||
private async Task Loop(CancellationToken token)
|
||||
{
|
||||
Console.WriteLine("🎥 Frame processing loop active...");
|
||||
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var frame = new Mat();
|
||||
|
||||
// Читаем кадр с таймаутом
|
||||
if (_capture!.Read(frame))
|
||||
{
|
||||
if (!frame.IsEmpty && frame.Width > 0 && frame.Height > 0)
|
||||
{
|
||||
var face = _recognition.ProcessFrame(frame);
|
||||
|
||||
if (face != null)
|
||||
{
|
||||
// Лимит частоты отправок (не чаще раза в 2 секунды)
|
||||
if ((DateTime.Now - _lastTime).TotalMilliseconds > 2000)
|
||||
{
|
||||
_lastTime = DateTime.Now;
|
||||
_onDetected(face);
|
||||
|
||||
string status = face.IsRecognized
|
||||
? $"✅ Recognized: {face.MatchedEmployee?.Name}"
|
||||
: "❌ Unknown face";
|
||||
|
||||
Console.WriteLine($"{DateTime.Now:HH:mm:ss} - {status}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Если Read вернул false, возможно потеря связи
|
||||
Console.WriteLine("⚠️ Failed to read frame, retrying...");
|
||||
await Task.Delay(500, token);
|
||||
}
|
||||
|
||||
// Небольшая задержка, чтобы не грузить процессор на 100%
|
||||
await Task.Delay(50, token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Это нормальная ситуация при остановке
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"⚠️ Error in camera loop: {ex.Message}");
|
||||
await Task.Delay(1000, token);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("🛑 Camera loop finished.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (_cts == null) return;
|
||||
|
||||
Console.WriteLine("Stopping camera service...");
|
||||
_cts.Cancel();
|
||||
|
||||
try
|
||||
{
|
||||
// Ждем завершения задачи, но не бесконечно
|
||||
if (_task != null)
|
||||
{
|
||||
_task.Wait(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
// Игнорируем ошибки отмены задачи
|
||||
foreach (var inner in ex.InnerExceptions)
|
||||
{
|
||||
if (!(inner is TaskCanceledException || inner is OperationCanceledException))
|
||||
{
|
||||
Console.WriteLine($"Unexpected error during stop: {inner.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error stopping task: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
Stop();
|
||||
_capture?.Dispose();
|
||||
_cts?.Dispose();
|
||||
_isDisposed = true;
|
||||
|
||||
Console.WriteLine("Camera resources released.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user