fea: 后端添加usb摄像头功能
This commit is contained in:
@@ -26,6 +26,8 @@
|
||||
<PackageReference Include="NLog" Version="5.4.0" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="14.3.0" />
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
|
||||
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.10" />
|
||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.119" />
|
||||
</ItemGroup>
|
||||
|
@@ -1,8 +1,14 @@
|
||||
#define USB_CAMERA
|
||||
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Peripherals.CameraClient; // 添加摄像头客户端引用
|
||||
|
||||
#if USB_CAMERA
|
||||
using OpenCvSharp;
|
||||
#endif
|
||||
|
||||
namespace server.Services;
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +88,7 @@ public class HttpVideoStreamService : BackgroundService
|
||||
private HttpListener? _httpListener;
|
||||
private readonly int _serverPort = 8080;
|
||||
private readonly int _frameRate = 30; // 30 FPS
|
||||
|
||||
|
||||
// 动态分辨率配置
|
||||
private int _frameWidth = 640; // 默认640x480
|
||||
private int _frameHeight = 480;
|
||||
@@ -95,6 +101,13 @@ public class HttpVideoStreamService : BackgroundService
|
||||
private int _cameraPort = 8888; // 默认端口
|
||||
private readonly object _cameraLock = new object();
|
||||
|
||||
// USB Camera 相关
|
||||
#if USB_CAMERA
|
||||
private VideoCapture? _usbCamera;
|
||||
private bool _usbCameraEnable = false;
|
||||
private readonly object _usbCameraLock = new object();
|
||||
#endif
|
||||
|
||||
// 模拟 FPGA 图像数据
|
||||
private int _frameCounter = 0;
|
||||
private readonly List<HttpListenerResponse> _activeClients = new List<HttpListenerResponse>();
|
||||
@@ -346,9 +359,16 @@ public class HttpVideoStreamService : BackgroundService
|
||||
|
||||
if (requestPath == "/video-stream")
|
||||
{
|
||||
// MJPEG 流请求
|
||||
// MJPEG 流请求(FPGA)
|
||||
_ = Task.Run(() => HandleMjpegStreamAsync(response, cancellationToken), cancellationToken);
|
||||
}
|
||||
#if USB_CAMERA
|
||||
else if (requestPath == "/usb-camera")
|
||||
{
|
||||
// USB Camera MJPEG流请求
|
||||
_ = Task.Run(() => HandleUsbCameraStreamAsync(response, cancellationToken), cancellationToken);
|
||||
}
|
||||
#endif
|
||||
else if (requestPath == "/snapshot")
|
||||
{
|
||||
// 单帧图像请求
|
||||
@@ -382,6 +402,87 @@ public class HttpVideoStreamService : BackgroundService
|
||||
}
|
||||
}
|
||||
|
||||
// USB Camera MJPEG流处理
|
||||
#if USB_CAMERA
|
||||
private async Task HandleUsbCameraStreamAsync(HttpListenerResponse response, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (_usbCameraLock)
|
||||
{
|
||||
if (_usbCamera == null)
|
||||
{
|
||||
_usbCamera = new VideoCapture(0);
|
||||
_usbCamera.Fps = _frameRate;
|
||||
_usbCamera.FrameWidth = _frameWidth;
|
||||
_usbCamera.FrameHeight = _frameHeight;
|
||||
_usbCameraEnable = _usbCamera.IsOpened();
|
||||
}
|
||||
}
|
||||
if (!_usbCameraEnable || _usbCamera == null || !_usbCamera.IsOpened())
|
||||
{
|
||||
response.StatusCode = 500;
|
||||
await response.OutputStream.FlushAsync(cancellationToken);
|
||||
response.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
response.ContentType = "multipart/x-mixed-replace; boundary=--boundary";
|
||||
response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
response.Headers.Add("Pragma", "no-cache");
|
||||
response.Headers.Add("Expires", "0");
|
||||
|
||||
using (var mat = new Mat())
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
bool grabbed;
|
||||
lock (_usbCameraLock)
|
||||
{
|
||||
grabbed = _usbCamera.Read(mat);
|
||||
}
|
||||
if (!grabbed || mat.Empty())
|
||||
{
|
||||
await Task.Delay(50, cancellationToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 编码为JPEG
|
||||
byte[]? jpegData = null;
|
||||
try
|
||||
{
|
||||
jpegData = mat.ToBytes(".jpg", new int[] { (int)ImwriteFlags.JpegQuality, 80 });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "USB Camera帧编码JPEG失败");
|
||||
continue;
|
||||
}
|
||||
if (jpegData == null)
|
||||
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 / _frameRate, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex, "USB Camera MJPEG流处理异常");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { response.Close(); } catch { }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private async Task HandleMjpegStreamAsync(HttpListenerResponse response, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -1046,9 +1147,9 @@ public class HttpVideoStreamService : BackgroundService
|
||||
}
|
||||
|
||||
logger.Info("开始初始化摄像头自动对焦功能");
|
||||
|
||||
|
||||
var result = await _camera!.InitAutoFocus();
|
||||
|
||||
|
||||
if (result.IsSuccessful && result.Value)
|
||||
{
|
||||
logger.Info("摄像头自动对焦功能初始化成功");
|
||||
@@ -1085,9 +1186,9 @@ public class HttpVideoStreamService : BackgroundService
|
||||
}
|
||||
|
||||
logger.Info("开始执行摄像头自动对焦");
|
||||
|
||||
|
||||
var result = await _camera!.PerformAutoFocus();
|
||||
|
||||
|
||||
if (result.IsSuccessful && result.Value)
|
||||
{
|
||||
logger.Info("摄像头自动对焦执行成功");
|
||||
|
Reference in New Issue
Block a user