C++驱动海康威视摄像头

1、说明

最近作的一个项目须要调用一个海康威视的摄像头,获取每一帧的数据。刚拿到手的时候有些不知所措,花了两三个小时看了下官方文档,下面分享一下代码ios

2、运行环境

opencv+摄像头依赖bash

摄像头依赖可在海康威视官网找到,内容以下:函数

#头文件
DataType.h
DecodeCardSdk.h
HCNetSDK.h
plaympeg4.h

#动态/静态连接库
AnalyzeData.dll
AudioIntercom.dll
AudioRender.dll
D3DCompiler_43.dll
d3dx9_43.dll
EagleEyeRender.dll
GdiPlus.dll
GdiPlus.lib
HCAlarm.dll
HCAlarm.lib
HCCore.dll
HCCore.lib
HCCoreDevCfg.dll
HCDisplay.dll
HCGeneralCfgMgr.dll
HCGeneralCfgMgr.lib
HCIndustry.dll
HCNetSDK.dll
HCNetSDK.lib
HCPlayBack.dll
HCPreview.dll
HCPreview.lib
HCVoiceTalk.dll
HWDecode.dll
libiconv2.dll
libmmd.dll
MP_Render.dll
OpenAL32.dll
PlayCtrl.dll
PlayCtrl.lib
StreamTransClient.dll
SuperRender.dll
SystemTransform.dll
YUVProcess.dll

3、代码(提供两种方案)

方案一:经过“抓拍”接口抓取每一帧(实际基本不能实时,大概最快一秒一帧)测试

#define _CRT_SECURE_NO_WARNINGS
#include <cstdlib>
#include <cstring>
#include <direct.h>  
#include <io.h>
#include <iostream>
#include <time.h>
#include <fstream>
#include <cstring>
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
using namespace std;

int main_old()
{
	// 初始化设备
	NET_DVR_Init();

	//设置链接时间与重连时间
	NET_DVR_SetConnectTime(2000, 3);                                                            //等待时间,重连次数
	NET_DVR_SetReconnect(10000, true);                                                          //重连间隔,是否重连

	//注册设备
	NET_DVR_DEVICEINFO_V30 struDeviceInfo;                                                      //定义设备参数的结构体,NET_DVR_Login_V30中初始化
	char ip[] = "192.168.0.64";
	int port = 8000;
	char user[] = "account";
	char password[] = "password";
	LONG lUserID = NET_DVR_Login_V30(ip, port,user,password, &struDeviceInfo);   //注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号

	//注册失败
	if (lUserID < 0)
	{
		printf("Login error, %d\n", NET_DVR_GetLastError());
		NET_DVR_Cleanup();
		return 0;
	}

	NET_DVR_JPEGPARA JpegPara;
	JpegPara.wPicSize = 19;			//1920*1080
	JpegPara.wPicQuality = 0;		//最好质量

	const DWORD  maxBuffer = 1000000;
	char *jpeg = (char*)calloc(sizeof(char), maxBuffer);
	DWORD size = 0;

	//计算时间
	for (int i = 0; i < 100; i++)
	{
		clock_t start = clock();
		if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
		{
			std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
			continue;
		}
		clock_t end = clock();

		std::cout << "帧花费时间:" << ((double)(end - start)) / CLOCKS_PER_SEC << "s" << endl;
	}


	//图片写入磁盘
	if (_access("Pictures", 0) == -1)
	{
		int i = _mkdir("Pictures");
	}

	for (int i = 0; i < 1000; i++)
	{
		if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
		{
			std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
			continue;
		}

		std::cout <<"size of "<<i<<".jpeg: "<< size <<"Byte" << std::endl;

		ofstream outFile;
		char fileName[100];
		sprintf(fileName, "Pictures\\%d.jpeg", i);

		outFile.open(fileName, ios::binary | ios::out);
		if (!outFile.is_open())
		{
			std::cout << "fail to create/open Pictures\\" << i << ".jpeg" << std::endl;
			continue;
		}
		outFile.write(jpeg, size);
		outFile.close();

		std::cout << std::endl;
	}

	free(jpeg);
	//注销用户
	NET_DVR_Logout(lUserID);
	//释放SDK资源
	NET_DVR_Cleanup();
	system("pause");
	return 0;
}

方案二:经过实时回调获取每一帧(基本能作到实时)spa

#include <cstdlib>
#include <cstring>
#include <direct.h>  
#include <stdlib.h>
#include <io.h>
#include <iostream>
#include "Windows.h"
#include "HCNetSDK.h"
#include "plaympeg4.h"
#include <opencv2\opencv.hpp>
#include <time.h>

using namespace std;
using namespace cv;

//回调函数声明
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2);	// 二级回调
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser);		// 数据触发。(V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser);								// 异常时触发
//void CALLBACK CBFun(long nPort, char *pBuf, long nSize, long nWidth, long nHeight, long nStamp, long nType, long nReceved);

clock_t startTime, endTime;

int main()
{
	//初始化摄像头
	NET_DVR_Init();
	NET_DVR_SetConnectTime(2000, 1);														//链接等待2s,重连1次
	NET_DVR_SetReconnect(10000, true);														//10秒重连,启动重连

	//图片写入磁盘,测试使用
	if (_access("Pictures_in_T_YV12", 0) == -1)
	{
		int i = _mkdir("Pictures_in_T_YV12");
	}

	//定义登陆相关参数
	char ip[] = "192.168.0.64";
	int port = 8000;
        char user[] = "account";
	char password[] = "password";
	NET_DVR_DEVICEINFO_V30 struDeviceInfo;													//存储设备相关参数

	//启动登陆
	LONG userID = NET_DVR_Login_V30(ip, port, user, password, &struDeviceInfo);				//注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号
	//登陆结果判断
	if (userID < 0)
	{
		printf("登陆摄像头出错, 错误代码:%d\n", NET_DVR_GetLastError());
		NET_DVR_Cleanup();
		system("pause");
		return 0;
	}

	NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);					//设置异常回调

	//定义预览参数
	NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
	struPlayInfo.hPlayWnd = NULL;															//不使用预览界面
	struPlayInfo.lChannel = 1;																//预览通道号 = 1
	struPlayInfo.dwStreamType = 0;															//0:主码流
	struPlayInfo.dwLinkMode = 0;															//0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP

	//设置实时回调
	LONG handle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, fRealDataCallBack, NULL);		//启动实时预览
	//预览判断
	if (handle < 0)
	{
		printf("实时预览调用出错,错误代码:%d\n", NET_DVR_GetLastError());
		NET_DVR_Logout(userID);
		NET_DVR_Cleanup();
		system("pause");
		return 0;
	}

	//waitKey();
	Sleep(-1);

	//相关释放
	NET_DVR_StopRealPlay(handle);			//关闭预览
	NET_DVR_Logout(userID);					//注销用户
	NET_DVR_Cleanup();						//释放SDK资源

	system("pause");
	return 0;
}

//实时数据触发调用  (V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
	static LONG nPort = -1;														//实时预览通道号

	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD: //头部数据
		if (!PlayM4_GetPort(&nPort))											//申请播放port,存为全局变量,供后面使用
		{
			break;
		}

		if (dwBufSize > 0)														//数据长度>0
		{
			if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME))				//设置流处理模式  STREAME_REALTIME:尽力实时,不阻塞   STREAME_FILE:按照时间戳
			{
				break;
			}

			if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 100000))	//打开流接口,缓冲区设置为最大
			{
				break;
			}

			if (!PlayM4_Play(nPort, NULL))										//开始处理,不设置窗口句柄
			{
				break;
			}

			if (!PlayM4_SetDecCallBack(nPort, DecCBFun))						//设置自定义的解码回调函数 DecCBFun
			{
				break;
			}
		}
		break;

	case NET_DVR_STREAMDATA: //流数据
		if (dwBufSize > 0 && nPort != -1)
		{
			if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
			{
				cout << "error" << PlayM4_GetLastError(nPort) << endl;
				break;
			}
		}
		break;

	default:				//其余数据
		if (dwBufSize > 0 && nPort != -1)
		{
			if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
			{
				break;
			}
		}
		break;
	}
}

//实时解码回调
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
	static int number = 0;

	if (pFrameInfo->nType == T_YV12)	//YV12:视频格式  PCM:音频  
	{
		Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
		cvtColor(src, src, CV_YUV2BGR_YV12);

		char name[100];
		sprintf_s(name, "Pictures_in_T_YV12\\%d.jpg", number);	//保存在磁盘为jpg
		imwrite(name, src);
		number++;
	}
}

//异常触发
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	switch (dwType)
	{
	case EXCEPTION_RECONNECT:										//预览时触发"重连"信号
		printf("设备从新链接,当前时间为:%d\n", (int)time(NULL));
		break;
	default:														//默认不做处理
		break;
	}
}