編寫C++法式使DirectShow停止視頻捕獲。本站提示廣大學習愛好者:(編寫C++法式使DirectShow停止視頻捕獲)文章只能為提供參考,不一定能成為您想要的結果。以下是編寫C++法式使DirectShow停止視頻捕獲正文
視頻捕獲Graph的構建
一個可以或許捕獲音頻或許視頻的graph圖都稱之為捕獲graph圖。捕獲graph圖比普通的文件回放graph圖要龐雜很多,dshow供給了一個Capture Graph Builder COM組件使得捕獲graph圖的生成加倍簡略。Capture Graph Builder供給了一個ICaptureGraphBuilder2接口,這個接口供給了一些辦法用來構建和掌握捕獲graph。
起首創立一個Capture Graph Builder對象和一個graph manger對象,然後用filter graph manager 作參數,挪用ICaptureGraphBuilder2::SetFiltergraph來初始化Capture Graph Builder。看上面的代碼吧:
HRESULT InitCaptureGraphBuilder(IGraphBuilder **ppGraph, //Receives the pointer ICaptureGraphBuilder2 **ppBuilder) //Receives the pointer { if(!ppGraph || !ppBuilder) { return E_POINTER; } IGraphBuilder *pGraph = NULL; ICaptureGraphBuilder2 *pBuild = NULL; //Create the Capture Graph Builder HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pGraph); if(SECCEEDED(hr)) { //Create the Filter Graph Manager hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph); if(SECCEEDED(hr)) { //Initialize the Capture Graph Builder pBuild->SetFiltergraph(pGraph); //Return both interface pointers to the caller *ppBuild = pBuild; *ppGraph = pGraph; //The caller must release both interface return S_OK; } else { pBuild->Release(); } } return hr; //Failed }
視頻捕獲的裝備
如今很多新的視頻捕獲裝備都采取的是WDM驅動辦法,在WDM機制中,微軟供給了一個自力於硬件裝備的驅動,稱為類驅動法式。驅動法式的供給商供給的驅動法式稱為minidrivers。Minidrivers供給了直接和硬件打交道的函數,在這些函數中挪用了類驅動。
在directshow的filter圖表中,任何一個WDM捕獲裝備都是做為一個WDM Video Capture過濾器(Filter)湧現。WDM Video Capture過濾器依據驅動法式的特點構建本身的filter
Direcshow中視頻捕獲的Filter Pin的品種
捕獲Filter普通都有兩個或多個輸入pin,他們輸入的媒體類型都一樣,好比預覽pin和捕獲pin,是以依據媒體類型就不克不及很好的差別這些pin。此時就要依據pin的功效來差別每一個pin了,每一個pin都有一個GUID,稱為pin的品種。
假如想細心的懂得pin的品種,請看前面的相干內容Working with Pin Categories。關於年夜多半的運用來講,ICaptureGraphBuilder2供給了一些函數可以主動肯定pin的品種。
預覽pin和捕獲pin
視頻捕獲Filter都供給了預覽和捕獲的輸入pin,預覽pin用來將視頻流在屏幕上顯示,捕獲pin用來將視頻流寫入文件。
預覽pin和輸入pin有上面的差別:
1 為了包管捕獲pin對視頻桢流量,預覽pin需要的時刻可以停滯。
2 經由捕獲pin的視頻桢都有時光戳,然則預覽pin的視頻流沒有時光戳。
預覽pin的視頻流之所以沒有時光戳的緣由在於filter圖表治理器在視頻流裡加一個很小的latency,假如捕獲時光被以為就是render時光的話,視頻renderFilter就以為視頻流有一個小小的延遲,假如此時render filter試圖持續播放的時刻,就會丟桢。去失落時光戳就包管了視頻桢來了便可以播放,不消期待,也不丟桢。
Video Port pin
Video Port是一個介於視頻裝備(TV)和視頻卡之間的硬件裝備。同過Video Port,視頻數據可以直接發送到圖象卡上,經由過程硬件的籠罩,視頻可以直接在屏幕顯示出來。Video Port就是銜接兩個裝備的。
應用Video Port的最年夜利益是,不消CPU的任何任務,視頻流直接寫入內存中。
假如捕獲裝備應用了Video Port,捕獲Filter就用一個video port pin取代預覽pin。
video port pin的品種GUID為PIN_CATEGORY_VIDEOPORT
一個捕獲filter至多有一個Capture pin,別的,它能夠有一個預覽pin 和一個video port pin,或許二者都沒有,或許filter有許多的capture pin,和預覽pin,每個pin都代表一種媒體類型,是以一個filter可以有一個視頻capture pin,視頻預覽pin,音頻捕獲pin,音頻預覽pin。
Upstream WDM Filters
在捕獲Filter之上,WDM裝備能夠須要額定的filters,上面就是這些filter
雖然這些都是一些自力的filter,然則他們能夠代表的是統一個硬件裝備,每一個filter都掌握裝備的分歧函數,這些filter經由過程pin銜接起來,然則在pin中沒稀有據活動。是以,這些pin 的銜接和媒體類型有關。他們應用一個GUID值來界說一個給定裝備的minidriver,例如:TV tuner Filter 和video capture filter都支撐統一種medium。
在現實運用中,假如你應用ICaptureGraphBuilder2來創立你的capture graphs,這些filters就會主動被添加到你的graph中。更多的具體材料,可以參考WDM Class Driver Filters。
選擇一個視頻捕獲裝備(Select capture device)
若何選擇一個視頻捕獲裝備,可以采取體系裝備列舉,具體材料拜見Using the System Device Enumerator 。enumerator可以依據filter的品種前往一個裝備的monikers。Moniker是一個com對象,可以拜見IMoniker的SDK。
關於捕獲裝備,上面兩品種是相干的。
上面的代碼演示了若何列舉一個視頻捕獲裝備
ICreateDevEnum *pDevEnum = NULL; IEnumMoniker *pEnum = NULL; //Create the system device enumerator HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCT_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&pDevEnum)); if(SUCCEEDED(hr)) { //創立一個列舉器,列舉視頻裝備 hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); }
IEnumMoniker接口pEnum前往一個IMoniker接口的列表,代表一系列的moniker,你可以顯示一切的裝備,然後讓用戶選擇一個。
采取IMoniker::BindToStorage辦法,前往一個IPropertyBag接口指針。然後挪用IPropertyBag::Read讀取moniker的屬性。上面看看都包括甚麼屬性:
1 FriendlyName 是裝備的名字
2 Description 屬性僅僅實用於DV和D-VHS/MPEG攝象機,假如這個屬性可用,這個屬性更具體的描寫了裝備的材料
3DevicePath 這個屬性是弗成讀的,然則每一個裝備都有一個舉世無雙的。你可以用這個屬性來差別統一個裝備的分歧實例
上面的代碼演示了若何顯示遍歷裝備的稱號 ,接下面的代碼
HWND hList; //Handle to the list box IMoniker *pMoniker = NULL; while(pEnum->Next(1, &pMoniker, NULL) == S_OK) { IPropertyBag *pPropBag; hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)(&pPropBag)); if(FAILED(hr)) { pMoniker->Release(); continue; //Skip this one, maybe the next one will work } VARIANT varName; hr = pPropBag->Read(L"Description", &varName, 0); if(FAILED(hr)) { hr = pPropBag->Read(L"FriendlyName", &varName, 0); } if(SECCEEDED(hr)) { //Add it to the application's list box USES_CONVERSION; (long)SendMessage(hList, LB_ADDSTRING, 0, (LPARAM)OLE2T(varName.bstrVal)); VariantClear(&varName); } pPropBag->Release(); pMoniker->Release(); }
假如用戶選中了一個裝備挪用IMoniker::BindToObject為裝備生成filter,然後將filter參加到graph中。
IBaseFilter *pCap = NULL; hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap); if(SECCEEDED(hr)) { hr = m_pGraph->AddFilter(pCap, L"Capture Filter");
為了創立可以預覽視頻的graph,可以挪用上面的代碼:
ICaptureGraphBuilder2 *pBuild; //Capture Graph Builder //Initialize pBuild(not shown) ... IBaseFilter *pCap; //Video capture filter hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pCap, NULL, NULL); }
若何捕獲視頻流並保留到文件(Capture video to File)
1 將視頻流保留到AVI文件
AVI Mux filter吸收從capture pin過去的視頻流,然後將其打包成AVI流。音頻流也能夠銜接到AVI Mux Filter上,如許mux filter就將視頻流和視頻流分解AVI流。File writer將AVI流寫入到文件中。
可以像上面如許構建graph圖
IBaseFilter *pMux; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, //Specifies AVI for the target file L"C:\\Example.avi", //File name &pMux, //Receives a pointer to the mux NULL); //(Optional)Receives a pointer to the file sink
第一個參數注解文件的類型,這裡注解是AVI,第二個參數是制訂文件的稱號。關於AVI文件,SetOutputFileName函數會創立一個AVI mux Filter 和一個 File writer Filter ,而且將兩個filter添加到graph圖中,在這個函數中,經由過程File Writer Filter 要求IFileSinkFilter接口,然後挪用IFileSinkFilter::SetFileName辦法,設置文件的稱號。然後將兩個filter銜接起來。第三個參數前往一個指向 AVI Mux的指針,同時,它也經由過程第四個參數前往一個IFileSinkFilter參數,假如你不須要這個參數,你可以將這個參數設置成NULL。
然後,你應當挪用上面的函數將capture filter 和AVI Mux銜接起來。
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, //Pin category &MEDIATYPE_Video, //Media type pCap, //Capture filter NULL, //Intermediate filter(optional) pMux); //Mux or file sink filter //Release the mux filter pMux->Release();
第5個參數就是應用的下面函數前往的pMux指針。
當捕獲音頻的時刻,媒體類型要設置為MEDIATYPE_Audio,假如你從兩個分歧的裝備捕獲視頻和音頻,你最好將音頻設置成主流,如許可以避免兩個數據流間drift,由於avi mux filter為同步音頻,會調劑視頻的播放速度的。為了設置master 流,挪用IConfigAviMux::SetMasterStream辦法,可以采取以下的代碼:
IConfigAviMux *pConfigMux = NULL; hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux); if(SUCCEEDED(hr)) { pConfigMux->SetMasterStream(1); pConfigMux->Release(); }
SetMasterStream的參數指的是數據流的數量,這個是由挪用RenderStream的順序決議的。例如,假如你挪用RenderStream起首用於視頻流,然後是音頻,那末視頻流就是0,音頻流就是1。
添加編碼filter
IBaseFilter *pEncoder; //Add it to the filter graph pGraph->AddFilter(pEncoder, L"Encode"); //Render the stream hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, pEncoder, pMux); pEncoder->Release();
為了將視頻流保留成並編碼成windows media video (WMV)格局的文件,將capture pin連到WM ASF Writer filter。
構建graph圖最簡略的辦法就是將在ICaptureGraphBuilder2::SetOutputFileName辦法中指定MEDIASUBTYPE_Asf的filter。以下
IBaseFilter *pASFWriter = 0; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Asf, //Create a windows media file L"C:\\VidCap.wmv", //File name &pASFWriter, //Receives a pointer to the filter NULL); //Receives an IFileSinkFilter interface pointer(optional)
參數MEDIASUBTYPE_Asf 告知graph builder,要應用wm asf writer作為文件吸收器,因而,pbuild 就創立這個filter,將其添加到graph圖中,然後挪用IFileSinkFilter::SetFileName來設置輸入文件的名字。第三個參數用來前往一個ASF writer指針,第四個參數用來前往文件的指針。
在將任何pin銜接到WM ASF Writer之前,必定要對WM ASF Writer停止一下設置,你可以同過WM ASF Writer的IConfigAsfWriter接口指針來停止設置。
IConfigAsfWriter *pConfig = 0; hr = pASFWriter->QueryInterface(IID_IConfigAsfWriter, (void**)&pConfig); if(SUCCEEDED(hr)) { //Configure the ASF Writer filter pConfig->Release(); }然後挪用ICaptureGraphBuilder2::RenderStream將capture Filter 和 ASF writer銜接起來:
hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, //Capture pin &MEDIATYPE_Video, //Video. Use MEDIATYPE_Audio for audio pCap, //Pointer to the capture filter 0, pASFWriter); //Pointer to the sink filter(ASF Filter)
3保留成自界說的文件格局
假如你想將文件保留成本身的格局,你必需有本身的 file writer。看上面的代碼:
IBaseFilter *pMux = 0; IFileSinkFilter *pSink = 0; hr = pBuild->SetOutputFileName(&CLSID_MyCustomMuxFilter, //開辟本身的Filter L"C:\\VidCap.avi", &pMux, &pSink);
4若何將視頻流保留進多個文件
當你將視頻流保留進一個文件後,假如你想開端保留第二個文件,這時候,你應當起首將graph停滯,然後經由過程IFileSinkFilter::SetFileName轉變 File Writer 的文件稱號。留意,IFileSinkFilter指針你可以在SetOutputFileName時經由過程第四個參數前往的。
看看保留多個文件的代碼:
IBaseFilter *pMux = 0; IFileSinkFilter *pSink = 0; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi, L"C:\\YourFileName.avi", &pMux, &pSink); if(SUCCEEDED(hr)) { hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, NULL, pMux); if(SUCCEEDED(hr)) { pControl->Run(); pControl->Stop(); //Change the file name and run the graph again pSink->SetFileName(L"YourFileName02.avi", 0); pControl->Run(); } pMux->Release(); pSink->Release(); }