新增 Mjpegplayer 用来播放 Web 流
This commit is contained in:
@@ -163,10 +163,9 @@ public class ServiceConfig
|
||||
|
||||
string ip = parts[0].Trim();
|
||||
string portStr = parts[1].Trim();
|
||||
string type = parts[2].Trim().ToLower();
|
||||
|
||||
// ★★★ 提取第四个字段作为备注 ★★★
|
||||
string desc = parts.Length >= 4 ? parts[3].Trim() : "未命名终端";
|
||||
string desc = parts.Length >= 4 ? parts[2].Trim() : "未命名终端";
|
||||
|
||||
if (int.TryParse(portStr, out int port))
|
||||
{
|
||||
@@ -180,16 +179,10 @@ public class ServiceConfig
|
||||
};
|
||||
|
||||
// 添加前检查 Uri 是否重复 (备注不参与排重)
|
||||
if (type == "video")
|
||||
{
|
||||
if (!config.VideoEndpoints.Any(e => e.Uri == zmqUri))
|
||||
config.VideoEndpoints.Add(endpoint);
|
||||
}
|
||||
else if (type == "command" || type == "text")
|
||||
{
|
||||
if (!config.CommandEndpoints.Any(e => e.Uri == zmqUri))
|
||||
config.CommandEndpoints.Add(endpoint);
|
||||
}
|
||||
if (!config.VideoEndpoints.Any(e => e.Uri == zmqUri))
|
||||
config.VideoEndpoints.Add(endpoint);
|
||||
if (!config.CommandEndpoints.Any(e => e.Uri == zmqUri))
|
||||
config.CommandEndpoints.Add(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,15 +409,15 @@ public class CameraManager : IDisposable, IAsyncDisposable
|
||||
/// <para>参数2: IsOnline (true=在线, false=离线)</para>
|
||||
/// <para>参数3: Reason (变更原因)</para>
|
||||
/// </summary>
|
||||
public event Action<long, bool, string>? OnDeviceStatusChanged;
|
||||
public event Action<long, string, bool, string>? OnDeviceStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// [内部方法] 供 Sentinel 调用,触发事件冒泡
|
||||
/// </summary>
|
||||
internal void NotifyStatusChange(long deviceId, bool isOnline, string reason)
|
||||
internal void NotifyStatusChange(long deviceId, string ipAddress, bool isOnline, string reason)
|
||||
{
|
||||
// 仅仅是触发 C# 事件,完全不知道网络发送的存在
|
||||
OnDeviceStatusChanged?.Invoke(deviceId, isOnline, reason);
|
||||
OnDeviceStatusChanged?.Invoke(deviceId, ipAddress, isOnline, reason);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -152,7 +152,7 @@ public class ConnectivitySentinel
|
||||
: $"持续断连超过{OFFLINE_DURATION_THRESHOLD}秒";
|
||||
|
||||
// ★★★ 核心动作:通知 Manager ★★★
|
||||
_manager.NotifyStatusChange(device.Id, isLogicallyOnline, reason);
|
||||
_manager.NotifyStatusChange(device.Id, device.IpAddress, isLogicallyOnline, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,4 +49,25 @@ public static class DahuaPlaySDK
|
||||
|
||||
[DllImport(DLL_PATH)]
|
||||
public static extern bool PLAY_SetStreamOpenMode(int nPort, uint nMode);
|
||||
|
||||
// 解码模式枚举
|
||||
public enum DecodeType
|
||||
{
|
||||
DECODE_SW = 1, // 软解 (CPU)
|
||||
DECODE_HW = 2, // 硬解拷贝模式 (GPU解码后拷贝回内存)
|
||||
DECODE_HW_FAST = 3, // 硬解直接显示模式 (GPU解码直接渲染,最高性能)
|
||||
DECODE_HW_NV_CUDA = 7, // 英伟达显卡 CUDA 硬解 (Ayay 推荐,多路并发最强)
|
||||
DECODE_HW_D3D11 = 8 // D3D11 硬解
|
||||
}
|
||||
|
||||
// 渲染模式枚举
|
||||
public enum RenderType
|
||||
{
|
||||
RENDER_GDI = 1,
|
||||
RENDER_D3D9 = 4,
|
||||
RENDER_D3D11 = 7
|
||||
}
|
||||
|
||||
[DllImport(DLL_PATH, EntryPoint = "PLAY_SetEngine")]
|
||||
public static extern bool PLAY_SetEngine(int nPort, DecodeType decodeType, RenderType renderType);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using OpenCvSharp;
|
||||
using Serilog;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Security;
|
||||
using static SHH.CameraSdk.DahuaPlaySDK;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
@@ -173,8 +174,35 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
{
|
||||
_playPort = port;
|
||||
DahuaPlaySDK.PLAY_SetStreamOpenMode(_playPort, 0);
|
||||
|
||||
// 打开流
|
||||
DahuaPlaySDK.PLAY_OpenStream(_playPort, IntPtr.Zero, 0, 1024 * 1024 * 2);
|
||||
|
||||
// =================================================================================
|
||||
// 🚀 [新增代码] 性能优化:尝试开启大华 GPU 硬解码
|
||||
// 位置:必须在 PLAY_OpenStream 之后,PLAY_Play 之前
|
||||
// =================================================================================
|
||||
try
|
||||
{
|
||||
// nDecodeEngine: 1 = 开启硬解码 (Nvidia/Intel)
|
||||
// 注意:大华 SDK 若不支持会自动降级,try-catch 仅为了防止 P/Invoke 签名缺失崩溃
|
||||
// Optimized: 使用新版接口开启硬件解码,优先尝试 CUDA 以保证 Ayay 的多路并发性能
|
||||
// nPort 是通过 PLAY_GetFreePort 获取的播放通道号
|
||||
bool success = PLAY_SetEngine(_playPort, DecodeType.DECODE_HW_NV_CUDA, RenderType.RENDER_D3D11);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
// 如果显卡不支持 CUDA,降级为普通硬解或软解
|
||||
PLAY_SetEngine(_playPort, DecodeType.DECODE_HW, RenderType.RENDER_D3D9);
|
||||
}
|
||||
_sdkLog.Information($"[Perf] Dahua 尝试开启硬解码. ID:{_config.Id} Port:{_playPort}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_sdkLog.Warning($"[Perf] Dahua 开启硬解码失败: {ex.Message}");
|
||||
}
|
||||
|
||||
// 设置回调与播放
|
||||
_decCallBack = new DahuaPlaySDK.DECCBFUN(SafeOnDecodingCallBack);
|
||||
DahuaPlaySDK.PLAY_SetDecCallBack(_playPort, _decCallBack);
|
||||
DahuaPlaySDK.PLAY_Play(_playPort, IntPtr.Zero);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
namespace SHH.CameraSdk;
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
/// <summary>
|
||||
/// 海康播放库 PlayCtrl.dll 的封装
|
||||
@@ -343,5 +341,11 @@ public static class HikPlayMethods
|
||||
[DllImport(DllName)]
|
||||
public static extern bool PlayM4_ResetSourceBuffer(int nPort);
|
||||
|
||||
/// <summary>
|
||||
/// [新增] 开启硬件解码
|
||||
/// </summary>
|
||||
[DllImport(DllName)]
|
||||
public static extern bool PlayM4_SetHardWareDecode(int nPort, int nMode);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -54,8 +54,8 @@ public class HikVideoSource : BaseVideoSource,
|
||||
private volatile int _connectionEpoch = 0; // 连接轮询版本号
|
||||
|
||||
// 回调委托(强引用防止GC回收)
|
||||
private HikNativeMethods.REALDATACALLBACK? _realDataCallBack;
|
||||
private HikPlayMethods.DECCBFUN? _decCallBack;
|
||||
private readonly HikNativeMethods.REALDATACALLBACK _realDataCallBack;
|
||||
private readonly HikPlayMethods.DECCBFUN _decCallBack;
|
||||
|
||||
// 图像处理资源, 内存复用对象
|
||||
private Mat? _sharedYuvMat;
|
||||
@@ -77,6 +77,11 @@ public class HikVideoSource : BaseVideoSource,
|
||||
_timeProvider = new HikTimeSyncProvider(this);
|
||||
_rebootProvider = new HikRebootProvider(this);
|
||||
_ptzProvider = new HikPtzProvider(this);
|
||||
|
||||
// Modified: [Fix GC Crash] 移除此处的 new REALDATACALLBACK
|
||||
// 直接使用构造函数初始化的 _realDataCallBack,保证委托地址在整个对象生命周期内不变
|
||||
_realDataCallBack = new HikNativeMethods.REALDATACALLBACK(SafeOnRealDataReceived);
|
||||
_decCallBack = new HikPlayMethods.DECCBFUN(SafeOnDecodingCallBack);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -424,8 +429,10 @@ public class HikVideoSource : BaseVideoSource,
|
||||
bBlocked = false
|
||||
};
|
||||
|
||||
_realDataCallBack = new HikNativeMethods.REALDATACALLBACK(SafeOnRealDataReceived);
|
||||
_realPlayHandle = HikNativeMethods.NET_DVR_RealPlay_V40(_userId, ref previewInfo, _realDataCallBack, IntPtr.Zero);
|
||||
|
||||
// Optimized: [Fix GC Crash] 显式保活,防止 JIT 在 P/Invoke 过程中激进回收(双重保险)
|
||||
GC.KeepAlive(_realDataCallBack);
|
||||
|
||||
return _realPlayHandle >= 0;
|
||||
}
|
||||
@@ -480,8 +487,26 @@ public class HikVideoSource : BaseVideoSource,
|
||||
return;
|
||||
}
|
||||
|
||||
_decCallBack = new HikPlayMethods.DECCBFUN(SafeOnDecodingCallBack);
|
||||
// =================================================================================
|
||||
// 🚀 [新增代码] 尝试开启 GPU 硬解码 (1=开启, 0=关闭)
|
||||
// 位置:必须在 OpenStream 成功之后,SetDecCallBack 之前
|
||||
// =================================================================================
|
||||
try
|
||||
{
|
||||
HikPlayMethods.PlayM4_SetHardWareDecode(_playPort, 1);
|
||||
_sdkLog.Information($"[Perf] Hik 尝试开启硬解码. ID:{_config.Id} Port:{_playPort}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 即使失败也不影响流程,仅记录警告
|
||||
_sdkLog.Warning($"[Perf] Hik 开启硬解码失败: {ex.Message}");
|
||||
}
|
||||
|
||||
HikPlayMethods.PlayM4_SetDecCallBackEx(_playPort, _decCallBack, IntPtr.Zero, 0);
|
||||
|
||||
// Optimized: [Fix GC Crash] 显式保活
|
||||
GC.KeepAlive(_decCallBack);
|
||||
|
||||
HikPlayMethods.PlayM4_Play(_playPort, IntPtr.Zero);
|
||||
|
||||
_sdkLog.Debug($"[SDK] Hik 播放端口初始化成功, ID:{_config.Id} IP:{_config.IpAddress} Port:{_config.Port} Name:{_config.Name}, UserID: {_userId}, 播放端口:{_playPort}");
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ayay.SerilogLogs\Ayay.SerilogLogs.csproj" />
|
||||
<ProjectReference Include="..\SHH.Contracts\SHH.Contracts.csproj" />
|
||||
<ProjectReference Include="..\SHH.Contracts.Grpc\SHH.Contracts.Grpc.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user