- 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 * 无人观看时停止推流节省带宽
183 lines
5.5 KiB
C#
183 lines
5.5 KiB
C#
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
|
||
);
|
||
}
|
||
}
|
||
}
|