程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 編寫C++法式使DirectShow停止視頻捕獲

編寫C++法式使DirectShow停止視頻捕獲

編輯:關於C++

編寫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試圖持續播放的時刻,就會丟桢。去失落時光戳就包管了視頻桢來了便可以播放,不消期待,也不丟桢。

  • 預覽pin的品種GUID為PIN_CATEGORY_PREVIEW
  • 捕獲pin的品種GUID為PIN_CATEGORY_CAPTURE

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

  • TV Tuner Filter
  • TV Audio Filter.
  • Analog Video Crossbar 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。

關於捕獲裝備,上面兩品種是相干的。

  • CLSID_AudioInputDeviceCategory 音頻裝備
  • CLSID_VideoInputDeviceCategory 視頻裝備

上面的代碼演示了若何列舉一個視頻捕獲裝備

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(); 

2 將視頻流保留成wmv格局的文件

為了將視頻流保留成並編碼成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(); 
} 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved