Files
Ayay/SHH.CameraSdk/Program.cs
twice109 e9f5975a79 1、解决使用 Cv2.ImShow 播放画面,闪烁一下窗口不见的问题
2、之前注册播放、分析帧,回调时必须判定是否当前注册源,现增加新方法可以不用判定
3、将之前程序一运行就播放,调整为手动指定 IsRunning 值来控制
2025-12-26 06:14:55 +08:00

178 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using OpenCvSharp;
using SHH.CameraSdk;
using System.Diagnostics;
namespace SHH.CameraSdk
{
/// <summary>
/// A 方案:标准控制台结构 (显式 Main 方法 + STAThread)
/// </summary>
public class Program
{
// [关键点 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);
}
Console.WriteLine("[系统] 正在停机...");
await app.StopAsync();
}
// ==============================================================================
// Static Methods (原 Local Functions 转换为类的静态方法)
// ==============================================================================
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();
// 注入服务
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()));
// 关键:注入单例 Manager
builder.Services.AddSingleton(manager);
var webApp = builder.Build();
// 配置管道
webApp.UseSwagger();
webApp.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Diagnostics V1"));
webApp.UseCors("AllowAll");
webApp.MapControllers();
// 异步启动,不阻塞主线程
_ = webApp.RunAsync("http://0.0.0.0:5000");
Console.WriteLine("[Web] 监控API已启动: http://localhost:5000");
return webApp;
}
// [关键点 5] 方法签名修改:接收 FrameConsumer 参数
static async Task ConfigureBusinessLogic(CameraManager manager, FrameConsumer renderer)
{
// 1. 配置设备
var config = new VideoSourceConfig
{
Id = 101,
Brand = DeviceBrand.HikVision,
// IpAddress = "172.16.41.206",
IpAddress = "192.168.5.9",
Port = 8000,
Username = "admin",
Password = "RRYFOA",
StreamType = 0 // 主码流
};
manager.AddDevice(config);
if (manager.GetDevice(101) is HikVideoSource hikCamera)
{
// 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);
Console.WriteLine("Frame Enqueued");
});
// [消费者 B] - 绝对只会收到 8fps
GlobalStreamDispatcher.Subscribe("Process_B_Local", (deviceId, frame) =>
{
if (deviceId == 101)
{
Console.WriteLine($"[Process B] 本地渲染一帧 (8fps节奏)");
}
});
// [消费者 AI]
GlobalStreamDispatcher.Subscribe("AI_Engine_Core", (deviceId, frame) =>
{
if (deviceId == 101)
{
Console.WriteLine($" >>> [AI] 分析一帧...");
}
});
}
}
}
}