directshow的中文資料之設備列舉和捕捉接口

 

這篇解釋和示例如何通過DirectShow的接口去初始化和訪問系統的硬件設備。代表性的,DirectShow應用程序使用下面類型的硬件。

音/視頻捕捉卡
音頻或視頻回放卡
音頻或視頻壓縮或解壓卡(象MPEG解碼器)
下面將以AV設備作參考。


如何列舉設備

包括在DirectShow SDK中的接口,類,和例子提供了音/視頻捕捉和回放的功能。因爲文件源過濾器和filter graph manager處理了內在的工作,所有,添加捕捉功能到一個應用程序中,只需添加很少的代碼。你可以通過列舉系統硬件設備和得到設備列表完成特別的任務(例如:所有的視頻捕捉卡的列表)。DirectShow自動爲win32和Video for Windows 設備實例化過濾器。

要AV設備工作,首先,你必須檢測當前系統存在的設備。ICreateDevEnum接口建立指定類型的列表。提供你需要的檢測和設置硬件的功能。訪問一個指定的設備有三步,詳細的說明和代碼如下:

建立系統硬件設備的列表
首先,申明一個列表指針,然後通過 CoCreateInstance 建立。CLSID_SystemDeviceEnum是我們想建立對象的類型,IID_ICreateDevEnum是接口的GUID。

    ICreateDevEnum  *pCreateDevEnum ;
    CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
     IID_ICreateDevEnum, (void**)&pCreateDevEnum) ;


其次,建立一個特別類型的硬件設備的列表(例如視頻捕捉卡)
申明一個IEnumMoniker接口,並把他傳給ICreateDevEnum::CreateClassEnumerator 方法。你就可以使用他訪問新得到的列表了。

    IEnumMoniker *pEnumMon ;
    pCreateDevEnum->CreateClassEnumerator(
    [specify device GUID here]
    &pEnumMon, 0);

最後,列舉列表直到你得到你想要的設備爲止。
如果先前的CreateClassEnumerator調用成功了,你可以用IEnumMoniker::Next得到設備。調用IMoniker::BindToObject建立一個和選擇的device聯合的filter,並且裝載filter的屬性(CLSID,FriendlyName, and DevicePath)。不需要爲if語句的(1 == cFetched) 困惑,在測試合法性之前,pEnumMon->Next(1, &pMon, &cFetched)方法會設置他爲返回對象的數字(如果成功了爲1)。

    ULONG cFetched = 0;
    IMoniker *pMon ;

    if (S_OK == (pEnumMon->Next(1, &pMon, &cFetched))  &&  (1 == cFetched))
    {
        pMon->BindToObject(0, 0, IID_IBaseFilter, (void **)&[desired interface here]) ;

好,現在你有了一個IMoniker指針,你可以添加設備的filter到filter graph。一旦你添加了filter,你就不需要IMoniker指針,設備列表,或系統設備列表。

     pGraph->AddFilter([desired interface here], L"[filter name here]") ;
     pMon->Release() ;  // Release moniker
 }
 pEnumMon->Release() ; // Release the class enumerator
    }
    pCreateDevEnum->Release();

 

實例:AMCap中的設備列表代碼

AMCap例子中,把所有的接口指針和一些成員變量保存在一個全局結構gcap中了。
定義如下:
struct _capstuff {
    char szCaptureFile[_MAX_PATH];
    WORD wCapFileSize;  // size in Meg
    ICaptureGraphBuilder *pBuilder;
    IVideoWindow *pVW;
    IMediaEventEx *pME;
    IAMDroppedFrames *pDF;
    IAMVideoCompression *pVC;
    IAMVfwCaptureDialogs *pDlg;
    IAMStreamConfig *pASC;      // for audio cap
    IAMStreamConfig *pVSC;      // for video cap
    IBaseFilter *pRender;
    IBaseFilter *pVCap, *pACap;
    IGraphBuilder *pFg;
    IFileSinkFilter *pSink;
    IConfigAviMux *pConfigAviMux;
    int  iMasterStream;
    BOOL fCaptureGraphBuilt;
    BOOL fPreviewGraphBuilt;
    BOOL fCapturing;
    BOOL fPreviewing;
    BOOL fCapAudio;
    int  iVideoDevice;
    int  iAudioDevice;
    double FrameRate;
    BOOL fWantPreview;
    long lCapStartTime;
    long lCapStopTime;
    char achFriendlyName[120];
    BOOL fUseTimeLimit;
    DWORD dwTimeLimit;
} gcap;


例子用uIndex變量循環列舉系統的硬件設備。
BOOL InitCapFilters()
{
    HRESULT hr;
    BOOL f;
    UINT uIndex = 0;

MakeBuilder函數建立了一個filter graph builder(參考建立一個捕捉程序)。

    f = MakeBuilder();

建立設備列表對象,得到ICreateDevEnum接口

    ICreateDevEnum *pCreateDevEnum;
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                          IID_ICreateDevEnum, (void**)&pCreateDevEnum);

建立一個特別類型的硬件設備的列表,類的ID是CLSID_VideoInputDeviceCategory。現在有了一個IEnumMoniker指針,可以訪問捕捉設備的列表了。

    IEnumMoniker *pEm;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEm, 0);
    pCreateDevEnum->Release();   // We don't need the device enumerator anymore
    pEm->Reset();      // Go to the start of the
                                                         enumerated list 


現在需要實際的設備了,調用IEnumMoniker::Next ,然後用得到的指針pM調用IMoniker::BindToObject,綁定filter到設備。如果你不想建立聯合的filter,使用IMoniker::BindToStorage 代替IMoniker::BindToObject。
 

    ULONG cFetched;
    IMoniker *pM;     // This will access the actual devices
    gcap.pVCap = NULL;
    while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
    {
if ((int)uIndex == gcap.iVideoDevice) { // This is the one we want.  Instantiate it.
     hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pVCap);
                    pM->Release();   // We don't need the moniker pointer anymore
     break;
 }
                pM->Release();
 uIndex++;
    }
    pEm->Release();    // We've got the device; don't need the
                                           enumerator anymore


當有了設備後,通過接口指針去測量幀數,得到driver的名字,得到捕捉的尺寸(size)。在例子中,把每個指針都存儲才gcap全局結構中了。
, and get the capture size. AMCap stores each pointer in the gcap global structure.

    // We use this interface to get the number of captured and dropped frames
    gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
    IID_IAMDroppedFrames, (void **)&gcap.pDF);

    // We use this interface to get the name of the driver
    gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
    IID_IAMVideoCompression, (void **)&gcap.pVC);

    // We use this interface to set the frame rate and get the capture size
    gcap.pBuilder->FindCaptureInterface(gcap.pVCap,
    IID_IAMVideoStreamConfig, (void **)&gcap.pVSC);

然後得到媒體的類型和顯示窗口的大小去匹配視頻格式的尺寸。

  AM_MEDIA_TYPE *pmt;
    gcap.pVSC->GetFormat(&pmt);   // Current capture format

    ResizeWindow(HEADER(pmt->pbFormat)->biWidth,
    HEADER(pmt->pbFormat)->biHeight);
    DeleteMediaType(pmt);


現在,已經有了視頻設備和他的相關信息,重複這個過程,得到音頻設和他的信息並存儲到全局機構中去。注意,這次是用參數CLSID_AudioInputDeviceCategory 調用ICreateDevEnum::CreateClassEnumerator 。

    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                          IID_ICreateDevEnum, (void**)&pCreateDevEnum);
    uIndex = 0;
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
        &pEm, 0);
    pCreateDevEnum->Release();

    pEm->Reset();
    gcap.pACap = NULL;
    while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
    {
 if ((int)uIndex == gcap.iAudioDevice) {          // this is the one we want
     hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&gcap.pACap);
                    pM->Release();
     break;
 }
                pM->Release();
 uIndex++;
    }
    pEm->Release();

AMCap also repeats the process of retrieving the format interface, this time for the audio device.

    hr = gcap.pBuilder->FindCaptureInterface(gcap.pACap,
    IID_IAMAudioStreamConfig, (void **)&gcap.pASC);
}

 

如何保持DirectShow Filter (Properties) 道具

IPropertyBag 和 IPersistPropertyBag 接口存儲和返回Properties的"bags"組。通過這些接口存儲的Properties是可以持久保持的。同一個對象在不同的實例之間,他們保持一致。Filter可以存儲他們的Properties(CLSID, FriendlyName, and DevicePath)。當一個filter存儲完他的Properties之後,實例一個filter時,DirectShow會自動得到他們。添加功能到你的filter中,執行IPersistPropertyBag接口和他的方法。你可以用IPropertyBag::Read 方法裝載filter Properties 到Win32 VARIANT 變量中,然後初始化輸入輸出pin。


下面的代碼演示DirectShow的VfWCapture filter如何執行IPersistPropertyBag::Load方法的。記住:在執行期間,你的filter必須提供一個有效的IPropertyBag指針。


STDMETHODIMP CVfwCapture::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog)
{
    HRESULT hr;
    CAutoLock cObjectLock(m_pLock);  // Locks the object; automatically unlocks it in the destructor.

    if (m_pStream)    // If the filter already exists for this stream
 return E_UNEXPECTED;

    VARIANT var;    // VARIANT from Platform SDK
    var.vt = VT_I4;    // four-byte integer (long)
    hr = pPropBag->Read(L"VFWIndex", &var, 0); // VFWIndex is the private name used by the Vidcap Class Manager to refer to the VFW Capture filter
    if(SUCCEEDED(hr))    // If it read the properties successfully
    {
        hr = S_OK;    // Defaults return value to S_OK
        m_iVideoId = var.lVal;   // Stores the specified hardware device number
        CreatePins(&hr);    // Inits the pins, replacing the return value if necessary
    }
    return hr;     // Returns S_OK or an error value, if CreatePins failed

發佈了34 篇原創文章 · 獲贊 1 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章