当前位置: 代码网 > it编程>前端脚本>Python > 如何降低海康、大华等网络摄像头调用的高延迟问题(一):海康威视网络摄像头的python sdk使用(opencv读取sdk流)

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

2024年08月01日 Python 我要评论
先说效果,我是用的AI推理的实时流,延迟从高达7秒降到小于1秒如果觉得这个延迟还不能接受,下一章,给大家介绍点上不得台面的小方法。

目录

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文件中

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

3.运行test_main.py

获取实时画面

 2.opencv读取sdk流

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

运行即可

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

# 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()
 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com