此贴能起到的作用
通过这个帖子,能了解到如何用Python调用海康SDK,实现业务逻辑需要结合哪些资料,这些接口的参数是怎么样的,如何翻译成Python,如何传参,参数中的一些变量,常量可以怎样查找。
戳这里获得demo源码
开发资源
海康威视SDK下载 https://www.hikvision.com/cn/download_61.html
SDK只有对linux和windows的支持,没有对mac的支持,所以mac开发比较累
基于SDK开发
- SDK中给到了基于Java,C#等demo(这写可以帮助我们了解部分变量的定义和值域,很重要,可以在Python构建实体的时候作为参考),但是没有python。
- linux和windows的SDK中分别是.so和.dll,对于python我们需要ctypes库来完成二次开发
- 硬件产品开发文档,这里有详细硬件功能调用链,很详细,不过demo是c++的,另外demo中的一些变量或者常量不能查看引用,这里我们需要结合Java或者C#的包去查看具体值域。
-
https://open.hikvision.com/hardware/definitions/接口或实体.html
,这个路径下是接口的详细文档。
代码实践
- 基础SDK调用实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | from ctypes import * import os import logging import hkws.model.login as login import hkws.model.preview as preview from hkws.callback import hikFunc from hkws.callback import g_real_data_call_back class HKAdapter: so_list = [] # 加载目录下所有so文件 def add_lib(self, path, suffix): files = os.listdir(path) for file in files: if not os.path.isdir(path + file): if file.endswith(suffix): self.so_list.append(path + file) else: self.add_lib(path + file + "/", suffix) # python 调用 sdk 指定方法 def call_cpp(self, func_name, *args): for so_lib in self.so_list: try: lib = cdll.LoadLibrary(so_lib) try: value = eval("lib.%s" % func_name)(*args) logging.info("调用的库:" + so_lib) logging.info("执行成功,返回值:" + str(value)) return value except: continue except: continue # logging.info("库文件载入失败:" + so_lib ) logging.error("没有找到接口!") return False |
- 初始化SDK及释放SDK
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 初始化海康微视 sdk def init_sdk(self): init_res = self.call_cpp("NET_DVR_Init") # SDK初始化 if init_res: logging.info("SDK初始化成功") return True else: error_info = self.call_cpp("NET_DVR_GetLastError") logging.error("SDK初始化错误:" + str(error_info)) return False # 释放sdk def sdk_clean(self): result = self.call_cpp("NET_DVR_Cleanup") logging.info("释放资源", result) |
- 用户设备登录
请求所用参数,这里需要用python ctypes参照https://open.hikvision.com/hardware/definitions/NET_DVR_Login_V40.html 所给出的结构写出对应的python类,有些常量具体数值是没有的,需要结合之前所说的Java,C#的demo看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | class NET_DVR_USER_LOGIN_INFO(Structure): _fields_ = [ ("sDeviceAddress", c_byte * 129), # 设备地址,IP或者普通域名 ("byUseTransport", c_byte), # 是否启用能力透传 0:不启动,默认 1:启动 ("wPort", c_uint16), # 设备端口号 ("sUserName", c_byte * 64), # 登录用户名 ("sPassword", c_byte * 64), # 登录密码 # ("fLoginResultCallBack",) # ("bUseAsynLogin", c_bool), # 是否异步登录, 0:否 1:是 ("byProxyType", c_byte), # 代理服务器类型:0- 不使用代理,1- 使用标准代理,2- 使用EHome代理 # 是否使用UTC时间: # 0 - 不进行转换,默认; # 1 - 输入输出UTC时间,SDK进行与设备时区的转换; # 2 - 输入输出平台本地时间,SDK进行与设备时区的转换 ("byUseUTCTime", c_byte), # 登录模式(不同模式具体含义详见“Remarks”说明): # 0- SDK私有协议, # 1- ISAPI协议, # 2- 自适应(设备支持协议类型未知时使用,一般不建议) ("byLoginMode", c_byte), # ISAPI协议登录时是否启用HTTPS(byLoginMode为1时有效): # 0 - 不启用, # 1 - 启用, # 2 - 自适应(设备支持协议类型未知时使用,一般不建议) ("byHttps", c_byte), # 代理服务器序号,添加代理服务器信息时相对应的服务器数组下表值 ("iProxyID", c_long), # 保留,置为0 ("byRes3", c_byte * 120), ] # 设备参数结构体。 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), # 设备模拟通道个数,数字(IP)通道最大个数为byIPChanNum + byHighDChanNum*256 ("byStartChan", c_byte), # 模拟通道的起始通道号,从1开始。数字通道的起始通道号见下面参数byStartDChan ("byAudioChanNum", c_byte), # 设备语音对讲通道数 ("byIPChanNum", c_byte), # 设备最大数字通道个数,低8位,搞8位见byHighDChanNum. 可以根据ip通道个数是否调用NET_DVR_GetDVRConfig (配置命令NET_DVR_GET_IPPARACFG_V40)获得模拟和数字通道的相关参数 ("byZeroChanNum", c_byte), # 零通道编码个数 ("byMainProto", c_byte), # 主码流传输协议类型: 0 - private, 1 - rtsp, 2- 同时支持私有协议和rtsp协议去留(默认采用私有协议取流) ("bySubProto", c_byte), # 字码流传输协议类型: 0 - private , 1 - rtsp , 2 - 同时支持私有协议和rtsp协议取流 (默认采用私有协议取流) # 能力,位与结果为0表示不支持,1 # 表示支持 # bySupport & 0x1,表示是否支持智能搜索 # bySupport & 0x2,表示是否支持备份 # bySupport & 0x4,表示是否支持压缩参数能力获取 # bySupport & 0x8, 表示是否支持双网卡 # bySupport & 0x10, 表示支持远程SADP # bySupport & 0x20, 表示支持Raid卡功能 # bySupport & 0x40, 表示支持IPSAN目录查找 # bySupport & 0x80, 表示支持rtp over rtsp ("bySupport", c_byte), # 能力集扩充,位与结果为0表示不支持,1 # 表示支持 # bySupport1 & 0x1, 表示是否支持snmp # v30 # bySupport1 & 0x2, 表示是否支持区分回放和下载 # bySupport1 & 0x4, 表示是否支持布防优先级 # bySupport1 & 0x8, 表示智能设备是否支持布防时间段扩展 # bySupport1 & 0x10, 表示是否支持多磁盘数(超过33个) # bySupport1 & 0x20, 表示是否支持rtsp over http # bySupport1 & 0x80, 表示是否支持车牌新报警信息,且还表示是否支持NET_DVR_IPPARACFG_V40配置 ("bySupport1", c_byte), # 能力集扩充,位与结果为0表示不支持,1 # 表示支持 # bySupport2 & 0x1, 表示解码器是否支持通过URL取流解码 # bySupport2 & 0x2, 表示是否支持FTPV40 # bySupport2 & 0x4, 表示是否支持ANR(断网录像) # bySupport2 & 0x20, 表示是否支持单独获取设备状态子项 # bySupport2 & 0x40, 表示是否是码流加密设备 ("bySupport2", c_byte), ("wDevType", c_uint16), # 设备型号,详见下文列表 # 能力集扩展,位与结果:0 - 不支持,1 - 支持 # bySupport3 & 0x1, 表示是否支持多码流 # bySupport3 & 0x4, 表示是否支持按组配置,具体包含通道图像参数、报警输入参数、IP报警输入 / 输出接入参数、用户参数、设备工作状态、JPEG抓图、定时和时间抓图、硬盘盘组管理等 # bySupport3 & 0x20,表示是否支持通过DDNS域名解析取流 ("bySupport3", c_byte), # 是否支持多码流,按位表示,位与结果:0 - 不支持,1 - 支持 # byMultiStreamProto & 0x1, 表示是否支持码流3 # byMultiStreamProto & 0x2, 表示是否支持码流4 # byMultiStreamProto & 0x40, 表示是否支持主码流 # byMultiStreamProto & 0x80, 表示是否支持子码流 ("byMultiStreamProto", c_byte), ("byStartDChan", c_byte), # 起始数字通道号,0表示无数字通道,比如DVR或IPC ("byStartDTalkChan", c_byte), # 起始数字对讲通道号,区别于模拟对讲通道号,0表示无数字对讲通道 ("byHighDChanNum", c_byte), # 数字通道个数,高8位 # 能力集扩展,按位表示,位与结果:0 - 不支持,1 - 支持 # bySupport4 & 0x01, 表示是否所有码流类型同时支持RTSP和私有协议 # bySupport4 & 0x10, 表示是否支持域名方式挂载网络硬盘 ("bySupport4", c_byte), # 支持语种能力,按位表示,位与结果:0 - 不支持,1 - 支持 # byLanguageType == 0,表示老设备,不支持该字段 # byLanguageType & 0x1,表示是否支持中文 # byLanguageType & 0x2,表示是否支持英文 ("byLanguageType", c_byte), ("byVoiceInChanNum", c_byte), # 音频输入通道数 ("byStartVoiceInChanNo", c_byte), # 音频输入起始通道号,0表示无效 ("byRes3", c_byte * 2), # 保留,置为0 ("byMirrorChanNum", c_byte), # 镜像通道个数,录播主机中用于表示导播通道 ("wStartMirrorChanNo", c_uint16), # 起始镜像通道号 ("byRes2", c_byte * 2)] # 保留,置为0 class NET_DVR_DEVICEINFO_V40(Structure): _fields_ = [ ("struDeviceV30", NET_DVR_DEVICEINFO_V30), # 设备参数 ("bySupportLock", c_byte), # 设备是否支持锁定功能,bySuportLock 为1时,dwSurplusLockTime和byRetryLoginTime有效 ("byRetryLoginTime", c_byte), # 剩余可尝试登陆的次数,用户名,密码错误时,此参数有效 # 密码安全等级: 0-无效,1-默认密码,2-有效密码,3-风险较高的密码, # 当管理员用户的密码为出厂默认密码(12345)或者风险较高的密码时,建议上层客户端提示用户名更改密码 ("byPasswordLevel", c_byte), ("byProxyType", c_byte), # 代理服务器类型,0-不使用代理,1-使用标准代理,2-使用EHome代理 # 剩余时间,单位:秒,用户锁定时次参数有效。在锁定期间,用户尝试登陆,不算用户名密码输入对错 # 设备锁定剩余时间重新恢复到30分钟 ("dwSurplusLockTime", c_ulong), # 字符编码类型(SDK所有接口返回的字符串编码类型,透传接口除外): # 0 - 无字符编码信息(老设备) # 1 - GB2312 ("byCharEncodeType", c_byte), # 支持v50版本的设备参数获取,设备名称和设备类型名称长度扩展为64字节 ("bySupportDev5", c_byte), # 登录模式(不同的模式具体含义详见"Remarks"说明:0- SDK私有协议,1- ISAPI协议) ("byLoginMode", c_byte), # 保留,置为0 ("byRes2", c_byte * 253), ] class NET_DVR_Login_V40(Structure): _fields_ = [ ("pLoginInfo", NET_DVR_USER_LOGIN_INFO), ("lpDeviceInfo", NET_DVR_DEVICEINFO_V40) ] |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 用户登录指定摄像机设备 def login(self, address="192.168.1.1", port=8000, user="admin", pwd="admin"): # 设置连接时间 set_overtime = self.call_cpp("NET_DVR_SetConnectTime", 5000, 4) # 设置超时 if set_overtime: logging.info(address + ", 设置超时时间成功") else: error_info = self.call_cpp("NET_DVR_GetLastError") logging.error(address + ", 设置超时错误信息:" + str(error_info)) return False # 设置重连 self.call_cpp("NET_DVR_SetReconnect", 10000, True) b_address = bytes(address, "ascii") b_user = bytes(user, "ascii") b_pwd = bytes(pwd, "ascii") struLoginInfo = login.NET_DVR_USER_LOGIN_INFO() struLoginInfo.bUseAsynLogin = 0 # 同步登陆 i = 0 for o in b_address: struLoginInfo.sDeviceAddress[i] = o i += 1 struLoginInfo.wPort = port i = 0 for o in b_user: struLoginInfo.sUserName[i] = o i += 1 i = 0 for o in b_pwd: struLoginInfo.sPassword[i] = o i += 1 device_info = login.NET_DVR_DEVICEINFO_V40() loginInfo1 = byref(struLoginInfo) loginInfo2 = byref(device_info) user_id = self.call_cpp("NET_DVR_Login_V40", loginInfo1, loginInfo2) logging.info(address + ", 登录结果:" + str(user_id)) if user_id == -1: # -1表示失败,其他值表示返回的用户ID值。 error_info = self.call_cpp("NET_DVR_GetLastError") logging.error(address + ", 登录错误信息:" + str(error_info)) return user_id |
- 调用网络摄像机获得视频数据流
这里会有callback的概念,这里是针对视频流的回调,得到视频流后可以自定义视频流的处理,比如直接对接openCV等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | # 定义callback @CFUNCTYPE(None, c_long, c_ulong, c_byte, c_ulong, c_ulong) def g_real_data_call_back(lRealPlayHandle: c_long, dwDataType: c_ulong, pBuffer: c_byte, dwBufSize: c_ulong, dwUser: c_ulong): print('callback pBufSize is ', lRealPlayHandle, dwBufSize) # 预览参数结构体 class NET_DVR_PREVIEWINFO(Structure): _fields_ = [ # 通道号,目前设备模拟通道号从1开始,数字通道的起始通道号通过 # NET_DVR_GetDVRConfig(配置命令NET_DVR_GET_IPPARACFG_V40)获取(dwStartDChan) ('lChannel', c_long), # 码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推 ('dwStreamType', c_ulong), # 连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输) ('dwLinkMode', c_ulong), # 播放窗口的句柄,为NULL表示不解码显示 ('hPlayWnd', c_void_p), # 0-非阻塞取流,1- 阻塞取流 # 若设为不阻塞,表示发起与设备的连接就认为连接成功,如果发生码流接收失败、播放失败等 # 情况以预览异常的方式通知上层。在循环播放的时候可以减短停顿的时间,与NET_DVR_RealPlay # 处理一致。 # 若设为阻塞,表示直到播放操作完成才返回成功与否,网络异常时SDK内部connect失败将会有5s # 的超时才能够返回,不适合于轮询取流操作。 ('bBlocked', c_bool), # 是否启用录像回传: 0-不启用录像回传,1-启用录像回传。ANR断网补录功能, # 客户端和设备之间网络异常恢复之后自动将前端数据同步过来,需要设备支持。 ('bPassbackRecord', c_bool), # 延迟预览模式:0-正常预览,1-延迟预览 ('byPreviewMode', c_byte), # 流ID,为字母、数字和"_"的组合,IChannel为0xffffffff时启用此参数 ('byStreamID', c_byte * 32), # 应用层取流协议:0-私有协议,1-RTSP协议。 # 主子码流支持的取流协议通过登录返回结构参数NET_DVR_DEVICEINFO_V30的byMainProto、bySubProto值得知。 # 设备同时支持私协议和RTSP协议时,该参数才有效,默认使用私有协议,可选RTSP协议。 ('byProtoType', c_byte), # 保留,置为0 ('byRes1', c_byte), # 码流数据编解码类型:0-通用编码数据,1-热成像探测器产生的原始数据 # (温度数据的加密信息,通过去加密运算,将原始数据算出真实的温度值) ('byVideoCodingType', c_byte), # 播放库播放缓冲区最大缓冲帧数,取值范围:1、6(默认,自适应播放模式) 15:置0时默认为1 ('dwDisplayBufNum', c_ulong), # 保留,置为0 ('byRes', c_byte * 216), ] |
视频流回调可以在NET_DVR_RealPlay_V40中直接得到,也可以用以下返回的lRealPlayHandle去调用callback_real_data()获得,回调所得的数据可以在回调函数里面操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | def start_preview(self, cbFunc: hikFunc, userId=0): req = preview.NET_DVR_PREVIEWINFO() req.hPlayWnd = None req.lChannel = 1 # 预览通道号 req.dwStreamType = 0 # 码流类型:0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推 req.dwLinkMode = 0 # 连接方式:0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RTP/HTTP,6-HRUDP(可靠传输) req.bBlocked = 1 # 0-非阻塞 1-阻塞 struPlayInfo = byref(req) # 这个回调函数不适合长时间占用 # fRealDataCallBack_V30 = preview.REALDATACALLBACK lRealPlayHandle = self.call_cpp("NET_DVR_RealPlay_V40", userId, struPlayInfo, cbFunc, None) print("start_preview lrealPlayHandle is ", lRealPlayHandle) if lRealPlayHandle < 0: self.logout(userId) self.sdk_clean() return lRealPlayHandle def stop_preview(self, lRealPlayHandle): self.call_cpp("NET_DVR_StopRealPlay", lRealPlayHandle) def callback_real_data(self, lRealPlayHandle: c_long, cbFunc: g_real_data_call_back, dwUser: c_ulong): return self.call_cpp("NET_DVR_SetRealDataCallBack", lRealPlayHandle, cbFunc, dwUser) |
参考资料
- https://www.hikvision.com/cn/download_61.html
- https://open.hikvision.com/docs/e3fc37bb504f98c2dfb2adec5a74cfef
- https://docs.python.org/3.6/library/ctypes.html
- https://coolview.github.io/2018/09/26/Python%20%E8%B0%83%E7%94%A8%E6%B5%B7%E5%BA%B7%20SDK/