ConfigureCameraAsync(string address, int port)
{
if (string.IsNullOrWhiteSpace(address))
{
logger.Error("摄像头地址不能为空");
return false;
}
if (port <= 0 || port > 65535)
{
logger.Error("摄像头端口必须在1-65535范围内");
return false;
}
try
{
lock (_cameraLock)
{
// 关闭现有连接
if (_camera != null)
{
logger.Info("关闭现有摄像头连接");
// Camera doesn't have Dispose method, set to null
_camera = null;
}
// 更新配置
_cameraAddress = address;
_cameraPort = port;
// 创建新的摄像头客户端
_camera = new Camera(_cameraAddress, _cameraPort);
logger.Info("摄像头配置已更新: {Address}:{Port}", _cameraAddress, _cameraPort);
}
// Init Camera
{
var ret = await _camera.Init();
if (!ret.IsSuccessful)
{
logger.Error(ret.Error);
throw ret.Error;
}
if (!ret.Value)
{
logger.Error($"Camera Init Failed!");
throw new Exception($"Camera Init Failed!");
}
}
return true;
}
catch (Exception ex)
{
logger.Error(ex, "配置摄像头连接时发生错误");
return false;
}
}
///
/// 测试摄像头连接
///
/// 连接测试结果
public async Task<(bool IsSuccess, string Message)> TestCameraConnectionAsync()
{
try
{
Camera? testCamera = null;
lock (_cameraLock)
{
if (_camera == null)
{
return (false, "摄像头未配置");
}
testCamera = _camera;
}
// 尝试读取一帧数据来测试连接
var result = await testCamera.ReadFrame();
if (result.IsSuccessful)
{
logger.Info("摄像头连接测试成功: {Address}:{Port}", _cameraAddress, _cameraPort);
return (true, "连接成功");
}
else
{
logger.Warn("摄像头连接测试失败: {Error}", result.Error);
return (false, result.Error.ToString());
}
}
catch (Exception ex)
{
logger.Error(ex, "摄像头连接测试出错");
return (false, ex.Message);
}
}
///
/// 获取摄像头连接状态
///
/// 连接状态信息
public CameraStatus GetCameraStatus()
{
return new CameraStatus
{
Address = _cameraAddress,
Port = _cameraPort,
IsConfigured = _camera != null,
ConnectionString = $"{_cameraAddress}:{_cameraPort}"
};
}
///
/// 执行 HTTP 视频流服务
///
/// 取消令牌
/// 任务
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
logger.Info("启动 HTTP 视频流服务,端口: {Port}", _serverPort);
// 初始化默认摄像头连接
await ConfigureCameraAsync(_cameraAddress, _cameraPort);
// 创建 HTTP 监听器
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://localhost:{_serverPort}/");
_httpListener.Start();
logger.Info("HTTP 视频流服务已启动,监听端口: {Port}", _serverPort);
// 开始接受客户端连接
_ = Task.Run(() => AcceptClientsAsync(stoppingToken), stoppingToken);
// 开始生成视频帧
while (!stoppingToken.IsCancellationRequested)
{
if (_cameraEnable)
{
await GenerateVideoFrames(stoppingToken);
}
else
{
await Task.Delay(500, stoppingToken);
}
}
}
catch (HttpListenerException ex)
{
logger.Error(ex, "HTTP 视频流服务启动失败,请确保您有管理员权限或使用netsh配置URL前缀权限");
}
catch (Exception ex)
{
logger.Error(ex, "HTTP 视频流服务启动失败");
}
}
private async Task AcceptClientsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested && _httpListener != null && _httpListener.IsListening)
{
try
{
// 等待客户端连接
var context = await _httpListener.GetContextAsync();
var request = context.Request;
var response = context.Response;
logger.Info("新HTTP客户端连接: {RemoteEndPoint}", request.RemoteEndPoint);
// 处理不同的请求路径
var requestPath = request.Url?.AbsolutePath ?? "/";
if (requestPath == "/video-stream")
{
// MJPEG 流请求
_ = Task.Run(() => HandleMjpegStreamAsync(response, cancellationToken), cancellationToken);
}
else if (requestPath == "/snapshot")
{
// 单帧图像请求
await HandleSnapshotRequestAsync(response, cancellationToken);
}
else if (requestPath == "/video-feed.html")
{
// HTML页面请求
await SendVideoHtmlPageAsync(response);
}
else
{
// 默认返回简单的HTML页面,提供链接到视频页面
await SendIndexHtmlPageAsync(response);
}
}
catch (HttpListenerException)
{
// HTTP监听器可能已停止
break;
}
catch (ObjectDisposedException)
{
// 对象可能已被释放
break;
}
catch (Exception ex)
{
logger.Error(ex, "接受HTTP客户端连接时发生错误");
}
}
}
private async Task HandleMjpegStreamAsync(HttpListenerResponse response, CancellationToken cancellationToken)
{
try
{
// 设置MJPEG流的响应头
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");
// 跟踪活跃的客户端
lock (_clientsLock)
{
_activeClients.Add(response);
}
logger.Debug("已启动MJPEG流,客户端: {RemoteEndPoint}", response.OutputStream?.GetHashCode() ?? 0);
// 保持连接直到取消或出错
try
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(100, cancellationToken); // 简单的保活循环
}
}
catch (TaskCanceledException)
{
// 预期的取消
}
logger.Debug("MJPEG流已结束,客户端: {ClientId}", response.OutputStream?.GetHashCode() ?? 0);
}
catch (Exception ex)
{
logger.Error(ex, "处理MJPEG流时出错");
}
finally
{
lock (_clientsLock)
{
_activeClients.Remove(response);
}
try
{
response.Close();
}
catch
{
// 忽略关闭时的错误
}
}
}
private async Task HandleSnapshotRequestAsync(HttpListenerResponse response, CancellationToken cancellationToken)
{
try
{
// 获取当前帧
var imageData = await GetFPGAImageData();
// 获取当前分辨率
int currentWidth, currentHeight;
lock (_resolutionLock)
{
currentWidth = _frameWidth;
currentHeight = _frameHeight;
}
// 直接使用Common.Image.ConvertRGB24ToJpeg进行转换
var jpegResult = Common.Image.ConvertRGB24ToJpeg(imageData, currentWidth, currentHeight, 80);
if (!jpegResult.IsSuccessful)
{
logger.Error("RGB24转JPEG失败: {Error}", jpegResult.Error);
response.StatusCode = 500;
response.Close();
return;
}
var jpegData = jpegResult.Value;
// 设置响应头
response.ContentType = "image/jpeg";
response.ContentLength64 = jpegData.Length;
response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
// 发送JPEG数据
await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
await response.OutputStream.FlushAsync(cancellationToken);
logger.Debug("已发送快照图像,大小:{Size} 字节", jpegData.Length);
}
catch (Exception ex)
{
logger.Error(ex, "处理快照请求时出错");
}
finally
{
response.Close();
}
}
private async Task SendVideoHtmlPageAsync(HttpListenerResponse response)
{
string html = $@"
FPGA 视频流
FPGA 实时视频流
状态: 连接中...
";
response.ContentType = "text/html";
response.ContentEncoding = Encoding.UTF8;
byte[] buffer = Encoding.UTF8.GetBytes(html);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
response.Close();
}
private async Task SendIndexHtmlPageAsync(HttpListenerResponse response)
{
string html = $@"
FPGA WebLab 视频服务
FPGA WebLab 视频服务
HTTP流媒体服务端口: {_serverPort}
";
response.ContentType = "text/html";
response.ContentEncoding = Encoding.UTF8;
byte[] buffer = Encoding.UTF8.GetBytes(html);
response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
response.Close();
}
private async Task GenerateVideoFrames(CancellationToken cancellationToken)
{
var frameInterval = TimeSpan.FromMilliseconds(1000.0 / _frameRate);
var lastFrameTime = DateTime.UtcNow;
while (!cancellationToken.IsCancellationRequested && _cameraEnable)
{
try
{
var frameStartTime = DateTime.UtcNow;
// 从 FPGA 获取图像数据
var imageData = await GetFPGAImageData();
var imageAcquireTime = DateTime.UtcNow;
// 如果有图像数据,立即开始广播(不等待)
if (imageData != null && imageData.Length > 0)
{
// 异步广播帧,不阻塞下一帧的获取
_ = Task.Run(async () =>
{
try
{
await BroadcastFrameAsync(imageData, cancellationToken);
}
catch (Exception ex)
{
logger.Error(ex, "异步广播帧时发生错误");
}
}, cancellationToken);
_frameCounter++;
var frameEndTime = DateTime.UtcNow;
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)
{
break;
}
catch (Exception ex)
{
logger.Error(ex, "生成视频帧时发生错误");
await Task.Delay(100, cancellationToken); // 减少错误恢复延迟
}
}
}
///
/// 从 FPGA 获取图像数据
/// 实际从摄像头读取 RGB565 格式数据并转换为 RGB24
///
private async Task GetFPGAImageData()
{
var startTime = DateTime.UtcNow;
Camera? currentCamera = null;
lock (_cameraLock)
{
currentCamera = _camera;
}
if (currentCamera == null)
{
logger.Error("摄像头客户端未初始化");
return new byte[0];
}
try
{
// 获取当前分辨率
int currentWidth, currentHeight;
lock (_resolutionLock)
{
currentWidth = _frameWidth;
currentHeight = _frameHeight;
}
// 从摄像头读取帧数据
var readStartTime = DateTime.UtcNow;
var result = await currentCamera.ReadFrame();
var readEndTime = DateTime.UtcNow;
var readTime = (readEndTime - readStartTime).TotalMilliseconds;
if (!result.IsSuccessful)
{
logger.Error("读取摄像头帧数据失败: {Error}", result.Error);
return new byte[0];
}
var rgb565Data = result.Value;
// 验证数据长度是否正确
if (!Common.Image.ValidateImageDataLength(rgb565Data, currentWidth, currentHeight, 2))
{
logger.Warn("摄像头数据长度不匹配,期望: {Expected}, 实际: {Actual}",
currentWidth * currentHeight * 2, rgb565Data.Length);
}
// 将 RGB565 转换为 RGB24
var convertStartTime = DateTime.UtcNow;
var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, currentWidth, currentHeight, isLittleEndian: false);
var convertEndTime = DateTime.UtcNow;
var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;
if (!rgb24Result.IsSuccessful)
{
logger.Error("RGB565转RGB24失败: {Error}", rgb24Result.Error);
return new byte[0];
}
var totalTime = (DateTime.UtcNow - startTime).TotalMilliseconds;
if (_frameCounter % 30 == 0) // 每秒更新一次日志
{
logger.Debug("帧 {FrameNumber} 数据获取性能 - 读取: {ReadTime:F1}ms, 转换: {ConvertTime:F1}ms, 总计: {TotalTime:F1}ms, RGB565: {RGB565Size} 字节, RGB24: {RGB24Size} 字节",
_frameCounter, readTime, convertTime, totalTime, rgb565Data.Length, rgb24Result.Value.Length);
}
return rgb24Result.Value;
}
catch (Exception ex)
{
logger.Error(ex, "获取FPGA图像数据时发生错误");
return new byte[0];
}
}
///
/// 向所有连接的客户端广播帧数据
///
private async Task BroadcastFrameAsync(byte[] frameData, CancellationToken cancellationToken)
{
if (frameData == null || frameData.Length == 0)
{
logger.Warn("尝试广播空帧数据");
return;
}
// 获取当前分辨率
int currentWidth, currentHeight;
lock (_resolutionLock)
{
currentWidth = _frameWidth;
currentHeight = _frameHeight;
}
// 直接使用Common.Image.ConvertRGB24ToJpeg进行转换
var jpegResult = Common.Image.ConvertRGB24ToJpeg(frameData, currentWidth, currentHeight, 80);
if (!jpegResult.IsSuccessful)
{
logger.Error("RGB24转JPEG失败: {Error}", jpegResult.Error);
return;
}
var jpegData = jpegResult.Value;
// 使用Common中的方法准备MJPEG帧数据
var mjpegFrameHeader = Common.Image.CreateMjpegFrameHeader(jpegData.Length);
var mjpegFrameFooter = Common.Image.CreateMjpegFrameFooter();
var clientsToRemove = new List();
var clientsToProcess = new List();
// 获取当前连接的客户端列表
lock (_clientsLock)
{
clientsToProcess.AddRange(_activeClients);
}
if (clientsToProcess.Count == 0)
{
return; // 没有活跃客户端
}
// 向每个活跃的客户端并行发送帧
var sendTasks = clientsToProcess.Select(async client =>
{
try
{
// 发送帧头部
await client.OutputStream.WriteAsync(mjpegFrameHeader, 0, mjpegFrameHeader.Length, cancellationToken);
// 发送JPEG数据
await client.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
// 发送结尾换行符
await client.OutputStream.WriteAsync(mjpegFrameFooter, 0, mjpegFrameFooter.Length, cancellationToken);
// 确保数据立即发送
await client.OutputStream.FlushAsync(cancellationToken);
return (client, success: true, error: (Exception?)null);
}
catch (Exception ex)
{
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);
}
}
if (_frameCounter % 30 == 0 && clientsToProcess.Count > 0) // 每秒记录一次日志
{
logger.Debug("已向 {ClientCount} 个客户端发送第 {FrameNumber} 帧,大小:{Size} 字节",
clientsToProcess.Count, _frameCounter, jpegData.Length);
}
// 移除断开连接的客户端
if (clientsToRemove.Count > 0)
{
lock (_clientsLock)
{
foreach (var client in clientsToRemove)
{
_activeClients.Remove(client);
try { client.Close(); }
catch { /* 忽略关闭错误 */ }
}
}
logger.Info("已移除 {Count} 个断开连接的客户端,当前连接数: {ActiveCount}",
clientsToRemove.Count, _activeClients.Count);
}
}
///
/// 获取连接的客户端端点列表
///
public List GetConnectedClientEndpoints()
{
List endpoints = new List();
lock (_clientsLock)
{
foreach (var client in _activeClients)
{
endpoints.Add($"Client-{client.OutputStream?.GetHashCode() ?? 0}");
}
}
return endpoints;
}
///
/// 获取服务状态信息
///
public ServiceStatus GetServiceStatus()
{
var cameraStatus = GetCameraStatus();
return new ServiceStatus
{
IsRunning = (_httpListener?.IsListening ?? false) && _cameraEnable,
ServerPort = _serverPort,
FrameRate = _frameRate,
Resolution = $"{_frameWidth}x{_frameHeight}",
ConnectedClients = ConnectedClientsCount,
ClientEndpoints = GetConnectedClientEndpoints(),
CameraStatus = cameraStatus
};
}
///
/// 停止 HTTP 视频流服务
///
public override async Task StopAsync(CancellationToken cancellationToken)
{
logger.Info("正在停止 HTTP 视频流服务...");
_cameraEnable = false;
if (_httpListener != null && _httpListener.IsListening)
{
_httpListener.Stop();
_httpListener.Close();
}
// 关闭所有客户端连接
lock (_clientsLock)
{
foreach (var client in _activeClients)
{
try { client.Close(); }
catch { /* 忽略关闭错误 */ }
}
_activeClients.Clear();
}
// 关闭摄像头连接
lock (_cameraLock)
{
_camera = null;
}
await base.StopAsync(cancellationToken);
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();
}
///
/// 设置视频流分辨率
///
/// 宽度
/// 高度
/// 设置结果
public async Task<(bool IsSuccess, string Message)> SetResolutionAsync(int width, int height)
{
try
{
logger.Info($"正在设置视频流分辨率为 {width}x{height}");
// 验证分辨率
if (!IsSupportedResolution(width, height))
{
var message = $"不支持的分辨率: {width}x{height},支持的分辨率: 640x480, 1280x720";
logger.Error(message);
return (false, message);
}
Camera? currentCamera = null;
lock (_cameraLock)
{
currentCamera = _camera;
}
if (currentCamera == null)
{
var message = "摄像头未配置,无法设置分辨率";
logger.Error(message);
return (false, message);
}
// 设置摄像头分辨率
var cameraResult = await currentCamera.ChangeResolution(width, height);
if (!cameraResult.IsSuccessful)
{
var message = $"设置摄像头分辨率失败: {cameraResult.Error}";
logger.Error(message);
return (false, message);
}
// 更新HTTP服务的分辨率配置
lock (_resolutionLock)
{
_frameWidth = width;
_frameHeight = height;
}
var successMessage = $"视频流分辨率已成功设置为 {width}x{height}";
logger.Info(successMessage);
return (true, successMessage);
}
catch (Exception ex)
{
var message = $"设置分辨率时发生错误: {ex.Message}";
logger.Error(ex, message);
return (false, message);
}
}
///
/// 获取当前分辨率
///
/// 当前分辨率(宽度, 高度)
public (int Width, int Height) GetCurrentResolution()
{
lock (_resolutionLock)
{
return (_frameWidth, _frameHeight);
}
}
///
/// 检查是否支持该分辨率
///
/// 宽度
/// 高度
/// 是否支持
private bool IsSupportedResolution(int width, int height)
{
var resolution = $"{width}x{height}";
return resolution == "640x480" || resolution == "1280x720";
}
///
/// 获取支持的分辨率列表
///
/// 支持的分辨率列表
public List<(int Width, int Height, string Name)> GetSupportedResolutions()
{
return new List<(int, int, string)>
{
(640, 480, "640x480 (VGA)"),
(1280, 720, "1280x720 (HD)")
};
}
#region 自动对焦功能
///
/// 检查摄像头是否已配置
///
/// 是否已配置
public bool IsCameraConfigured()
{
lock (_cameraLock)
{
return _camera != null && !string.IsNullOrEmpty(_cameraAddress);
}
}
///
/// 初始化摄像头自动对焦功能
///
/// 初始化结果
public async Task InitAutoFocusAsync()
{
try
{
lock (_cameraLock)
{
if (_camera == null)
{
logger.Error("摄像头未配置,无法初始化自动对焦");
return false;
}
}
logger.Info("开始初始化摄像头自动对焦功能");
var result = await _camera!.InitAutoFocus();
if (result.IsSuccessful && result.Value)
{
logger.Info("摄像头自动对焦功能初始化成功");
return true;
}
else
{
logger.Error($"摄像头自动对焦功能初始化失败: {result.Error?.Message ?? "未知错误"}");
return false;
}
}
catch (Exception ex)
{
logger.Error(ex, "初始化摄像头自动对焦功能时发生异常");
return false;
}
}
///
/// 执行摄像头自动对焦
///
/// 对焦结果
public async Task PerformAutoFocusAsync()
{
try
{
lock (_cameraLock)
{
if (_camera == null)
{
logger.Error("摄像头未配置,无法执行自动对焦");
return false;
}
}
logger.Info("开始执行摄像头自动对焦");
var result = await _camera!.PerformAutoFocus();
if (result.IsSuccessful && result.Value)
{
logger.Info("摄像头自动对焦执行成功");
return true;
}
else
{
logger.Error($"摄像头自动对焦执行失败: {result.Error?.Message ?? "未知错误"}");
return false;
}
}
catch (Exception ex)
{
logger.Error(ex, "执行摄像头自动对焦时发生异常");
return false;
}
}
#endregion
}