原標題:視頻融合協議安防監控系統EasyCVR支持大華SDK接入設備錄像下載流程說明

上一篇我們講了EasyCVR視頻協議融合平臺已經支持了大華SDK協議設備的接入,並且分享了錄像回放流程,目前其他私有協議仍在拓展和開發當中。本文我們再分享一下EasyCVR接入大華SDK協議設備後的錄像下載流程。

錄像下載,即用戶通過 SDK 獲取存儲設備上存有的錄像並保存到本地的過程。允許用戶對當前所選通道的錄像進行下載,並可將視頻導出到本地硬盤或者外接設備U盤等。

錄像下載方式包括以下:

按文件下載

即用戶傳入需要下載的錄像文件信息,SDK可將指定的錄像文件下載並保存到用戶指定的文件中。同時,用戶也可以提供一個回調函數的指針,SDK將指定的錄像文件的數據通過回調函數回調給用戶,由用戶自行處理。

  1. 完成SDK初始化流程。
  2. 初始化成功後,調用 CLIENT_LoginEx2 登錄設備。
  3. 調用 CLIENT_SetDeviceMode 設置錄像查詢時的錄像碼流類型,對應emType爲DH_RECORD_STREAM_TYPE,建議設置爲“0-主輔碼流”,否則在少數設備上會無法獲得結果。如果只需要主碼流錄像,可以在結果中濾除輔碼流錄像信息。具體請參見“附錄 2 枚舉定義”中的 EM_USEDEV_MODE 枚舉說明。
  4. 可通過以下兩種方式查詢錄像文件:
  5. 調用CLIENT_FindFile獲取錄像查詢句柄,再循環調用CLIENT_FindNextFile 接口,逐次獲取下一個錄像文件信息,最後調用CLIENT_FindClose 關閉錄像查詢句柄。
  6. 調用CLIENT_QueryRecordFile一次性獲取某時間段內的所有錄像文件信息。
  7. 獲取到錄像文件信息後,調用 CLIENT_DownloadByRecordFileEx 開始錄像文件下載,形參 sSavedFileName 和 fDownLoadDataCallBack 中至少有一個需爲有效值。
  8. 下載過程中,根據用戶需求調用CLIENT_GetDownloadPos查詢錄像下載進度。
  9. 錄像下載完畢後,調用 CLIENT_StopDownload 停止下載。
  10. 業務使用完後,調用 CLIENT_Logout 退出設備。
  11. SDK功能使用完後,調用 CLIENT_Cleanup 釋放SDK資源。

#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;
}

相關文章