How to reduce the high latency problem of network camera calls such as Hikvision and Dahua (1): Use of python sdk for Hikvision web cameras (opencv reads sdk stream)

Table of contents

1.python sdk use

1. Download Hikvision SDK

 2.opencv reads sdk stream


 Let’s talk about the effect first. I used the real-time stream of AI inference, and the delay dropped from up to 7 seconds to less than 1 second.

If you feel that this delay is still unacceptable, in the next chapter, I will introduce you to a small way to get the best results.

SDK (Software Development Kit) is the abbreviation of Software Development Kit, which is a collection of tools, libraries and documents used to develop specific software or applications. The SDK provides the resources and interfaces required for development to help developers build applications more efficiently.

SDKs typically include the following:

  1. Tools: The SDK provides a series of development tools, such as compilers, debuggers, IDEs (integrated development environments), etc., for writing, debugging, and testing code.
  2. Libraries: Libraries in the SDK are pre-compiled, reusable code modules that contain common functions and algorithms. Developers can directly call these libraries to simplify the development process.
  3. Sample code: The SDK usually comes with some sample code to show how to use the functions and interfaces provided by the SDK to help developers get started quickly and understand the development process.
  4. Documentation: The SDK provides detailed documentation, including API reference, development guides, sample code explanations, etc., to help developers understand the functions and usage of the SDK.
  5. Dependencies: The SDK may need to depend on other software or libraries, such as operating systems, third-party libraries, etc. Developers need to meet these dependencies to use the SDK.

The role of the SDK is to simplify the development process, provide the resources and interfaces required for development, and save developers time and energy. By using the SDK, developers can quickly build feature-rich, efficient applications without having to write all the code and functionality from scratch.

1.python sdk use

In the past, python was often used to read USB cameras because its language style is easy to read and quick to get started. At first, when using rtsp stream to read Hikvision's network camera, the video screen will be delayed and stuck. If the real-time requirements are high (at least a frame rate equivalent to the web preview effect), use rtsp stream to read. It seems undesirable. This article adopts the method of calling HikVision’s SDK in python to read the IP camera. In terms of frame rate, the effect is equivalent to the network preview.

1. Download Hikvision SDK

 

After downloading and decompressing

Enter the following path

Hikvision-HCNetSDKV6.1.9.48_build20230410_win64---Demo example---5- Python development example---1-Preview stream decoding Demo

1. Find the lib path, it should be empty

You need to choose win or linux

2. Return to the main directory, select the library file, copy all the files (actually only part of the library files are required according to the official documentation, but you can package them all in a fool-proof way), and paste them into the win file in the lib folder above

The following are the operating instructions from the official documentation

1. When updating the device network SDK, HCNetSDK.dll, HCCore.dll, HCNetSDKCom folder, libssl-1_1.dll, libcrypto-1_1.dll, hlog.dll, hpr.dll, zlib1 in the SDK development package [library file] .dll, PlayCtrl.dll, SuperRender.dll, AudioRender.dll and other files must be loaded into the program. [HCNetSDKCom folder] (including the functional component dll library file inside) needs to be loaded together with HCNetSDK.dll and HCCore.dll. Place it in the same directory, and the HCNetSDKCom folder name cannot be modified.

2. If the self-developed software cannot properly implement the corresponding functions and the program does not specify the path to the loaded dll library, please try to delete HCNetSDK.dll while the program is running. If it can be deleted, it means that the program may call the dll file in the Windows->System32 directory of the system disk. It is recommended to delete or update the relevant dll files in this directory; if it cannot be deleted, right-click the dll file and select Properties to confirm the SDK library version.

3. If the corresponding function still cannot be implemented after following the above steps, please judge the reason based on the error number returned by NET_DVR_GetLastError.

3. Run test_main.py

Get real-time footage

 2.opencv reads sdk stream

Paste the following code into the same directory as test_main.py

Just run

If you have any questions, please leave a message in the comment area

# coding=utf-8
import os
import platform
from HCNetSDK import *
from PlayCtrl import *
import numpy as np
import time
import cv2
 
class HKCam(object):
    def __init__(self,camIP,username,password,devport=8000):
        # 登录的设备信息
        self.DEV_IP = create_string_buffer(camIP.encode())
        self.DEV_PORT =devport
        self.DEV_USER_NAME = create_string_buffer(username.encode())
        self.DEV_PASSWORD = create_string_buffer(password.encode())
        self.WINDOWS_FLAG = False if platform.system() != "Windows" else True
        self.funcRealDataCallBack_V30 = None
        self.recent_img = None #最新帧
        self.n_stamp = None #帧时间戳
        self.last_stamp = None #上次时间戳
        # 加载库,先加载依赖库                                                                   # 1 根据操作系统,加载对应的dll文件
        if self.WINDOWS_FLAG:            
            os.chdir(r'./lib/win')
            self.Objdll = ctypes.CDLL(r'./HCNetSDK.dll')  # 加载网络库
            self.Playctrldll = ctypes.CDLL(r'./PlayCtrl.dll')  # 加载播放库
        else:
            os.chdir(r'./lib/linux')
            self.Objdll = cdll.LoadLibrary(r'./libhcnetsdk.so')
            self.Playctrldll = cdll.LoadLibrary(r'./libPlayCtrl.so')
        # 设置组件库和SSL库加载路径                                                              # 2 设置组件库和SSL库加载路径
        self.SetSDKInitCfg()
        # 初始化DLL
        self.Objdll.NET_DVR_Init()                                                               # 3 相机初始化
        # 启用SDK写日志
        self.Objdll.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)
        os.chdir(r'../../') # 切换工作路径到../../
        # 登录
        (self.lUserId, self.device_info) = self.LoginDev()                                       # 4 登录相机
        self.Playctrldll.PlayM4_ResetBuffer(self.lUserId,1)#清空指定缓冲区的剩余数据。这个地方传进来的是self.lUserId,为什么呢?
        print(self.lUserId)
        if self.lUserId < 0:#登录失败
            err = self.Objdll.NET_DVR_GetLastError()
            print('Login device fail, error code is: %d' % self.Objdll.NET_DVR_GetLastError())
            # 释放资源
            self.Objdll.NET_DVR_Cleanup()
            exit()
        else:
            print(f'摄像头[{camIP}]登录成功!!')
        self.start_play()                                                                         # 5 开始播放
        time.sleep(1)
 
    def start_play(self,):
        #global funcRealDataCallBack_V30                                                                        
        self.PlayCtrl_Port = c_long(-1)  # 播放句柄
        # 获取一个播放句柄 #wuzh获取未使用的通道号
        if not self.Playctrldll.PlayM4_GetPort(byref(self.PlayCtrl_Port)):
            print(u'获取播放库句柄失败')
        # 定义码流回调函数       
        self.funcRealDataCallBack_V30 = REALDATACALLBACK(self.RealDataCallBack_V30)
        # 开启预览
        self.preview_info = NET_DVR_PREVIEWINFO()
        self.preview_info.hPlayWnd = 0
        self.preview_info.lChannel = 1  # 通道号
        self.preview_info.dwStreamType = 0  # 主码流
        self.preview_info.dwLinkMode = 0  # TCP
        self.preview_info.bBlocked = 1  # 阻塞取流
        # 开始预览并且设置回调函数回调获取实时流数据
        self.lRealPlayHandle = self.Objdll.NET_DVR_RealPlay_V40(self.lUserId, byref(self.preview_info), self.funcRealDataCallBack_V30, None)
        if self.lRealPlayHandle < 0:
            print ('Open preview fail, error code is: %d' %self. Objdll.NET_DVR_GetLastError())
            # 登出设备
            self.Objdll.NET_DVR_Logout(self.lUserId)
            # 释放资源
            self.Objdll.NET_DVR_Cleanup()
            exit()
 
    def SetSDKInitCfg(self,):
        # 设置SDK初始化依赖库路径
        # 设置HCNetSDKCom组件库和SSL库加载路径
        # print(os.getcwd())
        if self.WINDOWS_FLAG:
            strPath = os.getcwd().encode('gbk')
            sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
            sdk_ComPath.sPath = strPath
            self.Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
            self.Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'\libcrypto-1_1-x64.dll'))
            self.Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'\libssl-1_1-x64.dll'))
        else:
            strPath = os.getcwd().encode('utf-8')
            sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()
            sdk_ComPath.sPath = strPath
            self.Objdll.NET_DVR_SetSDKInitCfg(2, byref(sdk_ComPath))
            self.Objdll.NET_DVR_SetSDKInitCfg(3, create_string_buffer(strPath + b'/libcrypto.so.1.1'))
            self.Objdll.NET_DVR_SetSDKInitCfg(4, create_string_buffer(strPath + b'/libssl.so.1.1'))
    def LoginDev(self,):
        # 登录注册设备
        device_info = NET_DVR_DEVICEINFO_V30()
        lUserId = self.Objdll.NET_DVR_Login_V30(self.DEV_IP, self.DEV_PORT, self.DEV_USER_NAME, self.DEV_PASSWORD, byref(device_info))
        return (lUserId, device_info)
    def read(self,):
        while self.n_stamp==self.last_stamp:
            continue
        self.last_stamp=self.n_stamp
        return self.n_stamp,self.recent_img
 
    def DecCBFun(self,nPort, pBuf, nSize, pFrameInfo, nUser, nReserved2):
            if pFrameInfo.contents.nType == 3:
                t0 = time.time()
                # 解码返回视频YUV数据,将YUV数据转成jpg图片保存到本地
                # 如果有耗时处理,需要将解码数据拷贝到回调函数外面的其他线程里面处理,避免阻塞回调导致解码丢帧
                nWidth = pFrameInfo.contents.nWidth
                nHeight = pFrameInfo.contents.nHeight
                #nType = pFrameInfo.contents.nType
                dwFrameNum = pFrameInfo.contents.dwFrameNum
                nStamp = pFrameInfo.contents.nStamp
                #print(nWidth, nHeight, nType, dwFrameNum, nStamp, sFileName)
                YUV = np.frombuffer(pBuf[:nSize],dtype=np.uint8)
                YUV = np.reshape(YUV,[nHeight+nHeight//2,nWidth])
                img_rgb = cv2.cvtColor(YUV,cv2.COLOR_YUV2BGR_YV12)
                self.recent_img,self.n_stamp = img_rgb,nStamp
 
    def RealDataCallBack_V30(self,lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):
        # 码流回调函数
         if dwDataType == NET_DVR_SYSHEAD:
            # 设置流播放模式
            self.Playctrldll.PlayM4_SetStreamOpenMode(self.PlayCtrl_Port, 0)
            # 打开码流,送入40字节系统头数据
            if self.Playctrldll.PlayM4_OpenStream(self.PlayCtrl_Port, pBuffer, dwBufSize, 1024*1024):
                # 设置解码回调,可以返回解码后YUV视频数据
                #global FuncDecCB
                self.FuncDecCB = DECCBFUNWIN(self.DecCBFun)
                self.Playctrldll.PlayM4_SetDecCallBackExMend(self.PlayCtrl_Port, self.FuncDecCB, None, 0, None)
                # 开始解码播放
                if self.Playctrldll.PlayM4_Play(self.PlayCtrl_Port, None):
                    print(u'播放库播放成功')
                else:
                    print(u'播放库播放失败')
            else:
                print(u'播放库打开流失败')
         elif dwDataType == NET_DVR_STREAMDATA:
            self.Playctrldll.PlayM4_InputData(self.PlayCtrl_Port, pBuffer, dwBufSize)
         else:
            print (u'其他数据,长度:', dwBufSize)
            
            
    def release(self):
        self.Objdll.NET_DVR_StopRealPlay(self.lRealPlayHandle)
        if self.PlayCtrl_Port.value > -1:
            self.Playctrldll.PlayM4_Stop(self.PlayCtrl_Port)
            self.Playctrldll.PlayM4_CloseStream( self.PlayCtrl_Port)
            self.Playctrldll.PlayM4_FreePort( self.PlayCtrl_Port)
            PlayCtrl_Port = c_long(-1)
            self.Objdll.NET_DVR_Logout(self.lUserId)
            self.Objdll.NET_DVR_Cleanup()
        print('释放资源结束')
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()
 
if __name__=="__main__":
    camIP ='192.168.1.122'
    #camIP ='192.168.3.157'
    DEV_PORT = 8000
    username ='admin'
    password = 'admin'
    HIK= HKCam(camIP,username,password)
    last_stamp = 0
    while True:
        t0 =time.time()
        n_stamp,img = HIK.read()
        last_stamp=n_stamp
        '''
        TODO
        '''
        kkk = cv2.waitKey(1)
        if kkk ==ord('q'):
            break
    HIK.release()
 

Guess you like

Origin blog.csdn.net/weixin_45303602/article/details/133831876