python实现海康sdk二次开发,移动侦测事件(二)

您所在的位置:网站首页 海康威视设置失败错误码为0*12 python实现海康sdk二次开发,移动侦测事件(二)

python实现海康sdk二次开发,移动侦测事件(二)

2024-07-15 14:20| 来源: 网络整理| 查看: 265

python实现海康sdk二次开发,移动侦测事件(一)

------------------------------------------------------------------------------------------------------------------------------------------------------------

こうしん 2020-12.7

一般来说,发现移动侦测后,都需要进行抓图,

请参见

python 海康威视ipc抓图

-------------------------------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------------------------------

こうしん 2021/3/15

之前一直使用的是单独的摄像机,没有发现移动侦测的回调bug,在此更正,

@CFUNCTYPE(c_int, c_long, MSesGCallback.NET_DVR_ALARMER, c_char_p, c_ulong, c_void_p)

中,第四个参数需要使用

POINTER(c_char)

python 海康威视ipc抓图

-------------------------------------------------------------------------------------------------------------------------------------------------------------

在上篇博文里,我们完成了海康摄像头的配置、sdk的下载,在本篇文章,我们开始着手开发海康sdk

对于实现海康摄像头自带的移动侦测事件,大概的流程为

引入海康sdk初始化海康sdk和设置连接时间登录设置报警回调函数部防撤防退去

该流程,也是符合海康的官方sdk流程的  

1 sdk引入

我们为了开发python版本的海康摄像头的相关功能,需要使用C++版本提供的sdk,假设在上篇博客中,下载的sdk路径为:D:\Backup\solo\hkoython\dll

采用如下代码读入海康sdk

from ctypes import * class HkAdapter: dll_path = r"D:\Backup\solo\hkoython\dll" def load_hkdll(self): # 载入HCCore.dll hccode = WinDLL(__class__.dll_path + "\\HCCore.dll") # 载入HCNetSDK.dll hcnetsdk = cdll.LoadLibrary(__class__.dll_path + "\\HCNetSDK.dll") return hcnetsdk

在代码段中,我们采用winDLL的形式,读入了两个dll文件,而目录下,有很多dll文件,我们是用不到的,我们只需要按照海康sdk读取我们需要的两个核心文件即可。

这里要注意,dll_path文件夹里的子文件夹  文件的名称都是不可以更改的,因为我们没有显式引入,但是hknetsdk.dll是会去调用其他dll的,因此我们保持原样放好即可。

我们这里将hcnetsdk变量返回,以后调用c++函数,采用hknetsdk.functionname()的形式即可

2.初始化

我们采用hknetsdk.functionname()的方式,调用初始化函数即可

def init(hksdk): if not hksdk.NET_DVR_Init(): print("初始失败") return False if not hksdk.NET_DVR_SetConnectTime(): print("设置连接时间失败") return False print("初始化成功") return True

在该init方法中,我们调用了海康dll提供的两个函数。都使用缺省参数。

NET_DVR_Init(): 初始化 NET_DVR_SetConnectTime():设置连接时间,缺省为DWORD dwWaitTime = 3000, DWORD dwTryTimes = 3

可以发现,采用ctypes调用这种没有参数函数相当简单

3.登录 3.1 ctypes结构体与数据类型对应

C++版的登录函数为

NET_DVR_USER_LOGIN_INFO loginInfo = { 0 }; NET_DVR_DEVICEINFO_V40 deviceInfo = { 0 }; //登录方式设置为同步 loginInfo.bUseAsynLogin = 0; const char *deviceAddress, *userName, *passWord; WORD wPort = 8000; LONG userID; deviceAddress = "192.168.8.112"; userName = "admin"; passWord = "hk123456"; strcpy_s(loginInfo.sDeviceAddress, deviceAddress); strcpy_s(loginInfo.sUserName, userName); strcpy_s(loginInfo.sPassword, passWord); loginInfo.wPort = wPort; userID = NET_DVR_Login_V40(&loginInfo, &deviceInfo);

可以看到,需要为登录函数NET_DVR_Login_V40传递两个参数,参数类型为两个结构体类型的地址,在ctypes中,使用Structure的子类,来定义结构体,固定写法为

    

class ClassName(Structure): _fields_ = [ # 该标量的名称,形式都是固定的 # 每个元素为("结构体属性名",数据类型)的元组形式 #,结构体属性名字段必须与c++的必须要完全一致 ("结构体属性1", 数据类型), ("结构体属性2", 数据类型), ... ]

结构体属性直接复制C++的即可,而数据类型需要对应到Ctype数据类型。如下图所示。

2.2 数据准备

我们分别按照C++的结构体说明,在一个文件里,定义两个类,分别表示登录所需要的结构体

 

from ctypes import * class NET_DVR_DEVICEINFO_V30(Structure): _fields_ = [ ("sSerialNumber", c_byte * 48), # 序列号 ("byAlarmInPortNum", c_byte), # 模拟报警输入个数 ("byAlarmOutPortNum", c_byte), # 模拟报警输出个数 ("byDiskNum", c_byte), # 硬盘个数 ("byDVRType", c_byte), # 设备类型,详见下文列表 ("byChanNum", c_byte), ("byStartChan", c_byte), ("byAudioChanNum", c_byte), # 设备语音对讲通道数 ("byIPChanNum", c_byte), ("byZeroChanNum", c_byte), # 零通道编码个数 ("byMainProto", c_byte), # 主码流传输协议类型: ("bySubProto", c_byte), # 字码流传输协议类型: ("bySupport", c_byte), ("bySupport1", c_byte), ("bySupport2", c_byte), ("wDevType", c_uint16), # 设备型号,详见下文列表 ("bySupport3", c_byte), ("byMultiStreamProto", c_byte), ("byStartDChan", c_byte), # 起始数字通道号,0表示无数字通道,比如DVR或IPC ("byStartDTalkChan", c_byte), ("byHighDChanNum", c_byte), # 数字通道个数,高8位 ("bySupport4", c_byte), ("byLanguageType", c_byte), ("byVoiceInChanNum", c_byte), # 音频输入通道数 ("byStartVoiceInChanNo", c_byte), # 音频输入起始通道号,0表示无效 ("bySupport5", c_byte), ("bySupport6", c_byte), ("byMirrorChanNum", c_byte), # 镜像通道个数,录播主机中用于表示导播通道 ("wStartMirrorChanNo", c_uint16), ("bySupport7", c_byte), ("byRes2", c_byte )] # 保留,置为0 class NET_DVR_DEVICEINFO_V40(Structure): _fields_ = [ # struDeviceV30结构体中包括接口体,我们只需要额外定义一下该子结构体即可 ("struDeviceV30", NET_DVR_DEVICEINFO_V30), ("bySupportLock", c_byte),#设备是否支持锁定功能,bySupportLock为1时,dwSurplusLockTime和byRetryLoginTime有效 ("byRetryLoginTime", c_byte), ("byPasswordLevel", c_byte), ("byProxyType", c_byte), ("dwSurplusLockTime", c_ulong), ("byCharEncodeType", c_byte), ("bySupportDev5", c_byte), ("bySupport", c_byte), ("byLoginMode", c_byte), ("dwOEMCode", c_ulong), ("iResidualValidity", c_int), ("byResidualValidity", c_byte), ("bySingleStartDTalkChan", c_byte), ("bySingleDTalkChanNums", c_byte), ("byPassWordResetLevel", c_byte), ("bySupportStreamEncrypt", c_byte), ("byMarketType", c_byte), ("byRes2", c_byte*253), ] class NET_DVR_USER_LOGIN_INFO(Structure): _fields_ = [ ("sDeviceAddress", c_char * 129), ("byUseTransport", c_byte), ("wPort", c_uint16), ("sUserName", c_char * 64), ("sPassword", c_char * 64), ("bUseAsynLogin", c_int), ("byProxyType", c_byte), ("byUseUTCTime", c_byte), ("byLoginMode", c_byte), ("byHttps", c_byte), ("iProxyID", c_long), ("byVerifyMode", c_byte), ("byRes3", c_byte * 120) ] 3.3 login

我们获得了两个结构体,直接对其初始化,复制,调用登录即可。

def login(hksdk, url, usename, password, port=8000): # python的String向ctype里的c_char传递的数据需要进行bytes编码 burl = bytes(url, "ascii") busename = bytes(usename, "ascii") bpassword = bytes(password, "ascii") # 初始化两个结构体 login_info = NET_DVR_Login_V40.NET_DVR_USER_LOGIN_INFO() device_info = NET_DVR_Login_V40.NET_DVR_DEVICEINFO_V40() # 设置登录信息 login_info.wPort = port login_info.bUseAsynLogin = 0 login_info.sUserName = busename login_info.sPassword = bpassword login_info.sDeviceAddress = burl # 获得引用,byref基本等于c++的&符号 param_login = byref(login_info) # 传递的为指针则使用该种方式 param_device = byref(device_info) # 执行NET_DVR_Login_V40函数,获取userid useid = hksdk.NET_DVR_Login_V40(param_login, param_device) # 登录成功时,useid的值为0、1.....,失败时为-1 # 可以调用NET_DVR_GetLastError查看错误码 if useid == -1: print("登录失败,错误码为{}".format(hksdk.NET_DVR_GetLastError())) else: print("登录成功,用户id为{}".format(useid)) return useid 4 布防 4.1 设置报警回调函数

在海康sdk中,当触发了报警时间后,会自动调用用户设置的回调函数,该函数原型为

BOOL CALLBACK MSesGCallback(LONG lCommand, NET_DVR_ALARMER *pAlarmer, char *pAlarmInfo, DWORD dwBufLen, void* pUser) ;

该回调函数需要的形参除了基本数据类型外,还需要一个结构体,因此我们先定义该结构体

from ctypes import * class NET_DVR_ALARMER(Structure): _fields_ = [ ("byUserIDValid",c_byte), ("bySerialValid", c_byte), ("byVersionValid", c_byte), ("byDeviceNameValid", c_byte), ("byMacAddrValid",c_byte), ("byLinkPortValid", c_byte), ("byDeviceIPValid", c_byte), ("bySocketIPValid", c_byte), ("lUserID", c_long), ("sSerialNumber", c_byte*48), ("sDeviceName", c_char*32), ("byMacAddr", c_byte*6), ("wLinkPort", c_uint16), ("sDeviceIP", c_char*128), ("sSocketIP", c_char*128), ("byIpProtocol", c_byte), ("byRes1", c_byte*2), ("bJSONBroken", c_byte), ("wSocketPort", c_uint16), ("byRes2", c_byte*6) ]

我们写一个python方法作为回调函数

# 更正,此处第四个参数应该换成POINTER(c_char) @CFUNCTYPE(c_int, c_long, MSesGCallback.NET_DVR_ALARMER, POINTER(c_char), c_ulong, c_void_p) def alarm_callback(lCommand: c_long, pAlarmer: MSesGCallback.NET_DVR_ALARMER, pAlarmInfo: POINTER(c_char), dwBufLen: c_ulong, pUser: c_void_p): print("收到警报信息,类型为 {} \t发送者为 {}".format(lCommand, pAlarmer.lUserID)) if lCommand == 0x4000: print("消息类型为 COMM_ALARM_V30", end=" --> ") alarm_info = ALARM.NET_DVR_ALARMINFO_V30() # 更正,换成POINTER(c_char)后,不需要调用decode方法 memmove(addressof(alarm_info), pAlarmInfo, sizeof(alarm_info)) if alarm_info.dwAlarmType == 3: print("子事件为移动侦测") else: print("子事件 None") else: print("消息类型为None") return 1

ctype可以用装饰器的形式来定义回调函数,明确返回值类型和输入值类型,在上述代码中为

@CFUNCTYPE(返回类型,参数类型1,参数类型2...)

该回调函数首先会判断是不是通用警报信息,如果是,在判断是不是为移动侦测。

在判断移动侦测时,我们使用了一个新的结构体NET_DVR_ALARMINFO_V30,我们将在4.2中,定义该结构体

4.2 启用回调函数,布防

布防函数为

NET_DVR_SETUPALARM_PARAM setupParam = { 0 }; setupParam.dwSize = sizeof(NET_DVR_SETUPALARM_PARAM); setupParam.byLevel = 0; //优先级 0 高 1低 LONG handle = NET_DVR_SetupAlarmChan_V41(userID, &setupParam);

可见我们需要定义一个新的结构体NET_DVR_SETUPALARM_PARAM

class NET_DVR_SETUPALARM_PARAM(Structure): _fields_ = [ ("dwSize", c_ulong), ("byLevel", c_byte), # 布防优先级 ("byAlarmInfoType", c_byte), ("byRetAlarmTypeV40", c_byte), ("byRetDevInfoVersion", c_byte), ("byRetVQDAlarmType", c_byte), ("byFaceAlarmDetection", c_byte), ("bySupport", c_byte), ("byBrokenNetHttp", c_byte), ("wTaskNo", c_uint16), ("byDeployType", c_byte), ("byRes1", c_byte*2), ("byAlarmTypeURL", c_byte), ("byCustomCtrl", c_byte), ] class NET_DVR_ALARMINFO_V30(Structure): _fields_ =[ ("dwAlarmType",c_ulong), ("dwAlarmInputNumber",c_ulong), ("byAlarmOutputNumber",c_byte*96), ("byAlarmRelateChannel",c_byte*64), ("byChannel",c_byte*64), ("byChannel",c_byte*33) ]

据此,我们可以完成设置回调函数,启用布防

def deploy(hksdk, userid): # 布防 # 设置回调函数 callback_status = hksdk.NET_DVR_SetDVRMessageCallBack_V31(alarm_callback, None) if callback_status == -1: print("设置回调函数失败,错误码为".format(hksdk.NET_DVR_GetLastError())) return -1 else: print("设置回调函数成功") # 启用布防 setup_alarm = ALARM.NET_DVR_SETUPALARM_PARAM() setup_alarm.dwSize = sizeof(ALARM.NET_DVR_SETUPALARM_PARAM) setup_alarm.byLevel = 0 setup_alarm.byAlarmInfoType = 1 handle = hksdk.NET_DVR_SetupAlarmChan_V41(userid, setup_alarm) if handle < 0: print("布防失败,错误码为{}".format(hksdk.NET_DVR_GetLastError())) else: print("布防成功") return handle 5 撤防

撤防较为简单,只需要一个由布防得到的handle参数即可

def disdeploy(hksdk, handle): # 布防 disdeploy_result = hksdk.NET_DVR_CloseAlarmChan_V30(handle) if disdeploy_result == -1: print("撤防失败,错误码为{}".format(hksdk.NET_DVR_GetLastError())) else: print("撤防成功")

 

6 反初始化

将推出登录和反初始化做到一起,也较为简单,一个需要一个int类型参数useid,一个不需要参数

def uinit(hksdk, useid): isOK = hksdk.NET_DVR_Logout(useid) if isOK == -1: print("登出失败错误码为{}".format(hksdk.NET_DVR_GetLastError())) else: print("登出成功") hksdk.NET_DVR_Cleanup()

至此,我们所有的功能函数都写完了

7 run it

写一个main函数,执行上述过程

if __name__ == "__main__": print("-----------初始化与登录---------") hkadapter = HkAdapter() hksdk = hkadapter.load_hkdll() init(hksdk) userid = login(hksdk, "xxx.xxx.xxx.xxx", "xxxxxx", "xxxxxxx") print("----------初始化与登录完成---------") print("-----------布防与撤防---------") print("开始部防。。。") handle = deploy(hksdk, userid) if handle >= 0: print("***************警告信息输出begin******************") # 主线程sleep的时间就是程序布防的时间 time.sleep(100) print("****************警告信息输出end*******************") print("开始撤防。。。") disdeploy(hksdk, handle) print("-----------布防与撤防结束---------") uinit(hksdk, userid) 8 After

很粗糙的写了一个小程序,还需要全方位的细致优化,大家有什么好的意见和建议可以留言

参考

Python实现海康威视SDK二次开发(开源库)

ctypes --- Python 的外部函数库



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3