DirectShow 枚举系统硬件设备和 filter

发布于 2021-11-26


做音视频的采集,我们需要枚举系统中可用的硬件设备,比如摄像头、话筒等等。还有系统可用的 DirectShow filter。

System Device Enumerator

int main() {
  // 初始化 COM
  ScopedCOMInitializer com_initializer;
  if (!com_initializer.IsSucceeded()) {
    printf("ERROR:init com failed.\n");
    return 0;
  }

  // 创建 System Device Enumerator
  CComPtr<ICreateDevEnum> system_device_enum;
  HRESULT hr =
      CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                       IID_ICreateDevEnum, (void **)&system_device_enum);
  if (FAILED(hr)) {
    printf("ERROR:Could not create ICreateDevEnum.\n");
    return 0;
  }

  // 创建某种类型枚举器
  CComPtr<IEnumMoniker> video_compressor_category;
  hr = system_device_enum->CreateClassEnumerator(CLSID_VideoCompressorCategory,
                                                 &video_compressor_category, 0);
  if (FAILED(hr)) {
    printf("ERROR:Could not create CLSID_VideoCompressorCategory.\n");
    return 0;
  }

  CComPtr<IMoniker> moniker;
  ULONG cFetched;
  while (video_compressor_category->Next(1, &moniker, &cFetched) == S_OK) {
    CComPtr<IPropertyBag> property_bag;
    hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&property_bag);
    if (FAILED(hr)) {
      continue;
    }

    // 获得名字
    CComVariant name;
    hr = property_bag->Read(L"FriendlyName", &name, 0);
    if (FAILED(hr)) {
      continue;
    }
    wprintf(L"%s\n", name.bstrVal);

    // 把 IMoniker 转换成对应的 Filter
    CComPtr<IBaseFilter> filter;
    hr = moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)&filter);

    moniker = nullptr;
  }
  return 0;
}

输出结果:
WMVideo8 Encoder DMO
WMVideo9 Encoder DMO
MSScreen 9 encoder DMO
DV Video Encoder
MJPEG Compressor
Cinepak Codec by Radius
Intel IYUV ?????
Intel IYUV ?????
Microsoft RLE
Microsoft Video 1

System Device Enumerator 用于创建某种类型的枚举器,再通过枚举器枚举到设备名称,比如音频采集或者视频压缩。可以把它转换成一个 Filter

上面代码就是枚举系统上可用的 VideoCompressor Filter。获取到 Filter 之后,后续就可用添加到 filter graph 中使用了。

Filter Mapper

int main() {
  ScopedCOMInitializer com_initializer;
  if (!com_initializer.IsSucceeded()) {
    return 0;
  }

  CComPtr<IFilterMapper2> filter_mapper;
  HRESULT hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC,
                                IID_IFilterMapper2, (void **)&filter_mapper);
  if (FAILED(hr)) {
    return 0;
  }

  GUID arrayInTypes[2];
  arrayInTypes[0] = MEDIATYPE_Video;
  arrayInTypes[1] = MEDIASUBTYPE_dvsd;

  CComPtr<IEnumMoniker> enum_moniker;
  hr = filter_mapper->EnumMatchingFilters(
      &enum_moniker,
      0,                    // Reserved.
      TRUE,                 // Use exact match?
      MERIT_DO_NOT_USE + 1, // Minimum merit.
      TRUE,                 // At least one input pin?
      1,                    // Number of major type/subtype pairs for input.
      arrayInTypes,         // Array of major type/subtype pairs for input.
      NULL,                 // Input medium.
      NULL,                 // Input pin category.
      FALSE,                // Must be a renderer?
      TRUE,                 // At least one output pin?
      0,                    // Number of major type/subtype pairs for output.
      NULL,                 // Array of major type/subtype pairs for output.
      NULL,                 // Output medium.
      NULL);                // Output pin category.

  CComPtr<IMoniker> moniker;
  ULONG cFetched;
  while (enum_moniker->Next(1, &moniker, &cFetched) == S_OK) {
    CComPtr<IPropertyBag> property_bag;
    hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&property_bag);
    if (FAILED(hr)) {
      continue;
    }

    // 获得名字
    CComVariant name;
    hr = property_bag->Read(L"FriendlyName", &name, 0);
    if (FAILED(hr)) {
      continue;
    }
    wprintf(L"%s\n", name.bstrVal);

    // 把 IMoniker 转换成对应的 Filter
    CComPtr<IBaseFilter> filter;
    hr = moniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)&filter);

    moniker = nullptr;
  }
  return 0;
}

输出结果:
LAV Video Decoder
DV Video Decoder

Filter Mapper 是专门用户枚举系统中的 DirectShow filter。它有很多参数来指定要枚举的 filter,功能很强大。

参考