fix: 修复摄像头无法正常启动,以及关闭摄像头会导致后端崩溃的问题

This commit is contained in:
2025-08-18 19:14:02 +08:00
parent 7265b10870
commit 1b5b0e28e3
8 changed files with 640 additions and 75 deletions

View File

@@ -3,7 +3,6 @@ using System.Text;
using System.Collections.Concurrent;
using DotNext;
using DotNext.Threading;
using FlashCap;
namespace server.Services;
@@ -108,7 +107,7 @@ public class HttpVideoStreamService : BackgroundService
var devices = camera.GetDevices();
for (int i = 0; i < devices.Count; i++)
logger.Info($"Device[{i}]: {devices[i].Name}");
await camera.StartAsync(1, 3840, 2160, 30);
await camera.StartAsync(1, 2592, 1994, 30);
return camera;
}
catch (Exception ex)
@@ -120,14 +119,11 @@ public class HttpVideoStreamService : BackgroundService
private Optional<VideoStreamClient> TryGetClient(string boardId)
{
if (_clientDict.TryGetValue(boardId, out var client))
{
return client;
}
return null;
return _clientDict.TryGetValue(boardId, out var client) ? client : null;
}
private Optional<VideoStreamClient> GetOrCreateClient(string boardId, int initWidth, int initHeight)
private Optional<VideoStreamClient> GetOrCreateClient(
string boardId, int initWidth, int initHeight)
{
if (_clientDict.TryGetValue(boardId, out var client))
{
@@ -185,6 +181,8 @@ public class HttpVideoStreamService : BackgroundService
{
var client = _clientDict[clientKey];
client.CTS.Cancel();
if (!client.Camera.IsValueCreated) continue;
using (await client.Lock.AcquireWriteLockAsync(cancellationToken))
{
var camera = await client.Camera.WithCancellation(cancellationToken);
@@ -251,26 +249,28 @@ public class HttpVideoStreamService : BackgroundService
}
var client = clientOpt.Value;
var token = CancellationTokenSource.CreateLinkedTokenSource(
client.CTS.Token, cancellationToken).Token;
var clientToken = client.CTS.Token;
try
{
token.ThrowIfCancellationRequested();
logger.Info("新HTTP客户端连接: {RemoteEndPoint}", context.Request.RemoteEndPoint);
if (path == "/video")
{
// MJPEG 流请求FPGA
await HandleMjpegStreamAsync(context.Response, client, cancellationToken);
await HandleMjpegStreamAsync(context.Response, client, token);
}
else if (path == "/usbCamera")
{
// USB Camera MJPEG流请求
await HandleUsbCameraStreamAsync(context.Response, client, cancellationToken);
await HandleUsbCameraStreamAsync(context.Response, client, token);
}
else if (path == "/snapshot")
{
// 单帧图像请求
await HandleSnapshotRequestAsync(context.Response, client, cancellationToken);
await HandleSnapshotRequestAsync(context.Response, client, token);
}
else if (path == "/html")
{
@@ -300,10 +300,26 @@ public class HttpVideoStreamService : BackgroundService
private async Task HandleUsbCameraStreamAsync(
HttpListenerResponse response, VideoStreamClient client, CancellationToken cancellationToken)
{
var camera = await _usbCamera.WithCancellation(cancellationToken);
Action<byte[]> frameHandler = async (jpegData) =>
{
try
{
var header = Encoding.ASCII.GetBytes("--boundary\r\nContent-Type: image/jpeg\r\nContent-Length: " + jpegData.Length + "\r\n\r\n");
await response.OutputStream.WriteAsync(header, 0, header.Length, cancellationToken);
await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
await response.OutputStream.WriteAsync(new byte[] { 0x0D, 0x0A }, 0, 2, cancellationToken); // \r\n
await response.OutputStream.FlushAsync(cancellationToken);
}
catch
{
logger.Error("Error sending MJPEG frame");
}
};
try
{
var camera = await _usbCamera.WithCancellation(cancellationToken);
if (!camera.IsCapturing)
{
logger.Error("USB Camera is not capturing");
@@ -320,32 +336,17 @@ public class HttpVideoStreamService : BackgroundService
logger.Info("Start USB Camera MJPEG Stream");
camera.FrameReady += frameHandler;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
var jpegData = camera.GetLatestFrame();
if (jpegData == null)
{
logger.Warn("USB Camera MJPEG帧获取失败");
await Task.Delay(1000 / client.FrameRate, cancellationToken);
continue;
}
// MJPEG帧头
var header = Encoding.ASCII.GetBytes("--boundary\r\nContent-Type: image/jpeg\r\nContent-Length: " + jpegData.Length + "\r\n\r\n");
await response.OutputStream.WriteAsync(header, 0, header.Length, cancellationToken);
await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
await response.OutputStream.WriteAsync(new byte[] { 0x0D, 0x0A }, 0, 2, cancellationToken); // \r\n
await response.OutputStream.FlushAsync(cancellationToken);
await Task.Delay(1000 / client.FrameRate, cancellationToken);
logger.Info("USB Camera MJPEG帧发送成功");
await Task.Delay(-1, cancellationToken);
}
}
catch (OperationCanceledException ex)
catch (OperationCanceledException)
{
logger.Info(ex, "USB Camera MJPEG 串流取消");
logger.Info("USB Camera MJPEG 串流取消");
}
catch (Exception ex)
{
@@ -353,7 +354,8 @@ public class HttpVideoStreamService : BackgroundService
}
finally
{
camera.FrameReady -= frameHandler;
logger.Info("Usb Camera Stream Stopped");
try { response.Close(); } catch { }
}
}
@@ -744,15 +746,14 @@ public class HttpVideoStreamService : BackgroundService
using (await client.Lock.AcquireWriteLockAsync())
{
if (enable)
{
client.CTS = new CancellationTokenSource();
}
else
if (!enable || client.CTS.IsCancellationRequested)
{
client.CTS.Cancel();
client.CTS = new CancellationTokenSource();
}
if (!client.Camera.IsValueCreated) return;
var camera = await client.Camera.WithCancellation(client.CTS.Token);
var disableResult = await camera.EnableHardwareTrans(enable);
if (disableResult.IsSuccessful && disableResult.Value)
@@ -763,7 +764,7 @@ public class HttpVideoStreamService : BackgroundService
}
catch (Exception ex)
{
logger.Error(ex, $"Exception occurred while disabling HDMI transmission for camera {boardId}");
logger.Error(ex, $"Exception occurred while disabling video transmission for {boardId}");
}
}