选择捕获设备

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]

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

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

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

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

    类别 GUID 说明
    CLSID_AudioInputDeviceCategory 音频捕获设备
    CLSID_VideoInputDeviceCategory 视频捕获设备

     

    如果视频摄像头具有集成麦克风,则它将显示在这两个类别中。 但是,出于枚举、设备创建和数据流式处理的目的,系统将相机和麦克风视为单独的设备。

  3. 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 捕获筛选器。 名字对象属性作为 VARIANT 值返回。 设备名字对象支持以下属性。

属性 说明 VARIANT 类型
“FriendlyName” 设备的名称。 VT_BSTR
“说明” 设备的说明。 VT_BSTR
“DevicePath” 标识设备的唯一字符串。 仅 (视频捕获设备。) VT_BSTR
“WaveInID” 音频捕获设备的标识符。 仅 (音频捕获设备。) 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");
}

音频捕获

视频捕获