降低CPU使用率,处置好因降低CPU使用率带来的颜色偏差
This commit is contained in:
@@ -5,9 +5,16 @@
|
||||
/// </summary>
|
||||
public class SdkGlobal
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否保存摄像头配置
|
||||
/// </summary>
|
||||
/// <summary>是否保存摄像头配置</summary>
|
||||
public static bool SaveCameraConfigEnable { get; set; } = false;
|
||||
|
||||
/// <summary>是否使用 TurboJpegWrapper 降低图片编码开销</summary>
|
||||
public static bool UseTurboJpegWrapper { get; set;} = true;
|
||||
|
||||
/// <summary>禁用 TurboJpegWrapper</summary>
|
||||
public static void DisableTurboJpegAcceleration()
|
||||
{
|
||||
UseTurboJpegWrapper = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ayay.SerilogLogs;
|
||||
using Lennox.LibYuvSharp;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using System.Runtime.ExceptionServices;
|
||||
@@ -92,7 +93,7 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
{
|
||||
string err = NETClient.GetLastError();
|
||||
NETClient.Logout(_loginId);
|
||||
throw new Exception($"大华预览失败: {err}");
|
||||
throw new Exception($"大华预览失败, {err}");
|
||||
}
|
||||
|
||||
_sdkLog.Information($"[SDK] Dahua 取流成功 => RealPlayID:{_realPlayId}");
|
||||
@@ -184,6 +185,8 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
// =================================================================================
|
||||
try
|
||||
{
|
||||
_sdkLog.Information($"[Perf] Dahua 尝试开启硬解码. ID:{_config.Id} Port:{_playPort}");
|
||||
|
||||
// nDecodeEngine: 1 = 开启硬解码 (Nvidia/Intel)
|
||||
// 注意:大华 SDK 若不支持会自动降级,try-catch 仅为了防止 P/Invoke 签名缺失崩溃
|
||||
// Optimized: 使用新版接口开启硬件解码,优先尝试 CUDA 以保证 Ayay 的多路并发性能
|
||||
@@ -195,7 +198,6 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
// 如果显卡不支持 CUDA,降级为普通硬解或软解
|
||||
PLAY_SetEngine(_playPort, DecodeType.DECODE_HW, RenderType.RENDER_D3D9);
|
||||
}
|
||||
_sdkLog.Information($"[Perf] Dahua 尝试开启硬解码. ID:{_config.Id} Port:{_playPort}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -223,7 +225,7 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
/// </summary>
|
||||
[HandleProcessCorruptedStateExceptions] // 捕获非托管状态损坏异常 (AccessViolation)
|
||||
[SecurityCritical]
|
||||
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref DahuaPlaySDK.FRAME_INFO pFrameInfo, IntPtr nUser, int nReserved2)
|
||||
private unsafe void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref DahuaPlaySDK.FRAME_INFO pFrameInfo, IntPtr nUser, int nReserved2)
|
||||
{
|
||||
// 1. 基础指针检查
|
||||
if (pBuf == IntPtr.Zero || nSize <= 0) return;
|
||||
@@ -307,15 +309,37 @@ public class DahuaVideoSource : BaseVideoSource
|
||||
smartFrame = _framePool?.Get();
|
||||
if (smartFrame == null) return; // 池满丢帧
|
||||
|
||||
// =========================================================================================
|
||||
// ⚡ [核心操作:零拷贝转换]
|
||||
// 大华 PlaySDK 默认输出 I420 (YUV420P)。
|
||||
// 使用 Mat.FromPixelData 封装指针,避免内存拷贝。
|
||||
// =========================================================================================
|
||||
using (var yuvMat = Mat.FromPixelData(currentHeight + currentHeight / 2, currentWidth, MatType.CV_8UC1, pBuf))
|
||||
{
|
||||
Cv2.CvtColor(yuvMat, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_I420);
|
||||
}
|
||||
int width = pFrameInfo.nWidth;
|
||||
int height = pFrameInfo.nHeight;
|
||||
|
||||
// 计算 YUV 分量地址
|
||||
byte* pY = (byte*)pBuf;
|
||||
byte* pU = pY + (width * height);
|
||||
byte* pV = pU + (width * height / 4);
|
||||
|
||||
// 目标 BGR 地址
|
||||
byte* pDst = (byte*)smartFrame.InternalMat.Data;
|
||||
|
||||
// 调用 LibYuvSharp
|
||||
// 注意:LibYuvSharp 内部通常处理的是 BGR 顺序,
|
||||
// 如果发现图像发蓝,请将 pU 和 pV 的位置对调
|
||||
LibYuv.I420ToRGB24(
|
||||
pY, width,
|
||||
pU, width / 2,
|
||||
pV, width / 2,
|
||||
pDst, width * 3,
|
||||
width, height
|
||||
);
|
||||
|
||||
//// =========================================================================================
|
||||
//// ⚡ [核心操作:零拷贝转换]
|
||||
//// 大华 PlaySDK 默认输出 I420 (YUV420P)。
|
||||
//// 使用 Mat.FromPixelData 封装指针,避免内存拷贝。
|
||||
//// =========================================================================================
|
||||
//using (var yuvMat = Mat.FromPixelData(currentHeight + currentHeight / 2, currentWidth, MatType.CV_8UC1, pBuf))
|
||||
//{
|
||||
// Cv2.CvtColor(yuvMat, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_I420);
|
||||
//}
|
||||
|
||||
// =========================================================================================
|
||||
// 🛡️ [第三道防线:空结果防御]
|
||||
|
||||
@@ -341,11 +341,18 @@ public static class HikPlayMethods
|
||||
[DllImport(DllName)]
|
||||
public static extern bool PlayM4_ResetSourceBuffer(int nPort);
|
||||
|
||||
// =========================================================================
|
||||
// 🚀 [修正] 适配 V6.1.9+ 新版 SDK 的硬解码 API
|
||||
// =========================================================================
|
||||
/// <summary>
|
||||
/// [新增] 开启硬件解码
|
||||
/// 设置解码引擎 (扩展版)
|
||||
/// 对应 C++: BOOL PlayM4_SetDecodeEngineEx(LONG nPort, DWORD dwEngine);
|
||||
/// </summary>
|
||||
[DllImport(DllName)]
|
||||
public static extern bool PlayM4_SetHardWareDecode(int nPort, int nMode);
|
||||
/// <param name="nPort">通道号</param>
|
||||
/// <param name="dwEngine">0:软解, 1:显卡硬解(D3D9), 2:显卡硬解(D3D11), 3:Intel核显</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("PlayCtrl.dll")]
|
||||
public static extern bool PlayM4_SetDecodeEngineEx(int nPort, uint dwEngine);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Ayay.SerilogLogs;
|
||||
using Lennox.LibYuvSharp;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
using SHH.CameraSdk.HikFeatures;
|
||||
@@ -487,20 +488,27 @@ public class HikVideoSource : BaseVideoSource,
|
||||
return;
|
||||
}
|
||||
|
||||
// =================================================================================
|
||||
// 🚀 [新增代码] 尝试开启 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}");
|
||||
}
|
||||
//// =================================================================================
|
||||
//// 🚀 [新增代码] 性能优化:适配新版 SDK 开启硬解码
|
||||
//// =================================================================================
|
||||
//try
|
||||
//{
|
||||
// // 尝试调用 Ex 版本的接口 (参数 2 表示 D3D11 硬解)
|
||||
// if (HikPlayMethods.PlayM4_SetDecodeEngineEx(_playPort, 1))
|
||||
// {
|
||||
// _sdkLog.Information($"[Perf] Hik 强制硬解码(SetDecodeEngineEx)已开启. ID:{_config.Id}");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 如果返回 false,打印一下错误码
|
||||
// uint err = HikPlayMethods.PlayM4_GetLastError(_playPort);
|
||||
// _sdkLog.Warning($"[Perf] Hik 硬解码开启失败 Err={err}.");
|
||||
// }
|
||||
//}
|
||||
//catch (EntryPointNotFoundException)
|
||||
//{
|
||||
// _sdkLog.Warning($"[Perf] PlayM4_SetDecodeEngineEx 也没找到,这太奇怪了。");
|
||||
//}
|
||||
|
||||
HikPlayMethods.PlayM4_SetDecCallBackEx(_playPort, _decCallBack, IntPtr.Zero, 0);
|
||||
|
||||
@@ -536,7 +544,7 @@ public class HikVideoSource : BaseVideoSource,
|
||||
/// <param name="nReserved2"></param>
|
||||
[HandleProcessCorruptedStateExceptions]
|
||||
[SecurityCritical]
|
||||
private void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
|
||||
private unsafe void SafeOnDecodingCallBack(int nPort, IntPtr pBuf, int nSize, ref HikPlayMethods.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
|
||||
{
|
||||
//Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 帧抵达.");
|
||||
|
||||
@@ -633,11 +641,35 @@ public class HikVideoSource : BaseVideoSource,
|
||||
smartFrame = _framePool.Get();
|
||||
if (smartFrame == null) return; // 池满丢帧
|
||||
|
||||
// Optimized: [原因] 使用局部作用域封装 YUV 转换,确保原生指针尽快脱离
|
||||
using (var rawYuvWrapper = Mat.FromPixelData(currentHeight + currentHeight / 2, currentWidth, MatType.CV_8UC1, pBuf))
|
||||
{
|
||||
Cv2.CvtColor(rawYuvWrapper, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_YV12);
|
||||
}
|
||||
|
||||
int width = pFrameInfo.nWidth;
|
||||
int height = pFrameInfo.nHeight;
|
||||
|
||||
// 计算 YUV 分量地址
|
||||
byte* pY = (byte*)pBuf;
|
||||
byte* pU = pY + (width * height);
|
||||
byte* pV = pU + (width * height / 4);
|
||||
|
||||
// 目标 BGR 地址
|
||||
byte* pDst = (byte*)smartFrame.InternalMat.Data;
|
||||
|
||||
// 调用 LibYuvSharp
|
||||
// 注意:LibYuvSharp 内部通常处理的是 BGR 顺序,
|
||||
// 如果发现图像发蓝,请将 pU 和 pV 的位置对调
|
||||
LibYuv.I420ToRGB24(
|
||||
pY, width,
|
||||
pU, width / 2,
|
||||
pV, width / 2,
|
||||
pDst, width * 3,
|
||||
width, height
|
||||
);
|
||||
|
||||
|
||||
//// Optimized: [原因] 使用局部作用域封装 YUV 转换,确保原生指针尽快脱离
|
||||
//using (var rawYuvWrapper = Mat.FromPixelData(currentHeight + currentHeight / 2, currentWidth, MatType.CV_8UC1, pBuf))
|
||||
//{
|
||||
// Cv2.CvtColor(rawYuvWrapper, smartFrame.InternalMat, ColorConversionCodes.YUV2BGR_YV12);
|
||||
//}
|
||||
|
||||
// =========================================================
|
||||
// 【新增防御】: 检查转换结果是否有效
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<BaseOutputPath>D:\Codes\Ayay\SHH.CameraService\bin</BaseOutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -15,6 +16,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Lennox.LibYuvSharp" Version="1.1.2" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
|
||||
|
||||
Reference in New Issue
Block a user