当前位置: 首页 > news >正文

海康威视监控相机实时性研究

文章目录

  • 一. 海康威视IVMS-4200自带软件延迟
  • 二. SDK解码播放延迟
  • 三. 软件解码(opencv)播放延迟
  • 四. 使用python调用c库的dll

一. 海康威视IVMS-4200自带软件延迟

大概300ms左右
在这里插入图片描述

二. SDK解码播放延迟

大概250ms左右
在这里插入图片描述
代码地址: https://download.csdn.net/download/qq_30150579/87578614

三. 软件解码(opencv)播放延迟

大概1000ms
在这里插入图片描述

#include <stdio.h>
#include <iostream>
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
#include <time.h>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;//数据解码回调函数,将YV_12格式的视频数据流转码为可供opencv处理的BGR类型的图片数据。
void CALLBACK DecCBFun(long nPort, char* pBuf, long nSize, FRAME_INFO* pFrameInfo, long nUser, long nReserved2)
{if (pFrameInfo->nType == T_YV12){cv::Mat BGRImage(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);cv::Mat YUVImage(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (unsigned char*)pBuf);cvtColor(YUVImage, BGRImage, cv::COLOR_YUV2BGR_YV12);cv::imshow("1",BGRImage);cv::waitKey(1);}
}// 回调函数,用于处理从海康相机获取的图像数据
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE* pBuffer, DWORD dwBufSize, void* pUser)
{switch (dwDataType){case NET_DVR_SYSHEAD: //获取的数据为系统头static LONG nPort;std::cout << "获取到系统头" << std::endl;std::cout << "1" << std::endl;if (!PlayM4_GetPort(&nPort))  //获取播放库未使用的通道号{std::cout << "获取播放库通道失败" << std::endl;std::cout << "2" << std::endl;break;}else{std::cout << "获取播放库通道成功" << std::endl;std::cout << "3" << std::endl;}if (dwBufSize > 0){if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME))  //设置实时流播放模式{std::cout << u8"设置实时流播放失败" << std::endl;std::cout << "4" << std::endl;break;}else{std::cout << "设置实时流播放成功" << std::endl;std::cout << "5" << std::endl;}if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 1024)) //打开流接口{std::cout << "打开流接口失败" << std::endl;std::cout << "6" << std::endl;break;}else{std::cout << "打开流接口成功" << std::endl;std::cout << "7" << std::endl;}if (!PlayM4_SetDecCallBackExMend(nPort, DecCBFun, NULL, 0, NULL))  //设置播放流的回调函数{std::cout << "设置回调函数失败" << std::endl;std::cout << "8" << std::endl;break;}else{std::cout << "设置回调函数成功" << std::endl;std::cout << "9" << std::endl;}if (!PlayM4_Play(nPort, NULL))  //开始播放{std::cout << "设置播放失败" << std::endl;std::cout << "10" << std::endl;break;}else{std::cout << "设置播放成功" << std::endl;std::cout << "11" << std::endl;}}case NET_DVR_STREAMDATA: //获取的数据为码流数据if (dwBufSize > 0 && nPort != -1){if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))  //将数据送入播放器{break;}}}
}void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{char tempbuf[256] = {0};switch(dwType){case EXCEPTION_RECONNECT:    //预览时重连printf("----------reconnect--------%d\n", time(NULL));break;default:break;}
}int main() {// 初始化海康SDKNET_DVR_Init();//设置连接时间与重连时间NET_DVR_SetConnectTime(2000, 1);NET_DVR_SetReconnect(10000, true);//设置异常消息回调函数NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);// 注册设备LONG lUserID;//登录参数,包括设备地址、登录用户、密码等NET_DVR_USER_LOGIN_INFO struLoginInfo = {0};struLoginInfo.bUseAsynLogin = 0; //同步登录方式strcpy(struLoginInfo.sDeviceAddress, "192.168.1.64"); //设备IP地址struLoginInfo.wPort = 8000; //设备服务端口strcpy(struLoginInfo.sUserName, "admin"); //设备登录用户名strcpy(struLoginInfo.sPassword, "nflg1234"); //设备登录密码//设备信息, 输出参数NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = {0};lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);if (lUserID < 0){printf("Login failed, error code: %d\n", NET_DVR_GetLastError());NET_DVR_Cleanup();return 0;}//启动预览并设置回调数据流LONG lRealPlayHandle;NET_DVR_PREVIEWINFO struPlayInfo = {0};struPlayInfo.hPlayWnd     = NULL;         //需要SDK解码时句柄设为有效值,仅取流不解码时可设为空struPlayInfo.lChannel     = 1;       //预览通道号struPlayInfo.dwStreamType = 0;       //0-主码流,1-子码流,2-码流3,3-码流4,以此类推struPlayInfo.dwLinkMode   = 0;       //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTPstruPlayInfo.bBlocked     = 1;       //0- 非阻塞取流,1- 阻塞取流lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &struPlayInfo, fRealDataCallBack, NULL);if (lRealPlayHandle < 0){printf("NET_DVR_RealPlay_V40 error, %d\n", NET_DVR_GetLastError());NET_DVR_Logout(lUserID);NET_DVR_Cleanup();return 0;}// 主循环:从全局列表中获取图像并显示while (true){}// 释放资源NET_DVR_StopRealPlay(lRealPlayHandle);NET_DVR_Logout(lUserID);NET_DVR_Cleanup();DeleteCriticalSection(&g_cs_frameList);return 0;
}

四. 使用python调用c库的dll

大概300ms
在这里插入图片描述

# coding=utf-8
import timefrom HCNetSDK import *
from PlayCtrl import *class devClass:def __init__(self):self.hikSDK, self.playM4SDK = self.LoadSDK()  # 加载sdk库self.iUserID = -1  # 登录句柄self.lRealPlayHandle = -1  # 预览句柄self.wincv = None  # windows环境下的参数self.win = None  # 预览窗口self.FuncDecCB = None  # 解码回调self.PlayCtrlPort = C_LONG(-1)  # 播放通道号self.basePath = ''  # 基础路径self.preview_file = ''  # linux预览取流保存路径self.funcRealDataCallBack_V30 = REALDATACALLBACK(self.RealDataCallBack_V30)  # 预览回调函数# self.msg_callback_func = MSGCallBack_V31(self.g_fMessageCallBack_Alarm)  # 注册回调函数实现def LoadSDK(self):hikSDK = NoneplayM4SDK = Nonetry:print("netsdkdllpath: ", netsdkdllpath)hikSDK = load_library(netsdkdllpath)playM4SDK = load_library(playM4dllpath)except OSError as e:print('动态库加载失败', e)return hikSDK, playM4SDK# 设置SDK初始化依赖库路径def SetSDKInitCfg(self):# 设置HCNetSDKCom组件库和SSL库加载路径if sys_platform == 'windows':basePath = os.getcwd().encode('gbk')strPath = basePath + b'\lib'sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()sdk_ComPath.sPath = strPathprint('strPath: ', strPath)if self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_SDK_PATH.value,byref(sdk_ComPath)):print('NET_DVR_SetSDKInitCfg: 2 Succ')if self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_LIBEAY_PATH.value,create_string_buffer(strPath + b'\libcrypto-1_1-x64.dll')):print('NET_DVR_SetSDKInitCfg: 3 Succ')if self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_SSLEAY_PATH.value,create_string_buffer(strPath + b'\libssl-1_1-x64.dll')):print('NET_DVR_SetSDKInitCfg: 4 Succ')else:basePath = os.getcwd().encode('utf-8')strPath = basePath + b'\lib'sdk_ComPath = NET_DVR_LOCAL_SDK_PATH()sdk_ComPath.sPath = strPathif self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_SDK_PATH.value,byref(sdk_ComPath)):print('NET_DVR_SetSDKInitCfg: 2 Succ')if self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_LIBEAY_PATH.value,create_string_buffer(strPath + b'/libcrypto.so.1.1')):print('NET_DVR_SetSDKInitCfg: 3 Succ')if self.hikSDK.NET_DVR_SetSDKInitCfg(NET_SDK_INIT_CFG_TYPE.NET_SDK_INIT_CFG_SSLEAY_PATH.value,create_string_buffer(strPath + b'/libssl.so.1.1')):print('NET_DVR_SetSDKInitCfg: 4 Succ')self.basePath = basePath# 通用设置,日志/回调事件类型等def GeneralSetting(self):# 日志的等级(默认为0):0-表示关闭日志,1-表示只输出ERROR错误日志,2-输出ERROR错误信息和DEBUG调试信息,3-输出ERROR错误信息、DEBUG调试信息和INFO普通信息等所有信息# self.hikSDK.NET_DVR_SetLogToFile(3, b'./SdkLog_Python/', False)self.hikSDK.NET_DVR_SetLogToFile(3, bytes('./SdkLog_Python/', encoding="utf-8"), False)# 登录设备def LoginDev(self, ip, username, pwd):# 登录参数,包括设备地址、登录用户、密码等struLoginInfo = NET_DVR_USER_LOGIN_INFO()struLoginInfo.bUseAsynLogin = 0  # 同步登录方式struLoginInfo.sDeviceAddress = ip  # 设备IP地址struLoginInfo.wPort = 8000  # 设备服务端口struLoginInfo.sUserName = username  # 设备登录用户名struLoginInfo.sPassword = pwd  # 设备登录密码struLoginInfo.byLoginMode = 0# 设备信息, 输出参数struDeviceInfoV40 = NET_DVR_DEVICEINFO_V40()self.iUserID = self.hikSDK.NET_DVR_Login_V40(byref(struLoginInfo), byref(struDeviceInfoV40))if self.iUserID < 0:print("Login failed, error code: %d" % self.hikSDK.NET_DVR_GetLastError())self.hikSDK.NET_DVR_Cleanup()else:print('登录成功,设备序列号:%s' % str(struDeviceInfoV40.struDeviceV30.sSerialNumber, encoding="utf8").rstrip('\x00'))# 登出设备def LogoutDev(self):if self.iUserID > -1:# 撤销布防,退出程序时调用self.hikSDK.NET_DVR_Logout(self.iUserID)def DecCBFun(self, nPort, pBuf, nSize, pFrameInfo, nUser, nReserved2):# 解码回调函数if pFrameInfo.contents.nType == 3:# 解码返回视频YUV数据,将YUV数据转成jpg图片保存到本地# 如果有耗时处理,需要将解码数据拷贝到回调函数外面的其他线程里面处理,避免阻塞回调导致解码丢帧sFileName = ('./pic/test_stamp[%d].jpg' % pFrameInfo.contents.nStamp)nWidth = pFrameInfo.contents.nWidthnHeight = pFrameInfo.contents.nHeightnType = pFrameInfo.contents.nTypedwFrameNum = pFrameInfo.contents.dwFrameNumnStamp = pFrameInfo.contents.nStampprint(nWidth, nHeight, nType, dwFrameNum, nStamp, sFileName)lRet = self.playM4SDK.PlayM4_ConvertToJpegFile(pBuf, nSize, nWidth, nHeight, nType,c_char_p(sFileName.encode()))# if lRet == 0:#     print('PlayM4_ConvertToJpegFile fail, error code is:', self.playM4SDK.PlayM4_GetLastError(nPort))# else:#     print('PlayM4_ConvertToJpegFile success')# 将视频流保存到本地def writeFile(self, filePath, pBuffer, dwBufSize):# 使用memmove函数将指针数据读到数组中data_array = (c_byte * dwBufSize)()memmove(data_array, pBuffer, dwBufSize)# 判断文件路径是否存在if not os.path.exists(filePath):# 如果不存在,使用 open() 函数创建一个空文件open(filePath, "w").close()preview_file_output = open(filePath, 'ab')preview_file_output.write(data_array)preview_file_output.close()def RealDataCallBack_V30(self, lPlayHandle, dwDataType, pBuffer, dwBufSize, pUser):# 码流回调函数if sys_platform == 'linux':# 码流回调函数if dwDataType == NET_DVR_SYSHEAD:from datetime import datetime# 获取当前时间的datetime对象current_time = datetime.now()timestamp_str = current_time.strftime('%Y%m%d_%H%M%S')self.preview_file = f'./previewVideo{timestamp_str}.mp4'elif dwDataType == NET_DVR_STREAMDATA:self.writeFile(self.preview_file, pBuffer, dwBufSize)else:print(u'其他数据,长度:', dwBufSize)elif sys_platform == 'windows':if dwDataType == NET_DVR_SYSHEAD:# 设置流播放模式self.playM4SDK.PlayM4_SetStreamOpenMode(self.PlayCtrlPort, 0)# 打开码流,送入40字节系统头数据if self.playM4SDK.PlayM4_OpenStream(self.PlayCtrlPort, pBuffer, dwBufSize, 1024 * 1024):# 设置解码回调,可以返回解码后YUV视频数据self.FuncDecCB = DECCBFUNWIN(self.DecCBFun)self.playM4SDK.PlayM4_SetDecCallBackExMend(self.PlayCtrlPort, self.FuncDecCB, None, 0, None)# 开始解码播放if self.playM4SDK.PlayM4_Play(self.PlayCtrlPort, self.wincv.winfo_id()):print(u'播放库播放成功')else:print(u'播放库播放失败')else:print(f'播放库打开流失败, 错误码:{self.playM4SDK.PlayM4_GetLastError(self.PlayCtrlPort)}')elif dwDataType == NET_DVR_STREAMDATA:self.playM4SDK.PlayM4_InputData(self.PlayCtrlPort, pBuffer, dwBufSize)else:print(u'其他数据,长度:', dwBufSize)def startPlay(self, playTime):# 获取一个播放句柄if not self.playM4SDK.PlayM4_GetPort(byref(self.PlayCtrlPort)):print(f'获取播放库句柄失败, 错误码:{self.playM4SDK.PlayM4_GetLastError(self.PlayCtrlPort)}')if sys_platform == 'linux':# 开始预览preview_info = NET_DVR_PREVIEWINFO()preview_info.hPlayWnd = 0preview_info.lChannel = 1  # 通道号preview_info.dwStreamType = 0  # 主码流preview_info.dwLinkMode = 0  # TCPpreview_info.bBlocked = 1  # 阻塞取流# 开始预览并且设置回调函数回调获取实时流数据self.lRealPlayHandle = self.hikSDK.NET_DVR_RealPlay_V40(self.iUserID, byref(preview_info),self.funcRealDataCallBack_V30,None)if self.lRealPlayHandle < 0:print('Open preview fail, error code is: %d' % self.hikSDK.NET_DVR_GetLastError())# 登出设备self.hikSDK.NET_DVR_Logout(self.iUserID)# 释放资源self.hikSDK.NET_DVR_Cleanup()exit()time.sleep(playTime)elif sys_platform == 'windows':import tkinterfrom tkinter import Button# 创建窗口self.win = tkinter.Tk()# 固定窗口大小self.win.resizable(0, 0)self.win.overrideredirect(True)sw = self.win.winfo_screenwidth()# 得到屏幕宽度sh = self.win.winfo_screenheight()# 得到屏幕高度# 窗口宽高ww = 1920wh = 1080x = (sw - ww) / 2y = (sh - wh) / 2self.win.geometry("%dx%d+%d+%d" % (ww, wh, x, y))# 创建退出按键b = Button(self.win, text='退出', command=self.win.quit)b.pack()# 创建一个Canvas,设置其背景色为白色self.wincv = tkinter.Canvas(self.win, bg='white', width=ww, height=wh)self.wincv.pack()# 开始预览preview_info = NET_DVR_PREVIEWINFO()preview_info.hPlayWnd = 0preview_info.lChannel = 1  # 通道号preview_info.dwStreamType = 0  # 主码流preview_info.dwLinkMode = 0  # TCPpreview_info.bBlocked = 1  # 阻塞取流# 开始预览并且设置回调函数回调获取实时流数据self.lRealPlayHandle = self.hikSDK.NET_DVR_RealPlay_V40(self.iUserID, byref(preview_info),self.funcRealDataCallBack_V30,None)if self.lRealPlayHandle < 0:print('Open preview fail, error code is: %d' % self.hikSDK.NET_DVR_GetLastError())# 登出设备self.hikSDK.NET_DVR_Logout(self.iUserID)# 释放资源self.hikSDK.NET_DVR_Cleanup()exit()# show Windowsself.win.mainloop()def stopPlay(self):# 关闭预览self.hikSDK.NET_DVR_StopRealPlay(self.lRealPlayHandle)# 停止解码,释放播放库资源if self.PlayCtrlPort.value > -1:self.playM4SDK.PlayM4_Stop(self.PlayCtrlPort)self.playM4SDK.PlayM4_CloseStream(self.PlayCtrlPort)self.playM4SDK.PlayM4_FreePort(self.PlayCtrlPort)self.PlayCtrlPort = C_LONG(-1)if __name__ == '__main__':dev = devClass()dev.SetSDKInitCfg()  # 设置SDK初始化依赖库路径dev.hikSDK.NET_DVR_Init()  # 初始化sdkdev.GeneralSetting()  # 通用设置,日志,回调函数等dev.LoginDev(ip=b'192.168.1.64', username=b"admin", pwd=b"nflg1234")  # 登录设备dev.startPlay(playTime=5)  # playTime用于linux环境控制预览时长,windows环境无效dev.stopPlay()dev.LogoutDev()# 释放资源dev.hikSDK.NET_DVR_Cleanup()
http://www.dtcms.com/a/271421.html

相关文章:

  • 深度学习遇到的问题
  • 一[3.7] YOLO系列基础(2)- “Bottleneck模块详解”
  • JavaScript对象的深度拷贝
  • 17.Spring Boot的Bean详解(新手版)
  • 十、Rocky Linux 9.x 在线安装Nginx 1.28.0
  • 豆包编写Java程序小试
  • 电子元器件基础知识总结
  • 基于SpringBoot+Vue的疫情问卷调查与返校信息管理系统】前后端分离
  • 城市地质大数据平台:透视地下空间,赋能智慧未来
  • git断点续传,中断后继续下载
  • 【计算机三级网络】——IP校园网大题(第二道):路由代码填空
  • 如何选择时序数据库:关键因素与实用指南
  • 20250709: WSL+Pycharm 搭建 Python 开发环境
  • 数据结构--堆的实现
  • 【黑马点评】(四)分布式锁
  • 深入理解 Spring AOP 代理机制:JDK 动态代理与 CGLIB 的对比与选择
  • c++学习-类中类成员变量的创建和释放顺序2-资源new出来的对象未被手动delete
  • 19-C#静态方法与静态类
  • HashMap的get与put流程源码深度解析
  • EPS 转 SVG/PNG 转换器集合
  • 托卡马克装置的发电过程解析
  • C++面试冲刺笔记1:虚函数的基本工作原理
  • thinkphp使用redis抢单实例
  • 深入理解MyBatis的MapperBuilderAssistant:如何解析Mapper XML文件?
  • 自然语言处理中probe探测是什么意思。
  • 基于docker进行渗透测试环境的快速搭建(在ubantu中docker设置代理)
  • EF提高性能(查询禁用追踪)(关闭延迟加载)
  • Java+AI精准广告革命:实时推送系统实战指南
  • 人工智能学习81-Yolo预测类
  • JavaEE-初阶-多线程初阶