using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; using SHH.CameraSdk; // 引用你的业务核心 namespace SHH.CameraService; public class Program { public static async Task Main(string[] args) { // 缓冲时间 (您之前写了20000ms即20秒,可能是为了附加调试器。如果觉得太慢可以改回 2000) for(var i=1; i<10; i++) Thread.Sleep(1000); // 1. 解析配置 var config = ServiceConfig.BuildFromArgs(args); // ---【补全变量定义】--- // A. 补全 webPort (统一使用 config.BasePort) int webPort = config.BasePort; // B. 补全 processIdInt (用于 FileStorage 和 CameraSdk) // 逻辑:尝试将 AppId 解析为数字;如果 AppId 是字符串(如"CameraApp_01"),则默认给 1,或者根据 BasePort 推算 int processIdInt = config.NumericId; Console.Title = $"SHH Gateway - {config.AppId} (Web: {webPort})"; #region --- 2. 硬件环境预热 --- InitHardwareEnv(); #endregion #region --- 3. 构建 WebHost --- var builder = WebApplication.CreateBuilder(args); // ★★★ 核心:注入全局配置 ★★★ builder.Services.AddSingleton(config); // ------------------------------------------------------------- // A. 注册新架构组件 // ------------------------------------------------------------- builder.Services.AddSingleton(); // 推流服务 (连接 config.TargetClients 里的 :6002) builder.Services.AddHostedService(); // 指令客户端 (连接 config.TargetClients 里的 :6001) builder.Services.AddHostedService(); // 进程守护 builder.Services.AddHostedService(); // ------------------------------------------------------------- // B. 注册 SDK 业务服务 // ------------------------------------------------------------- // 使用刚刚补全的 processIdInt builder.Services.AddSingleton(new FileStorageService(processIdInt)); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(sp => new ImageScaleCluster(4, sp.GetRequiredService())); builder.Services.AddSingleton(sp => new ImageEnhanceCluster(4, sp.GetRequiredService())); builder.Services.AddHostedService(); // 使用补全的 processIdInt builder.Services.AddCameraSdk(processIdInt); builder.Services.AddHostedService(); builder.Services.AddSingleton(); builder.Services.AddControllers().AddApplicationPart(typeof(CamerasController).Assembly); builder.Services.AddControllers().AddApplicationPart(typeof(MonitorController).Assembly); // ------------------------------------------------------------- // C. Web API 基础 // ------------------------------------------------------------- builder.Services.AddControllers().AddControllersAsServices(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { // 【修正】使用 config.AppId c.SwaggerDoc("v1", new OpenApiInfo { Title = $"Gateway {config.AppId}", Version = "v1" }); }); builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod())); var app = builder.Build(); //// ======================================================================= //// ★★★ 核心接入点:连接 [现有分发器] 与 [新推流通道] ★★★ //// ======================================================================= //// 1. 获取刚刚注册的数据通道 //var videoChannel = app.Services.GetRequiredService(); ////var config = app.Services.GetRequiredService(); //// 2. 订阅你现有的全局事件 (这里就是“取货”的地方) //// 每当 HikVideoSource 采集到一帧并调用 Dispatch 时,这里就会触发 //GlobalStreamDispatcher.OnGlobalFrame += (deviceId, smartFrame) => //{ // // 3. 数据处理:将 OpenCvSharp Mat 转为 JPG 字节流 (网络传输必须压缩) // byte[] jpgData = EncodeToJpg(smartFrame); // if (jpgData != null && jpgData.Length > 0) // { // // 4. 封装载荷 // var payload = new VideoPayload // { // // 使用 AppId 或 DeviceId 作为标识 // CameraId = config.AppId, // OriginalImageBytes = jpgData, // CaptureTime = DateTime.Now, // OriginalWidth = smartFrame.TargetWidth, // OriginalHeight = smartFrame.TargetHeight // }; // // 5. 扔进通道 (Fire-and-Forget,不阻塞你原来的显示逻辑) // // WriteAsync 是 ValueTask,这里忽略等待,追求最高吞吐 // _ = videoChannel.WriteAsync(payload); // } //}; //Console.WriteLine("[System] 全局流已桥接到 ZeroMQ 推流通道"); app.UseSwagger(); app.UseSwaggerUI(); app.UseCors("AllowAll"); app.MapControllers(); // 【修正】使用 webPort Console.WriteLine($"[System] Web API 已启动: http://0.0.0.0:{webPort}"); await app.RunAsync($"http://0.0.0.0:{webPort}"); #endregion } static void InitHardwareEnv() { Console.WriteLine("=== 工业级视频接入服务启动 ==="); } /// /// 内存转码:Mat -> Jpg Bytes /// static byte[] EncodeToJpg(SmartFrame frame) { try { // 假设 SmartFrame 内部持有 OpenCvSharp.Mat 类型的 InternalMat if (frame != null && frame.InternalMat != null && !frame.InternalMat.Empty()) { // 80 是 JPG 质量参数,平衡画质与带宽 return frame.InternalMat.ImEncode(".jpg", new int[] { 1, 80 }); } } catch { // 容错处理,防止一帧损坏导致程序崩溃 } return Array.Empty(); } }