如何正确枚举Windows系统中安装的DirectShow视频编码器?
正确枚举系统中安装的DirectShow视频编码器
要解决Windows 10 x64下部分视频编码器分散在不同类别的问题,需要结合枚举多类别+针对性过滤的方式,既不会遗漏目标编码器,也能排除无关对象。
核心思路
- 优先枚举标准视频压缩器类别
CLSID_VideoCompressorCategory,这里包含大部分规范注册的编码器。 - 再枚举
CLSID_LegacyAmFilterCategory类别,但对其中的对象进行过滤,仅保留真正的视频编码器。 - 过滤逻辑:通过检查滤镜的输出引脚媒体类型,判断是否支持压缩视频格式——这是判断一个滤镜是否为视频编码器最可靠的方式。
实现代码示例
#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
相关产品推荐
相关产品推荐

