DirectShow——选择捕获设备
ffmpeger 发布于 2021-02-10

要选择音频或视频捕获设备,请使用系统设备枚举器,如使用系统设备枚举器主题中所述。系统设备枚举器返回按设备类别选择的设备名字对象的集合。名字对象是包含有关另一个对象的信息的COM对象。名字对象使应用程序能够在不实际创建对象的情况下获取有关对象的信息。稍后,应用程序可以使用名字对象来创建对象。有关名字的更多信息,请参阅IMoniker的文档。

要使用系统设备枚举器,请执行以下步骤。

1、调用CoCreateInstance创建系统设备枚举器的实例。

2、调用ICreateDevEnum::CreateClasseEnumerator并将设备类别指定为GUID。对于捕获设备,以下类别是相关的。

Category GUID
Description
CLSID_AudioInputDeviceCategory
Audio capture devices
CLSID_VideoInputDeviceCategory
Video capture devices

如果摄像机带有内置麦克风,则会同时出现在这两种类型中。但是,出于枚举、设备创建和数据流的目的,系统将摄像头和麦克风视为单独的设备。

CreateClassEnumerator方法返回一个指向IEnumMoniker接口的指针。要枚举名字对象,请调用IEnumMoniker::Next。

下面的代码为指定的设备类别创建枚举器。

#include <windows.h>
#include <dshow.h>

#pragma comment(lib, "strmiids")

HRESULT EnumerateDevices(REFGUID category, IEnumMoniker **ppEnum)
{
    // Create the System Device Enumerator.
    ICreateDevEnum *pDevEnum;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,  
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));

    if (SUCCEEDED(hr))
    {
        // Create an enumerator for the category.
        hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
        if (hr == S_FALSE)
        {
            hr = VFW_E_NOT_FOUND;  // The category is empty. Treat as an error.
        }
        pDevEnum->Release();
    }
    return hr;  

}

IEnumMoniker接口枚举IMoniker接口的列表,每个接口表示一个设备名字对象。应用程序可以从名字对象读取属性,或者使用名字对象为设备创建DirectShow捕获筛选器。名字对象属性作为变量值返回。设备名字对象支持以下属性。

Property
Description
VARIANT Type
"FriendlyName"
The name of the device.
VT_BSTR
"Description"
A description of the device.
VT_BSTR
"DevicePath"
A unique string that identifies the device. (Video capture devices only.)
VT_BSTR
"WaveInID"
The identifier for an audio capture device. (Audio capture devices only.)
VT_I4

“FriendlyName”和“Description”属性适合在UI中显示。
“FriendlyName”属性可用于每个设备。它包含设备的可读名称。
“Description”属性仅适用于DV和D-VHS/MPEG摄像机设备。有关更多信息,请参阅MSDV驱动程序和MSTape驱动程序。如果可用,它包含比“FriendlyName”属性更具体的设备描述。通常包括供应商名称。
“DevicePath”属性不是人类可读的字符串,但对于系统上的每个视频捕获设备都保证是唯一的。可以使用此属性区分同一型号设备的两个或多个实例。
如果存在“WaveInID”属性,则表示DirectShow捕获过滤器在内部使用波形音频API与设备通信。“WaveInID”属性的值对应于waveIn*函数(如waveInOpen)使用的标识符。


要从名字对象读取属性,请执行以下步骤。
1、调用IMoniker::BindToStorage获取指向IPropertyBag接口的指针。
2、用IPropertyBag::Read读取属性。

下面的代码示例演示如何枚举设备名字对象列表并获取属性。

void DisplayDeviceInformation(IEnumMoniker *pEnum)
{
    IMoniker *pMoniker = NULL;

    while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
    {
        IPropertyBag *pPropBag;
        HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
        if (FAILED(hr))
        {
            pMoniker->Release();
            continue;  
        } 

        VARIANT var;
        VariantInit(&var);

        // Get description or friendly name.
        hr = pPropBag->Read(L"Description", &var, 0);
        if (FAILED(hr))
        {
            hr = pPropBag->Read(L"FriendlyName", &var, 0);
        }
        if (SUCCEEDED(hr))
        {
            printf("%S\n", var.bstrVal);
            VariantClear(&var); 
        }

        hr = pPropBag->Write(L"FriendlyName", &var);

        // WaveInID applies only to audio capture devices.
        hr = pPropBag->Read(L"WaveInID", &var, 0);
        if (SUCCEEDED(hr))
        {
            printf("WaveIn ID: %d\n", var.lVal);
            VariantClear(&var); 
        }

        hr = pPropBag->Read(L"DevicePath", &var, 0);
        if (SUCCEEDED(hr))
        {
            // The device path is not intended for display.
            printf("Device path: %S\n", var.bstrVal);
            VariantClear(&var); 
        }

        pPropBag->Release();
        pMoniker->Release();
    }
}

void main()
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        IEnumMoniker *pEnum;

        hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
        if (SUCCEEDED(hr))
        {
            DisplayDeviceInformation(pEnum);
            pEnum->Release();
        }
        hr = EnumerateDevices(CLSID_AudioInputDeviceCategory, &pEnum);
        if (SUCCEEDED(hr))
        {
            DisplayDeviceInformation(pEnum);
            pEnum->Release();
        }
        CoUninitialize();
    }
}
要为设备创建DirectShow捕获筛选器,请调用IMoniker::BindToObject方法以获取IBaseFilter指针。然后调用IFilterGraph::AddFilter将筛选器添加到筛选器图:

IBaseFilter *pCap = NULL;
hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap);
if (SUCCEEDED(hr))
{
    hr = m_pGraph->AddFilter(pCap, L"Capture Filter");
}

ffmpeger
关注 私信
文章
63
关注
0
粉丝
0