• 如何降低海康、大华等网络摄像头调用的高延迟问题(一):海康威视网络摄像头的python sdk使用(opencv读取sdk流)


    目录

    1.python sdk使用

    1.海康SDK下载

     2.opencv读取sdk流


     先说效果,我是用的AI推理的实时流,延迟从高达7秒降到小于1秒

    如果觉得这个延迟还不能接受,下一章,给大家介绍点上不得台面的小方法

    SDK(Software Development Kit)是软件开发工具包的缩写,它是一组用于开发特定软件或应用程序的工具、库和文档的集合。SDK提供了开发所需的资源和接口,帮助开发者更高效地构建应用程序。

    SDK通常包含以下内容:

    1. 工具:SDK提供了一系列开发工具,如编译器、调试器、IDE(集成开发环境)等,用于编写、调试和测试代码。
    2. 库:SDK中的库是预先编译好的可重用代码模块,包含常见的功能和算法,开发者可以直接调用这些库来简化开发过程。
    3. 示例代码:SDK通常附带一些示例代码,展示如何使用SDK提供的功能和接口,帮助开发者快速上手并理解开发流程。
    4. 文档:SDK提供详细的文档,包括API参考、开发指南、示例代码解释等,帮助开发者了解SDK的功能和使用方法。
    5. 依赖项:SDK可能需要依赖其他软件或库,例如操作系统、第三方库等,开发者需要满足这些依赖关系才能使用SDK。

    SDK的作用是简化开发过程,提供开发所需的资源和接口,节省开发者的时间和精力。通过使用SDK,开发者可以快速构建功能丰富、高效的应用程序,而无需从头开始编写所有的代码和功能。

    1.python sdk使用

    之前常常采用python来读取usb摄像头,因为其语言风格易读且上手快。起先,使用rtsp流来读海康的网络相机,视频画面出现延迟卡顿的现象,如果对于实时性要求较高(起码得和网页预览效果相当的帧率)的话,用rtsp流读取的方式显得不可取,本文采用在python中调用HikVision的SDK读取IP相机的方式实现,帧率的话和网络预览效果相当

    1.海康SDK下载

     

    下载好解压后

    进入以下路径

    海康威视-HCNetSDKV6.1.9.48_build20230410_win64---Demo示例---5- Python开发示例---1-预览取流解码Demo

    1.找到这个lib路径,里面应该是空的

    看需要选择win或者linux

    2.返回主目录,选择库文件,复制全部文件(实际按官方文档只需要部分库文件,不过可以傻瓜式全部打包),粘贴到上面的lib文件夹的win文件中

    下面是官方文档的操作说明

    1. 更新设备网络SDK时,SDK开发包【库文件】里的HCNetSDK.dll、HCCore.dll、HCNetSDKCom文件夹、libssl-1_1.dll、libcrypto-1_1.dll、hlog.dll、hpr.dll、zlib1.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll等文件均要加载到程序里面,【HCNetSDKCom文件夹】(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。

    2. 如果自行开发软件不能正常实现相应功能,而且程序没有指定加载的dll库路径,请在程序运行的情况下尝试删除HCNetSDK.dll。如果可以删除,说明程序可能调用到系统盘Windows->System32目录下的dll文件,建议删除或者更新该目录下的相关dll文件;如果不能删除,dll文件右键选择属性确认SDK库版本。

    3. 如按上述步骤操作后还是不能实现相应功能,请根据NET_DVR_GetLastError返回的错误号判断原因。

    3.运行test_main.py

    获取实时画面

     2.opencv读取sdk流

    将下面代码贴到test_main.py的同级目录下

    运行即可

    有问题的朋友欢迎评论区留言

    1. # coding=utf-8
    2. import os
    3. import platform
    4. from HCNetSDK import *
    5. from PlayCtrl import *
    6. import numpy as np
    7. import time
    8. import cv2
    9. class HKCam(object):
    10. def __init__(self,camIP,username,password,devport=8000):
    11. # 登录的设备信息
    12. self.DEV_IP = create_string_buffer(camIP.encode())
    13. self.DEV_PORT =devport
    14. self.DEV_USER_NAME = create_string_buffer(username.encode())
    15. self.DEV_PASSWORD = create_string_buffer(password.encode())
    16. self.WINDOWS_FLAG = False if platform.system() != "Windows" else True
    17. self.funcRealDataCallBack_V30 = None
    18. self.recent_img = None #最新帧
    19. self.n_stamp = None #帧时间戳
    20. self.last_stamp = None #上次时间戳
    21. # 加载库,先加载依赖库 # 1 根据操作系统,加载对应的dll文件
    22. if self.WINDOWS_FLAG:
    23. os.chdir(r'./lib/win')
    24. self.Objdll = ctypes.CDLL(r'./HCNetSDK.dll') # 加载网络库
    25. self.Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll') # 加载播放库
    26. else:
    27. os.chdir(r'./lib/linux')
    28. self.Objdll = cdll.LoadLibrary(r'./libhcnetsdk.so')
    29. self.Playctrldll = cdll.LoadLibrary(r'./libPlayCtrl.so')
    30. # 设置组件库和SSL库加载路径 # 2 设置组件库和SSL库加载路径
    31. self.SetSDKInitCfg()
    32. # 初始化DLL
    33. self.Objdll.NET_DVR_Init() # 3 相机初始化
    34. # 启用SDK写日志
    35. self.Objdll.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)
    36. os.chdir(r'../../') # 切换工作路径到../../
    37. # 登录
    38. (self.lUserId, self.device_info) = self.LoginDev() # 4 登录相机
    39. self.Playctrldll.PlayM4_ResetBuffer(self.lUserId,1)#清空指定缓冲区的剩余数据。这个地方传进来的是self.lUserId,为什么呢?
    40. print(self.lUserId)
    41. if self.lUserId < 0:#登录失败
    42. err = self.Objdll.NET_DVR_GetLastError()
    43. print('Login device fail, error code is: %d' % self.Objdll.NET_DVR_GetLastError())
    44. # 释放资源
    45. self.Objdll.NET_DVR_Cleanup()
    46. exit()
    47. else:
    48. print(f'摄像头[{camIP}]登录成功!!')
    49. self.start_play() # 5 开始播放
    50. time.sleep(1)
    51. def start_play(self,):
    52. #global funcRealDataCallBack_V30
    53. self.PlayCtrl_Port = c_long(-1) # 播放句柄
    54. # 获取一个播放句柄 #wuzh获取未使用的通道号
    55. if not self.Playctrldll.PlayM4_GetPort(byref(self.PlayCtrl_Port)):
    56. print(u'获取播放库句柄失败')
    57. # 定义码流回调函数
    58. self.funcRealDataCallBack_V30 = REALDATACALLBACK(self.RealDataCallBack_V30)
    59. # 开启预览
    60. self.preview_info = NET_DVR_PREVIEWINFO()
    61. self.preview_info.hPlayWnd = 0
    62. self.preview_info.lChannel = 1 # 通道号
    63. self.preview_info.dwStreamType = 0 # 主码流
    64. self.preview_info.dwLinkMode = 0 # TCP
    65. self.preview_info.bBlocked = 1 # 阻塞取流
    66. # 开始预览并且设置回调函数回调获取实时流数据
    67. self.lRealPlayHandle = self.Objdll.NET_DVR_RealPlay_V40(self.lUserId, byref(self.preview_info), self.funcRealDataCallBack_V30, None)
    68. if self.lRealPlayHandle < 0:
    69. print ('Open preview fail, error code is: %d' %self. Objdll.NET_DVR_GetLastError())
    70. # 登出设备
    71. self.Objdll.NET_DVR_Logout(self.lUserId)
    72. # 释放资源
    73. self.Objdll.NET_DVR_Cleanup()
    74. exit()
    75. def SetSDKInitCfg(self,):
    76. # 设置SDK初始化依赖库路径
    77. # 设置HCNetSDKCom组件库和SSL库加载路径
    78. # print(os.getcwd())
    79. if self.WINDOWS_FLAG:
    80. strPath = os.getcwd().encode('gbk')
    81. sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
    82. sdk_ComPath.sPath = strPath
    83. self.Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
    84. self.Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'\libcrypto-1_1-x64.dll'))
    85. self.Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'\libssl-1_1-x64.dll'))
    86. else:
    87. strPath = os.getcwd().encode('utf-8')
    88. sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
    89. sdk_ComPath.sPath = strPath
    90. self.Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
    91. self.Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'/libcrypto.so.1.1'))
    92. self.Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'/libssl.so.1.1'))
    93. def LoginDev(self,):
    94. # 登录注册设备
    95. device_info = NET_DVR_DEVICEINFO_V30()
    96. lUserId = self.Objdll.NET_DVR_Login_V30(self.DEV_IP, self.DEV_PORT, self.DEV_USER_NAME, self.DEV_PASSWORD, byref(device_info))
    97. return (lUserId, device_info)
    98. def read(self,):
    99. while self.n_stamp==self.last_stamp:
    100. continue
    101. self.last_stamp=self.n_stamp
    102. return self.n_stamp,self.recent_img
    103. def DecCBFun(self,nPort, pBuf, nSize, pFrameInfo, nUser, nReserved2):
    104. if pFrameInfo.contents.nType == 3:
    105. t0 = time.time()
    106. # 解码返回视频YUV数据,将YUV数据转成jpg图片保存到本地
    107. # 如果有耗时处理,需要将解码数据拷贝到回调函数外面的其他线程里面处理,避免阻塞回调导致解码丢帧
    108. nWidth = pFrameInfo.contents.nWidth
    109. nHeight = pFrameInfo.contents.nHeight
    110. #nType = pFrameInfo.contents.nType
    111. dwFrameNum = pFrameInfo.contents.dwFrameNum
    112. nStamp = pFrameInfo.contents.nStamp
    113. #print(nWidth, nHeight, nType, dwFrameNum, nStamp, sFileName)
    114. YUV = np.frombuffer(pBuf[:nSize],dtype=np.uint8)
    115. YUV = np.reshape(YUV,[nHeight+nHeight//2,nWidth])
    116. img_rgb = cv2.cvtColor(YUV,cv2.COLOR_YUV2BGR_YV12)
    117. self.recent_img,self.n_stamp = img_rgb,nStamp
    118. def RealDataCallBack_V30(self,lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):
    119. # 码流回调函数
    120. if dwDataType == NET_DVR_SYSHEAD:
    121. # 设置流播放模式
    122. self.Playctrldll.PlayM4_SetStreamOpenMode(self.PlayCtrl_Port, 0)
    123. # 打开码流,送入40字节系统头数据
    124. if self.Playctrldll.PlayM4_OpenStream(self.PlayCtrl_Port, pBuffer, dwBufSize, 1024*1024):
    125. # 设置解码回调,可以返回解码后YUV视频数据
    126. #global FuncDecCB
    127. self.FuncDecCB = DECCBFUNWIN(self.DecCBFun)
    128. self.Playctrldll.PlayM4_SetDecCallBackExMend(self.PlayCtrl_Port, self.FuncDecCB, None, 0, None)
    129. # 开始解码播放
    130. if self.Playctrldll.PlayM4_Play(self.PlayCtrl_Port, None):
    131. print(u'播放库播放成功')
    132. else:
    133. print(u'播放库播放失败')
    134. else:
    135. print(u'播放库打开流失败')
    136. elif dwDataType == NET_DVR_STREAMDATA:
    137. self.Playctrldll.PlayM4_InputData(self.PlayCtrl_Port, pBuffer, dwBufSize)
    138. else:
    139. print (u'其他数据,长度:', dwBufSize)
    140. def release(self):
    141. self.Objdll.NET_DVR_StopRealPlay(self.lRealPlayHandle)
    142. if self.PlayCtrl_Port.value > -1:
    143. self.Playctrldll.PlayM4_Stop(self.PlayCtrl_Port)
    144. self.Playctrldll.PlayM4_CloseStream( self.PlayCtrl_Port)
    145. self.Playctrldll.PlayM4_FreePort( self.PlayCtrl_Port)
    146. PlayCtrl_Port = c_long(-1)
    147. self.Objdll.NET_DVR_Logout(self.lUserId)
    148. self.Objdll.NET_DVR_Cleanup()
    149. print('释放资源结束')
    150. def __enter__(self):
    151. return self
    152. def __exit__(self, exc_type, exc_val, exc_tb):
    153. self.release()
    154. if __name__=="__main__":
    155. camIP ='192.168.1.122'
    156. #camIP ='192.168.3.157'
    157. DEV_PORT = 8000
    158. username ='admin'
    159. password = 'admin'
    160. HIK= HKCam(camIP,username,password)
    161. last_stamp = 0
    162. while True:
    163. t0 =time.time()
    164. n_stamp,img = HIK.read()
    165. last_stamp=n_stamp
    166. '''
    167. TODO
    168. '''
    169. kkk = cv2.waitKey(1)
    170. if kkk ==ord('q'):
    171. break
    172. HIK.release()

  • 相关阅读:
    前端KOA搭建服务器——part1
    Mybatis——动态SQL及缓存
    【ESP8266点焊机】基于 ESP8266 for Arduino
    1Panel开源面板项目GitHub Star数量突破20,000!
    Redis持久化
    字节(byte)和位(bit)
    apollo自动驾驶进阶学习之:在apollo中模拟障碍物的三种方法
    奈奎斯特定理、香农定理
    (Ljava/lang/String;)Ljava/lang/Integer; @65: areturn
    UE5 Lyra中的UI层级与资产管理
  • 原文地址:https://blog.csdn.net/weixin_45303602/article/details/133831876