【技术教程】视频融合协议安防监控系统EasyCVR支持大华SDK接入设备录像下载流程

本文主要是介绍【技术教程】视频融合协议安防监控系统EasyCVR支持大华SDK接入设备录像下载流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一篇我们讲了EasyCVR视频协议融合平台已经支持了大华SDK协议设备的接入,并且分享了录像回放流程,目前其他私有协议仍在拓展和开发当中。本文我们再分享一下EasyCVR接入大华SDK协议设备后的录像下载流程。

录像.png

录像下载,即用户通过 SDK 获取存储设备上存有的录像并保存到本地的过程。允许用户对当前所选通道的录像进行下载,并可将视频导出到本地硬盘或者外接设备U盘等。

45.png

录像下载方式包括以下:

按文件下载
即用户传入需要下载的录像文件信息,SDK可将指定的录像文件下载并保存到用户指定的文件中。同时,用户也可以提供一个回调函数的指针,SDK将指定的录像文件的数据通过回调函数回调给用户,由用户自行处理。

  1. 完成SDK初始化流程。
  2. 初始化成功后,调用 CLIENT_LoginEx2 登录设备。
  3. 调用 CLIENT_SetDeviceMode 设置录像查询时的录像码流类型,对应emType为DH_RECORD_STREAM_TYPE,建议设置为“0-主辅码流”,否则在少数设备上会无法获得结果。如果只需要主码流录像,可以在结果中滤除辅码流录像信息。具体请参见“附录 2 枚举定义”中的 EM_USEDEV_MODE 枚举说明。
  4. 可通过以下两种方式查询录像文件:
    调用CLIENT_FindFile获取录像查询句柄,再循环调用CLIENT_FindNextFile 接口,逐次获取下一个录像文件信息,最后调用CLIENT_FindClose 关闭录像查询句柄。
    调用CLIENT_QueryRecordFile一次性获取某时间段内的所有录像文件信息。
  5. 获取到录像文件信息后,调用 CLIENT_DownloadByRecordFileEx 开始录像文件下载,形参 sSavedFileName 和 fDownLoadDataCallBack 中至少有一个需为有效值。
  6. 下载过程中,根据用户需求调用CLIENT_GetDownloadPos查询录像下载进度。
  7. 录像下载完毕后,调用 CLIENT_StopDownload 停止下载。
  8. 业务使用完后,调用 CLIENT_Logout 退出设备。
  9. SDK功能使用完后,调用 CLIENT_Cleanup 释放SDK资源。

44.png

#include <windows.h> 
#include <stdio.h> 
#include <vector> 
#include "dhnetsdk.h" 
#pragma comment(lib , "dhnetsdk.lib") 
static BOOL g_bNetSDKInitFlag = FALSE; 
static LLONG g_lLoginHandle = 0L; 
static LLONG g_lDownloadHandle = 0L; 
static char g_szDevIp[32] = "172.11.1.30"; 
static WORD g_nPort = 37777; // tcp 连接端口,需与期望登录设备页面 tcp 端口配置一致 
static char g_szUserName[64] = "admin"; 
static char g_szPasswd[64] = "admin"; 
static const int g_nMaxRecordFileCount = 5000; 
//************************************************************************* 
******** 
// 常用回调集合声明 
// 设备断线回调函数 
// 不建议在该回调函数中调用 SDK 接口 
// 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK 会调用该函数 
void CALLBACK DisConnectFunc(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, 
DWORD dwUser); 
// 断线重连成功回调函数
// 不建议在该回调函数中调用SDK接口 
// 通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数 
void CALLBACK HaveReConnect(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, 
LDWORD dwUser); 
// 回放/下载进度回调函数 
// 不建议在该回调函数中调用SDK接口 
// dwDownLoadSize: -1 时表示本次回放/下载结束,-2 表示写文件失败,其他值表示有效数据 
// 通过 CLIENT_DownloadByRecordFileEx 设置该回调函数,当 SDK 收到回放/下载数据时, 
54 55 
SDK 会调用该函数 
void CALLBACK DownLoadPosCallBack(LLONG lPlayHandle, DWORD dwTotalSize, DWORD 
dwDownLoadSize, LDWORD dwUser); 
// 回放/下载 数据回调函数 
// 不建议在该回调函数中调用 SDK 接口 
// 回放时:参数返回,0:表示本次回调失败,下次回调会返回相同的数据,1:表示本次回调 
成功,下次回调会返回后续的数据 
// 下载时:不管回调函数返回值为多少都认为回调成功,下次回调会返回后续的数据 
// 通过 CLIENT_DownloadByRecordFileEx 设置该回调函数,当 SDK 收到回放/下载数据时, 
SDK 会调用该函数 
int CALLBACK DataCallBack(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, 
DWORD dwBufSize, LDWORD dwUser); 
//************************************************************************* 
******** 
void InitTest() 
{ 
// 初始化 SDK 
g_bNetSDKInitFlag = CLIENT_Init(DisConnectFunc, 0); 
if (FALSE == g_bNetSDKInitFlag) 
{ 
printf("Initialize client SDK fail; \n"); 
return; 
} 
else 
{ 
printf("Initialize client SDK done; \n"); 
} 
// 获取 SDK 版本信息 
// 此操作为可选操作 
DWORD dwNetSdkVersion = CLIENT_GetSDKVersion(); 
printf("NetSDK version is [%d]\n", dwNetSdkVersion); 
// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK 
内部会自动进行重连操作 
// 此操作为可选操作,但建议用户进行设置 
CLIENT_SetAutoReconnect(&HaveReConnect, 0); 
// 设置登录超时时间和尝试次数 56 
// 此操作为可选操作 
int nWaitTime = 5000; // 登录请求响应超时时间设置为 5s 
int nTryTimes = 3; // 登录时尝试建立链接 3 次 
CLIENT_SetConnectTime(nWaitTime, nTryTimes); 
// 设置更多网络参数,NET_PARAM 的 nWaittime,nConnectTryNum 成员与 
CLIENT_SetConnectTime 接口设置的登录设备超时时间和尝试次数意义相同 
// 此操作为可选操作 
NET_PARAM stuNetParm = {0}; 
stuNetParm.nConnectTime = 3000; // 登录时尝试建立链接的超时时间 
CLIENT_SetNetworkParam(&stuNetParm); 
NET_DEVICEINFO_Ex stDevInfoEx = {0}; 
int nError = 0; 
while(0 == g_lLoginHandle) 
{ 
// 登录设备 
g_lLoginHandle = CLIENT_LoginEx2(g_szDevIp, g_nPort, g_szUserName, 
g_szPasswd, EM_LOGIN_SPEC_CAP_TCP, NULL, &stDevInfoEx, &nError); 
if (0 == g_lLoginHandle) 
{ 
// 根据错误码,可以在 dhnetsdk.h 中找到相应的解释,此处打印的是 16 进制, 
头文件中是十进制,其中的转换需注意 
// 例如: 
// #define NET_NOT_SUPPORTED_EC(23) // 当前 SDK 未支持该功能,对应的 
错误码为 0x80000017, 23 对应的 16 进制为 0x17 
printf("CLIENT_LoginEx2 %s[%d]Failed!Last Error[%x]\n" , g_szDevIp , 
g_nPort , CLIENT_GetLastError()); 
} 
else 
{ 
printf("CLIENT_LoginEx2 %s[%d] Success\n" , g_szDevIp , g_nPort); 
} 
// 用户初次登录设备,可能要初始化一些数据才能正常实现业务功能,所以建议登录后等待一小段时间,具体等待时间因设备而异。 
Sleep(1000); 
printf("\n"); 
} 
}void RunTest() 
{ 
if (FALSE == g_bNetSDKInitFlag) 
{ 
return; 
} 
if (0 == g_lLoginHandle) 
{ 
return; 
} 
// 录像文件查询 
// 设置查询时的录像码流类型 
int nStreamType = 0; // 0-主辅码流,1-主码流,2-辅码流 
CLIENT_SetDeviceMode(g_lLoginHandle, DH_RECORD_STREAM_TYPE, 
&nStreamType); 
// 录像查询有两种实现方式:1,一次取完时间段内的所有录像文件;2,分次取时间段内 
的所有录像文件。 
// 此处通过第二种方案实现,第一种方案的实现可参考 CLIENT_QueryRecordFile 接口 
说明。 
int nChannelID = 0; // 通道号 
NET_TIME stuStartTime = {0}; 
stuStartTime.dwYear = 2015; 
stuStartTime.dwMonth = 9; 
stuStartTime.dwDay = 20; 
NET_TIME stuStopTime = {0}; 
stuStopTime.dwYear = 2015; 
stuStopTime.dwMonth = 9; 
stuStopTime.dwDay = 30; 
int lFindHandle = CLIENT_FindFile(g_lLoginHandle, nChannelID, 0, NULL, 
&stuStartTime, &stuStopTime, FALSE, 5000); 
if (0 == lFindHandle) 
{ 
printf("CLIENT_FindFile Failed!Last 
Error[%x]\n",CLIENT_GetLastError()); 
return; 
} 
// demo 的示例代码,以最大支持 g_nMaxRecordFileCount 录像文件为例。 
std::vector<NET_RECORDFILE_INFO> bufFileInfo(g_nMaxRecordFileCount); 
for (int nFileIndex = 0; nFileIndex < g_nMaxRecordFileCount; ++nFileIndex) 
57 { 
int result = CLIENT_FindNextFile(lFindHandle, 
&bufFileInfo[nFileIndex]); 
if (0 == result)// 录像文件信息数据取完 
{ 
break; 
} 
else if (1 != result)// 参数出错 
{ 
printf("CLIENT_FindNextFile Failed!Last 
Error[%x]\n",CLIENT_GetLastError()); 
break; 
} 
} 
//停止查找 
if(0 != lFindHandle) 
{ 
CLIENT_FindClose(lFindHandle); 
} 
// 将查询过来的第一个文件设置为下载文件 
NET_RECORDFILE_INFO stuNetFileInfo; 
if (nFileIndex > 0) 
{ 
memcpy(&stuNetFileInfo, (void *)&bufFileInfo[0], 
sizeof(stuNetFileInfo)); 
} 
else 
{ 
printf("no record, return\n"); 
return; 
} 
// 录像文件下载 
// 开启录像下载 
// 函数形参 sSavedFileName 和 fDownLoadDataCallBack 至少有一个为有效值 
// 实际应用中,一般根据需求选择直接保存至 sSavedFileName 或回调处理数据两者之 
一 
g_lDownloadHandle = CLIENT_DownloadByRecordFileEx(g_lLoginHandle, 
&stuNetFileInfo, "test.dav", DownLoadPosCallBack, NULL, DataCallBack, NULL); 
if (0 == g_lDownloadHandle) 
{ 
printf("CLIENT_DownloadByRecordFileEx: failed! Error code: %x.\n", 
58 59 
CLIENT_GetLastError()); 
} 
} 
void EndTest() 
{ 
printf("input any key to quit!\n"); 
getchar(); 
// 关闭下载,可在下载结束后调用,也可在下载中调用。 
if (0 != g_lDownloadHandle) 
{ 
if (FALSE == CLIENT_StopDownload(g_lDownloadHandle)) 
{ 
printf("CLIENT_StopDownload Failed, g_lDownloadHandle[%x]!Last 
Error[%x]\n" , g_lDownloadHandle, CLIENT_GetLastError()); 
} 
else 
{ 
g_lDownloadHandle = 0; 
} 
} 
// 退出设备 
if (0 != g_lLoginHandle) 
{ 
if(FALSE == CLIENT_Logout(g_lLoginHandle)) 
{ 
printf("CLIENT_Logout Failed!Last Error[%x]\n", 
CLIENT_GetLastError()); 
} 
else 
{ 
g_lLoginHandle = 0; 
} 
} 
// 清理初始化资源 
if (TRUE == g_bNetSDKInitFlag) 
{ 
CLIENT_Cleanup(); 
g_bNetSDKInitFlag = FALSE; 
} 
return; 
} 60 
int main() 
{ 
InitTest(); 
RunTest(); 
EndTest(); 
return 0; 
} 
//************************************************************************* 
******** 
// 常用回调集合定义 
void CALLBACK DisConnectFunc(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, 
DWORD dwUser) 
{ 
printf("Call DisConnectFunc\n"); 
printf("lLoginID[0x%x]", lLoginID); 
if (NULL != pchDVRIP) 
{ 
printf("pchDVRIP[%s]\n", pchDVRIP); 
} 
printf("nDVRPort[%d]\n", nDVRPort); 
printf("dwUser[%p]\n", dwUser); 
printf("\n"); 
} 
void CALLBACK HaveReConnect(LLONG lLoginID, char *pchDVRIP, LONG nDVRPort, 
LDWORD dwUser) 
{ 
printf("Call HaveReConnect\n"); 
printf("lLoginID[0x%x]", lLoginID); 
if (NULL != pchDVRIP) 
{ 
printf("pchDVRIP[%s]\n", pchDVRIP); 
} 
printf("nDVRPort[%d]\n", nDVRPort); 
printf("dwUser[%p]\n", dwUser); 
printf("\n"); 
} 
void CALLBACK DownLoadPosCallBack(LLONG lPlayHandle, DWORD dwTotalSize, DWORD 
dwDownLoadSize, LDWORD dwUser) 
{ 
// 若多个回放/下载使用相同的进度回调函数,则用户可通过 lPlayHandle 进行一一对 61 
应 
if (lPlayHandle == g_lDownloadHandle) 
{ 
printf("lPlayHandle[%p]\n", lPlayHandle); 
printf("dwTotalSize[%d]\n", dwTotalSize); 
printf("dwDownLoadSize[%d]\n", dwDownLoadSize); 
printf("dwUser[%p]\n", dwUser); 
printf("\n"); 
} 
} 
int CALLBACK DataCallBack(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, 
DWORD dwBufSize, LDWORD dwUser) 
{ 
int nRet = 0; 
printf("call DataCallBack\n"); 
// 若多个回放/下载使用相同的数据回调函数,则用户可通过 lRealHandle 进行一一对 
应 
if(lRealHandle == g_lDownloadHandle) 
{ 
printf("lPlayHandle[%p]\n", lRealHandle); 
printf("dwDataType[%d]\n", dwDataType); 
printf("pBuffer[%p]\n", pBuffer); 
printf("dwBufSize[%d]\n", dwBufSize); 
printf("dwUser[%p]\n", dwUser); 
printf("\n"); 
switch(dwDataType) 
{ 
case 0: 
//Original data 
// 用户在此处保存码流数据,离开回调函数后再进行解码或转发等一系列处理 
nRet = 1; 
break; 
case 1: 
//Standard video data 
break; 
case 2: 
//yuv data 
break; 
case 3: 
//pcm audio data 
break; 
case 4: 
//Original audio data 
break; 
default: 
break; 
} 
} 
return nRet; 
}

 

这篇关于【技术教程】视频融合协议安防监控系统EasyCVR支持大华SDK接入设备录像下载流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/930711

相关文章

定价129元!支持双频 Wi-Fi 5的华为AX1路由器发布

《定价129元!支持双频Wi-Fi5的华为AX1路由器发布》华为上周推出了其最新的入门级Wi-Fi5路由器——华为路由AX1,建议零售价129元,这款路由器配置如何?详细请看下文介... 华为 Wi-Fi 5 路由 AX1 已正式开售,新品支持双频 1200 兆、配有四个千兆网口、提供可视化智能诊断功能,建

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Ubuntu固定虚拟机ip地址的方法教程

《Ubuntu固定虚拟机ip地址的方法教程》本文详细介绍了如何在Ubuntu虚拟机中固定IP地址,包括检查和编辑`/etc/apt/sources.list`文件、更新网络配置文件以及使用Networ... 1、由于虚拟机网络是桥接,所以ip地址会不停地变化,接下来我们就讲述ip如何固定 2、如果apt安

PyCharm 接入 DeepSeek最新完整教程

《PyCharm接入DeepSeek最新完整教程》文章介绍了DeepSeek-V3模型的性能提升以及如何在PyCharm中接入和使用DeepSeek进行代码开发,本文通过图文并茂的形式给大家介绍的... 目录DeepSeek-V3效果演示创建API Key在PyCharm中下载Continue插件配置Con

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

Java下载文件中文文件名乱码的解决方案(文件名包含很多%)

《Java下载文件中文文件名乱码的解决方案(文件名包含很多%)》Java下载文件时,文件名中文乱码问题通常是由于编码不正确导致的,使用`URLEncoder.encode(filepath,UTF-8... 目录Java下载文件中文文件名乱码问题一般情况下,大家都是这样为了解决这个问题最终解决总结Java下

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL