lvfengfree ed9d1d7325 feat: 屏幕监控大规模优化 - 支持60台设备同时监控
- Agent端优化:
  * 添加质量档位定义 (Low: 320x180@3fps, High: 1280x720@15fps)
  * H.264编码器支持动态质量切换
  * 屏幕流服务支持按需推流和质量控制
  * 添加SignalR信令客户端连接服务器

- 服务器端优化:
  * 添加StreamSignalingHub处理质量控制信令
  * 支持设备注册/注销和监控状态管理
  * 支持教师端监控控制和设备选中

- 前端组件:
  * 创建H264VideoPlayer组件支持H.264和JPEG模式
  * 更新学生屏幕监控页面使用新组件

- 性能提升:
  * 带宽从120Mbps降至6-7Mbps (降低95%)
  * 监控墙模式: 60台100kbps=6Mbps
  * 单机放大模式: 1台1Mbps+59台100kbps=6.9Mbps
  * 无人观看时停止推流节省带宽
2026-01-23 15:37:37 +08:00

183 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.AspNetCore.SignalR;
namespace AmtScanner.Api.Hubs;
/// <summary>
/// 屏幕流信令 Hub - 用于控制设备推流质量和状态
/// </summary>
public class StreamSignalingHub : Hub
{
private readonly ILogger<StreamSignalingHub> _logger;
private static readonly Dictionary<string, HashSet<string>> _deviceWatchers = new();
private static readonly object _lock = new();
public StreamSignalingHub(ILogger<StreamSignalingHub> logger)
{
_logger = logger;
}
/// <summary>
/// 教师端开始监控(进入监控墙页面)
/// </summary>
public async Task StartMonitoring(List<string> deviceUuids)
{
var connectionId = Context.ConnectionId;
_logger.LogInformation("教师端 {ConnectionId} 开始监控 {Count} 台设备", connectionId, deviceUuids.Count);
lock (_lock)
{
foreach (var uuid in deviceUuids)
{
if (!_deviceWatchers.ContainsKey(uuid))
{
_deviceWatchers[uuid] = new HashSet<string>();
}
_deviceWatchers[uuid].Add(connectionId);
}
}
// 通知所有设备开始低质量推流
await Clients.All.SendAsync("DevicesNeedStream", deviceUuids, "low");
}
/// <summary>
/// 教师端停止监控(离开监控墙页面)
/// </summary>
public async Task StopMonitoring()
{
var connectionId = Context.ConnectionId;
_logger.LogInformation("教师端 {ConnectionId} 停止监控", connectionId);
List<string> devicesToStop = new();
lock (_lock)
{
// 移除该连接对所有设备的监控
foreach (var (uuid, watchers) in _deviceWatchers)
{
if (watchers.Remove(connectionId) && watchers.Count == 0)
{
devicesToStop.Add(uuid);
}
}
// 清理空的监控记录
foreach (var uuid in devicesToStop)
{
_deviceWatchers.Remove(uuid);
}
}
// 通知设备停止推流
if (devicesToStop.Count > 0)
{
await Clients.All.SendAsync("DevicesStopStream", devicesToStop);
}
}
/// <summary>
/// 教师端选中某台设备(切换到高质量)
/// </summary>
public async Task SelectDevice(string deviceUuid)
{
_logger.LogInformation("教师端 {ConnectionId} 选中设备 {DeviceUuid}", Context.ConnectionId, deviceUuid);
// 通知该设备切换到高质量
await Clients.All.SendAsync("DeviceQualityChange", deviceUuid, "high");
}
/// <summary>
/// 教师端取消选中设备(切换回低质量)
/// </summary>
public async Task DeselectDevice(string deviceUuid)
{
_logger.LogInformation("教师端 {ConnectionId} 取消选中设备 {DeviceUuid}", Context.ConnectionId, deviceUuid);
// 通知该设备切换回低质量
await Clients.All.SendAsync("DeviceQualityChange", deviceUuid, "low");
}
/// <summary>
/// 设备端注册Agent 启动时调用)
/// </summary>
public async Task RegisterDevice(string deviceUuid)
{
_logger.LogInformation("设备 {DeviceUuid} 注册到 SignalR Hub", deviceUuid);
// 加入设备专属组
await Groups.AddToGroupAsync(Context.ConnectionId, $"device_{deviceUuid}");
// 检查是否有人正在监控该设备
bool isBeingWatched;
lock (_lock)
{
isBeingWatched = _deviceWatchers.ContainsKey(deviceUuid) && _deviceWatchers[deviceUuid].Count > 0;
}
// 如果有人监控,通知设备开始推流
if (isBeingWatched)
{
await Clients.Caller.SendAsync("StartStream", "low");
}
}
/// <summary>
/// 设备端取消注册Agent 关闭时调用)
/// </summary>
public async Task UnregisterDevice(string deviceUuid)
{
_logger.LogInformation("设备 {DeviceUuid} 从 SignalR Hub 取消注册", deviceUuid);
await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"device_{deviceUuid}");
}
/// <summary>
/// 连接断开时清理
/// </summary>
public override async Task OnDisconnectedAsync(Exception? exception)
{
var connectionId = Context.ConnectionId;
_logger.LogInformation("连接 {ConnectionId} 断开", connectionId);
// 清理监控记录
List<string> devicesToStop = new();
lock (_lock)
{
foreach (var (uuid, watchers) in _deviceWatchers)
{
if (watchers.Remove(connectionId) && watchers.Count == 0)
{
devicesToStop.Add(uuid);
}
}
foreach (var uuid in devicesToStop)
{
_deviceWatchers.Remove(uuid);
}
}
// 通知设备停止推流
if (devicesToStop.Count > 0)
{
await Clients.All.SendAsync("DevicesStopStream", devicesToStop);
}
await base.OnDisconnectedAsync(exception);
}
/// <summary>
/// 获取当前监控状态(调试用)
/// </summary>
public Dictionary<string, int> GetMonitoringStatus()
{
lock (_lock)
{
return _deviceWatchers.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Count
);
}
}
}