refactor: 视频流前后端适配

This commit is contained in:
2025-08-11 13:09:30 +08:00
parent b95a61c532
commit c1d641c20c
7 changed files with 752 additions and 229 deletions

View File

@@ -13,6 +13,7 @@ namespace server.Services;
public class VideoStreamClient
{
public string? ClientId { get; set; } = string.Empty;
public bool IsEnabled { get; set; } = true;
public int FrameWidth { get; set; }
public int FrameHeight { get; set; }
public int FrameRate { get; set; }
@@ -35,28 +36,35 @@ public class VideoStreamClient
/// <summary>
/// 表示摄像头连接状态信息
/// </summary>
public class VideoEndpoint
public class VideoStreamEndpoint
{
public string BoardId { get; set; } = "";
public string MjpegUrl { get; set; } = "";
public string VideoUrl { get; set; } = "";
public string SnapshotUrl { get; set; } = "";
public required string BoardId { get; set; } = "";
public required string MjpegUrl { get; set; } = "";
public required string VideoUrl { get; set; } = "";
public required string SnapshotUrl { get; set; } = "";
public required string HtmlUrl { get; set; } = "";
public required string UsbCameraUrl { get; set; } = "";
public required bool IsEnabled { get; set; }
/// <summary>
/// 视频流的帧率FPS
/// </summary>
public int FrameRate { get; set; }
public required int FrameRate { get; set; }
public int FrameWidth { get; set; }
public int FrameHeight { get; set; }
/// <summary>
/// 视频分辨率(如 640x480
/// </summary>
public string Resolution { get; set; } = string.Empty;
public string Resolution => $"{FrameWidth}x{FrameHeight}";
}
/// <summary>
/// 表示视频流服务的运行状态
/// </summary>
public class ServiceStatus
public class VideoStreamServiceStatus
{
/// <summary>
/// 服务是否正在运行
@@ -71,7 +79,7 @@ public class ServiceStatus
/// <summary>
/// 当前连接的客户端端点列表
/// </summary>
public List<VideoEndpoint> ClientEndpoints { get; set; } = new();
public List<VideoStreamEndpoint> ClientEndpoints { get; set; } = new();
/// <summary>
/// 当前连接的客户端数量
@@ -106,36 +114,6 @@ public class HttpVideoStreamService : BackgroundService
_userManager = userManager;
}
/// <summary>
/// 初始化 HttpVideoStreamService
/// </summary>
public override async Task StartAsync(CancellationToken cancellationToken)
{
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://{Global.LocalHost}:{_serverPort}/");
_httpListener.Start();
logger.Info($"Video Stream Service started on port {_serverPort}");
await base.StartAsync(cancellationToken);
}
/// <summary>
/// 停止 HTTP 视频流服务
/// </summary>
public override async Task StopAsync(CancellationToken cancellationToken)
{
foreach (var clientKey in _clientDict.Keys)
{
var client = _clientDict[clientKey];
client.CTS.Cancel();
using (await client.Lock.AcquireWriteLockAsync(cancellationToken))
{
await client.Camera.EnableHardwareTrans(false);
}
}
_clientDict.Clear();
await base.StopAsync(cancellationToken);
}
private Optional<VideoStreamClient> TryGetClient(string boardId)
{
@@ -176,6 +154,36 @@ public class HttpVideoStreamService : BackgroundService
return client;
}
/// <summary>
/// 初始化 HttpVideoStreamService
/// </summary>
public override async Task StartAsync(CancellationToken cancellationToken)
{
_httpListener = new HttpListener();
_httpListener.Prefixes.Add($"http://{Global.LocalHost}:{_serverPort}/");
_httpListener.Start();
logger.Info($"Video Stream Service started on port {_serverPort}");
await base.StartAsync(cancellationToken);
}
/// <summary>
/// 停止 HTTP 视频流服务
/// </summary>
public override async Task StopAsync(CancellationToken cancellationToken)
{
foreach (var clientKey in _clientDict.Keys)
{
var client = _clientDict[clientKey];
client.CTS.Cancel();
using (await client.Lock.AcquireWriteLockAsync(cancellationToken))
{
await client.Camera.EnableHardwareTrans(false);
}
}
_clientDict.Clear();
await base.StopAsync(cancellationToken);
}
/// <summary>
/// 执行 HTTP 视频流服务
@@ -254,6 +262,11 @@ public class HttpVideoStreamService : BackgroundService
// 单帧图像请求
await HandleSnapshotRequestAsync(context.Response, client, cancellationToken);
}
else if (path == "/html")
{
// HTML页面请求
await SendIndexHtmlPageAsync(context.Response);
}
else
{
// 默认返回简单的HTML页面提供链接到视频页面
@@ -668,42 +681,12 @@ public class HttpVideoStreamService : BackgroundService
}
}
public VideoEndpoint GetVideoEndpoint(string boardId)
{
var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));
return new VideoEndpoint
{
BoardId = boardId,
MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={boardId}",
VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={boardId}",
SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={boardId}",
Resolution = $"{client.FrameWidth}x{client.FrameHeight}",
FrameRate = client.FrameRate
};
}
public List<VideoEndpoint> GetAllVideoEndpoints()
{
var endpoints = new List<VideoEndpoint>();
foreach (var boardId in _clientDict.Keys)
endpoints.Add(GetVideoEndpoint(boardId));
return endpoints;
}
public ServiceStatus GetServiceStatus()
{
return new ServiceStatus
{
IsRunning = true,
ServerPort = _serverPort,
ClientEndpoints = GetAllVideoEndpoints()
};
}
public async Task DisableHdmiTransmissionAsync(string boardId)
/// <summary>
/// 配置摄像头连接参数
/// </summary>
/// <param name="boardId">板卡ID</param>
/// <returns>配置是否成功</returns>
public async Task<bool> ConfigureCameraAsync(string boardId)
{
try
{
@@ -711,8 +694,67 @@ public class HttpVideoStreamService : BackgroundService
using (await client.Lock.AcquireWriteLockAsync())
{
var ret = await client.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!");
}
}
using (await client.Lock.AcquireWriteLockAsync())
{
var ret = await client.Camera.ChangeResolution(client.FrameWidth, client.FrameHeight);
if (!ret.IsSuccessful)
{
logger.Error(ret.Error);
throw ret.Error;
}
if (!ret.Value)
{
logger.Error($"Camera Resolution Change Failed!");
throw new Exception($"Camera Resolution Change Failed!");
}
}
return true;
}
catch (Exception ex)
{
logger.Error(ex, "配置摄像头连接时发生错误");
return false;
}
}
public async Task SetVideoStreamEnableAsync(string boardId, bool enable)
{
try
{
var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));
if (client.IsEnabled == enable)
return;
using (await client.Lock.AcquireWriteLockAsync())
{
if (enable)
{
client.CTS = new CancellationTokenSource();
}
else
{
client.CTS.Cancel();
}
var camera = client.Camera;
var disableResult = await camera.EnableHardwareTrans(false);
var disableResult = await camera.EnableHardwareTrans(enable);
if (disableResult.IsSuccessful && disableResult.Value)
logger.Info($"Successfully disabled camera {boardId} hardware transmission");
else
@@ -743,4 +785,41 @@ public class HttpVideoStreamService : BackgroundService
return false;
}
}
public VideoStreamEndpoint GetVideoEndpoint(string boardId)
{
var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));
return new VideoStreamEndpoint
{
BoardId = boardId,
MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={boardId}",
VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={boardId}",
SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={boardId}",
UsbCameraUrl = $"http://{Global.LocalHost}:{_serverPort}/usbCamera?boardId={boardId}",
HtmlUrl = $"http://{Global.LocalHost}:{_serverPort}/html?boardId={boardId}",
IsEnabled = client.IsEnabled,
FrameRate = client.FrameRate
};
}
public List<VideoStreamEndpoint> GetAllVideoEndpoints()
{
var endpoints = new List<VideoStreamEndpoint>();
foreach (var boardId in _clientDict.Keys)
endpoints.Add(GetVideoEndpoint(boardId));
return endpoints;
}
public VideoStreamServiceStatus GetServiceStatus()
{
return new VideoStreamServiceStatus
{
IsRunning = true,
ServerPort = _serverPort,
ClientEndpoints = GetAllVideoEndpoints()
};
}
}