You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确枚举Windows系统中安装的DirectShow视频编码器?

正确枚举系统中安装的DirectShow视频编码器

要解决Windows 10 x64下部分视频编码器分散在不同类别的问题,需要结合枚举多类别+针对性过滤的方式,既不会遗漏目标编码器,也能排除无关对象。

核心思路

  1. 优先枚举标准视频压缩器类别CLSID_VideoCompressorCategory,这里包含大部分规范注册的编码器。
  2. 再枚举CLSID_LegacyAmFilterCategory类别,但对其中的对象进行过滤,仅保留真正的视频编码器。
  3. 过滤逻辑:通过检查滤镜的输出引脚媒体类型,判断是否支持压缩视频格式——这是判断一个滤镜是否为视频编码器最可靠的方式。

实现代码示例

#include <dshow.h>
#include <atlbase.h>
#include <vector>

// 判断是否为压缩视频子类型
BOOL IsCompressedVideoSubtype(const GUID& subtype)
{
    // 可根据需求扩展更多压缩视频子类型
    const std::vector<GUID> compressedSubtypes = {
        MEDIASUBTYPE_MPEG2_VIDEO,
        MEDIASUBTYPE_H264,
        MEDIASUBTYPE_VP8,
        MEDIASUBTYPE_VP9,
        MEDIASUBTYPE_WMV3,
        MEDIASUBTYPE_MJPG,
        MEDIASUBTYPE_MPEG4
    };

    for (const auto& guid : compressedSubtypes)
    {
        if (subtype == guid)
            return TRUE;
    }
    return FALSE;
}

// 释放媒体类型资源
void DeleteMediaType(AM_MEDIA_TYPE* pmt)
{
    if (!pmt) return;
    if (pmt->cbFormat > 0 && pmt->pbFormat)
    {
        CoTaskMemFree(pmt->pbFormat);
        pmt->pbFormat = nullptr;
        pmt->cbFormat = 0;
    }
    if (pmt->pUnk)
    {
        pmt->pUnk->Release();
        pmt->pUnk = nullptr;
    }
    CoTaskMemFree(pmt);
}

// 枚举单一类别下的设备(针对标准视频压缩器类别)
HRESULT EnumerateStandardVideoEncoders(ICreateDevEnumPtr pDevEnum)
{
    IEnumMonikerPtr pEnumMoniker;
    HRESULT hr = pDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumMoniker, 0);
    if (hr == S_FALSE) return S_OK; // 该类别无设备
    if (FAILED(hr)) return hr;

    IMonikerPtr pMoniker;
    while (pEnumMoniker->Next(1, &pMoniker, nullptr) == S_OK)
    {
        IPropertyBagPtr pPropBag;
        hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&pPropBag);
        if (SUCCEEDED(hr))
        {
            VARIANT varName;
            VariantInit(&varName);
            if (SUCCEEDED(pPropBag->Read(L"FriendlyName", &varName, nullptr)))
            {
                wprintf(L"标准视频编码器:%s\n", varName.bstrVal);
            }
            VariantClear(&varName);
        }
        pMoniker.Release();
    }
    return S_OK;
}

// 枚举Legacy类别并过滤视频编码器
HRESULT EnumerateLegacyVideoEncoders(ICreateDevEnumPtr pDevEnum)
{
    IEnumMonikerPtr pEnumMoniker;
    HRESULT hr = pDevEnum->CreateClassEnumerator(CLSID_LegacyAmFilterCategory, &pEnumMoniker, 0);
    if (hr == S_FALSE) return S_OK; // 该类别无设备
    if (FAILED(hr)) return hr;

    IMonikerPtr pMoniker;
    while (pEnumMoniker->Next(1, &pMoniker, nullptr) == S_OK)
    {
        // 绑定到滤镜对象
        IBaseFilterPtr pFilter;
        hr = pMoniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, (void**)&pFilter);
        if (FAILED(hr))
        {
            pMoniker.Release();
            continue;
        }

        BOOL isVideoEncoder = FALSE;
        IEnumPinsPtr pEnumPins;
        if (SUCCEEDED(pFilter->EnumPins(&pEnumPins)))
        {
            IPinPtr pPin;
            while (pEnumPins->Next(1, &pPin, nullptr) == S_OK)
            {
                PIN_DIRECTION pinDir;
                if (SUCCEEDED(pPin->QueryDirection(&pinDir)) && pinDir == PINDIR_OUTPUT)
                {
                    // 枚举输出引脚支持的媒体类型
                    IEnumMediaTypesPtr pEnumMT;
                    if (SUCCEEDED(pPin->EnumMediaTypes(&pEnumMT)))
                    {
                        AM_MEDIA_TYPE* pMT = nullptr;
                        while (pEnumMT->Next(1, &pMT, nullptr) == S_OK)
                        {
                            if (pMT->majortype == MEDIATYPE_Video && IsCompressedVideoSubtype(pMT->subtype))
                            {
                                isVideoEncoder = TRUE;
                                DeleteMediaType(pMT);
                                break;
                            }
                            DeleteMediaType(pMT);
                        }
                        if (isVideoEncoder) break;
                    }
                }
                pPin.Release();
            }
            pEnumPins.Release();
        }

        // 确认是视频编码器后,输出名称
        if (isVideoEncoder)
        {
            IPropertyBagPtr pPropBag;
            hr = pMoniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, (void**)&pPropBag);
            if (SUCCEEDED(hr))
            {
                VARIANT varName;
                VariantInit(&varName);
                if (SUCCEEDED(pPropBag->Read(L"FriendlyName", &varName, nullptr)))
                {
                    wprintf(L"Legacy视频编码器:%s\n", varName.bstrVal);
                }
                VariantClear(&varName);
            }
        }

        pFilter.Release();
        pMoniker.Release();
    }
    return S_OK;
}

// 主枚举函数
HRESULT EnumerateAllVideoEncoders()
{
    CoInitialize(nullptr);
    CComPtr<ICreateDevEnum> pDevEnum;
    HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, nullptr, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);
    if (FAILED(hr)) return hr;

    // 枚举标准类别编码器
    hr = EnumerateStandardVideoEncoders(pDevEnum);
    if (FAILED(hr)) goto Cleanup;

    // 枚举并过滤Legacy类别编码器
    hr = EnumerateLegacyVideoEncoders(pDevEnum);

Cleanup:
    CoUninitialize();
    return hr;
}

关键说明

  • 多类别枚举:覆盖了标准压缩器类别和Legacy兼容类别,避免遗漏像Microsoft MPEG-2 Encoder这类特殊注册的编码器。
  • 媒体类型过滤:通过检查输出引脚的媒体类型,确保只保留能输出压缩视频的滤镜,彻底排除Legacy类别中的无关对象(比如音频滤镜、渲染器等)。
  • 可扩展性:IsCompressedVideoSubtype函数可以根据需求添加更多压缩视频子类型,适配更多编码器场景。

内容的提问来源于stack exchange,提问作者Kola73

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:15:27