diff --git a/EC.Util/CameraSDK/Common/CameraFactory.cs b/EC.Util/CameraSDK/Common/CameraFactory.cs index e9793e8..dc9f901 100644 --- a/EC.Util/CameraSDK/Common/CameraFactory.cs +++ b/EC.Util/CameraSDK/Common/CameraFactory.cs @@ -15,6 +15,7 @@ public class CameraFactory string hkOrDir = $"cameraSdks/hik/{sysEnv}/"; string dhOrDir = $"cameraSdks/dahua/{sysEnv}/"; string ysOrDir = $"cameraSdks/yushi/{sysEnv}/"; + // TODO: linux chmod 777, LD_LIBRARY_PATH foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.Length == 0) continue; diff --git a/EC.Util/CameraSDK/Common/CameraStruct.cs b/EC.Util/CameraSDK/Common/CameraStruct.cs index 2105df2..7cc8b1f 100644 --- a/EC.Util/CameraSDK/Common/CameraStruct.cs +++ b/EC.Util/CameraSDK/Common/CameraStruct.cs @@ -104,7 +104,12 @@ public enum CameraManufactor : int public enum CameraPort : int { HiK = 8000, - DaHua = 37777, + DaHua = + + + + + 37777, YuShi = 80, } diff --git a/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs b/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs index ad7a786..ff3bfe9 100644 --- a/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs +++ b/EC.Util/CameraSDK/DaHua/DaHuaOriSdk.cs @@ -14,7 +14,7 @@ public static class DaHuaOriSdk public const string LibSdkPath = @"./libs/dahua/libdhnetsdk.so"; #endif - private const bool Debug = true; + private const bool Debug = false; #endregion Fields @@ -40,7 +40,7 @@ public static class DaHuaOriSdk { dwSize = (uint)Marshal.SizeOf(typeof(NET_LOG_SET_PRINT_INFO)), bSetFilePath = 1, - szLogFilePath = Path.Combine("./log", "dahuaSdkLog", "sdk_log.log") + szLogFilePath = Path.Combine("./logs", "dahuaSdkLog", "sdk_log.log") }; CLIENT_LogOpen(ref logInfo); } @@ -1265,6 +1265,34 @@ public static class DaHuaOriSdk Realplay_Test = 255, } + [Flags] + public enum EM_REALDATA_FLAG + { + /// + /// raw data flag, corresponding param dwDataType in fRealDataCallBackEx / fRealDataCallBackEx2 is 0, 0x01 = 0x01 << 0 + /// 原始数据标志, 对应fRealDataCallBack(Ex/Ex2)回调函数中 dwDataType 为0, 0x01 = 0x01 << 0 + /// + RAW_DATA = 0x01, + + /// + /// data with frame info flag, corresponding param dwDataType in fRealDataCallBackEx / fRealDataCallBackEx2 is 1, 0x02 = 0x01 << 1 + /// 带有帧信息的数据标志, 对应fRealDataCallBack(Ex/Ex2)回调函数中 dwDataType 为1, 0x02 = 0x01 << 1 + /// + DATA_WITH_FRAME_INFO = 0x02, + + /// + /// YUV data flag, corresponding param dwDataType in fRealDataCallBackEx / fRealDataCallBackEx2 is 2, 0x04 = 0x01 << 2 + /// YUV 数据标志, 对应fRealDataCallBack(Ex/Ex2)回调函数中 dwDataType 为2, 0x04 = 0x01 << 2 + /// + YUV_DATA = 0x04, + + /// + /// PCM audio data flag, corresponding param dwDataType in fRealDataCallBackEx / fRealDataCallBackEx2 is 3, 0x08 = 0x01 << 3 + /// PCM 音频数据标志, 对应fRealDataCallBack(Ex/Ex2)回调函数中 dwDataType 为3, 0x08 = 0x01 << 3 + /// + PCM_AUDIO_DATA = 0x08, + } + #endregion Sdk Enum #region Sdk Struct @@ -1601,6 +1629,44 @@ public static class DaHuaOriSdk [DllImport(LibSdkPath)] public static extern IntPtr CLIENT_RealPlayEx(IntPtr lLoginID, int nChannelID, IntPtr hWnd, EM_RealPlayType rType); + /// + /// real-time monitor data callback function original shape---extensive. support 32bit and 64bit + /// 实时预览数据回调函数.支持32位和64位. + /// + /// monitor handle 预览句柄 + /// callback data type ,only data set in dwFlag will be callback:回调数据类型 + /// 0 original data (identicla SaveRealData saveddata) + /// 1 frame data + /// 2 yuv data + /// 3 pcm audio data + /// byte array, length is dwBufSize 回调数据缓存 + /// callback data, except type 0, other type is base on frame, one frame data per callback + /// pBuffer's size 回调数据的缓存大小 + /// pointer to parameter structure,based on different type 参数结构体的指针 + /// if type is 0(original) or 2(yuv), param is 0 + /// if callback data is frame data, pointer to NET_VideoFrameParam + /// if callback data is PCM data, pointer to NET_CBPCMDataParam + /// user data,which input above + public delegate void RealDataCallBack(IntPtr lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr param, IntPtr dwUser); + + /// + /// set real-time monitor data callback + /// 设置实时预览数据回调 + /// + /// monitor handle 预览句柄 + /// callback function 回调函数 + /// user data, there is no data, please use IntPtr.Zero 用户数据 + /// by bit, can combine, when it is 0x1f, callback the five types, 回调数据类型as: + /// 0x00000001 is equivalent with original data + /// 0x00000002 is MPEG4/H264 standard data + /// 0x00000004 YUV data + /// 0x00000008 PCM data + /// 0x00000010 original audio data + /// 0x0000001f above five data type + /// failed return false, successful return true 失败返回false 成功返回true + [DllImport(LibSdkPath)] + public static extern bool CLIENT_SetRealDataCallBackEx2(IntPtr lRealHandle, RealDataCallBack realDataCallBack, IntPtr dwUser, uint dwFlag); + /// /// stop real time monitoring /// 关闭实时监视 @@ -1611,4 +1677,5 @@ public static class DaHuaOriSdk public static extern bool CLIENT_StopRealPlayEx(IntPtr lRealHandle); #endregion Common Method + } \ No newline at end of file diff --git a/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs b/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs index 607f759..dd8eb1e 100644 --- a/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs +++ b/EC.Util/CameraSDK/DaHua/DaHuaSdk.cs @@ -8,7 +8,7 @@ public class DaHuaSdk : ICameraSdk private IntPtr LoginId { get; set; } = IntPtr.Zero; - private int Channel { get; } = 1; + private int Channel { get; } = 0; #endregion Fields @@ -34,10 +34,7 @@ public class DaHuaSdk : ICameraSdk stuOutParam.dwSize = (uint)Marshal.SizeOf(stuOutParam); LoginId = DaHuaOriSdk.CLIENT_LoginWithHighLevelSecurity(ref stuInParam, ref stuOutParam); ret = ConnectSuccess(); - if (ret) DaHuaOriSdk.CLIENT_SetAutoReconnect(delegate (IntPtr lLoginID, IntPtr pchDVRIP, int nDVRPort, IntPtr dwUser) - { - LoginId = lLoginID; - }, IntPtr.Zero); + if (ret) DaHuaOriSdk.CLIENT_SetAutoReconnect(null, IntPtr.Zero); else BuildException(); return ret; } @@ -154,23 +151,89 @@ public class DaHuaSdk : ICameraSdk #region Video Method + private IntPtr Hwnd { get; set; } + private IntPtr RealplayHandle { get; set; } = IntPtr.Zero; + private int RealplayPort { get; set; } = -1; + public override void StartPlay(IntPtr hwnd) { if (!ConnectSuccess() || IsPlaying()) return; - RealplayHandle = DaHuaOriSdk.CLIENT_RealPlayEx(LoginId, Channel, hwnd, DaHuaOriSdk.EM_RealPlayType.Realplay); + Hwnd = hwnd; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StartPlayLinux(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StartPlayLinux(); + else Hwnd = IntPtr.Zero; + } + + private void StartPlayWindows() + { + RealplayHandle = DaHuaOriSdk.CLIENT_RealPlayEx(LoginId, Channel, Hwnd, DaHuaOriSdk.EM_RealPlayType.Realplay); if (RealplayHandle == IntPtr.Zero) BuildException(); } + private void StartPlayLinux() + { + bool ret; + if (RealDataCallBack == null && RealplayPort < 0) + { + int nPort = -1; + if (!DhPlaySdk.PLAY_GetFreePort(ref nPort)) DhPlaySdk.BuildException(this, nPort); + if (!DhPlaySdk.PLAY_OpenStream(nPort, 0, 0, 3 * 1024 * 1024)) DhPlaySdk.BuildException(this, nPort); + if (!DhPlaySdk.PLAY_Play(nPort, Hwnd)) DhPlaySdk.BuildException(this, nPort); + RealplayPort = nPort; + } + RealplayHandle = DaHuaOriSdk.CLIENT_RealPlayEx(LoginId, Channel, IntPtr.Zero, DaHuaOriSdk.EM_RealPlayType.Realplay); + if (RealplayHandle < 0) BuildException(); + RealDataCallBack ??= DefaultRealDataCallBack; + ret = DaHuaOriSdk.CLIENT_SetRealDataCallBackEx2(RealplayHandle, RealDataCallBack, IntPtr.Zero, (uint)(DaHuaOriSdk.EM_REALDATA_FLAG.RAW_DATA)); + if (!ret) BuildException(); + } + public override void StopPlay() { if (!IsPlaying()) return; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StopPlayLinux(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StopPlayLinux(); + Hwnd = IntPtr.Zero; + } + + private void StopPlayWindows() + { bool ret = DaHuaOriSdk.CLIENT_StopRealPlayEx(RealplayHandle); RealplayHandle = IntPtr.Zero; if (!ret) BuildException(); } + private void StopPlayLinux() + { + bool ret = DaHuaOriSdk.CLIENT_StopRealPlayEx(RealplayHandle); + RealplayHandle = IntPtr.Zero; + RealDataCallBack = null; + if (RealplayPort >= 0) + { + DhPlaySdk.PLAY_Stop(RealplayPort); + DhPlaySdk.PLAY_CloseStream(RealplayPort); + DhPlaySdk.PLAY_ReleasePort(RealplayPort); + RealplayPort = -1; + } + if (!ret) BuildException(); + } + + public DaHuaOriSdk.RealDataCallBack? RealDataCallBack { get; set; } + + private void DefaultRealDataCallBack(IntPtr lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, IntPtr param, IntPtr dwUser) + { + if (RealplayPort < 0 || dwBufSize <= 0) return; + switch (dwDataType) + { + case 0: + //original unencrypted stream + DhPlaySdk.PLAY_InputData(RealplayPort, pBuffer, dwBufSize); + break; + } + } + public override bool IsPlaying() { return RealplayHandle != IntPtr.Zero; diff --git a/EC.Util/CameraSDK/DaHua/DhPlaySdk.cs b/EC.Util/CameraSDK/DaHua/DhPlaySdk.cs new file mode 100644 index 0000000..133c246 --- /dev/null +++ b/EC.Util/CameraSDK/DaHua/DhPlaySdk.cs @@ -0,0 +1,97 @@ +//#define Win + +using System.Runtime.InteropServices; + +namespace EC.Util.CameraSDK; + +/// +/// linux下 需要安装 opengl +/// +public class DhPlaySdk +{ + #region Fields + +#if Win + public const string LibSdkPath = @"./libs/dahua/dhplay.dll"; +#else + public const string LibSdkPath = @"./libs/dahua/libdhplay.so"; +#endif + + #endregion Fields + + static DhPlaySdk() + { + } + + #region struct enum + + public enum LogLevel + { + LOG_LevelUnknown = 0, // 未知等级 + LOG_LevelFatal, // fatal等级,当设置为此等级时,有一种打印输出(fatal)都有输出 + LOG_LevelError, // error等级,当设置为此等级时,有两种打印输出(fatal,error)都有输出 + LOG_LevelWarn, // warn等级,当设置为此等级时,有三种打印输出(fatal,error,warn)都有输出 + LOG_LevelInfo, // info等级,当设置为此等级时,有四种打印输出(fatal,error,warn,info)都有输出 + LOG_LevelTrace, // Trace等级,当设置为此等级时,有五种打印输出(fatal,error,warn,info,trace)都有输出 + LOG_LevelDebug // Debug等级,当设置为此等级时,以上六种打印(fatal,error,warn,info,trace,debug)都有输出 + } + + /* 渲染模式 */ + public enum RenderType + { + RENDER_NOTSET = 0, // 未设置 + RENDER_GDI, // GDI渲染 + RENDER_X11 = RENDER_GDI, // 非windows平台X11渲染 + RENDER_DDRAW, // ddraw渲染 + RENDER_OPENGL = RENDER_DDRAW, // 非windows平台opengl渲染 + RENDER_D3D, // D3D渲染,默认等同于D3D9渲染 + RENDER_D3D9 = RENDER_D3D, // D3D9渲染 + RENDER_WGL, // windows平台opengl渲染 + RENDER_D3D11 // D3D11渲染 + } + + #endregion struct enum + + #region PlaySdk + + public static void BuildException(ICameraSdk cameraSdk, int nPort) + { + string err = $"DhPlaySdk failed, error code={PLAY_GetLastErrorEx()}"; + throw CameraException.New(cameraSdk.CameraInfo, -1, err); + } + + [DllImport(LibSdkPath)] + public static extern bool PLAY_GetFreePort(ref int nPort); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_ReleasePort(int nPort); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_SetRenderMode(int nPort, RenderType nMode); + + [DllImport(LibSdkPath)] + public static extern uint PLAY_GetLastErrorEx(); + + [DllImport(LibSdkPath)] + public static extern uint PLAY_SetPrintLogLevel(LogLevel logLevel); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_SetStreamOpenMode(int nPort, uint nMode); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_OpenStream(int nPort, IntPtr pFileHeadBuf, uint nSize, uint nBufPoolSize); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_Play(int nPort, IntPtr hWnd); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_InputData(int nPort, IntPtr pBuf, uint nSize); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_Stop(int nPort); + + [DllImport(LibSdkPath)] + public static extern bool PLAY_CloseStream(int nPort); + + #endregion PlaySdk +} \ No newline at end of file diff --git a/EC.Util/CameraSDK/HiK/HiKOriSdk.cs b/EC.Util/CameraSDK/HiK/HiKOriSdk.cs index d7492f1..421dfc5 100644 --- a/EC.Util/CameraSDK/HiK/HiKOriSdk.cs +++ b/EC.Util/CameraSDK/HiK/HiKOriSdk.cs @@ -34,7 +34,7 @@ public class HiKOriSdk bool ret = NET_DVR_Init(); InitSuccess = ret; if (!ret) throw new Exception("HiKOriSdk global init failure."); - if (Debug) NET_DVR_SetLogToFile(3, Path.Combine("./log", "hikSdkLog"), true); + if (Debug) NET_DVR_SetLogToFile(3, Path.Combine("./logs", "hikSdkLog"), true); return ret; } @@ -387,19 +387,10 @@ public class HiKOriSdk **********************************************************/ [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)] - public static extern int NET_DVR_RealPlay_V40(int iUserID, ref NET_DVR_PREVIEWINFO lpPreviewInfo, RealDataCallBack fRealDataCallBack_V30, IntPtr pUser); - - /********************************************************* - Function: REALDATACALLBACK - Desc: (�ص�����) - Input: - Output: - Return: - **********************************************************/ - //public delegate void SETREALDATACALLBACK(int lRealHandle, uint dwDataType, IntPtr pBuffer, uint dwBufSize, uint dwUser); + public static extern int NET_DVR_RealPlay_V40(int iUserID, ref NET_DVR_PREVIEWINFO lpPreviewInfo, RealDataCallBack realDataCallBack, IntPtr pUser); [DllImport(LibSdkPath, CallingConvention = CallingConvention.StdCall)] - public static extern bool NET_DVR_SetRealDataCallBack(int lRealHandle, RealDataCallBack fRealDataCallBack, IntPtr pUser); + public static extern bool NET_DVR_SetRealDataCallBack(int lRealHandle, RealDataCallBack realDataCallBack, IntPtr pUser); /********************************************************* Function: NET_DVR_StopRealPlay diff --git a/EC.Util/CameraSDK/HiK/HiKSdk.cs b/EC.Util/CameraSDK/HiK/HiKSdk.cs index 8be31d2..1585524 100644 --- a/EC.Util/CameraSDK/HiK/HiKSdk.cs +++ b/EC.Util/CameraSDK/HiK/HiKSdk.cs @@ -66,12 +66,6 @@ public class HiKSdk : ICameraSdk throw CameraException.New(CameraInfo, (int)errCode); } - public void BuildPlayCtrlException(int nPort) - { - string err = $"PlayCtrlSdk failed, error code={PlayCtrlSdk.PlayM4_GetLastError(nPort)}"; - throw CameraException.New(CameraInfo, -1, err); - } - #endregion Base Method #region Ptz Method @@ -160,12 +154,12 @@ public class HiKSdk : ICameraSdk #region Video Method + private IntPtr Hwnd { get; set; } + private int RealplayHandle { get; set; } = -1; private int RealplayPort { get; set; } = -1; - private IntPtr Hwnd { get; set; } - public override void StartPlay(IntPtr hwnd) { if (!ConnectSuccess() || IsPlaying()) return; @@ -192,11 +186,11 @@ public class HiKSdk : ICameraSdk private void StartPlayLinux() { - if (RealplayPort < 0 && RealDataCallBack == null) + if (RealDataCallBack == null && RealplayPort < 0) { int nPort = -1; bool ret = PlayCtrlSdk.PlayM4_GetPort(ref nPort); - if (!ret) BuildPlayCtrlException(nPort); + if (!ret) PlayCtrlSdk.BuildException(this, nPort); RealplayPort = nPort; } HiKOriSdk.NET_DVR_PREVIEWINFO previewInfo = new() @@ -253,7 +247,7 @@ public class HiKSdk : ICameraSdk PlayCtrlSdk.PlayM4_SetStreamOpenMode(RealplayPort, 0); PlayCtrlSdk.PlayM4_OpenStream(RealplayPort, pBuffer, dwBufSize, 2 * 1024 * 1024); PlayCtrlSdk.PlayM4_SetDisplayBuf(RealplayPort, 5); - if (!PlayCtrlSdk.PlayM4_Play(RealplayPort, Hwnd)) BuildPlayCtrlException(RealplayPort); + if (!PlayCtrlSdk.PlayM4_Play(RealplayPort, Hwnd)) PlayCtrlSdk.BuildException(this, RealplayPort); break; case HiKOriSdk.NET_DVR_STREAMDATA: diff --git a/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs b/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs index 50ccb50..b896741 100644 --- a/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs +++ b/EC.Util/CameraSDK/HiK/PlayCtrlSdk.cs @@ -36,6 +36,12 @@ public class PlayCtrlSdk #region Sdk Method + public static void BuildException(ICameraSdk cameraSdk, int nPort) + { + string err = $"PlayCtrlSdk failed, error code={PlayM4_GetLastError(nPort)}"; + throw CameraException.New(cameraSdk.CameraInfo, -1, err); + } + [DllImport(LibSdkPath)] public static extern bool PlayM4_GetPort(ref int nPort); diff --git a/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs b/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs index 9a84c32..8d84072 100644 --- a/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs +++ b/EC.Util/CameraSDK/YuShi/YuShiOriSdk.cs @@ -14,7 +14,7 @@ public class YuShiOriSdk public const string LibSdkPath = @"./libs/yushi/libNetDEVSDK.so"; #endif - private const bool Debug = true; + private const bool Debug = false; #endregion Fields @@ -35,7 +35,7 @@ public class YuShiOriSdk if (!ret) throw new Exception("YuShiOriSdk global init failure."); if (Debug) { - string logPath = Path.Combine("./log", "yushiSdkLog"); + string logPath = Path.Combine("./logs", "yushiSdkLog"); Directory.CreateDirectory(logPath); NETDEV_SetLogPath(logPath); } @@ -236,8 +236,10 @@ public class YuShiOriSdk [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int NETDEV_PTZPreset_Other(IntPtr lpUserID, int dwChannelID, int dwPTZPresetCmd, byte[] szPresetName, int dwPresetID); + public delegate void RealDataCallBack(IntPtr lpPlayHandle, IntPtr pucBuffer, uint dwBufSize, uint dwMediaDataType, IntPtr lpUserParam); + [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] - public static extern IntPtr NETDEV_RealPlay(IntPtr lpUserID, ref NETDEV_PREVIEWINFO_S pstPreviewInfo, IntPtr cbPlayDataCallBack, IntPtr lpUserData); + public static extern IntPtr NETDEV_RealPlay(IntPtr lpUserID, ref NETDEV_PREVIEWINFO_S pstPreviewInfo, RealDataCallBack cbPlayDataCallBack, IntPtr lpUserData); [DllImport(LibSdkPath, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] public static extern int NETDEV_StopRealPlay(IntPtr lpRealHandle); diff --git a/EC.Util/CameraSDK/YuShi/YuShiSdk.cs b/EC.Util/CameraSDK/YuShi/YuShiSdk.cs index 94874b6..db2bcf1 100644 --- a/EC.Util/CameraSDK/YuShi/YuShiSdk.cs +++ b/EC.Util/CameraSDK/YuShi/YuShiSdk.cs @@ -111,31 +111,80 @@ public class YuShiSdk : ICameraSdk #region Video Method + private IntPtr Hwnd { get; set; } + private IntPtr RealplayHandle { get; set; } = IntPtr.Zero; + private int RealplayPort { get; set; } = -1; + public override void StartPlay(IntPtr hwnd) { if (!ConnectSuccess() || IsPlaying()) return; + Hwnd = hwnd; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StartPlayWindows(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StartPlayLinux(); + else Hwnd = IntPtr.Zero; + } + + private void StartPlayWindows() + { YuShiOriSdk.NETDEV_PREVIEWINFO_S stPreviewInfo = new() { - hPlayWnd = hwnd, + hPlayWnd = Hwnd, dwChannelID = Channel, dwStreamType = 0, // YuShiOriSdk.NETDEV_LIVE_STREAM_INDEX_E dwLinkMode = 1, // YuShiOriSdk.NETDEV_PROTOCAL_E dwFluency = 0, // YuShiOriSdk.NETDEV_PICTURE_FLUENCY_E }; - RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, IntPtr.Zero, IntPtr.Zero); + RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, null, IntPtr.Zero); + if (RealplayHandle == IntPtr.Zero) BuildException(); + } + + private void StartPlayLinux() + { + YuShiOriSdk.NETDEV_PREVIEWINFO_S stPreviewInfo = new() + { + hPlayWnd = Hwnd, + dwChannelID = Channel, + dwStreamType = 0, // YuShiOriSdk.NETDEV_LIVE_STREAM_INDEX_E + dwLinkMode = 1, // YuShiOriSdk.NETDEV_PROTOCAL_E + dwFluency = 0, // YuShiOriSdk.NETDEV_PICTURE_FLUENCY_E + }; + RealplayHandle = YuShiOriSdk.NETDEV_RealPlay(LoginId, ref stPreviewInfo, null, IntPtr.Zero); if (RealplayHandle == IntPtr.Zero) BuildException(); } public override void StopPlay() { if (!IsPlaying()) return; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) StopPlayWindows(); + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) StopPlayLinux(); + Hwnd = IntPtr.Zero; + } + + private void StopPlayWindows() + { + int ret = YuShiOriSdk.NETDEV_StopRealPlay(RealplayHandle); + RealplayHandle = IntPtr.Zero; + if (ret == YuShiOriSdk.FALSE) BuildException(); + } + + private void StopPlayLinux() + { int ret = YuShiOriSdk.NETDEV_StopRealPlay(RealplayHandle); RealplayHandle = IntPtr.Zero; + RealDataCallBack = null; if (ret == YuShiOriSdk.FALSE) BuildException(); } + public YuShiOriSdk.RealDataCallBack? RealDataCallBack { get; set; } + + private void DefaultRealDataCallBack(IntPtr lpPlayHandle, IntPtr pucBuffer, uint dwBufSize, uint dwMediaDataType, IntPtr lpUserParam) + { + if (RealplayPort < 0 || dwBufSize <= 0) return; + Console.WriteLine($"{lpPlayHandle},{pucBuffer},{dwBufSize},{dwMediaDataType},{lpUserParam}"); + } + public override bool IsPlaying() { return RealplayHandle != IntPtr.Zero; diff --git a/EC.Util/Common/ReflectUtil.cs b/EC.Util/Common/ReflectUtil.cs new file mode 100644 index 0000000..3dae62b --- /dev/null +++ b/EC.Util/Common/ReflectUtil.cs @@ -0,0 +1,94 @@ +using System.Reflection; + +namespace EC.Util.Common; + +public class ReflectUtil +{ + private static readonly BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + + public static bool ContainProperty(object instance, string propertyName) + { + if (instance == null || string.IsNullOrEmpty(propertyName)) return false; + PropertyInfo? property = instance.GetType()?.GetProperty(propertyName, bindingAttr); + return property != null; + } + + public static T? GetProperty(object instance, string propertyName) + { + if (instance == null || string.IsNullOrEmpty(propertyName)) return default; + PropertyInfo? property = instance.GetType()?.GetProperty(propertyName, bindingAttr); + try + { + return (T?)property?.GetValue(instance, null); + } + catch (Exception) + { + return default; + } + } + + public static bool GetProperty(object instance, string propertyName, out T? val) + { + val = GetProperty(instance, propertyName); + return val != null; + } + + public static bool ContainField(object instance, string propertyName) + { + if (instance == null || string.IsNullOrEmpty(propertyName)) return false; + FieldInfo? field = instance.GetType()?.GetField(propertyName, bindingAttr); + return field != null; + } + + public static T? GetField(object instance, string propertyName) + { + if (instance == null || string.IsNullOrEmpty(propertyName)) return default; + FieldInfo? field = instance.GetType()?.GetField(propertyName, bindingAttr); + try + { + return (T?)field?.GetValue(instance); + } + catch (Exception) + { + return default; + } + } + + public static bool GetField(object instance, string propertyName, out T? val) + { + val = GetField(instance, propertyName); + return val != null; + } + + public static void SetField(object instance, string propertyName, T? val) + { + if (instance == null || string.IsNullOrEmpty(propertyName)) return; + FieldInfo? field = instance.GetType()?.GetField(propertyName, bindingAttr); + try + { + field?.SetValue(instance, val); + } + catch (Exception) + { + } + } + + public static MethodInfo? GetMethod(object instance, string name) + { + MethodInfo? method = instance.GetType().GetMethod(name); + return method; + } + + public static T? RunMethod(object instance, string name, object?[]? parameters) + { + try + { + MethodInfo? method = instance.GetType().GetMethod(name); + return (T?)method?.Invoke(instance, parameters); + } + catch (Exception) + { + return default; + } + } +} \ No newline at end of file diff --git a/EC.Util/Common/SystemUtil.cs b/EC.Util/Common/SystemUtil.cs deleted file mode 100644 index 900b312..0000000 --- a/EC.Util/Common/SystemUtil.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Runtime.InteropServices; - -namespace EC.Util.Common; - -public static class SystemUtil -{ - [DllImport("Kernel32")] - public static extern void AllocConsole(); - - [DllImport("Kernel32")] - public static extern void FreeConsole(); -} \ No newline at end of file diff --git a/EC.Util/EC.Util.csproj b/EC.Util/EC.Util.csproj index 5d3e0f2..f924341 100644 --- a/EC.Util/EC.Util.csproj +++ b/EC.Util/EC.Util.csproj @@ -4,6 +4,7 @@ net7.0 enable enable + true diff --git a/EC.Util/Platform/LinuxUtil.cs b/EC.Util/Platform/LinuxUtil.cs new file mode 100644 index 0000000..6a8d17d --- /dev/null +++ b/EC.Util/Platform/LinuxUtil.cs @@ -0,0 +1,155 @@ +using System.Runtime.InteropServices; + +namespace EC.Util.Platform; + +public static class LinuxUtil +{ + #region const + + private const string libX11 = "libX11.so.6"; + private const string libX11Randr = "libXrandr.so.2"; + private const string libX11Ext = "libXext.so.6"; + private const string libXInput = "libXi.so.6"; + private const string libXCursor = "libXcursor.so.1"; + + private enum PropertyMode + { + Replace = 0, + Prepend = 1, + Append = 2 + } + + [Flags] + private enum MotifFlags + { + Functions = 1, + Decorations = 2, + InputMode = 4, + Status = 8 + } + + [Flags] + private enum MotifFunctions + { + All = 0x01, + Resize = 0x02, + Move = 0x04, + Minimize = 0x08, + Maximize = 0x10, + Close = 0x20 + } + + [Flags] + private enum MotifDecorations + { + All = 0x01, + Border = 0x02, + ResizeH = 0x04, + Title = 0x08, + Menu = 0x10, + Minimize = 0x20, + Maximize = 0x40, + } + + [Flags] + private enum MotifInputMode + { + Modeless = 0, + ApplicationModal = 1, + SystemModal = 2, + FullApplicationModal = 3 + } + + #endregion const + + #region struct + + [StructLayout(LayoutKind.Sequential)] + private struct MotifWmHints + { + public IntPtr flags; + public IntPtr functions; + public IntPtr decorations; + public IntPtr input_mode; + public IntPtr status; + + public override string ToString() + { + return string.Format("MotifWmHints SensorDict { get; } = new(); public int SensorTotal diff --git a/JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs b/JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs index c00abf1..c157360 100644 --- a/JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs +++ b/JiLinApp.Docking/VibrateAlarm/Service/AsyncTcpServer.cs @@ -112,6 +112,9 @@ public class AsyncTcpServer : IDisposable { Listener.Stop(); } + catch + { + } finally { foreach (var client in Clients.Values) @@ -179,40 +182,36 @@ public class AsyncTcpServer : IDisposable private void AcceptTcpClient(TcpListener listener) { - listener.BeginAcceptTcpClient(HandleTcpClientAccepted, listener); + TaskUtil.RunCatch(() => listener.BeginAcceptTcpClient(HandleTcpClientAccepted, listener)); } private void HandleTcpClientAccepted(IAsyncResult ar) { - if (!IsRunning()) return; - if (ar.AsyncState is not TcpListener listener) return; - - TcpClient? client; try { - client = listener.EndAcceptTcpClient(ar); + if (ar.AsyncState is not TcpListener listener) return; + TcpClient? client = listener.EndAcceptTcpClient(ar); if (client == null || !client.Connected) { client?.Client.Disconnect(false); return; } + + // add client connection to cache + string clientAddr = client.ClientAddr(); + byte[] buffer = new byte[client.ReceiveBufferSize]; + TcpClientState clientState = new(client, buffer); + RaiseClientConnected(clientAddr, clientState); + + // begin to read data + ReadBuffer(clientState); + + // keep listening to accept next connection + AcceptTcpClient(listener); } catch (Exception) { - return; } - - // add client connection to cache - string clientAddr = client.ClientAddr(); - byte[] buffer = new byte[client.ReceiveBufferSize]; - TcpClientState clientState = new(client, buffer); - RaiseClientConnected(clientAddr, clientState); - - // begin to read data - ReadBuffer(clientState); - - // keep listening to accept next connection - AcceptTcpClient(listener); } private void ReadBuffer(TcpClientState clientState) @@ -220,12 +219,19 @@ public class AsyncTcpServer : IDisposable try { NetworkStream stream = clientState.GetStream; - if (clientState.IsRead) return; - lock (clientState.IsReadLock) + int tryTime = 3, tryInterval = 200; + bool sleepFlag = false; + for (int i = 0; i < tryTime; i++) { - if (clientState.IsRead) return; - clientState.IsRead = true; - stream.BeginRead(clientState.Buffer, 0, clientState.Buffer.Length, HandleDatagramReceived, clientState); + if (sleepFlag) Thread.Sleep(tryInterval); + lock (clientState.IsReadLock) + { + sleepFlag = clientState.IsRead; + if (sleepFlag) continue; + clientState.IsRead = true; + stream.BeginRead(clientState.Buffer, 0, clientState.Buffer.Length, HandleDatagramReceived, clientState); + break; + } } } catch (IOException e) @@ -243,9 +249,7 @@ public class AsyncTcpServer : IDisposable private void HandleDatagramReceived(IAsyncResult ar) { - if (!IsRunning()) return; if (ar.AsyncState is not TcpClientState clientState) return; - int readNum; string clientAddr = clientState.Client.ClientAddr(); try @@ -270,11 +274,8 @@ public class AsyncTcpServer : IDisposable Buffer.BlockCopy(clientState.Buffer, 0, receivedBytes, 0, readNum); RaiseDatagramReceived(clientState, receivedBytes); - lock (clientState.IsReadLock) - { - clientState.IsRead = false; - } // continue listening for tcp datagram packets + lock (clientState.IsReadLock) clientState.IsRead = false; ReadBuffer(clientState); } @@ -320,10 +321,7 @@ public class AsyncTcpServer : IDisposable public void SendToAll(byte[] datagram) { if (!IsRunning()) return; - foreach (var client in Clients.Values) - { - Send(client.Client, datagram); - } + foreach (var client in Clients.Values) Send(client.Client, datagram); } /// @@ -332,7 +330,6 @@ public class AsyncTcpServer : IDisposable /// 报文 public void SendToAll(string datagram) { - if (!IsRunning()) return; SendToAll(Encoding.GetBytes(datagram)); } @@ -376,10 +373,7 @@ public class AsyncTcpServer : IDisposable public void SendToAllAsync(byte[] datagram) { if (!IsRunning()) return; - foreach (var client in Clients.Values) - { - SendAsync(client.Client, datagram); - } + foreach (var client in Clients.Values) SendAsync(client.Client, datagram); } /// @@ -388,7 +382,6 @@ public class AsyncTcpServer : IDisposable /// 报文 public void SendToAllAsync(string datagram) { - if (!IsRunning()) return; SendToAllAsync(Encoding.GetBytes(datagram)); } @@ -403,6 +396,7 @@ public class AsyncTcpServer : IDisposable { string clientAddr = client.ClientAddr(); RaiseClientDisconnected(clientAddr, client); + return; } } diff --git a/JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs b/JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs index 4d44171..7c61d36 100644 --- a/JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs +++ b/JiLinApp.Docking/VibrateAlarm/Service/TcpManager.cs @@ -151,9 +151,12 @@ public class TcpManager #region Analysis + private int SendCmdInterval { get; } = 6000; + private void AnalysisClientMessage(ref ClientMessage clientMsg) { List msgList = clientMsg.GetMessageList(); + ClientMessage taskClientMsg = clientMsg; foreach (byte[] msg in msgList) { bool vaild = msg.Length >= 19 && msg[0] == 0xAA && msg[1] == 0xAA; @@ -167,8 +170,18 @@ public class TcpManager case 0x00: Console.WriteLine("主机登录:{0}", clientMsg.ClientAddr); ResponseHostLogin_10(clientMsg, mm); - RequestHostAutoUploadState_24(clientMsg); - RequestSensorList_07(clientMsg); + + if (clientMsg.Init_0_21) break; + if (!Monitor.TryEnter(clientMsg.Init_0_21, 500)) break; + Task.Run(() => + { + while (!taskClientMsg.Init_0_21) + { + Thread.Sleep(SendCmdInterval); + RequestSensorsTurnOn_21(taskClientMsg); + } + Monitor.Exit(taskClientMsg.Init_0_21); + }); break; case 0x01: @@ -204,7 +217,8 @@ public class TcpManager case 0x17: Console.WriteLine("主机返回传感器列表:{0}", clientMsg.ClientAddr); - for (int j = 2; j < mm.Data.Length; j++) + clientMsg.Init_2_17 = true; + for (int j = 0; j < mm.Data.Length; j++) { sensorAddr = Convert.ToByte((mm.Data[j] + mm.Data[++j] * 256)); if (clientMsg.SensorDict.ContainsKey(sensorAddr)) continue; @@ -238,12 +252,36 @@ public class TcpManager case 0x31: Console.WriteLine("传感器全部启动响应:{0}", clientMsg.ClientAddr); + clientMsg.Init_0_21 = true; + if (clientMsg.Init_1_24) break; + if (!Monitor.TryEnter(clientMsg.Init_1_24, 500)) break; + Task.Run(() => + { + while (!taskClientMsg.Init_1_24) + { + Thread.Sleep(SendCmdInterval); + RequestHostAutoUploadState_24(taskClientMsg); + } + Monitor.Exit(taskClientMsg.Init_1_24); + }); SetDataResponse(mm, 0x21); break; case 0x34: Console.WriteLine("主机启动自动上传响应:{0}", clientMsg.ClientAddr); + clientMsg.Init_1_24 = true; + if (clientMsg.Init_2_17) break; + if (!Monitor.TryEnter(clientMsg.Init_2_17, 500)) break; + Task.Run(() => + { + while (!taskClientMsg.Init_2_17) + { + Thread.Sleep(SendCmdInterval); + RequestSensorList_07(taskClientMsg); + } + Monitor.Exit(taskClientMsg.Init_2_17); + }); SetDataResponse(mm, 0x24); break; @@ -396,6 +434,7 @@ public class TcpManager Request = msg }; SendRequestTry(ref request); + Thread.Sleep(500); } return true; } @@ -432,7 +471,7 @@ public class TcpManager { Request = msg }; - return SendRequestMust(ref request); + return SendRequestTry(ref request); } /// @@ -485,6 +524,7 @@ public class TcpManager Request = msg }; SendRequestTry(ref request); + Thread.Sleep(500); } return true; } @@ -525,6 +565,7 @@ public class TcpManager Request = msg }; SendRequestTry(ref request); + Thread.Sleep(500); } return true; } @@ -558,7 +599,7 @@ public class TcpManager { Request = msg }; - return SendRequestMust(ref request); + return SendRequestTry(ref request); } /// @@ -641,13 +682,13 @@ public class TcpManager private byte FrameInc { get { return (byte)(++Frame % byte.MaxValue); } } - private int SendTryTime { get; set; } = 5; + private int SendTryTime { get; set; } = 3; - private int SendTryInterval { get; set; } = 200; + private int SendTryInterval { get; set; } = 500; - private int ReqWaitTime { get; set; } = 3 * 10; + private int ReqWaitTime { get; set; } = 3; - private int ReqWaitInterval { get; set; } = 100; + private int ReqWaitInterval { get; set; } = 2000; private DataMessage GetSendMessageHead(int deviceId, ClientMessage client, byte funNum, byte dataLen) { @@ -676,7 +717,7 @@ public class TcpManager if (send) break; Thread.Sleep(SendTryInterval); } - Console.WriteLine("Send to {0}:{1} => {2}, {3}", clientMsg.ClientIp, clientMsg.ClientPort, cmd, send); + Console.WriteLine("Send to {0}:{1} => {2}, send:{3}", clientMsg.ClientIp, clientMsg.ClientPort, cmd, send); return send; } @@ -684,37 +725,29 @@ public class TcpManager { if (request.Request == null) return false; request.Request.FrameNum = FrameInc; - bool send = SendMessage(request.Request.ReceiveIp, request.Request.Encode()); - if (!send) return false; - bool respond = false; SetDataRequest(request); - for (int i = 0; i < ReqWaitTime; i++) - { - respond = IsResponse(request); - if (respond) break; - Thread.Sleep(ReqWaitInterval); - } + bool send = SendMessage(request.Request.ReceiveIp, request.Request.Encode()); RemoveDataRequest(request); - return respond; + return send; } private bool SendRequestMust(ref DataRequest request) { if (request.Request == null) return false; request.Request.FrameNum = FrameInc; + SetDataRequest(request); bool send, respond = false; do { send = SendMessage(request.Request.ReceiveIp, request.Request.Encode()); - if (!send) continue; - SetDataRequest(request); + if (!send) { Thread.Sleep(SendTryInterval); continue; } for (int i = 0; i < ReqWaitTime; i++) { respond = IsResponse(request); if (respond) break; Thread.Sleep(ReqWaitInterval); } - } while (!send && !respond); + } while (!send || !respond); RemoveDataRequest(request); return true; } @@ -737,27 +770,26 @@ public class TcpManager private void SetDataRequest(DataRequest request) { - string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}"; + string key = $"{request.Request.FunctionNum:X}-{request.Request.FrameNum}"; DataRequestDict[key] = request; } private void SetDataResponse(DataMessage msg, byte funcNum) { - string key = $"{funcNum}-{msg.FrameNum}"; - DataRequest? item = DataRequestDict[key]; - if (item != null) item.Responce = msg; + string key = $"{funcNum:X}-{msg.FrameNum}"; + if (DataRequestDict.TryGetValue(key, out DataRequest? item)) item.Responce = msg; } private void RemoveDataRequest(DataRequest request) { - string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}"; + string key = $"{request.Request.FunctionNum:X}-{request.Request.FrameNum}"; DataRequestDict.Remove(key); } private bool IsResponse(DataRequest request) { - string key = $"{request.Request.FunctionNum}-{request.Request.FrameNum}"; - DataRequest? item = DataRequestDict[key]; + string key = $"{request.Request.FunctionNum:X}-{request.Request.FrameNum}"; + DataRequestDict.TryGetValue(key, out DataRequest? item); return item != null && item.Responce != null; } diff --git a/JiLinApp.Docking/config/alarmcodes.json b/JiLinApp.Docking/config/alarmcodes.json index 53c3382..6f259e8 100644 --- a/JiLinApp.Docking/config/alarmcodes.json +++ b/JiLinApp.Docking/config/alarmcodes.json @@ -5,6 +5,24 @@ "Type": "错误", "Content": "接收到未定义的代码" }, + { + "Id": "1001", + "Level": "1", + "Type": "紧急", + "Content": "经线掉断报警" + }, + { + "Id": "1002", + "Level": "1", + "Type": "紧急", + "Content": "纬线掉断报警" + }, + { + "Id": "1003", + "Level": "1", + "Type": "紧急", + "Content": "经纬线掉断报警" + }, { "Id": "1100", "Level": "1", diff --git a/JiLinApp/Components/CameraRealPlay.axaml.cs b/JiLinApp/Components/CameraRealPlay.axaml.cs index 0895509..84d942b 100644 --- a/JiLinApp/Components/CameraRealPlay.axaml.cs +++ b/JiLinApp/Components/CameraRealPlay.axaml.cs @@ -1,8 +1,10 @@ using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Platform; using Avalonia.Threading; using EC.Util.CameraSDK; +using EC.Util.Platform; using JiLinApp.Core.Avalonia; using JiLinApp.Docking.Ptz; using System; @@ -23,6 +25,19 @@ public partial class CameraRealPlay : Window { InitializeComponent(); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + IPlatformHandle? pfHandle = TryGetPlatformHandle(); + if (pfHandle != null) WinUtil.DisableMinMaximizeButton(pfHandle.Handle); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && PlatformImpl != null) + { + IntPtr _display = ControlsUtil.GetDisplay(PlatformImpl); + IntPtr _handle = ControlsUtil.GetHandle(PlatformImpl); + IntPtr _atom = ControlsUtil.GetAtom(PlatformImpl, "_MOTIF_WM_HINTS"); + LinuxUtil.DisableMinMaximizeButton(_display, _handle, _atom); + } + //̨Ӧ¼ List