新增 Mjpegplayer 用来播放 Web 流
This commit is contained in:
172
SHH.MjpegPlayer/Bootstrapper.cs
Normal file
172
SHH.MjpegPlayer/Bootstrapper.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
using Ayay.SerilogLogs;
|
||||
using CoreWCF;
|
||||
using CoreWCF.Configuration;
|
||||
using CoreWCF.Description;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Player.MJPEG;
|
||||
using Serilog;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
|
||||
namespace SHH.MjpegPlayer
|
||||
{
|
||||
public static class Bootstrapper
|
||||
{
|
||||
private static readonly ILogger _sysLog = Log.ForContext("SourceContext", LogModules.Core);
|
||||
|
||||
#region LoadConfig
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static MjpegConfig LoadConfig()
|
||||
{
|
||||
// [修复] 路径处理脆弱性:使用 BaseDirectory 拼接,避免相对路径替换的风险
|
||||
// 生产环境:强制使用绝对路径确保能找到配置文件
|
||||
if (!Debugger.IsAttached)
|
||||
{
|
||||
JsonConfigUris.MjpegConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName(JsonConfigUris.MjpegConfig));
|
||||
}
|
||||
|
||||
// 加载配置文件
|
||||
var cfg = JsonConfig.Load<MjpegConfig>(JsonConfigUris.MjpegConfig);
|
||||
if (cfg == null)
|
||||
{
|
||||
cfg = new MjpegConfig();
|
||||
JsonConfig.Save(cfg, JsonConfigUris.MjpegConfig, "MjpegServer配置项");
|
||||
_sysLog.Warning("未找到配置文件,已生成默认配置: {Path}", JsonConfigUris.MjpegConfig);
|
||||
}
|
||||
MjpegStatics.Cfg = cfg;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ValidateEnvironment
|
||||
|
||||
/// <summary>
|
||||
/// 检查 IP 与端口
|
||||
/// </summary>
|
||||
public static void ValidateEnvironment()
|
||||
{
|
||||
var cfg = MjpegStatics.Cfg;
|
||||
|
||||
// IP 地址检查
|
||||
IPAddress? ipAddress;
|
||||
if (!IPAddress.TryParse(cfg.SvrMjpegIp, out ipAddress))
|
||||
{
|
||||
ipAddress = IPAddress.Any;
|
||||
_sysLog.Warning("配置的 IP 地址非法,将使用 IPAddress. Any: {Ip}.", cfg.SvrMjpegIp);
|
||||
}
|
||||
|
||||
// 端口检查 => Wcf 接收图片接口
|
||||
var portsToCheck = new List<int> { cfg.WcfPushImagePort, cfg.SvrMjpegPortBegin, cfg.SvrMjpegPortEnd };
|
||||
foreach (var port in portsToCheck)
|
||||
{
|
||||
if (!port.IsServerPort())
|
||||
{
|
||||
_sysLog.Error("端口配置无效, Port: {Port}.", port);
|
||||
ExitApp($"端口配置无效, Port: {port}");
|
||||
}
|
||||
}
|
||||
|
||||
// 端口检查 => Mjpeg 服务端口
|
||||
if (!cfg.SvrMjpegPortBegin.IsServerPort())
|
||||
{
|
||||
_sysLog.Fatal("WCF 接收端口被占用, Port:{Port}.", cfg.WcfPushImagePort);
|
||||
// 退出应用
|
||||
ExitApp("端口占用.");
|
||||
}
|
||||
|
||||
// [修复] 循环逻辑错误:将 < 改为 <=,确保最后一个端口也被检测
|
||||
for (var i = cfg.SvrMjpegPortBegin; i <= cfg.SvrMjpegPortEnd; i++)
|
||||
{
|
||||
if (!i.PortOccupiedProc())
|
||||
{
|
||||
// 退出应用
|
||||
_sysLog.Fatal("MJPEG 监听端口被占用, Port:{Port}", i);
|
||||
ExitApp($"MJPEG 监听端口被占用, Port:{i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region StartWcfEngine
|
||||
|
||||
/// <summary>
|
||||
/// 内部 WCF 引擎初始化 (CoreWCF)
|
||||
/// </summary>
|
||||
public static void StartWcfEngine(MjpegConfig cfg)
|
||||
{
|
||||
// Optimized: 内存监控提升
|
||||
MemoryWatchdog.Start(300, 2048);
|
||||
|
||||
var builder = WebApplication.CreateBuilder();
|
||||
builder.WebHost.UseUrls($"http://*:{cfg.WcfPushImagePort}");
|
||||
|
||||
// 托管日志
|
||||
builder.Host.UseSerilog(_sysLog);
|
||||
|
||||
builder.Services.AddServiceModelServices();
|
||||
builder.Services.AddServiceModelMetadata();
|
||||
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>()
|
||||
.AddSingleton<MjpegImagesService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var wsBinding = new WSHttpBinding(SecurityMode.None);
|
||||
wsBinding.MaxReceivedMessageSize = cfg.SvrPushImageMaxRecMsgSize;
|
||||
|
||||
// Modified: [原因] 强制转换 IApplicationBuilder 修复 UseServiceModel 的二义性
|
||||
((IApplicationBuilder)app).UseServiceModel(serviceBuilder =>
|
||||
{
|
||||
serviceBuilder.AddService<MjpegImagesService>(opt =>
|
||||
{
|
||||
opt.BaseAddresses.Add(new Uri($"http://0.0.0.0:{cfg.WcfPushImagePort}"));
|
||||
})
|
||||
.AddServiceEndpoint<MjpegImagesService, IMjpegImagesService>(
|
||||
wsBinding,
|
||||
$"/{cfg.SvrNamePushImage}",
|
||||
new Uri($"http://0.0.0.0:{cfg.WcfPushImagePort}/{cfg.SvrNamePushImage}")
|
||||
);
|
||||
});
|
||||
|
||||
// 关闭元数据暴露增强安全性
|
||||
var meta = app.Services.GetRequiredService<ServiceMetadataBehavior>();
|
||||
meta.HttpGetEnabled = false;
|
||||
meta.HttpsGetEnabled = false;
|
||||
|
||||
Task.Run(() => app.Run());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ExitApp
|
||||
|
||||
/// <summary>
|
||||
/// 应用程序退出
|
||||
/// </summary>
|
||||
/// <param name="exitMsg"></param>
|
||||
/// <param name="waitSeconds"></param>
|
||||
public static void ExitApp(string exitMsg, int waitSeconds = 5)
|
||||
{
|
||||
// [修复] 尝试停止所有 MjpegServer 监听
|
||||
try { MjpegServer.StopAll(); } catch { }
|
||||
|
||||
var iSleep = waitSeconds * 2;
|
||||
for (var i = 0; i < iSleep; i++)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
// 退出程序
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user