阶段性批量提交
This commit is contained in:
87
SHH.CameraService/ZeroMQBridgeWorker.cs
Normal file
87
SHH.CameraService/ZeroMQBridgeWorker.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using SHH.CameraSdk;
|
||||
|
||||
namespace SHH.CameraService;
|
||||
|
||||
public class ZeroMQBridgeWorker : BackgroundService
|
||||
{
|
||||
private readonly ServiceConfig _config;
|
||||
private readonly VideoDataChannel _channel; // 数据源
|
||||
|
||||
public ZeroMQBridgeWorker(ServiceConfig config, VideoDataChannel channel)
|
||||
{
|
||||
_config = config;
|
||||
_channel = channel;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// 1. 如果不是主动/混合模式,不需要连接
|
||||
if (!_config.ShouldConnect) return;
|
||||
|
||||
// ★★★ 核心修正:直接读取解析好的视频地址列表 ★★★
|
||||
// 这些地址来自参数 --uris "IP,VideoPort&CommandPort" 中的 VideoPort 部分 (符号左边)
|
||||
var streamUris = _config.VideoEndpoints;
|
||||
|
||||
if (streamUris.Count == 0)
|
||||
{
|
||||
Console.WriteLine("[推流] 未在参数中找到视频通道地址(位于&符号左侧),跳过连接。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 初始化 Publisher Socket
|
||||
// 特点:只需 Send 一次,底层会自动分发给所有 Connect 的 Dashboard
|
||||
using var pubSocket = new PublisherSocket();
|
||||
|
||||
// 设置发送高水位 (HWM)
|
||||
// 防止网络拥塞或接收端处理慢时,内存无限增长。超过50帧积压就开始丢弃旧帧。
|
||||
pubSocket.Options.SendHighWatermark = 50;
|
||||
|
||||
// 3. 连接所有视频目标
|
||||
foreach (var uri in streamUris)
|
||||
{
|
||||
Console.WriteLine($"[推流] 连接视频接收端: {uri}");
|
||||
pubSocket.Connect(uri);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[推流] 服务就绪 (AppId: {_config.AppId}),等待视频帧...");
|
||||
|
||||
// 4. 推流循环
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从通道读取最新帧 (支持异步等待)
|
||||
// 注意:这里使用了之前 VideoDataChannel 暴露出来的 Reader 属性
|
||||
var payload = await _channel.Reader.ReadAsync(stoppingToken);
|
||||
|
||||
// 简单校验
|
||||
if (payload == null || payload.OriginalImageBytes == null || payload.OriginalImageBytes.Length == 0)
|
||||
continue;
|
||||
|
||||
// 构造 Topic (通常用 AppId 作为 Topic,这样 Dashboard 可以按需订阅)
|
||||
string topic = _config.AppId;
|
||||
|
||||
// 发送两帧:[Topic] [ImageBytes]
|
||||
// 这样 Dashboard 的 Subscriber 可以通过 Subscribe(topic) 来过滤
|
||||
pubSocket.SendMoreFrame(topic)
|
||||
.SendFrame(payload.OriginalImageBytes);
|
||||
|
||||
// 调试日志 (生产环境建议注释掉,否则刷屏)
|
||||
// Console.WriteLine($"[推流] Sent {payload.OriginalImageBytes.Length} bytes");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break; // 正常退出
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[推流] 发送异常: {ex.Message}");
|
||||
// 发生错误稍微停顿,防止死循环占用 CPU
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user