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