當然你可以直接用現(xiàn)成的虛擬攝像頭軟件實現(xiàn)這個功能。不過當初我開發(fā)這個插件的原因是,需要在Flash產(chǎn)品里面共享桌面云稚,如果此時需要引導用戶安裝一個第三方的虛擬攝像頭體驗不好,所以公司希望我自己開發(fā)一個虛擬攝像頭沈堡,一鍵安裝減少用戶的使用門檻静陈。所謂的虛擬攝像頭實際上在windows系統(tǒng)上注冊了一個特殊dll,這個dll是一個COM組件诞丽。
虛擬攝像頭需要用到Direct Show編程鲸拥。
下載Direct Show開發(fā)代碼
里面有如下的文件夾,我只需要用到第一個文件夾里面的代碼—— baseclasses
baseclasses
capture
common
dmo
dvd
filters
misc
players
vmr9
創(chuàng)建工程
打開Visual Studio 僧免,新建一個win32 Dll項目刑赶。
打開屬性頁,在VC++ 目錄一欄中的庫目錄里面添加剛才的baseclasses的路徑懂衩,這樣我們就能在項目中引用這個目錄里的代碼了角撞。
在C/C++屬性頁里面的附加庫目錄里面也把baseclasses的路徑填入。
在dll.cpp中勃痴,我們需要把Filter注冊成COM組件。
分別需要調用AMovieSetupRegisterServer函數(shù)热康、CreateComObject函數(shù)以及IFilterMapper2接口的RegisterFilter函數(shù)完成注冊沛申。
主邏輯
在頭文件中,我們需要聲明兩個類
class CVCamStream;
class CVCam : public CSource
{
public:
//////////////////////////////////////////////////////////////////////////
// IUnknown
//////////////////////////////////////////////////////////////////////////
static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
IFilterGraph *GetGraph() {return m_pGraph;}
static int cx, cy;
static HANDLE SocketThread;
static SOCKET ClientSocket;
private:
CVCam(LPUNKNOWN lpunk, HRESULT *phr);
};
class CVCamStream : public CSourceStream, public IAMDroppedFrames,public IAMStreamConfig, public IKsPropertySet
{
public:
COM組件只需要實現(xiàn)CUnknown接口即可姐军。我們繼承了Direct Show的CSource類铁材,那么就已經(jīng)實現(xiàn)了這個接口尖淘。
CVCamStream類用來實現(xiàn)圖像數(shù)據(jù)的輸出。
在CVCam的構造函數(shù)里面我們創(chuàng)建CVCamStream類的實例
m_paStreams = (CSourceStream **) new CVCamStream*[1];
m_paStreams[0] = new CVCamStream(phr, this, L"Flex COM");
在實現(xiàn)COM接口的QueryInterface函數(shù)中著觉,我們調用了CVCamStream類的QueryInterface
HRESULT CVCam::QueryInterface(REFIID riid, void **ppv)
{
//Forward request for IAMStreamConfig & IKsPropertySet to the pin
if(riid == _uuidof(IAMStreamConfig) ||
riid == _uuidof(IAMDroppedFrames) ||
riid == _uuidof(IKsPropertySet))
return m_paStreams[0]->QueryInterface(riid, ppv);
else
return CSource::QueryInterface(riid, ppv);
}
定義媒體類型
HRESULT CVCamStream::GetMediaType(int iPosition, CMediaType *pmt)
在這個函數(shù)中村生,我們配置了媒體的具體的格式參數(shù),比如24位RGB格式饼丘,圖像的寬高等等趁桃。
另外還需要對GetStreamCaps函數(shù)進行實現(xiàn),配置媒體的格式肄鸽。
HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
捕獲桌面
系統(tǒng)會調用FillBuffer函數(shù)卫病,在這個函數(shù)中,我們將捕獲到的數(shù)據(jù)填充到緩沖里面典徘,Direct Show會處理剩下的事情蟀苛。
HRESULT CVCamStream::FillBuffer(IMediaSample *pms)
捕獲桌面只需要用到一個函數(shù)CopyScreenToBitmap
HANDLE hDib = CopyScreenToBitmap(&ScreenRect, pData, (BITMAPINFO *)&(pVih->bmiHeader), m_hCursor);
if (hDib) DeleteObject(hDib);
pData是我們定義的一個指針,通過下面的代碼逮诲,我們的pData就指向了緩存帜平,數(shù)據(jù)填充到pData指向的內存中。
BYTE *pData;
pms->GetPointer(&pData);
進階
實際產(chǎn)品會有很多需求梅鹦,光實現(xiàn)捕獲桌面是遠遠不夠的裆甩,我們需要對這個捕獲進行控制,比如捕獲制定區(qū)域帘瞭,停止捕獲淑掌,恢復捕獲等等。那么就涉及到和COM進行通訊了蝶念。
我們可以通過VS的窗口設計器創(chuàng)建一個windows窗口抛腕,然后提供一個用戶操作界面。
如何響應這個窗口的用戶操作呢媒殉?
通過windows消息
INT_PTR CALLBACK WindowMessage(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
當然最關鍵是需要在Flash產(chǎn)品的程序里面喚起這個窗口担敌,需要用的socket編程。
SOCKET Listen_Sock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN serverAddr;
ZeroMemory((char *)&serverAddr, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(1234);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(Listen_Sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
listen(Listen_Sock, 5);
C++的socket編程十分的繁瑣廷蓉,其他語言都會進行封裝全封,讓開發(fā)變的十分便利。
在一個線程里面寫個死循環(huán)進行讀取socket的數(shù)據(jù)桃犬,這是比較初級的多線程阻塞式的socket編程刹悴。對于我們這個程序是綽綽有余了。畢竟不是服務器攒暇,不需要面對并發(fā)的問題土匀。