1. 程式人生 > >基於大華SDK 實現大華NVR 取流顯示和檔案回放功能的實現

基於大華SDK 實現大華NVR 取流顯示和檔案回放功能的實現

最近在做關於大華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:部分程式碼借鑑了某位博主的程式碼,但現在已經忘記了是哪位博主了,若該博主能看到這篇博文,請與我聯絡,我新增上相關引用說明,非常感謝該博主的程式碼給了我很大幫助。