110 lines
3.8 KiB
C#
110 lines
3.8 KiB
C#
|
|
using NetMQ;
|
|||
|
|
using NetMQ.Sockets;
|
|||
|
|
using SHH.Contracts; // 引用你 ExportCode_Dump2.txt 里的契约
|
|||
|
|
|
|||
|
|
namespace SHH.CameraDashboard
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 视频推流接收服务 (服务端模式)
|
|||
|
|
/// 职责:监听本地端口,被动接收来自采集端的 Push 数据流
|
|||
|
|
/// </summary>
|
|||
|
|
public class VideoPushServer : IDisposable
|
|||
|
|
{
|
|||
|
|
private PullSocket? _pullSocket;
|
|||
|
|
private bool _isRunning;
|
|||
|
|
private Task? _listenTask;
|
|||
|
|
|
|||
|
|
// 当收到完整视频帧时触发,UI 层订阅此事件来更新画面
|
|||
|
|
// VideoPayload 定义在 ExportCode_Dump2.txt 中
|
|||
|
|
public event Action<VideoPayload>? OnFrameReceived;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动监听
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="port">本机开放的监听端口 (例如 6000)</param>
|
|||
|
|
public void Start(int port)
|
|||
|
|
{
|
|||
|
|
if (_isRunning) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_pullSocket = new PullSocket();
|
|||
|
|
|
|||
|
|
// 1. 设置高水位 (HWM),防止渲染不及导致内存溢出
|
|||
|
|
// 与发送端 ForwarderClient 的 HWM_LIMIT = 50 保持策略一致
|
|||
|
|
_pullSocket.Options.ReceiveHighWatermark = 50;
|
|||
|
|
|
|||
|
|
// 2. 【核心】绑定本地端口 (Bind),等待别人连我
|
|||
|
|
// 允许局域网内任何 IP 推送数据过来
|
|||
|
|
_pullSocket.Bind($"tcp://*:{port}");
|
|||
|
|
|
|||
|
|
_isRunning = true;
|
|||
|
|
|
|||
|
|
// 3. 开启后台接收线程
|
|||
|
|
_listenTask = Task.Run(ReceiveLoop);
|
|||
|
|
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"[PushServer] 服务已启动,正在监听端口: {port}...");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"[PushServer] 启动失败: {ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ReceiveLoop()
|
|||
|
|
{
|
|||
|
|
while (_isRunning && _pullSocket != null)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 接收多帧消息 (超时控制以便能优雅退出)
|
|||
|
|
NetMQMessage? msg = null;
|
|||
|
|
if (!_pullSocket.TryReceiveMultipartMessage(TimeSpan.FromMilliseconds(500), ref msg))
|
|||
|
|
{
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 协议校验:必须包含 3 帧 (JSON + Raw + Target)
|
|||
|
|
// 对应 ExportCode_Dump1.txt 中 ForwarderClient.Push 的 msg.Append 顺序
|
|||
|
|
if (msg == null || msg.FrameCount < 3) continue;
|
|||
|
|
|
|||
|
|
// 3. 解析数据帧
|
|||
|
|
string jsonMeta = msg[0].ConvertToString();
|
|||
|
|
byte[] originalBytes = msg[1].Buffer;
|
|||
|
|
byte[] targetBytes = msg[2].Buffer;
|
|||
|
|
|
|||
|
|
// 4. 反序列化元数据
|
|||
|
|
// 使用你 Core/JsonHelper.cs 里的稳健反序列化
|
|||
|
|
var payload = JsonHelper.Deserialize<VideoPayload>(jsonMeta);
|
|||
|
|
|
|||
|
|
if (payload != null)
|
|||
|
|
{
|
|||
|
|
// 5. 组装二进制数据 (因为传输时是分离的)
|
|||
|
|
payload.OriginalImageBytes = originalBytes;
|
|||
|
|
payload.TargetImageBytes = targetBytes;
|
|||
|
|
|
|||
|
|
// 6. 触发事件 (抛出给 ViewModel)
|
|||
|
|
OnFrameReceived?.Invoke(payload);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
System.Diagnostics.Debug.WriteLine($"[PushServer] 接收异常: {ex.Message}");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Stop()
|
|||
|
|
{
|
|||
|
|
_isRunning = false;
|
|||
|
|
_pullSocket?.Close();
|
|||
|
|
_pullSocket?.Dispose();
|
|||
|
|
_pullSocket = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Dispose()
|
|||
|
|
{
|
|||
|
|
Stop();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|