基於大華SDK 實現大華NVR 取流顯示和檔案回放功能的實現
阿新 • • 發佈:2019-02-14
最近在做關於大華nvr 獲取大華攝像頭的資料流和儲存到硬碟中的視訊檔案,發現網上關於大華攝像頭的二次開發的部落格比較少,特分享一下最近完成的關於大華nvr的兩個小功能,希望能夠給大家一些啟發與幫助。
#include <stdio.h>
#include <iostream>
#include <Windows.h>
#include "dhnetsdk.h"
#include "dhplay.h"
#include <cstring>
#include <winCon.h>
#include "opencv2/opencv.hpp"
using namespace std;
#define SWITCH 1
#define PLAYPORT 1
typedef struct VideoData
{
char* data;
int width;
int height;
}TVideoData; //視訊資料結構體
list<VideoData> videolist;//list儲存視訊資料
//解碼函式 將YUV420解碼為IplImage
IplImage* YUV420_To_IplImage_Opencv(char* pYUV420, int width, int height)
{
if (!pYUV420)
{
return NULL;
}
IplImage *yuvimage, *rgbimg, *yimg, *uimg, *vimg, *uuimg, *vvimg;
int nWidth = width;
int nHeight = height;
rgbimg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);
yuvimage = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3 );
yimg = cvCreateImageHeader(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
uimg = cvCreateImageHeader(cvSize(nWidth / 2, nHeight / 2), IPL_DEPTH_8U, 1);
vimg = cvCreateImageHeader(cvSize(nWidth / 2, nHeight / 2), IPL_DEPTH_8U, 1);
uuimg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
vvimg = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 1);
cvSetData(yimg, pYUV420, nWidth);
cvSetData(uimg, pYUV420 + nWidth*nHeight, nWidth / 2);
cvSetData(vimg, pYUV420 + long(nWidth*nHeight*1.25), nWidth / 2);
cvResize(uimg, uuimg, CV_INTER_LINEAR);
cvResize(vimg, vvimg, CV_INTER_LINEAR);
cvMerge(yimg, uuimg, vvimg, NULL, yuvimage);
cvCvtColor(yuvimage, rgbimg, CV_YCrCb2RGB);
cvReleaseImage(&uuimg);
cvReleaseImage(&vvimg);
cvReleaseImageHeader(&yimg);
cvReleaseImageHeader(&uimg);
cvReleaseImageHeader(&vimg);
cvReleaseImage(&yuvimage);
if (!rgbimg)
{
return NULL;
}
CvSize sz;
IplImage *desc;
sz.width = rgbimg->width*0.5;
sz.height = rgbimg->height*0.5;
desc = cvCreateImage(sz, rgbimg->depth, rgbimg->nChannels);
cvResize(rgbimg, desc, CV_INTER_CUBIC);
cvShowImage("test", desc);
cvWaitKey(1);
cvReleaseImage(&desc);
return rgbimg;
}
//裝置斷線回撥函式
void CALLBACK DisConnectFunc(LONG lLoginID, char *pchDVRIP, LONG nDVRPort, DWORD dwUser)
{
printf("裝置斷線.\n");
return;
}
//自動重連回調函式
void CALLBACK AutoReConnectFunc(LONG lLoginID, char *pchDVRIP, LONG nDVRPort, DWORD dwUser)
{
printf("自動重連成功.\n");
return;
}
//實時資料流回撥函式 由於使用的NVR所以使用擴充套件回撥函式
void CALLBACK RealDataCallBackEx(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, LONG lParam, DWORD dwUser)
{
if (dwDataType == 0) //原始視訊流送播放庫
{
PLAY_InputData(PLAYPORT, pBuffer, dwBufSize);
}
}
//解碼回撥函式
void CALLBACK DecCBFun(LONG nPort, char * pBuf, LONG nSize, FRAME_INFO * pFrameInfo, void* pUserData, LONG nReserved2)
{
// pbuf裡的資料是YUV I420格式的資料
if (pFrameInfo->nType == 3) //視訊資料
{
//將回調獲取的YUV420資料放入list資料結構中
//這種方式可以保證所有資料不會丟失
//若做實時顯示,可以進行丟幀處理來降低卡頓。
TVideoData data;
data.data = (char*)malloc(sizeof(char)*nSize);
memcpy(data.data,pBuf,nSize);
data.height = pFrameInfo->nHeight;
data.width = pFrameInfo->nWidth;
videolist.push_back(data);
}
return;
}
//檔案回放下載進度回撥函式
void CALLBACK cbDownLoadPos(LLONG lPlayHandle, DWORD dwTotalSize, DWORD dwDownLoadSize, LDWORD dwUser)
{
//printf("cbDownLoadPos\n");
}
//檔案回放資料回撥函式
int CALLBACK fDownLoadDataCallBack(LLONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, LDWORD dwUser)
{
if (dwDataType == 0) //原始視訊流送播放庫
{
PLAY_InputData(PLAYPORT, pBuffer, dwBufSize);
}
return 1;
}
//資料處理函式
DWORD WINAPI DataDeal(LPVOID lpParameter)
{
while (1)
{
while (videolist.size() == 0);
TVideoData data = videolist.front();
double Time = (double)cvGetTickCount();
YUV420_To_IplImage_Opencv(data.data, data.width, data.height);
Time = (double)cvGetTickCount() - Time;
printf("run time = %gms\n", Time / (cvGetTickFrequency() * 1000));
free(data.data);
videolist.pop_front();
}
return 0;
}
int main(void)
{
//功能一:實時顯示預覽
//功能二:檔案下載回放
PLAY_OpenStream(PLAYPORT, 0, 0, 1024*900);
PLAY_SetDecCallBackEx(PLAYPORT, DecCBFun, NULL);
PLAY_Play(PLAYPORT, NULL);
//以上程式碼為啟用解碼
CLIENT_LogClose();
NET_DEVICEINFO_Ex info_ex = { 0 };
int err = 0;
unsigned long lLogin = 0;
LLONG lSearch = 0;
LLONG lRealPlay = 0;
CLIENT_Init(DisConnectFunc, 0);
CLIENT_SetAutoReconnect(AutoReConnectFunc, 0);
lLogin = CLIENT_LoginEx2("192.168.0.101", 37777, "admin", "kz123456", EM_LOGIN_SPEC_CAP_TCP, NULL, &info_ex, &err);
if (lLogin == 0)
{
printf("login error!\r\n");
}
else
{
printf("login success!\r\n");
#if SWITCH //SWITCH 巨集定義,可通過修改該開關切換實時資料顯示和檔案回撥顯示
//1.實時取流。
lRealPlay = CLIENT_RealPlayEx(lLogin, 2, NULL, DH_RType_Realplay);
//CLIENT_RealPlayEx 第二個引數為NVR 播放通道 此處為單通道顯示可改為多通道預覽
if (lRealPlay != 0)
{
CLIENT_SetRealDataCallBackEx(lRealPlay, RealDataCallBackEx, 0, 0x0000001f);
}
HANDLE hThread1 = CreateThread(NULL, 0, DataDeal, NULL, 0, NULL);
#endif
#if !SWITCH
//2.檔案取流 回放
NET_RECORDFILE_INFO info = { 0 };
LPNET_TIME time_start = (LPNET_TIME)malloc(sizeof(LPNET_TIME));
LPNET_TIME time_end = (LPNET_TIME)malloc(sizeof(LPNET_TIME));
time_start->dwYear = 2017;
time_start->dwMonth = 11;
time_start->dwDay = 10;
time_start->dwHour = 9;
time_start->dwMinute = 10;
time_start->dwSecond = 0;
time_end->dwYear = 2017;
time_end->dwMonth = 11;
time_end->dwDay = 10;
time_end->dwHour = 9;
time_end->dwMinute = 15;
time_end->dwSecond = 0;
//設定回放時間段
lSearch = CLIENT_FindFile(lLogin, 0, 0, NULL, time_start, time_end, FALSE, 1000);
//CLIENT_FindFile 第二個引數為通道號,檔案回放只允許開啟一個通道進行檔案回放 第三個引數為檔案型別
int result = CLIENT_FindNextFile(lSearch, &info);
//查詢到符合引數的檔名 儲存於info結構體中
LONG state = CLIENT_PlayBackByRecordFileEx(lLogin,&info,NULL,cbDownLoadPos,NULL,fDownLoadDataCallBack,NULL);
HANDLE hThread1 = CreateThread(NULL, 0, DataDeal, NULL, 0, NULL);
#endif
}
getchar();
//釋放網路庫
CLIENT_StopRealPlay(lRealPlay);
CLIENT_Logout(lLogin);
CLIENT_Cleanup();
//關閉播放通道,釋放資源
PLAY_Stop(PLAYPORT);
PLAY_CloseStream(PLAYPORT);
return 0;
}
PS:部分程式碼借鑑了某位博主的程式碼,但現在已經忘記了是哪位博主了,若該博主能看到這篇博文,請與我聯絡,我新增上相關引用說明,非常感謝該博主的程式碼給了我很大幫助。