基于大华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:部分代码借鉴了某位博主的代码,但现在已经忘记了是哪位博主了,若该博主能看到这篇博文,请与我联系,我添加上相关引用说明,非常感谢该博主的代码给了我很大帮助。

此处附上代码工程,工程环境为VS2013
github:https://github.com/alonegiveup/NVRForDaHua.git

你可能感兴趣的:(C++)