3 Commits

7 changed files with 298 additions and 218 deletions

View File

@@ -35,57 +35,133 @@ public class NumberTest
} }
/// <summary> /// <summary>
/// 测试 BytesToUInt64 的正常与异常情况 /// 测试 BytesToUInt64 的正常与异常情况,覆盖不同参数组合
/// </summary> /// </summary>
[Fact] [Fact]
public void Test_BytesToUInt64() public void Test_BytesToUInt64()
{ {
// 正常大端 // 正常大端isLowNumHigh=false
var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01 }; var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01 };
var result = Number.BytesToUInt64((byte[])bytes.Clone(), false); var result = Number.BytesToUInt64((byte[])bytes.Clone());
Assert.True(result.IsSuccessful); Assert.True(result.IsSuccessful);
Assert.Equal(0x12345678ABCDEF01UL, result.Value); Assert.Equal(0x12345678ABCDEF01UL, result.Value);
// 正常小端 // 正常小端isLowNumHigh=true
var bytes2 = new byte[] { 0x01, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12 }; var bytes2 = new byte[] { 0x01, 0xEF, 0xCD, 0xAB, 0x78, 0x56, 0x34, 0x12 };
var result2 = Number.BytesToUInt64((byte[])bytes2.Clone(), true); var result2 = Number.BytesToUInt64((byte[])bytes2.Clone(), true);
Assert.True(result2.IsSuccessful); Assert.True(result2.IsSuccessful);
Assert.Equal(0x12345678ABCDEF01UL, result2.Value); Assert.Equal(0x12345678ABCDEF01UL, result2.Value);
// 异常:长度超限 // 长度不足8字节numLength=4大端
var result3 = Number.BytesToUInt64(new byte[9], false); var bytes3 = new byte[] { 0x12, 0x34, 0x56, 0x78 };
Assert.False(result3.IsSuccessful); var result3 = Number.BytesToUInt64((byte[])bytes3.Clone(), 0, 4, false);
Assert.True(result3.IsSuccessful);
Assert.Equal(0x1234567800000000UL, result3.Value);
// 异常:不足8字节 // 长度不足8字节numLength=4小端
var result4 = Number.BytesToUInt64(new byte[] { 0x01, 0x02 }, false); var bytes4 = new byte[] { 0x78, 0x56, 0x34, 0x12 };
Assert.False(result4.IsSuccessful); // BitConverter.ToUInt64 需要8字节 var result4 = Number.BytesToUInt64((byte[])bytes4.Clone(), 0, 4, true);
Assert.True(result4.IsSuccessful);
Assert.Equal(0x12345678UL, result4.Value);
// numLength=0
var bytes5 = new byte[] { 0x12, 0x34, 0x56, 0x78 };
var result5 = Number.BytesToUInt64((byte[])bytes5.Clone(), 0, 0, false);
Assert.True(result5.IsSuccessful);
Assert.Equal(0UL, result5.Value);
// offset测试
var bytes6 = new byte[] { 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0xEF, 0x01 };
var result6 = Number.BytesToUInt64(bytes6, 2, 8, false);
Assert.True(result6.IsSuccessful);
Assert.Equal(0x12345678ABCDEF01UL, result6.Value);
// numLength超限>8应返回异常
var bytes7 = new byte[9];
var result7 = Number.BytesToUInt64(bytes7, 0, 9, false);
Assert.False(result7.IsSuccessful);
// offset+numLength超限
var bytes8 = new byte[] { 0x01, 0x02, 0x03, 0x04 };
var result8 = Number.BytesToUInt64(bytes8, 2, 4, false);
Assert.True(result8.IsSuccessful);
Assert.Equal(0x0304000000000000UL, result8.Value);
// bytes长度不足offset+numLength
var bytes9 = new byte[] { 0x01, 0x02 };
var result9 = Number.BytesToUInt64(bytes9, 1, 2, true);
Assert.True(result9.IsSuccessful);
Assert.Equal(0x02UL, result9.Value);
// 空数组
var result10 = Number.BytesToUInt64(new byte[0], 0, 0, false);
Assert.True(result10.IsSuccessful);
Assert.Equal(0UL, result10.Value);
} }
/// <summary> /// <summary>
/// 测试 BytesToUInt32 的正常与异常情况 /// 测试 BytesToUInt32 的正常与异常情况,覆盖不同参数组合
/// </summary> /// </summary>
[Fact] [Fact]
public void Test_BytesToUInt32() public void Test_BytesToUInt32()
{ {
// 正常大端 // 正常大端isLowNumHigh=false
var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78 }; var bytes = new byte[] { 0x12, 0x34, 0x56, 0x78 };
var result = Number.BytesToUInt32((byte[])bytes.Clone(), false); var result = Number.BytesToUInt32((byte[])bytes.Clone());
Assert.True(result.IsSuccessful); Assert.True(result.IsSuccessful);
Assert.Equal(0x12345678U, result.Value); Assert.Equal(0x12345678U, result.Value);
// 正常小端 // 正常小端isLowNumHigh=true
var bytes2 = new byte[] { 0x78, 0x56, 0x34, 0x12 }; var bytes2 = new byte[] { 0x78, 0x56, 0x34, 0x12 };
var result2 = Number.BytesToUInt32((byte[])bytes2.Clone(), true); var result2 = Number.BytesToUInt32((byte[])bytes2.Clone(), true);
Assert.True(result2.IsSuccessful); Assert.True(result2.IsSuccessful);
Assert.Equal(0x12345678U, result2.Value); Assert.Equal(0x12345678U, result2.Value);
// 异常:长度超限 // 长度不足4字节numLength=2大端
var result3 = Number.BytesToUInt32(new byte[5], false); var bytes3 = new byte[] { 0x12, 0x34 };
Assert.False(result3.IsSuccessful); var result3 = Number.BytesToUInt32((byte[])bytes3.Clone(), 0, 2, false);
Assert.True(result3.IsSuccessful);
Assert.Equal(0x12340000U, result3.Value);
// 异常:不足4字节 // 长度不足4字节numLength=2小端
var result4 = Number.BytesToUInt32(new byte[] { 0x01, 0x02 }, false); var bytes4 = new byte[] { 0x34, 0x12 };
Assert.False(result4.IsSuccessful); // BitConverter.ToUInt32 需要4字节 var result4 = Number.BytesToUInt32((byte[])bytes4.Clone(), 0, 2, true);
Assert.True(result4.IsSuccessful);
Assert.Equal(0x1234U, result4.Value);
// numLength=0
var bytes5 = new byte[] { 0x12, 0x34, 0x56, 0x78 };
var result5 = Number.BytesToUInt32((byte[])bytes5.Clone(), 0, 0, false);
Assert.True(result5.IsSuccessful);
Assert.Equal(0U, result5.Value);
// offset测试
var bytes6 = new byte[] { 0x00, 0x00, 0x12, 0x34, 0x56, 0x78 };
var result6 = Number.BytesToUInt32(bytes6, 2, 4, false);
Assert.True(result6.IsSuccessful);
Assert.Equal(0x12345678U, result6.Value);
// numLength超限>4应返回异常
var bytes7 = new byte[5];
var result7 = Number.BytesToUInt32(bytes7, 0, 5, false);
Assert.False(result7.IsSuccessful);
// offset+numLength超限
var bytes8 = new byte[] { 0x01, 0x02, 0x03, 0x04 };
var result8 = Number.BytesToUInt32(bytes8, 2, 2, false);
Assert.True(result8.IsSuccessful);
Assert.Equal(0x03040000U, result8.Value);
// bytes长度不足offset+numLength
var bytes9 = new byte[] { 0x01, 0x02 };
var result9 = Number.BytesToUInt32(bytes9, 1, 1, true);
Assert.True(result9.IsSuccessful);
Assert.Equal(0x02U, result9.Value);
// 空数组
var result10 = Number.BytesToUInt32(new byte[0], 0, 0, false);
Assert.True(result10.IsSuccessful);
Assert.Equal(0U, result10.Value);
} }
/// <summary> /// <summary>

View File

@@ -11,6 +11,7 @@
<SpaRoot>../</SpaRoot> <SpaRoot>../</SpaRoot>
<SpaProxyServerUrl>http://localhost:5173</SpaProxyServerUrl> <SpaProxyServerUrl>http://localhost:5173</SpaProxyServerUrl>
<SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand> <SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
<NoWarn>CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -78,6 +78,56 @@ public class Number
return arr; return arr;
} }
/// <summary>
/// 二进制字节数组转成64bits整数
/// </summary>
/// <param name="bytes">二进制字节数组</param>
/// <param name="offset">字节数组偏移量</param>
/// <param name="numLength">字节数组长度</param>
/// <param name="isLowNumHigh">是否高位在数组索引低</param>
/// <returns>整数</returns>
public static Result<UInt64> BytesToUInt64(byte[] bytes, int offset = 0, int numLength = 8, bool isLowNumHigh = false)
{
if (bytes.Length < offset)
{
return new(new ArgumentException($"The Length of bytes is less than offset"));
}
if (numLength > 8)
{
return new(new ArgumentException($"The Length of bytes is greater than 8"));
}
if (numLength <= 0) return 0;
try
{
byte[] numBytes = new byte[8]; // 8字节
int copyLen = Math.Min(numLength, bytes.Length - offset);
if (isLowNumHigh)
{
// 小端:拷贝到低位
Buffer.BlockCopy(bytes, offset, numBytes, 0, copyLen);
}
else
{
// 大端:拷贝到高位
byte[] temp = new byte[copyLen];
Buffer.BlockCopy(bytes, offset, temp, 0, copyLen);
Array.Reverse(temp);
Buffer.BlockCopy(temp, 0, numBytes, 8 - copyLen, copyLen);
}
UInt64 num = BitConverter.ToUInt64(numBytes, 0);
return num;
}
catch (Exception error)
{
return new(error);
}
}
/// <summary> /// <summary>
/// 二进制字节数组转成64bits整数 /// 二进制字节数组转成64bits整数
/// </summary> /// </summary>
@@ -86,25 +136,78 @@ public class Number
/// <returns>整数</returns> /// <returns>整数</returns>
public static Result<UInt64> BytesToUInt64(byte[] bytes, bool isLowNumHigh = false) public static Result<UInt64> BytesToUInt64(byte[] bytes, bool isLowNumHigh = false)
{ {
if (bytes.Length > 8) return BytesToUInt64(bytes, 0, 8, isLowNumHigh);
{
return new(new ArgumentException(
"Unsigned long number can't over 8 bytes(64 bits).",
nameof(bytes)
));
} }
UInt64 num = 0; /// <summary>
int len = bytes.Length; /// 二进制字节数组转成64bits整数
/// </summary>
/// <param name="bytes">二进制字节数组</param>
/// <returns>整数</returns>
public static Result<UInt64> BytesToUInt64(byte[] bytes)
{
return BytesToUInt64(bytes, 0, 8, false);
}
/// <summary>
/// 二进制字节数组转成32bits整数
/// </summary>
/// <param name="bytes">二进制字节数组</param>
/// <param name="offset">字节数组偏移量</param>
/// <param name="numLength">整形所占字节数组长度</param>
/// <param name="isLowNumHigh">是否高位在数组索引低</param>
/// <returns>整数</returns>
public static Result<UInt32> BytesToUInt32(byte[] bytes, int offset = 0, int numLength = 4, bool isLowNumHigh = false)
{
if (bytes.Length < offset)
{
return new(new ArgumentException($"The Length of bytes is less than offset"));
}
if (numLength > 4)
{
return new(new ArgumentException($"The Length of bytes is greater than 4"));
}
if (bytes.Length < offset)
{
return new(new ArgumentException($"The Length of bytes is less than offset"));
}
if (numLength > 4)
{
return new(new ArgumentException($"The Length of bytes is greater than 4"));
}
if (numLength <= 0) return 0;
try try
{ {
if (!isLowNumHigh) byte[] numBytes = new byte[4]; // 4字节
{ int copyLen = Math.Min(numLength, bytes.Length - offset);
Array.Reverse(bytes); if (copyLen < 0) copyLen = 0;
}
num = BitConverter.ToUInt64(bytes, 0);
if (isLowNumHigh)
{
// 小端:拷贝到低位
if (copyLen > 0)
{
Buffer.BlockCopy(bytes, offset, numBytes, 0, copyLen);
}
}
else
{
// 大端:拷贝到高位
if (copyLen > 0)
{
byte[] temp = new byte[copyLen];
Buffer.BlockCopy(bytes, offset, temp, 0, copyLen);
Array.Reverse(temp);
Buffer.BlockCopy(temp, 0, numBytes, 4 - copyLen, copyLen);
}
}
UInt32 num = BitConverter.ToUInt32(numBytes, 0);
return num; return num;
} }
catch (Exception error) catch (Exception error)
@@ -121,32 +224,17 @@ public class Number
/// <returns>整数</returns> /// <returns>整数</returns>
public static Result<UInt32> BytesToUInt32(byte[] bytes, bool isLowNumHigh = false) public static Result<UInt32> BytesToUInt32(byte[] bytes, bool isLowNumHigh = false)
{ {
if (bytes.Length > 4) return BytesToUInt32(bytes, 0, 4, isLowNumHigh);
{
return new(new ArgumentException(
"Unsigned long number can't over 4 bytes(32 bits).",
nameof(bytes)
));
} }
UInt32 num = 0; /// <summary>
int len = bytes.Length; /// 二进制字节数组转成32bits整数
/// </summary>
try /// <param name="bytes">二进制字节数组</param>
/// <returns>整数</returns>
public static Result<UInt32> BytesToUInt32(byte[] bytes)
{ {
if (!isLowNumHigh) return BytesToUInt32(bytes, 0, 4, false);
{
Array.Reverse(bytes);
}
num = BitConverter.ToUInt32(bytes, 0);
return num;
}
catch (Exception error)
{
return new(error);
}
} }
/// <summary> /// <summary>

View File

@@ -100,6 +100,51 @@ class HdmiIn
return result.Value; 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);
}
/// <summary> /// <summary>
/// 获取当前分辨率 /// 获取当前分辨率
/// </summary> /// </summary>

View File

@@ -31,47 +31,39 @@ public class HttpHdmiVideoStreamService : BackgroundService
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
HttpListenerContext? context = null; if (_httpListener == null) continue;
try try
{ {
logger.Debug("Waiting for HTTP request..."); logger.Debug("Waiting for HTTP request...");
context = await _httpListener.GetContextAsync(); var contextTask = _httpListener.GetContextAsync();
var completedTask = await Task.WhenAny(contextTask, Task.Delay(-1, stoppingToken));
if (completedTask == contextTask)
{
var context = contextTask.Result;
logger.Debug($"Received request: {context.Request.Url?.AbsolutePath}"); logger.Debug($"Received request: {context.Request.Url?.AbsolutePath}");
if (context != null)
_ = HandleRequestAsync(context, stoppingToken);
} }
catch (ObjectDisposedException) else
{ {
// Listener closed, exit loop
break; break;
} }
catch (HttpListenerException)
{
// Listener closed, exit loop
break;
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "Error in GetContextAsync"); logger.Error(ex, "Error in GetContextAsync");
break; break;
} }
if (context != null)
_ = HandleRequestAsync(context, stoppingToken);
}
}
finally
{
_httpListener?.Close();
logger.Info("HDMI Video Stream Service stopped.");
} }
} }
public override async Task StopAsync(CancellationToken cancellationToken) public override async Task StopAsync(CancellationToken cancellationToken)
{ {
logger.Info("Stopping HDMI Video Stream Service..."); logger.Info("Stopping HDMI Video Stream Service...");
_httpListener?.Close();
// 禁用所有活跃的HDMI传输 // 禁用所有活跃的HDMI传输
var disableTasks = new List<Task>(); var disableTasks = new List<Task>();
@@ -229,9 +221,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
{ {
logger.Debug("处理HDMI快照请求"); logger.Debug("处理HDMI快照请求");
const int frameWidth = 960; // HDMI输入分辨率
const int frameHeight = 540;
// 从HDMI读取RGB565数据 // 从HDMI读取RGB565数据
var frameResult = await hdmiIn.ReadFrame(); var frameResult = await hdmiIn.ReadFrame();
if (!frameResult.IsSuccessful || frameResult.Value == null) if (!frameResult.IsSuccessful || frameResult.Value == null)
@@ -244,41 +233,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
return; return;
} }
var rgb565Data = frameResult.Value; var jpegData = 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;
// 设置响应头参考Camera版本 // 设置响应头参考Camera版本
response.ContentType = "image/jpeg"; response.ContentType = "image/jpeg";
@@ -314,8 +269,6 @@ public class HttpHdmiVideoStreamService : BackgroundService
logger.Debug("开始HDMI MJPEG流传输"); logger.Debug("开始HDMI MJPEG流传输");
int frameCounter = 0; int frameCounter = 0;
const int frameWidth = 960; // HDMI输入分辨率
const int frameHeight = 540;
while (!cancellationToken.IsCancellationRequested) while (!cancellationToken.IsCancellationRequested)
{ {
@@ -323,61 +276,13 @@ public class HttpHdmiVideoStreamService : BackgroundService
{ {
var frameStartTime = DateTime.UtcNow; var frameStartTime = DateTime.UtcNow;
// 从HDMI读取RGB565数据 var ret = await hdmiIn.GetMJpegFrame();
var readStartTime = DateTime.UtcNow; if (ret == null) continue;
var frameResult = await hdmiIn.ReadFrame(); var frame = ret.Value;
var readEndTime = DateTime.UtcNow;
var readTime = (readEndTime - readStartTime).TotalMilliseconds;
if (!frameResult.IsSuccessful || frameResult.Value == null) await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken);
{ await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
logger.Warn("HDMI帧读取失败或为空"); await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
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.FlushAsync(cancellationToken); await response.OutputStream.FlushAsync(cancellationToken);
frameCounter++; frameCounter++;
@@ -387,8 +292,8 @@ public class HttpHdmiVideoStreamService : BackgroundService
// 性能统计日志每30帧记录一次 // 性能统计日志每30帧记录一次
if (frameCounter % 30 == 0) if (frameCounter % 30 == 0)
{ {
logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 读取: {ReadTime:F1}ms, RGB转换: {ConvertTime:F1}ms, JPEG转换: {JpegTime:F1}ms, 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节", logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
frameCounter, readTime, convertTime, jpegTime, totalTime, jpegData.Length); frameCounter, totalTime, frame.data.Length);
} }
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -1,8 +1,5 @@
#define USB_CAMERA
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Peripherals.CameraClient; // 添加摄像头客户端引用 using Peripherals.CameraClient; // 添加摄像头客户端引用
#if USB_CAMERA #if USB_CAMERA
@@ -1003,38 +1000,6 @@ public class HttpVideoStreamService : BackgroundService
logger.Info("HTTP 视频流服务已停止"); logger.Info("HTTP 视频流服务已停止");
} }
/// <summary>
/// 释放资源
/// </summary>
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();
}
/// <summary> /// <summary>
/// 设置视频流分辨率 /// 设置视频流分辨率
/// </summary> /// </summary>

View File

@@ -21,7 +21,7 @@ public class ProgressReporter : ProgressInfo, IProgress<int>
set set
{ {
_stepProgress = value; _stepProgress = value;
ExpectedSteps = MaxProgress / value; _expectedSteps = MaxProgress / value;
} }
} }
public int ExpectedSteps public int ExpectedSteps
@@ -31,7 +31,7 @@ public class ProgressReporter : ProgressInfo, IProgress<int>
{ {
_expectedSteps = value; _expectedSteps = value;
MaxProgress = Number.IntPow(10, Number.GetLength(value)); MaxProgress = Number.IntPow(10, Number.GetLength(value));
StepProgress = MaxProgress / value; _stepProgress = MaxProgress / value;
} }
} }
public Func<int, Task>? ReporterFunc { get; set; } = null; public Func<int, Task>? ReporterFunc { get; set; } = null;
@@ -190,7 +190,7 @@ public class ProgressTrackerService : BackgroundService
{ {
logger.Error(ex, "Error during ProgressTracker cleanup"); logger.Error(ex, "Error during ProgressTracker cleanup");
} }
await Task.Delay(TimeSpan.FromSeconds(30)); await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
} }
} }