2025-12-26 03:18:21 +08:00
|
|
|
|
using Microsoft.AspNetCore.Builder;
|
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Microsoft.OpenApi.Models;
|
2025-12-26 06:14:55 +08:00
|
|
|
|
using OpenCvSharp;
|
2025-12-26 03:18:21 +08:00
|
|
|
|
using SHH.CameraSdk;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
namespace SHH.CameraSdk
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 06:14:55 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// A 方案:标准控制台结构 (显式 Main 方法 + STAThread)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class Program
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// [关键点 1] 显式声明 STA 线程模式,确保 OpenCV/GUI 窗口消息循环正常
|
|
|
|
|
|
[STAThread]
|
|
|
|
|
|
public static async Task Main(string[] args)
|
|
|
|
|
|
{
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// 1. 基础设施初始化
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
InitHardwareEnv();
|
|
|
|
|
|
using var cameraManager = new CameraManager();
|
|
|
|
|
|
|
|
|
|
|
|
// [关键点 2] 提升变量作用域
|
|
|
|
|
|
// 将渲染器(消费者)在 Main 中声明,确保它与主程序同寿命,不会被中途回收
|
|
|
|
|
|
using var remoteRenderer = new FrameConsumer("Process A Remote Preview");
|
|
|
|
|
|
remoteRenderer.Start();
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// 2. 启动 Web 监控与诊断服务
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
var app = await StartWebMonitoring(cameraManager);
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// 3. 业务编排:配置设备与流控策略 (8+2 演示)
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// [关键点 3] 将渲染器作为参数传递进去
|
|
|
|
|
|
await ConfigureBusinessLogic(cameraManager, remoteRenderer);
|
|
|
|
|
|
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// 4. 启动引擎与交互
|
|
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
Console.WriteLine("\n[系统] 正在启动全局管理引擎...");
|
|
|
|
|
|
await cameraManager.StartAsync();
|
|
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(">> 系统就绪。访问 http://localhost:5000/swagger 查看诊断信息。");
|
|
|
|
|
|
Console.WriteLine(">> 按 'S' 键退出...");
|
|
|
|
|
|
|
|
|
|
|
|
// [关键点 4] 阻塞主线程
|
|
|
|
|
|
// 只要这个循环在跑,remoteRenderer 就不会被 Dispose,窗口就会一直存在
|
|
|
|
|
|
while (Console.ReadKey(true).Key != ConsoleKey.S)
|
|
|
|
|
|
{
|
|
|
|
|
|
Thread.Sleep(100);
|
|
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
Console.WriteLine("[系统] 正在停机...");
|
|
|
|
|
|
await app.StopAsync();
|
|
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// ==============================================================================
|
|
|
|
|
|
// Static Methods (原 Local Functions 转换为类的静态方法)
|
|
|
|
|
|
// ==============================================================================
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
static void InitHardwareEnv()
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("=== 工业级视频 SDK 架构测试 (V3.3 分层版 - STA模式) ===");
|
|
|
|
|
|
Console.WriteLine("[硬件] 海康驱动预热中...");
|
|
|
|
|
|
HikNativeMethods.NET_DVR_Init();
|
|
|
|
|
|
HikSdkManager.ForceWarmUp(); // 强制加载 PlayCtrl.dll
|
|
|
|
|
|
Console.WriteLine("[硬件] 预热完成。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async Task<WebApplication> StartWebMonitoring(CameraManager manager)
|
|
|
|
|
|
{
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 注入服务
|
|
|
|
|
|
builder.Services.AddControllers();
|
|
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
|
builder.Services.AddSwaggerGen(c =>
|
|
|
|
|
|
{
|
|
|
|
|
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "SHH Camera Diagnostics", Version = "v1" });
|
|
|
|
|
|
});
|
|
|
|
|
|
builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()));
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 关键:注入单例 Manager
|
|
|
|
|
|
builder.Services.AddSingleton(manager);
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
var webApp = builder.Build();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 配置管道
|
|
|
|
|
|
webApp.UseSwagger();
|
|
|
|
|
|
webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1"));
|
|
|
|
|
|
webApp.UseCors("AllowAll");
|
|
|
|
|
|
webApp.MapControllers();
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 异步启动,不阻塞主线程
|
|
|
|
|
|
_ = webApp.RunAsync("http://0.0.0.0:5000");
|
|
|
|
|
|
Console.WriteLine("[Web] 监控API已启动: http://localhost:5000");
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
return webApp;
|
|
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// [关键点 5] 方法签名修改:接收 FrameConsumer 参数
|
|
|
|
|
|
static async Task ConfigureBusinessLogic(CameraManager manager, FrameConsumer renderer)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 1. 配置设备
|
|
|
|
|
|
var config = new VideoSourceConfig
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 06:14:55 +08:00
|
|
|
|
Id = 101,
|
|
|
|
|
|
Brand = DeviceBrand.HikVision,
|
2025-12-26 12:15:10 +08:00
|
|
|
|
IpAddress = "172.16.41.206",
|
|
|
|
|
|
//IpAddress = "192.168.5.9",
|
2025-12-26 06:14:55 +08:00
|
|
|
|
Port = 8000,
|
|
|
|
|
|
Username = "admin",
|
2025-12-26 12:15:10 +08:00
|
|
|
|
Password = "abcd1234",
|
|
|
|
|
|
//Password = "RRYFOA",
|
2025-12-26 06:14:55 +08:00
|
|
|
|
StreamType = 0 // 主码流
|
|
|
|
|
|
};
|
|
|
|
|
|
manager.AddDevice(config);
|
|
|
|
|
|
|
|
|
|
|
|
if (manager.GetDevice(101) is HikVideoSource hikCamera)
|
2025-12-26 03:18:21 +08:00
|
|
|
|
{
|
2025-12-26 06:14:55 +08:00
|
|
|
|
// 2. 注册需求 (告诉控制器我要什么)
|
|
|
|
|
|
// ----------------------------------------------------
|
|
|
|
|
|
hikCamera.Controller.Register("WPF_Display_Main", 8); // UI 要 8 帧
|
|
|
|
|
|
hikCamera.Controller.Register("AI_Behavior_Engine", 2); // AI 要 2 帧
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 注册差异化需求 (给每个消费者唯一的 AppId)
|
|
|
|
|
|
// ----------------------------------------------------
|
|
|
|
|
|
// 模拟:A 进程(如远程预览)带宽有限,只要 3fps
|
|
|
|
|
|
hikCamera.Controller.Register("Process_A_Remote", 20);
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟:B 进程(如本地大屏)性能强劲,要 8fps
|
|
|
|
|
|
hikCamera.Controller.Register("Process_B_Local", 8);
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟:AI 引擎
|
|
|
|
|
|
hikCamera.Controller.Register("AI_Engine_Core", 2);
|
|
|
|
|
|
|
|
|
|
|
|
// [已移除] 这里的 using var remoteRenderer = ... 已被移除
|
|
|
|
|
|
// 改为使用传入的 renderer 参数,确保其生命周期受控于 Main
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 精准订阅 (Subscribe 替代了 +=)
|
|
|
|
|
|
// ----------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// [消费者 A] - 绝对只会收到 3fps
|
|
|
|
|
|
GlobalStreamDispatcher.Subscribe("Process_A_Remote", 101, frame =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 关键:增加引用计数,防止在投递过程中被 Pipeline 回收
|
|
|
|
|
|
frame.AddRef();
|
|
|
|
|
|
|
|
|
|
|
|
// 投递到渲染线程 (FrameConsumer)
|
|
|
|
|
|
renderer.Enqueue(frame);
|
|
|
|
|
|
|
2025-12-26 12:15:10 +08:00
|
|
|
|
//Console.WriteLine("Frame Enqueued");
|
2025-12-26 06:14:55 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// [消费者 B] - 绝对只会收到 8fps
|
|
|
|
|
|
GlobalStreamDispatcher.Subscribe("Process_B_Local", (deviceId, frame) =>
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
//if (deviceId == 101)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// Console.WriteLine($"[Process B] 本地渲染一帧 (8fps节奏)");
|
|
|
|
|
|
//}
|
2025-12-26 06:14:55 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// [消费者 AI]
|
|
|
|
|
|
GlobalStreamDispatcher.Subscribe("AI_Engine_Core", (deviceId, frame) =>
|
|
|
|
|
|
{
|
2025-12-26 12:15:10 +08:00
|
|
|
|
//if (deviceId == 101)
|
|
|
|
|
|
//{
|
|
|
|
|
|
// Console.WriteLine($" >>> [AI] 分析一帧...");
|
|
|
|
|
|
//}
|
2025-12-26 06:14:55 +08:00
|
|
|
|
});
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
2025-12-26 06:14:55 +08:00
|
|
|
|
}
|
2025-12-26 03:18:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|