feat: 细化OV配置

This commit is contained in:
alivender 2025-07-13 10:51:24 +08:00
parent 15c6eefe30
commit 229e6e70ed
4 changed files with 826 additions and 288 deletions

File diff suppressed because it is too large Load Diff

View File

@ -82,8 +82,8 @@ public class HttpVideoStreamService : BackgroundService
private HttpListener? _httpListener; private HttpListener? _httpListener;
private readonly int _serverPort = 8080; private readonly int _serverPort = 8080;
private readonly int _frameRate = 30; // 30 FPS private readonly int _frameRate = 30; // 30 FPS
private readonly int _frameWidth = 640; private readonly int _frameWidth = 1280;
private readonly int _frameHeight = 480; private readonly int _frameHeight = 720;
// 摄像头客户端 // 摄像头客户端
private Camera? _camera; private Camera? _camera;
@ -559,21 +559,59 @@ public class HttpVideoStreamService : BackgroundService
private async Task GenerateVideoFrames(CancellationToken cancellationToken) private async Task GenerateVideoFrames(CancellationToken cancellationToken)
{ {
var frameInterval = TimeSpan.FromMilliseconds(1000.0 / _frameRate); var frameInterval = TimeSpan.FromMilliseconds(1000.0 / _frameRate);
var lastFrameTime = DateTime.UtcNow;
while (!cancellationToken.IsCancellationRequested && _cameraEnable) while (!cancellationToken.IsCancellationRequested && _cameraEnable)
{ {
try try
{ {
// 从 FPGA 获取图像数据(模拟) var frameStartTime = DateTime.UtcNow;
// 从 FPGA 获取图像数据
var imageData = await GetFPGAImageData(); var imageData = await GetFPGAImageData();
// 向所有连接的客户端发送帧 var imageAcquireTime = DateTime.UtcNow;
// 如果有图像数据,立即开始广播(不等待)
if (imageData != null && imageData.Length > 0)
{
// 异步广播帧,不阻塞下一帧的获取
_ = Task.Run(async () =>
{
try
{
await BroadcastFrameAsync(imageData, cancellationToken); await BroadcastFrameAsync(imageData, cancellationToken);
}
catch (Exception ex)
{
logger.Error(ex, "异步广播帧时发生错误");
}
}, cancellationToken);
_frameCounter++; _frameCounter++;
// 等待下一帧 var frameEndTime = DateTime.UtcNow;
await Task.Delay(frameInterval, cancellationToken); var frameProcessingTime = (frameEndTime - frameStartTime).TotalMilliseconds;
var imageAcquireElapsed = (imageAcquireTime - frameStartTime).TotalMilliseconds;
if (_frameCounter % 30 == 0) // 每秒记录一次性能信息
{
logger.Debug("帧 {FrameNumber} 性能统计 - 图像获取: {AcquireTime:F1}ms, 总处理: {ProcessTime:F1}ms",
_frameCounter, imageAcquireElapsed, frameProcessingTime);
}
}
// 动态调整延迟 - 基于实际处理时间
var elapsed = (DateTime.UtcNow - lastFrameTime).TotalMilliseconds;
var targetInterval = frameInterval.TotalMilliseconds;
var remainingDelay = Math.Max(0, targetInterval - elapsed);
if (remainingDelay > 0)
{
await Task.Delay(TimeSpan.FromMilliseconds(remainingDelay), cancellationToken);
}
lastFrameTime = DateTime.UtcNow;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -582,7 +620,7 @@ public class HttpVideoStreamService : BackgroundService
catch (Exception ex) catch (Exception ex)
{ {
logger.Error(ex, "生成视频帧时发生错误"); logger.Error(ex, "生成视频帧时发生错误");
await Task.Delay(1000, cancellationToken); // 错误恢复延迟 await Task.Delay(100, cancellationToken); // 减少错误恢复延迟
} }
} }
} }
@ -593,6 +631,7 @@ public class HttpVideoStreamService : BackgroundService
/// </summary> /// </summary>
private async Task<byte[]> GetFPGAImageData() private async Task<byte[]> GetFPGAImageData()
{ {
var startTime = DateTime.UtcNow;
Camera? currentCamera = null; Camera? currentCamera = null;
lock (_cameraLock) lock (_cameraLock)
@ -609,7 +648,10 @@ public class HttpVideoStreamService : BackgroundService
try try
{ {
// 从摄像头读取帧数据 // 从摄像头读取帧数据
var readStartTime = DateTime.UtcNow;
var result = await currentCamera.ReadFrame(); var result = await currentCamera.ReadFrame();
var readEndTime = DateTime.UtcNow;
var readTime = (readEndTime - readStartTime).TotalMilliseconds;
if (!result.IsSuccessful) if (!result.IsSuccessful)
{ {
@ -627,17 +669,23 @@ public class HttpVideoStreamService : BackgroundService
} }
// 将 RGB565 转换为 RGB24 // 将 RGB565 转换为 RGB24
var convertStartTime = DateTime.UtcNow;
var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, _frameWidth, _frameHeight, isLittleEndian: false); var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, _frameWidth, _frameHeight, isLittleEndian: false);
var convertEndTime = DateTime.UtcNow;
var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;
if (!rgb24Result.IsSuccessful) if (!rgb24Result.IsSuccessful)
{ {
logger.Error("RGB565转RGB24失败: {Error}", rgb24Result.Error); logger.Error("RGB565转RGB24失败: {Error}", rgb24Result.Error);
return new byte[0]; return new byte[0];
} }
var totalTime = (DateTime.UtcNow - startTime).TotalMilliseconds;
if (_frameCounter % 30 == 0) // 每秒更新一次日志 if (_frameCounter % 30 == 0) // 每秒更新一次日志
{ {
logger.Debug("成功获取第 {FrameNumber} 帧RGB565大小: {RGB565Size} 字节, RGB24大小: {RGB24Size} 字节", logger.Debug("帧 {FrameNumber} 数据获取性能 - 读取: {ReadTime:F1}ms, 转换: {ConvertTime:F1}ms, 总计: {TotalTime:F1}ms, RGB565: {RGB565Size} 字节, RGB24: {RGB24Size} 字节",
_frameCounter, rgb565Data.Length, rgb24Result.Value.Length); _frameCounter, readTime, convertTime, totalTime, rgb565Data.Length, rgb24Result.Value.Length);
} }
return rgb24Result.Value; return rgb24Result.Value;
@ -688,8 +736,8 @@ public class HttpVideoStreamService : BackgroundService
return; // 没有活跃客户端 return; // 没有活跃客户端
} }
// 向每个活跃的客户端发送帧 // 向每个活跃的客户端并行发送帧
foreach (var client in clientsToProcess) var sendTasks = clientsToProcess.Select(async client =>
{ {
try try
{ {
@ -705,19 +753,33 @@ public class HttpVideoStreamService : BackgroundService
// 确保数据立即发送 // 确保数据立即发送
await client.OutputStream.FlushAsync(cancellationToken); await client.OutputStream.FlushAsync(cancellationToken);
if (_frameCounter % 30 == 0) // 每秒记录一次日志 return (client, success: true, error: (Exception?)null);
{
logger.Debug("已向客户端 {ClientId} 发送第 {FrameNumber} 帧,大小:{Size} 字节",
client.OutputStream.GetHashCode(), _frameCounter, jpegData.Length);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Debug("发送帧数据时出错: {Error}", ex.Message); return (client, success: false, error: ex);
}
});
// 等待所有发送任务完成
var results = await Task.WhenAll(sendTasks);
// 处理发送结果
foreach (var (client, success, error) in results)
{
if (!success)
{
logger.Debug("发送帧数据时出错: {Error}", error?.Message ?? "未知错误");
clientsToRemove.Add(client); clientsToRemove.Add(client);
} }
} }
if (_frameCounter % 30 == 0 && clientsToProcess.Count > 0) // 每秒记录一次日志
{
logger.Debug("已向 {ClientCount} 个客户端发送第 {FrameNumber} 帧,大小:{Size} 字节",
clientsToProcess.Count, _frameCounter, jpegData.Length);
}
// 移除断开连接的客户端 // 移除断开连接的客户端
if (clientsToRemove.Count > 0) if (clientsToRemove.Count > 0)
{ {

View File

@ -60,8 +60,8 @@ public class UDPClientPool
var sendLen = socket.SendTo(buf, endPoint); var sendLen = socket.SendTo(buf, endPoint);
socket.Close(); socket.Close();
logger.Debug($"UDP socket send bytes to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:"); // logger.Debug($"UDP socket send bytes to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(buf).Replace("-", " ")}"); // logger.Debug($" Original Data: {BitConverter.ToString(buf).Replace("-", " ")}");
if (sendLen == buf.Length) { return true; } if (sendLen == buf.Length) { return true; }
else { return false; } else { return false; }
@ -91,9 +91,9 @@ public class UDPClientPool
var sendLen = socket.SendTo(sendBytes, endPoint); var sendLen = socket.SendTo(sendBytes, endPoint);
socket.Close(); socket.Close();
logger.Debug($"UDP socket send address package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:"); // logger.Debug($"UDP socket send address package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}"); // logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
logger.Debug($" Decoded Data: {pkg.ToString()}"); // logger.Debug($" Decoded Data: {pkg.ToString()}");
if (sendLen == sendBytes.Length) { return true; } if (sendLen == sendBytes.Length) { return true; }
else { return false; } else { return false; }
@ -124,8 +124,8 @@ public class UDPClientPool
var sendLen = socket.SendTo(sendBytes, endPoint); var sendLen = socket.SendTo(sendBytes, endPoint);
socket.Close(); socket.Close();
logger.Debug($"UDP socket send data package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:"); // logger.Debug($"UDP socket send data package to device {endPoint.Address.ToString()}:{endPoint.Port.ToString()}:");
logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}"); // logger.Debug($" Original Data: {BitConverter.ToString(pkg.ToBytes()).Replace("-", " ")}");
if (sendLen == sendBytes.Length) { return true; } if (sendLen == sendBytes.Length) { return true; }
else { return false; } else { return false; }

View File

@ -145,7 +145,7 @@ public class UDPServer
{ {
UDPData? data = null; UDPData? data = null;
logger.Debug($"Caller \"{callerName}|{callerLineNum}\": Try to find {ipAddr}-{taskID} UDP Data"); // logger.Debug($"Caller \"{callerName}|{callerLineNum}\": Try to find {ipAddr}-{taskID} UDP Data");
var startTime = DateTime.Now; var startTime = DateTime.Now;
var isTimeout = false; var isTimeout = false;
@ -164,7 +164,7 @@ public class UDPServer
dataQueue.Count > 0) dataQueue.Count > 0)
{ {
data = dataQueue.Dequeue(); data = dataQueue.Dequeue();
logger.Debug($"Find UDP Data: {data.ToString()}"); // logger.Debug($"Find UDP Data: {data.ToString()}");
break; break;
} }
} }
@ -212,7 +212,7 @@ public class UDPServer
dataQueue.Count > 0) dataQueue.Count > 0)
{ {
data = dataQueue.ToList(); data = dataQueue.ToList();
logger.Debug($"Find UDP Data Array: {JsonConvert.SerializeObject(data)}"); // logger.Debug($"Find UDP Data Array: {JsonConvert.SerializeObject(data)}");
break; break;
} }
} }
@ -290,8 +290,8 @@ public class UDPServer
// Handle RemoteEP // Handle RemoteEP
if (remoteEP is null) if (remoteEP is null)
{ {
logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:"); // logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:");
logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}"); // logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}");
goto BEGIN_RECEIVE; goto BEGIN_RECEIVE;
} }
@ -395,9 +395,9 @@ public class UDPServer
recvData = Encoding.ASCII.GetString(bytes, 0, bytes.Length); recvData = Encoding.ASCII.GetString(bytes, 0, bytes.Length);
} }
logger.Debug($"Receive Data from {data.Address}:{data.Port} at {data.DateTime.ToString()}:"); // logger.Debug($"Receive Data from {data.Address}:{data.Port} at {data.DateTime.ToString()}:");
logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}"); // logger.Debug($" Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}");
if (recvData.Length != 0) logger.Debug($" Decoded Data : {recvData}"); // if (recvData.Length != 0) logger.Debug($" Decoded Data : {recvData}");
} }
/// <summary> /// <summary>
@ -408,13 +408,13 @@ public class UDPServer
{ {
using (udpData.AcquireReadLock()) using (udpData.AcquireReadLock())
{ {
logger.Debug("Ready Data:"); // logger.Debug("Ready Data:");
foreach (var ip in udpData) foreach (var ip in udpData)
{ {
foreach (var data in ip.Value) foreach (var data in ip.Value)
{ {
logger.Debug(data.ToString()); // logger.Debug(data.ToString());
} }
} }
} }