主要功能:
海康SDK開發,通過連接NVR,實現連接NVR的2個相機同時採集(多線程),並進行opencv圖像格式轉換。
關鍵技術點:
1、回調函數
2、YV12->oepncv圖像格式轉換
3、多線程連接多IPcamera,同時採集
---------------------------分割線-------------------------------------------------------
1、回調函數
定義:回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作爲參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。
回調函數是繼承自C語言的。
在C++中,應只在與C代碼建立接口或與已有的回調接口打交道時,才使用回調函數。除了上述情況,在C++中應使用虛擬方法或仿函數(functor),而不是回調函數。
理解:即回調函數的目的是解耦,
Callback
下面以一段不完整的C語言代碼來呈現上圖的意思:
#include<stdio.h>
#include<softwareLib.h> // 包含Library Function所在讀得Software library庫的頭文件
int Callback() // Callback Function
{
// TODO
return 0;
}
int main() // Main program
{
// TODO
Library(Callback);
// TODO
return 0;
}
乍一看,回調似乎只是函數間的調用,和普通函數調用沒啥區別,但仔細一看,可以發現兩者之間的一個關鍵的不同:在回調中,主程序把回調函數像參數一樣傳入庫函數。這樣一來,只要我們改變傳進庫函數的參數,就可以實現不同的功能,這樣有沒有覺得很靈活?並且絲毫不需要修改庫函數的實現,這就是解耦。
再仔細看看,主函數和回調函數是在同一層的,而庫函數在另外一層,想一想,如果庫函數對我們不可見,我們修改不了庫函數的實現,也就是說不能通過修改庫函數讓庫函數調用普通函數那樣實現,那我們就只能通過傳入不同的回調函數了,這也就是在日常工作中常見的情況。
更詳細的講解請參考:
https://www.cnblogs.com/jiangzhaowei/p/9129105.html
https://baike.baidu.com/item/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0/7545973?fr=aladdin
實例:
//回調函數實例
void __stdcall show(void(*CallLBackFun)(const string&, InputArray), const string& winname, InputArray m)
{
namedWindow(winname,0);
CallLBackFun(winname, m);
waitKey(1);
}
void CALLBACK DecCBFun(long nPort,char *pBuf,long nSize,FRAME_INFO *pFrameInfo,void *nUser,void *nReserved2)
{
char buff[10];
sprintf_s(buff, "%d", nPort+11);
if (pFrameInfo->nType == T_YV12)
{
// YUV--> Mat格式轉換
Mat g_BGRImage;
g_BGRImage.create(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
Mat YUVImage(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (unsigned char*)pBuf);
cvtColor(YUVImage, g_BGRImage, COLOR_YUV2BGR_YV12);
//把opencv的imshow作爲回調函數使用
//imshow("dst", g_BGRImage);
show(imshow,buff, g_BGRImage);
}
}
2、YV12->oepncv圖像格式轉換
void CALLBACK DecCBFun(long nPort,char *pBuf,long nSize,FRAME_INFO *pFrameInfo,void *nUser,void *nReserved2)
{
if (pFrameInfo->nType == T_YV12)
{
// YUV--> Mat格式轉換
Mat g_BGRImage;
g_BGRImage.create(pFrameInfo->nHeight, pFrameInfo->nWidth, CV_8UC3);
Mat YUVImage(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, (unsigned char*)pBuf);
cvtColor(YUVImage, g_BGRImage, COLOR_YUV2BGR_YV12);
imshow("dst", g_BGRImage);
cvWaitKey(1);
}
}
3、多線程連接多IPcamera,同時採集
C語言多線程介紹:
c語言庫 process.h 中的函數, 用來創建一個線程 :_beginthreadex
unsigned long _beginthreadex(
void *security, // 安全屬性, 爲NULL時表示默認安全性
unsigned stack_size, // 線程的堆棧大小, 一般默認爲0
unsigned(_stdcall *start_address)(void *), // 所要啓動的線程函數
void *argilist, // 線程函數的參數, 是一個void*類型, 傳遞多個參數時用結構體
unsigned initflag, //新線程的初始狀態,0表示立即執行,CREATE_SUSPENDED
表示創建之後掛起
unsigned *threaddr // 用來接收線程ID
);
返回值 : // 成功返回新線程句柄, 失敗返回0
-------------分割線----------------------
#include <stdio.h>
#include <windows.h>
#include <process.h>
unsigned int __stdcall threadDemo(LPVOID) // void *
{
printf("我被執行啦!\n");
return 0;
}
int main()
{
HANDLE handle;
handle = (HANDLE)_beginthreadex(NULL, 0, ThreadDemo, NULL, 0, NULL);
return 0;
}
參考:https://blog.csdn.net/p312011150/article/details/81538247
實例:
#include <process.h>
typedef struct MyStruct
{
LONG lUserId;
LONG channel;
}MyStruct, *LPMyStruct;
----------------------------
unsigned int __stdcall play(void* user)
{
LPMyStruct stru = (LPMyStruct)user;
//啓動預覽並設置回調數據流
LONG lRealPlayHandle;
//HWND hWnd = GetConsoleWindowAPI(); //獲取窗口句柄
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = NULL; //需要SDK解碼時句柄設爲有效值,僅取流不解碼時可設爲空
struPlayInfo.lChannel = stru->channel + 32; //預覽通道號
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- 阻塞取流
lRealPlayHandle = NET_DVR_RealPlay_V40(stru->lUserId, &struPlayInfo, fRealDataCallBack, NULL);
if (lRealPlayHandle < 0)
{
printf("NET_DVR_RealPlay_V40 error\n");
printf("error :%d", NET_DVR_GetLastError());
NET_DVR_Logout(stru->lUserId);
NET_DVR_Cleanup();
return 0;
}
Sleep(10000);
//關閉預覽
NET_DVR_StopRealPlay(lRealPlayHandle);
return 0;
}
-------------------------
//多線程
MyStruct structdata, structdata2;
structdata.lUserId = lUserID;
structdata.channel = 7;
HANDLE handle, handle2;
handle = (HANDLE)_beginthreadex(NULL, 0, play, (void*)(&structdata), 0, NULL);
structdata2.lUserId = lUserID;
structdata2.channel = 3;
handle2 = (HANDLE)_beginthreadex(NULL, 0, play, (void*)(&structdata2), 0, NULL);
Sleep(20000);
-----------------------分割線----------------------------------------
源程序:
不能添加附件,大家去下載鏈接下載吧,資源分1分,沒有積分的留言聯繫我吧