选择压缩筛选器

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

多种类型的软件组件可以执行视频或音频压缩,例如:

  • 本机 DirectShow 筛选器
  • 视频压缩管理器 (VCM) 编解码器
  • 音频压缩管理器 (ACM) 编解码器
  • directX 媒体对象 (DSO)

在 DirectShow 中,VCM 编解码器由 AVI 压缩器筛选器包装,ACM 编解码器由 ACM 包装器筛选器包装DMO 包装器筛选器包装 DMO。 系统设备枚举器提供了一种一致的方式来枚举和创建其中任何压缩器类型,而无需担心基础模型。

有关系统设备枚举器的详细信息,请参阅 使用系统设备枚举器。 简言之,所有 DirectShow 筛选器都按类别分类,每个类别由 GUID 标识。 对于视频压缩器,类别 GUID CLSID_VideoCompressorCategory。 对于音频压缩器,它CLSID_AudioCompressorCategory。 为了枚举特定类别,系统设备枚举器会创建一个支持 IEnumMoniker 接口的枚举器对象。 应用程序使用此接口检索设备名字对象,其中每个设备名字对象表示 DirectShow 筛选器的一个实例。 可以使用名字对象创建筛选器,或者在不创建筛选器的情况下获取设备的友好名称。

若要枚举用户系统上可用的视频或音频压缩器,请执行以下操作:

  1. 调用 CoCreateInstance 以创建系统设备枚举器,其类 ID 为 CLSID_SystemDeviceEnum。
  2. 使用筛选器类别 GUID 调用 ICreateDevEnum::CreateClassEnumerator 。 方法返回 IEnumMoniker 接口指针。
  3. 使用 IEnumMoniker::Next 方法枚举设备名字对象。 此方法返回表示名字对象的 IMoniker 接口。

若要从名字对象获取友好名称,请执行以下操作:

  1. 调用 IMoniker::BindToStorage 方法。 此方法返回 IPropertyBag 接口指针。
  2. 使用 IPropertyBag::Read 方法读取 FriendlyName 属性。

通常,应用程序会显示压缩器列表,以便用户可以选择一个压缩器。 例如,以下代码使用可用视频压缩器的名称填充列表框。

void OnInitDialog(HWND hDlg)
{
    HRESULT hr;
    ICreateDevEnum *pSysDevEnum = NULL;
    IEnumMoniker *pEnum = NULL;
    IMoniker *pMoniker = NULL;

    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, 
        CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, 
        (void**)&pSysDevEnum);
    if (FAILED(hr))
    {
        // Handle the error.
    }    

    hr = pSysDevEnum->CreateClassEnumerator(
             CLSID_VideoCompressorCategory, &pEnum, 0);
    if (hr == S_OK)  // S_FALSE means nothing in this category.
    {
        while (S_OK == pEnum->Next(1, &pMoniker, NULL))
        {
            IPropertyBag *pPropBag = NULL;
            pMoniker->BindToStorage(0, 0, IID_IPropertyBag, 
                (void **)&pPropBag);
            VARIANT var;
            VariantInit(&var);
            hr = pPropBag->Read(L"FriendlyName", &var, 0);
            if (SUCCEEDED(hr))
            {
                LRESULT iSel = AddString(GetDlgItem(hDlg, 
                    IDC_CODEC_LIST), var.bstrVal);
            }   
            VariantClear(&var); 
            pPropBag->Release();
            pMoniker->Release();
        }
    }

    SendDlgItemMessage(hDlg, IDC_CODEC_LIST, 
                       LB_SETCURSEL, 0, 0);
    pSysDevEnum->Release();
    pEnum->Release();
}

若要从名字对象创建筛选器实例,请调用 IMoniker::BindToObject 方法。 方法返回 IBaseFilter 指针。

IBaseFilter *pFilter = NULL;
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, 
                                       (void**)&pFilter);
if (SUCCEEDED(hr))
{
    // Use the filter. 
    // Remember to release the IBaseFilter interface.
}

对于 VCM 编解码器,每个名字对象表示一个特定的编解码器,即使所有编解码器都由同一 AVI 压缩筛选器包装。 调用 BindToObject 会创建此筛选器的实例,并针对该编解码器进行初始化。 因此,不能直接在 AVI 压缩筛选器上调用 CoCreateInstance 。 必须遍历系统设备枚举器。

重新压缩 AVI 文件