diff --git a/server/src/Peripherals/HdmiInClient.cs b/server/src/Peripherals/HdmiInClient.cs
index 1c1090f..9b5080c 100644
--- a/server/src/Peripherals/HdmiInClient.cs
+++ b/server/src/Peripherals/HdmiInClient.cs
@@ -100,6 +100,51 @@ class HdmiIn
return result.Value;
}
+ public async ValueTask<(byte[] header, byte[] data, byte[] footer)?> GetMJpegFrame()
+ {
+ // 从HDMI读取RGB24数据
+ var readStartTime = DateTime.UtcNow;
+ var frameResult = await ReadFrame();
+ var readEndTime = DateTime.UtcNow;
+ var readTime = (readEndTime - readStartTime).TotalMilliseconds;
+
+ if (!frameResult.IsSuccessful || frameResult.Value == null)
+ {
+ logger.Warn("HDMI帧读取失败或为空");
+ return null;
+ }
+
+ var rgb24Data = frameResult.Value;
+
+ // 验证数据长度是否正确 (RGB24为每像素2字节)
+ var expectedLength = _currentWidth * _currentHeight * 2;
+ if (rgb24Data.Length != expectedLength)
+ {
+ logger.Warn("HDMI数据长度不匹配,期望: {Expected}, 实际: {Actual}",
+ expectedLength, rgb24Data.Length);
+ }
+
+ // 将RGB24转换为JPEG(参考Camera版本的处理)
+ var jpegStartTime = DateTime.UtcNow;
+ var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Data, _currentWidth, _currentHeight, 80);
+ var jpegEndTime = DateTime.UtcNow;
+ var jpegTime = (jpegEndTime - jpegStartTime).TotalMilliseconds;
+
+ if (!jpegResult.IsSuccessful)
+ {
+ logger.Error("HDMI RGB24转JPEG失败: {Error}", jpegResult.Error);
+ return null;
+ }
+
+ var jpegData = jpegResult.Value;
+
+ // 发送MJPEG帧(使用Camera版本的格式)
+ var mjpegFrameHeader = Common.Image.CreateMjpegFrameHeader(jpegData.Length);
+ var mjpegFrameFooter = Common.Image.CreateMjpegFrameFooter();
+
+ return (mjpegFrameHeader, jpegData, mjpegFrameFooter);
+ }
+
///
/// 获取当前分辨率
///
diff --git a/server/src/Services/HttpHdmiVideoStreamService.cs b/server/src/Services/HttpHdmiVideoStreamService.cs
index c773d57..a19aacf 100644
--- a/server/src/Services/HttpHdmiVideoStreamService.cs
+++ b/server/src/Services/HttpHdmiVideoStreamService.cs
@@ -32,46 +32,38 @@ public class HttpHdmiVideoStreamService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- try
+ while (!stoppingToken.IsCancellationRequested)
{
- while (!stoppingToken.IsCancellationRequested)
+ if (_httpListener == null) continue;
+ try
{
- HttpListenerContext? context = null;
- try
+ logger.Debug("Waiting for HTTP request...");
+ var contextTask = _httpListener.GetContextAsync();
+ var completedTask = await Task.WhenAny(contextTask, Task.Delay(-1, stoppingToken));
+ if (completedTask == contextTask)
{
- logger.Debug("Waiting for HTTP request...");
- context = await _httpListener.GetContextAsync();
+ var context = contextTask.Result;
logger.Debug($"Received request: {context.Request.Url?.AbsolutePath}");
+ if (context != null)
+ _ = HandleRequestAsync(context, stoppingToken);
}
- catch (ObjectDisposedException)
+ else
{
- // Listener closed, exit loop
break;
}
- catch (HttpListenerException)
- {
- // Listener closed, exit loop
- break;
- }
- catch (Exception ex)
- {
- logger.Error(ex, "Error in GetContextAsync");
- break;
- }
- if (context != null)
- _ = HandleRequestAsync(context, stoppingToken);
}
- }
- finally
- {
- _httpListener?.Close();
- logger.Info("HDMI Video Stream Service stopped.");
+ catch (Exception ex)
+ {
+ logger.Error(ex, "Error in GetContextAsync");
+ break;
+ }
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
logger.Info("Stopping HDMI Video Stream Service...");
+ _httpListener?.Close();
// 禁用所有活跃的HDMI传输
var disableTasks = new List();
@@ -229,9 +221,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
{
logger.Debug("处理HDMI快照请求");
- const int frameWidth = 960; // HDMI输入分辨率
- const int frameHeight = 540;
-
// 从HDMI读取RGB565数据
var frameResult = await hdmiIn.ReadFrame();
if (!frameResult.IsSuccessful || frameResult.Value == null)
@@ -244,41 +233,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
return;
}
- var rgb565Data = frameResult.Value;
-
- // 验证数据长度
- var expectedLength = frameWidth * frameHeight * 2;
- if (rgb565Data.Length != expectedLength)
- {
- logger.Warn("HDMI快照数据长度不匹配,期望: {Expected}, 实际: {Actual}",
- expectedLength, rgb565Data.Length);
- }
-
- // 将RGB565转换为RGB24
- var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, frameWidth, frameHeight, isLittleEndian: false);
- if (!rgb24Result.IsSuccessful)
- {
- logger.Error("HDMI快照RGB565转RGB24失败: {Error}", rgb24Result.Error);
- response.StatusCode = 500;
- var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to process HDMI snapshot");
- await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
- response.Close();
- return;
- }
-
- // 将RGB24转换为JPEG
- var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Result.Value, frameWidth, frameHeight, 80);
- if (!jpegResult.IsSuccessful)
- {
- logger.Error("HDMI快照RGB24转JPEG失败: {Error}", jpegResult.Error);
- response.StatusCode = 500;
- var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to encode HDMI snapshot");
- await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
- response.Close();
- return;
- }
-
- var jpegData = jpegResult.Value;
+ var jpegData = frameResult.Value;
// 设置响应头(参考Camera版本)
response.ContentType = "image/jpeg";
@@ -314,8 +269,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
logger.Debug("开始HDMI MJPEG流传输");
int frameCounter = 0;
- const int frameWidth = 960; // HDMI输入分辨率
- const int frameHeight = 540;
while (!cancellationToken.IsCancellationRequested)
{
@@ -323,61 +276,13 @@ public class HttpHdmiVideoStreamService : BackgroundService
{
var frameStartTime = DateTime.UtcNow;
- // 从HDMI读取RGB565数据
- var readStartTime = DateTime.UtcNow;
- var frameResult = await hdmiIn.ReadFrame();
- var readEndTime = DateTime.UtcNow;
- var readTime = (readEndTime - readStartTime).TotalMilliseconds;
+ var ret = await hdmiIn.GetMJpegFrame();
+ if (ret == null) continue;
+ var frame = ret.Value;
- if (!frameResult.IsSuccessful || frameResult.Value == null)
- {
- logger.Warn("HDMI帧读取失败或为空");
- continue;
- }
-
- var rgb565Data = frameResult.Value;
-
- // 验证数据长度是否正确 (RGB565为每像素2字节)
- var expectedLength = frameWidth * frameHeight * 2;
- if (rgb565Data.Length != expectedLength)
- {
- logger.Warn("HDMI数据长度不匹配,期望: {Expected}, 实际: {Actual}",
- expectedLength, rgb565Data.Length);
- }
-
- // 将RGB565转换为RGB24(参考Camera版本的处理)
- var convertStartTime = DateTime.UtcNow;
- var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, frameWidth, frameHeight, isLittleEndian: false);
- var convertEndTime = DateTime.UtcNow;
- var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;
-
- if (!rgb24Result.IsSuccessful)
- {
- logger.Error("HDMI RGB565转RGB24失败: {Error}", rgb24Result.Error);
- continue;
- }
-
- // 将RGB24转换为JPEG(参考Camera版本的处理)
- var jpegStartTime = DateTime.UtcNow;
- var jpegResult = Common.Image.ConvertRGB24ToJpeg(rgb24Result.Value, frameWidth, frameHeight, 80);
- var jpegEndTime = DateTime.UtcNow;
- var jpegTime = (jpegEndTime - jpegStartTime).TotalMilliseconds;
-
- if (!jpegResult.IsSuccessful)
- {
- logger.Error("HDMI RGB24转JPEG失败: {Error}", jpegResult.Error);
- continue;
- }
-
- var jpegData = jpegResult.Value;
-
- // 发送MJPEG帧(使用Camera版本的格式)
- var mjpegFrameHeader = Common.Image.CreateMjpegFrameHeader(jpegData.Length);
- var mjpegFrameFooter = Common.Image.CreateMjpegFrameFooter();
-
- await response.OutputStream.WriteAsync(mjpegFrameHeader, 0, mjpegFrameHeader.Length, cancellationToken);
- await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
- await response.OutputStream.WriteAsync(mjpegFrameFooter, 0, mjpegFrameFooter.Length, cancellationToken);
+ await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken);
+ await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
+ await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
await response.OutputStream.FlushAsync(cancellationToken);
frameCounter++;
@@ -387,8 +292,8 @@ public class HttpHdmiVideoStreamService : BackgroundService
// 性能统计日志(每30帧记录一次)
if (frameCounter % 30 == 0)
{
- logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 读取: {ReadTime:F1}ms, RGB转换: {ConvertTime:F1}ms, JPEG转换: {JpegTime:F1}ms, 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
- frameCounter, readTime, convertTime, jpegTime, totalTime, jpegData.Length);
+ logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
+ frameCounter, totalTime, frame.data.Length);
}
}
catch (Exception ex)
diff --git a/server/src/Services/HttpVideoStreamService.cs b/server/src/Services/HttpVideoStreamService.cs
index 3fc1b3e..16dbd34 100644
--- a/server/src/Services/HttpVideoStreamService.cs
+++ b/server/src/Services/HttpVideoStreamService.cs
@@ -1,8 +1,5 @@
-#define USB_CAMERA
-
using System.Net;
using System.Text;
-using System.Threading.Tasks;
using Peripherals.CameraClient; // 添加摄像头客户端引用
#if USB_CAMERA
@@ -414,7 +411,7 @@ public class HttpVideoStreamService : BackgroundService
{
_usbCamera = new VideoCapture(1);
_usbCamera.Fps = _frameRate;
- _usbCamera.FrameWidth = _frameWidth;
+ _usbCamera.FrameWidth = _frameWidth;
_usbCamera.FrameHeight = _frameHeight;
_usbCameraEnable = _usbCamera.IsOpened();
}
@@ -1003,38 +1000,6 @@ public class HttpVideoStreamService : BackgroundService
logger.Info("HTTP 视频流服务已停止");
}
- ///
- /// 释放资源
- ///
- public override void Dispose()
- {
- if (_httpListener != null)
- {
- if (_httpListener.IsListening)
- {
- _httpListener.Stop();
- }
- _httpListener.Close();
- }
-
- lock (_clientsLock)
- {
- foreach (var client in _activeClients)
- {
- try { client.Close(); }
- catch { /* 忽略关闭错误 */ }
- }
- _activeClients.Clear();
- }
-
- lock (_cameraLock)
- {
- _camera = null;
- }
-
- base.Dispose();
- }
-
///
/// 设置视频流分辨率
///
diff --git a/server/src/Services/ProgressTrackerService.cs b/server/src/Services/ProgressTrackerService.cs
index bf555a7..2283017 100644
--- a/server/src/Services/ProgressTrackerService.cs
+++ b/server/src/Services/ProgressTrackerService.cs
@@ -190,7 +190,7 @@ public class ProgressTrackerService : BackgroundService
{
logger.Error(ex, "Error during ProgressTracker cleanup");
}
- await Task.Delay(TimeSpan.FromSeconds(30));
+ await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}