关于海康威视网络摄像机二次开发问题

最近一个星期一直在研究海康威视的网络摄像机二次开发问题,纠结了很久。今天终于走通了一部分,记下来希望能够帮助到更多人。现在是用的  VS2013 + opencv 进行测试。

首先推荐两篇对我帮助很大的博文:

               http://blog.csdn.net/wanghuiqi2008/article/details/31410509

               这篇很详细的讲解了二次开发的框架,如果充分理解了作者的代码,那基本就差不多了。记得将#include "PlayM4.h"改为#include "plaympeg4.h",海康的SDK更新了

               http://blog.csdn.net/shangtao1989/article/details/50260661 

               这篇博文很重要的就是关于YV12转换到RGB的内容,我建议大家使用作者提供的第二方法去替代第一篇博文中YV12转RGB的方法。另外建议大家事先开辟用来转换的两个图像空间。这样在实时回调过程中能提高实时性。

                我的代码放在文章末尾(非计算机专业学生),希望对大家有用,我在这先给出一些这段时间遇到的问题,以及解决办法,可能有些笨拙,在此抛砖引玉。

                首先是配置的问题首先按照开发文档添加包含目录以及库目录,接着在链接器-输入-附加依赖项中添加HCNetSDK.lib;PlayCtrl.lib;ws2_32.lib;winmm.lib;GdiPlus.lib文件



接着将代码进行编译,这时可能会弹出缺少两个dll文件。注意:如果你是在debug模式下编译的,那就在生成的debug文件夹下添加相应文件。

截取一段开发文档中的原话:

1. 更新设备网络SDK时,SDK开发包【库文件】里的HCNetSDK.dll、HCCore.dll、PlayCtrl.dll、SuperRender.dll、AudioRender.dll、HCNetSDKCom文件夹等文件均要加载到程序里面,

  【HCNetSDKCom文件夹】(包含里面的功能组件dll库文件)需要和HCNetSDK.dll、HCCore.dll一起加载,放在同一个目录下,且HCNetSDKCom文件夹名不能修改。




再次编译调试就能正常运行了。

接着你会发现播放有些卡顿,一般有5S延时,甚至运行一段时间后会打印出很多 PlayM4_InputData failed  接着会报  error happened出错。

这个原因在SDK文档中说: 回调函数中不能执行可能会占用时间较长的接口或操作,不建议调用该SDK(HCNetSDK.dll)本身的接口

个人认为就是说在解码回调函数void CALLBACK DecCBFun(*******)中不能运行耗时代码,不信你把imshow()和waitkey()注释掉就好了。

解决办法有很多:

               1.可以尝试利用多线程在另一个线程中显示。

               2.在realse模式下运行,记得编译后在realse文件夹下添加相关文件。(realse模式运行速度立马提升,可以达到实时性,并且CPU占用也大大减小)

               3.登陆网络摄像机系统(网址就是自己改的IP),配置-视音频中降低分辨率(我的默认的是2560*1440,当我调到1280*720后在debug模式下也能满足实时性)

以上基本就是我遇到的问题,我也只是个小白,所以大家看后不要见笑。


最后给出自己的代码,非计算机专业,希望对大家有用。

#include <iostream>
#include "Windows.h" 
#include "HCNetSDK.h"
#include "plaympeg4.h"
#include <opencv2\opencv.hpp>  
#include "cv.h"  
#include "highgui.h" 


using namespace std;
using namespace cv;

int iPicNum = 0;//Set channel NO.  
LONG nPort = -1;
HWND hWnd = NULL;



//解码回调 视频为YUV数据(YV12),音频为PCM数据  
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
	long lFrameType = pFrameInfo->nType;

	if (lFrameType == T_YV12)
	{
		Mat dst(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);//这里nHeight为720,nWidth为1280,8UC3表示8bit uchar 无符号类型,3通道值
		Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (uchar*)pBuf);
		cvtColor(src, dst, CV_YUV2BGR_YV12);
 
		imshow("IPCamera", dst);
		waitKey(10);

		//此时是YV12格式的视频数据,保存在pBuf中,可以fwrite(pBuf,nSize,1,Videofile);  
		//fwrite(pBuf,nSize,1,fp);  
	}
	/***************
	else if (lFrameType ==T_AUDIO16)
	{
	//此时是音频数据,数据保存在pBuf中,可以fwrite(pBuf,nSize,1,Audiofile);

	}
	else
	{

	}
	*******************/

}

///实时流回调  
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
	DWORD dRet = 0;
	BOOL inData = FALSE;
	switch (dwDataType)
	{
	case NET_DVR_SYSHEAD:    //系统头  
		if (nPort >= 0)
		{
			break; //同一路码流不需要多次调用开流接口
		}
		if (!PlayM4_GetPort(&nPort)) //获取播放库未使用的通道号  
		{
			break;
		}
		if (dwBufSize > 0)
		{
			if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME))  //设置实时流播放模式
			{
				cout << "PlayM4_SetStreamOpenMode failed " << endl;
				break;
			}
			if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 1024))   //查询
			{
				cout << "PlayM4_OpenStream failed " << endl;
				dRet = PlayM4_GetLastError(nPort);
				break;
			}
			//设置解码回调函数 只解码不显示  
			if (!PlayM4_SetDecCallBack(nPort, DecCBFun))                    //查询
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}

			//设置解码回调函数 解码且显示  
			//if (!PlayM4_SetDecCallBackEx(nPort,DecCBFun,NULL,NULL))  
			//{  
			//  dRet=PlayM4_GetLastError(nPort);  
			//  break;  
			//}  

			//打开视频解码  
			if (!PlayM4_Play(nPort, hWnd))
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}

			//打开音频解码, 需要码流是复合流  
			/*if (!PlayM4_PlaySound(nPort))
			{
				dRet = PlayM4_GetLastError(nPort);
				break;
			}*/
		}
		break;

	case NET_DVR_STREAMDATA:   //码流数据  
		inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
			while (!inData)
			{
				Sleep(10);
				inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
				cout << "PlayM4_InputData failed 11111" << endl;
				break;
			}
		break;
	default:
		inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
		while (!inData)
			{
				Sleep(10);
				inData = PlayM4_InputData(nPort, pBuffer, dwBufSize);
				cout << "PlayM4_InputData failed 22222" << endl;
				break;
			}
		break;
	}
}

void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
	char tempbuf[256] = { 0 };
	switch (dwType)
	{
	case EXCEPTION_RECONNECT:    //预览时重连  
		cout << "----------reconnect--------" << endl;
		break;
	default:
		break;
	}
}

void main()
{

	//---------------------------------------  
	// 初始化  
	NET_DVR_Init();
	//设置连接时间与重连时间  
	NET_DVR_SetConnectTime(2000, 1);
	NET_DVR_SetReconnect(10000, true);


	//---------------------------------------  
	// 注册设备  
	LONG lUserID;
	NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 };
	NET_DVR_DEVICEINFO_V40 struDeviceInfo = { 0 };

	strcpy((char *)struLoginInfo.sDeviceAddress, "192.168.x.x"); //设备 IP 地址
	strcpy((char *)struLoginInfo.sUserName, "admin"); //设备登录用户名
	strcpy((char *)struLoginInfo.sPassword, "mima123456"); //设备登录密码
	struLoginInfo.wPort = 8000;
	struLoginInfo.bUseAsynLogin = 0; //同步登录,登录接口返回成功即登录成功

	lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfo);
	if (lUserID < 0)
	{
		cout << "NET_DVR_Login_V40 failed, error code: " << NET_DVR_GetLastError() << endl;
		NET_DVR_Cleanup();
		return;
	}


	int iRet;
	//获取通道 1 的压缩参数
	DWORD dwReturnLen;
	NET_DVR_COMPRESSIONCFG_V30 struParams = { 0 };
	iRet = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_COMPRESSCFG_V30, 1, &struParams, \
		sizeof(NET_DVR_COMPRESSIONCFG_V30), &dwReturnLen);
	if (!iRet)
	{
		printf("NET_DVR_GetDVRConfig NET_DVR_GET_COMPRESSCFG_V30 error.\n");
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return;
	}
	//设置通道 1 的压缩参数
	struParams.struNormHighRecordPara.dwVideoBitrate = 0.5;
	iRet = NET_DVR_SetDVRConfig(lUserID, NET_DVR_SET_COMPRESSCFG_V30, 1, \
		&struParams, sizeof(NET_DVR_COMPRESSIONCFG_V30));
	if (!iRet)
	{
		printf("NET_DVR_GetDVRConfig NET_DVR_SET_COMPRESSCFG_V30 error.\n");
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return;
	}
	//获取通道 1 的压缩参数
	iRet = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_COMPRESSCFG_V30, 1, \
		&struParams, sizeof(NET_DVR_COMPRESSIONCFG_V30), &dwReturnLen);
	if (!iRet)
	{
		printf("NET_DVR_GetDVRConfig NET_DVR_GET_COMPRESSCFG_V30 error.\n");
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return;
	}
	printf("Video Bitrate is %d\n", struParams.struNormHighRecordPara.dwVideoBitrate);



	//---------------------------------------  
	//设置异常消息回调函数  
	NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL);


	NET_DVR_PREVIEWINFO StruPlayInfo = { 0 };
	StruPlayInfo.hPlayWnd = NULL;  //窗口为空,设备SDK不解码只取流  
	StruPlayInfo.lChannel = 1;     //预览通道号
	StruPlayInfo.dwStreamType = 0; //0-主流码,1-子流码,2-流码3,3-流码4,以此类推
	StruPlayInfo.dwLinkMode = 0;   //0-TCP方式,1-UDP方式,2-多播方式,3-RTP方式,4-RTP/RTSP,5-RSTP/HTTP
	StruPlayInfo.bBlocked = 1;     //0-非堵塞取流,1-堵塞取流


	LONG lRealPlayHandle;
	lRealPlayHandle = NET_DVR_RealPlay_V40(lUserID, &StruPlayInfo, fRealDataCallBack, NULL);


	if (lRealPlayHandle<0)
	{
		cout << "NET_DVR_RealPlay_V40 failed! Error number:  " << NET_DVR_GetLastError() << endl;
		return;
	}
    
	cout << "The program is successful !!" << endl;
	Sleep(-1);

	//---------------------------------------  
	//关闭预览  
	if (!NET_DVR_StopRealPlay(lRealPlayHandle))
	{
		cout << "NET_DVR_StopRealPlay error! Error number: " << NET_DVR_GetLastError() << endl;
		NET_DVR_Logout(lUserID);
		NET_DVR_Cleanup();
		return;
	}
	//注销用户  
	NET_DVR_Logout(lUserID);
	NET_DVR_Cleanup();

	return;
}

猜你喜欢

转载自blog.csdn.net/qq_37541097/article/details/72566102