1. 程式人生 > >關於海康威視網路攝像機二次開發問題

關於海康威視網路攝像機二次開發問題

最近一個星期一直在研究海康威視的網路攝像機二次開發問題,糾結了很久。今天終於走通了一部分,記下來希望能夠幫助到更多人。現在是用的  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;
}