163 lines
6.0 KiB
C#
163 lines
6.0 KiB
C#
|
|
using Ayay.SerilogLogs;
|
|||
|
|
using Serilog;
|
|||
|
|
|
|||
|
|
namespace SHH.MjpegPlayer
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// RTMP 推流参数同步服务器
|
|||
|
|
/// 职责:定期将本地图片通道信息同步至流媒体服务器,并获取最新的 RTMP 推流地址。
|
|||
|
|
/// </summary>
|
|||
|
|
public class RtmpPushServer
|
|||
|
|
{
|
|||
|
|
private static ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
|
|||
|
|
|
|||
|
|
#region Instance
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取 RTMP 推流处理器的全局单例实例
|
|||
|
|
/// </summary>
|
|||
|
|
public static RtmpPushServer Instance { get; } = new RtmpPushServer();
|
|||
|
|
|
|||
|
|
// 私有构造函数防止外部 new
|
|||
|
|
private RtmpPushServer() { }
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Start
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动 RTMP 推流任务 (对接新架构 TaskManager)
|
|||
|
|
/// </summary>
|
|||
|
|
public void Start()
|
|||
|
|
{
|
|||
|
|
// Optimized: 使用 TaskManager.Run 替代旧的线程启动方式,实现任务可视化管理
|
|||
|
|
TaskManager.Run("RtmpPushSync", "Monitor", async (token) =>
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
_sysLog.Information("RTMP 推流同步任务正在启动...");
|
|||
|
|
|
|||
|
|
// 1. 初始化延迟:稍作延迟,等待系统其他组件初始化完成
|
|||
|
|
await Task.Delay(2000, token);
|
|||
|
|
|
|||
|
|
// 2. 配置校验
|
|||
|
|
if (!MjpegStatics.Cfg.UseRtmpServer)
|
|||
|
|
{
|
|||
|
|
_sysLog.Warning("配置未启用 RTMP 服务,推流任务已跳过");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region 启动连接检测
|
|||
|
|
|
|||
|
|
bool isConnected = false;
|
|||
|
|
while (!isConnected && !token.IsCancellationRequested)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var cfg = MjpegStatics.Cfg;
|
|||
|
|
var testItems = new List<object> { new { deviceIp = "", deviceId = "", algCode = "" } };
|
|||
|
|
|
|||
|
|
var result = testItems.PostJson<CfgRtmpReply>(cfg.RtmpServerDjhUri);
|
|||
|
|
if (result != null && result.IsSuccess)
|
|||
|
|
{
|
|||
|
|
_sysLog.Information("流媒体服务接口检测通过: {Uri}", cfg.RtmpServerDjhUri);
|
|||
|
|
isConnected = true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
_sysLog.Warning("检测流媒体服务接口失败, 30秒后再试... Uri: {Uri}", cfg.RtmpServerDjhUri);
|
|||
|
|
await Task.Delay(30000, token);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_sysLog.Error(ex, "流媒体服务接口检测异常");
|
|||
|
|
await Task.Delay(5000, token);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 周期性同步任务 (代替 OnDoTaskAction)
|
|||
|
|
|
|||
|
|
while (!token.IsCancellationRequested)
|
|||
|
|
{
|
|||
|
|
// 执行业务同步逻辑
|
|||
|
|
await SyncRtmpParametersAsync(token);
|
|||
|
|
|
|||
|
|
// 每 50ms 执行一次 (对应原 Start 中的 50ms 频率)
|
|||
|
|
await Task.Delay(50, token);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
catch (OperationCanceledException)
|
|||
|
|
{
|
|||
|
|
_sysLog.Information("RTMP 推流同步任务已正常取消");
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_sysLog.Fatal(ex, "RTMP 推流任务发生致命错误");
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 核心业务逻辑
|
|||
|
|
|
|||
|
|
private async Task SyncRtmpParametersAsync(CancellationToken token)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var cfg = MjpegStatics.Cfg;
|
|||
|
|
var imgChns = MjpegStatics.ImageChannels;
|
|||
|
|
var pushItems = new List<object>();
|
|||
|
|
|
|||
|
|
// 构建上报数据
|
|||
|
|
foreach (var kvp in imgChns.Channels)
|
|||
|
|
{
|
|||
|
|
if (token.IsCancellationRequested) return;
|
|||
|
|
|
|||
|
|
var imgChn = kvp.Value;
|
|||
|
|
if (imgChn == null || !imgChn.UseRtmp) continue;
|
|||
|
|
|
|||
|
|
pushItems.Add(new
|
|||
|
|
{
|
|||
|
|
deviceIp = string.IsNullOrEmpty(imgChn.IpAddress) ? "127.0.0.1" : imgChn.IpAddress,
|
|||
|
|
deviceId = imgChn.DeviceId.ToString(),
|
|||
|
|
algCode = imgChn.Type
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (pushItems.Count > 0)
|
|||
|
|
{
|
|||
|
|
// 使用 await 配合异步扩展,释放线程池线程
|
|||
|
|
// 如果你的 NetHttpExtension 已支持异步,则直接 await。
|
|||
|
|
// 否则使用 await Task.Run(() => pushItems.PostJson(...)) 暂时过渡。
|
|||
|
|
var result = await pushItems.PostJsonAsync<CfgRtmpReply>(cfg.RtmpServerDjhUri, 2000);
|
|||
|
|
|
|||
|
|
if (result?.rtmpVoList != null && result.IsSuccess)
|
|||
|
|
{
|
|||
|
|
foreach (var item in result.rtmpVoList)
|
|||
|
|
{
|
|||
|
|
var channel = imgChns.Get(item.deviceId, item.algCode);
|
|||
|
|
if (channel != null && channel.RtmpUri != item.rtmp)
|
|||
|
|
{
|
|||
|
|
// 假设后续我们会统计实例中的成功次数
|
|||
|
|
channel.RtmpUri = item.rtmp;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
// 使用统一的 Serilog 对象输出结构化日志
|
|||
|
|
_sysLog.Error(ex, "SyncRtmpParametersAsync 执行异常");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
}
|