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