皓月云天

紧张中保持一份松弛

松弛中保持一份紧张

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

【转载】编写DirectShow Filters—DirectShow and COM

Posted on 2011-08-02 05:39  皓月云天  阅读(1623)  评论(0编辑  收藏  举报

一、 如何实现IUnknown
Microsoft DirectShow基于组件对象模型(COM)。如果你编写自定义filter,你必须把它作为一个COM对象来实现。DirectShow基类提供一个实现此对象的框架。不过使用基类不是必须的,但它能简化开发进度。这篇文章描述一些COM对象的内部实现细节和它们在DirectShow基类的实现。
 
本文假定你知道如何编程COM客户端程序—换句话说,你理解在IUnknown中的方法—但是假定先前没有任何开发COM对象的经验。DirectShow处理开发一个COM对象的许多细节。如果你有开发COM对象的经验,你应该阅读using CUnknown章节,它描述了CUnknown基类。
 
COM是一个规范,不是一个实现。它定义一个组件必须遵循的规则;开发者需要实现些规则。在DirectShow中,所有对象继承一个C++基类的集合。这个基类的构造器和方法实现了COM的大多数例行工作,如保持一个一致的引用计数。自定义的filter继承自一个基类,继承这个类的功能。为了有效的使用基类,你需要大概理解如何实现COM规范。
 
1、 IUnknown如何工作
在IUnknown中的方法启用一个应用程序查询在组件上的接口和管理这个组件的引用计数。
1) 引用计数
引用计数是一个内部变量,在AddRef方法中增加,在Release方法中减少。基类管理引用计数并且在多个线程中同步访问引用计数。
2) 接口查询
为一个接口查询也是容易理解的。调用者传递两个参数:一个接口标识符(IID)和一个指针的地址。如果这个组件支持所查询的接口,它设置这个指针到这个接口,增加它自己的引用计数,并且返回S_OK。否则,它设置指针为NULL并且返回E_NOINTERFACE。下列伪代码显示QueryInterface方法的一般概要。组件聚合,在下一章描述,介绍一些额外的复杂性功能。
if (IID == IID_IUnknown)
    set pointer to (IUnknown *)this
    AddRef
    return S_OK
 
else if (IID == IID_ISomeInterface)
    set pointer to (ISomeInterface *)this
    AddRef
    return S_OK
 
else if ...

else
    set pointer to NULL
    return E_NOINTERFACE
QueryInterface方法在在一个组件中与另一个组件之间唯一不同是每个组件测试的IIDs不同。对于每个组件支持的接口,这个组件必须测试此接口的IID。
 
聚合和委托
组件聚合必须对于调用者是透明的。因此,这个聚合必须暴露一个单独的IUnknown接口,用聚合组件引用从外部组件的实现。否则,这个调用者将看到两个IUnknown接口在一个聚合中。如果这个组件不是聚合的,它使用它自己的实现。
 
为了支持这种行为,这个组件必须增加一个间接的层次。delegating IUnknown在合适的位置委托:如果有外部的组件或者这个组件的内部版本。nondelegating IUnknown如前面章节中的描述一样操作。
 
委托版本是公共的并且一直用IUnknown命名。这个非聚合版本被重命名为INonDelegatingUnknown。这个名字不是COM规范的一部分,因为它不是一个公共接口。
当这个客户建立一个组件的实例,它调用IClasFactory::CreateInstance方法。一个参数是指向聚合组件IUnknown接口的指针,或者如果这个新实例是不被聚合的则为NULL。这个组件使用这个参数为存在一个成员变量,这个成员变量显示了哪个IUnkown接口被使用,在下列例子中显示:
CMyComponent::CMyComponent(IUnknown *pOuterUnkown)
{
    if (pOuterUnknown == NULL)
        m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;
    else
        m_pUnknown = pOuterUnknown;
 
    [ ... more constructor code ... ]
}
在聚合IUnknown中的每个方法调用它的非聚合副本,在下列例子中显示:
HRESULT QueryInterface(REFIID iid, void **ppv)
{
    return m_pUnknown->QueryInterface(iid, ppv);
}
通过聚合的本质,在每个组件中聚合方法是一样的。只有非聚合版本是改变的。
2、 使用CUnknown
DirectShow在一个基类中实现的IUnknown叫做CUnknown。使用CUnknown来继承其它类,只重载改变组件的方法。大多数在DirectShow中的其它基类继承自CUnknown,所以你的组件可以直接从CUnknown或者其它基类中继承。
1) INonDelegatingUnknown
CUnknown实现INonDelegatingUnknown。它管理内存的引用计数,并且大多数情形下你的继承类能用无改变继承两个引用计数方法。要认识到当引用计数减到0时CUnknown删除它自己。换句话说,必须重载CUnknown::NonDelegatingQueryInerface,因为如果它接收任何IID而不是IID_IUnknown基类中这个方法返回E_NOINTERFACE。在你的继承类中,测试支持的IID的接口,如下列例子中所示:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {     
    return GetOwner()->QueryInterface(riid,ppv);      
};                                                         
STDMETHODIMP_(ULONG) AddRef() {                            
    return GetOwner()->AddRef();                           
};                                                         
STDMETHODIMP_(ULONG) Release() {                           
    return GetOwner()->Release();                          
};
函数GetInterface(查看COM helper function)设置这个指针,在一个线程安全方式中增加引用计数,并且返回S_OK。在默认情况下,调用基类方法并且返回这个结果。如果你从另一个基类中继承,调用它的NonDelegatingQueryInterface方法来代替。这个启用你去支持父类支持的所有接口。
2) IUnknown
和早期提及的一样,这个IUnknown聚合版本对于每个组件是一样的,因为它除了invoke非聚合版本的正确实例不做其它的事情。为了便利,combase.h头文件包含一个宏,DECLARE_IUNKNOWN,它声明三个非聚合方法作为inline方法。它扩展为下列代码:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {     
    return GetOwner()->QueryInterface(riid,ppv);           
};                                                         
STDMETHODIMP_(ULONG) AddRef() {                            
    return GetOwner()->AddRef();                           
};                                                         
STDMETHODIMP_(ULONG) Release() {                           
    return GetOwner()->Release();                          
};
函数CUnkown::GetOwner接收一个指向属于这个组件的IUnknown接口的指针。对于一个聚合组件,这个所有者是一个外部组件。另外,这个组件属于它自己。包括在你类定义的公共章节中的DECLARE_IUNKNOWN宏。
3) 类构造器
Your class constructor should invoke the constructor method for the parent class, in addition to anything it does that is specific to your class. The following example is a typical constructor method:
自定义的类构造器应该调用父类的构造器方法,做除这之外的任何事情是指定到你的类。下列代码是一个典型的构造器方法:
CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr)
    : CUnknown(tszName, pUnk, phr)
{
    /* Other initializations */
};
这个方法有下列参数,这个参数直接传递到CUnknown构造器方法中。
z tszName指定这个组件的命名
z pUnk是一个指向聚合IUnknown的指针
z pHr指向一个HRESULT值的指针,显示这个方法的成功与否。
4 ) 总结
下列代码例子显示一个支持IUnknown的继承类和一个假定用ISomeInterface命令的接口:
class CMyComponent : public CUnknown, public ISomeInterface
{
public:
    DECLARE_IUNKNOWN;
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    {
        if( riid == IID_ISomeInterface )
        {
            return GetInterface((ISomeInterface*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }
 
    CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr)
        : CUnknown(tszName, pUnk, phr)
    {
        /* Other initializations */
    };
    // More declarations will be added later.
};
这个例子演示下列点:
z CUnkown类实现IUnknown接口。这个新组件继承自CUnknown和这个组件支持的任何接口。这个组件可以代替的继承从CUnknown继承的另一个基类。
z DECLARE_IUNKNOWN宏声明这个聚合IUnknown方法作为inline方法。
z CUnknown类提供INonDelegatingUnknown的实现。
z为了支持一个不是IUnknown的接口,这个继承类必须重载NonDelegatingQueryInterface方法并且为新接口的IID测试。
z这个类构造器为CUnknown调用这个构造器方法。
写一个filter的下一个步骤是启用下一个应用程序建立这个组件的新实例。这需要理解DLL和它们相对于类厂和类构造器方法。更多信息,查看how to create a dll。
二、 如何建立一个DLL
这篇文章描述如何在Microsoft DirectShow中作为的一个DLL来实现一个组件。它是how to implement IUnknown的延续。那篇描述你的继承自CUnknown基类的组件如何实现IUnknown接口。
本篇文章包括下列章节。
注册一个DirectShow filter(相对于一般COM对象)需求一些额外步骤,这些步骤在这篇中没有涉及。注册filters的信息,查看how to register directshow filters。
1、 类厂和厂模板
在一个客户端建立一个COM对象实例之前,它使用CoGetClassObject函数建立一个对象类厂的一个实例。客户然后调用类厂的IClassFactoy::CreateInstance方法。这个类厂实际上建立这个组件并且返回指向被请求的接口的指针。(CoCreateInstance函数在这个函数调用内部合并这些步骤)
下列演示方法调用的次序。
 
CoGetClassObject调用DllGetClassObjection函数,这个函数定义在DLL中。这个函数建立类厂和返回一个指向在类厂上一个接口的指针。DirectShow已经实现DllGetClassObject,但这个函数在一个特定方式下依赖你的代码。为了理解它如何工作,你必须理解DirectShow如何实现类厂。
 
一个类厂是一个COM对象专门建立另一个COM对象。每个类厂建立一种类型对象。在Directshow中每个类厂是一个同样C++对象的一个实例,CClassFactory。类厂是由另一个类指定的,CFactoryTemplate,也叫做factory template。每个类厂保持一个指针这个类厂模板的指针。这个厂模板包含关于指定组件的信息,如组件的类标识符(CLSID),并且一个指向一个建立这个组件的函数。
这个DLL声明一个全局厂模板数组,在DLL中的每个组件的一个。当DllGetClassOjbect生成一个新类厂,它用一个匹配的CLSID为一个模板搜索这个数组。假定它发现了一个,它建立一个类厂,这个类厂保持一个指向这个匹配模板的指针。当客户调用IClassFactory::CreateInstance时,这个类厂调用定义在这个模板中的实例化函数。
下列图解显示这个方法调用的次序。
 这个结构的好处在于对于自定义的组件,只需要处理很少的事情,如函数的实例化,而不是实现整个类厂。
2、 类厂模板数组
厂模板包含下列公共成员变量:
const WCHAR *              m_Name;                // Name 名称
const CLSID *              m_ClsID;               // CLSID
LPFNNewCOMObject           m_lpfnNew;             // 建立一个组件的实例的函数
LPFNInitRoutine            m_lpfnInit;            //初始化函数(可选)
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; //设置信息
两个函数指针,m_lpfnNew和m_lpfnInit,使用下列类型定义:
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
第一个对于这个组件实例化函数。第二个是一个可选的初始化函数。如果你提供一个初始化函数,它从DLL入口点函数内部调用(这个DLL入口点在这篇文章后面讨论)
建议你建立一个包含一个命令为CMyComponent的DLL,它继承自CUunknown。你必须在你的DLL中提供如下项:
z初始化函数,一个公共方法,返回CMyComponent的一个新实例。
z一个全局厂模板数组,命名为g_Templates。这个数组为CMyComponent包含厂模板。
z一个全局变量命名为g_cTempaltes,它指定这个数组的大小。
下列例子显示如何声明这些项:
// Public method that returns a new instance.
CUnknown * WINAPI CMyComponent::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
    CMyComponent *pNewObject = new CMyComponent(NAME("My Component"), pUnk, pHr );
    if (pNewObject == NULL) {
        *pHr = E_OUTOFMEMORY;
    }
    return pNewObject;
}

CFactoryTemplate g_Templates[1] =
{
    {
      L"My Component",                // Name
      &CLSID_MyComponent,             // CLSID
      CMyComponent::CreateInstance,   // Method to create an instance of MyComponent
      NULL,                           // Initialization function
      NULL                            // Set-up information (for filters)
    }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);   
CreateInstance方法调用类构造器并且返回一个指向这个新类实例的指针。这个参数pUnk是一个指向聚合IUnknown的指针。你可以简单传递这个参数到类构造器。这个参数pHr是一个指向一个HRESULT值的指针。这个类构造器调用这个到一个适当的值,但如果这个构造器失败了,设置这个值为E_OUTOFMEMORY。
NAME宏在调试生成中产生一个字符串但在retail生成中为NULL。它用来在这个例子中给出这个组件一个名称,这个名称出现在调试日志里,但在最终版本中不占用内存。
The CreateInstance method can have any name, because the class factory refers to the function pointer in the factory template. However, g_Templates and g_cTemplates are global variables that the class factory expects to find, so they must have exactly those names.

CreateInstance方法可以有任何名字,因为它的类厂引用到在厂模板中的函数指针。可是,g_Template和g_cTemplates是全局变量,这个变量是类厂需要发现的,所以它们必须有具体的名字。
3、 DLL函数
一个DLL必须实现下列函数以便它能被注册,反注册,和载入到内存中。
z DllMain:这个DLL入口点。DllMain存放库定义函数名称。DirectShow实现使用名字DllEntryPoint。更多信息,查看平台SDK。
z DllGetClassObject:建立一个类厂实例。在前面章节中描述。
z DllCanUnloadNoew:查询这个DLL是否能安全的载出。
z DllRegisterServer:为DLL建立注册入口。
zDllUnregisterServer:为DLL移除注册入口。
这些,第一到三由DirectShow实现。如何你的厂模板在m_lpfnInit成员变量中提供一个初始化函数,这个函数被DLL入口函数内部调用。更多何时系统调用DLL入口点函数的信息,查看在平台中的DllMain。
你必须实现DllRegisterServer和DllUnregisterServer,但DirectShow提供一个命名为AMovieDllRegisterServer2的必须的函数。你的组件可以简单的包装这个函数,如下面例子中所示:
STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2( TRUE );
}
STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2( FALSE );
}
可是,在DllRegisterServer和DllUnregisterServer中你能根据需要自定义注册处理。如果你的DLL包含一个fitler,你可能需要去做额外的工作。更多信息,查看how to register directshow fitlers。
在你的模块定义(.def)文件中,暴露所有DLL函数除了入口点函数。下列是一个.def文件的例子。
EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE
你可以使用regsvr32.exe工具注册DLL。
三、 如何注册DirectShow Filters
如何去注册DirectShow filters
此篇文章描述如何去实现一个Microsoft DirectShow filter自注册。它包含下列章节:
1、 注册键值的布局
DirectShow filter在两个地方被注册:
1) 这个DLL包含被注册的filter,作为这个filter的COM服务器。当一个应用程序调用CoCreateInstance建立这个filter时,Microsoft Window COM库使用注册入口来定位这个DLL。
2) 在一个filter类别中有关于这个filter的额外信息。这个信息启用system device enumerator和filter mapper来定位这个filter。
filter不需要去注册额外的filter信息。当DLL被注册为COM服务器,一个应用程序可以建立这个filter并且增加它到一个filter graph,如果你想你的filter被system device enumerator或者fitler mapper发现,你必须注册额外信息。
DLL的注册入口有下列键值:
HKEY_CLASSES_ROOT
    CLSID
        Filter CLSID
            REG_SZ: (Default) = Friendly name
            InprocServer32
                REG_SZ: (Default) = File name of the DLL
                REG_SZ: ThreadingModel = Both
filter信息的注册入口有如下键值:
HKEY_CLASSES_ROOT
    CLSID
        Category
            Instance
                Filter CLSID
                    REG_SZ: CLSID = Filter CLSID
                    REG_BINARY: FilterData = Filter information
                    REG_SZ: FriendlyName = Friendly name
category是一个filter类别的GUID。查看(filter categories)。这个filter信息是被打包到一个二进制格式中。当它为一个filter搜索这个注册时IFilterMapper2接口解包这个数据。
在注册表中所有filter类别GUID在如下键值下列举:
HKEY_CLASSES_ROOT/CLSID/{DA4E3DA0-D07D-11d0-BD50-00A0C911CE86}/Instance
2、 声明filte信息
第一步是声明这个filter信息,如果需要,DirectShow为描述filter,pins和媒体类型定义如下结构:
Structure  Description
AMOVIESETUP_FILTER   描述一个filter
AMOVIESETUP_PIN   描述一个PIN
AMOVIESETUP_MEDIATYPE  描述一个媒体类型
这些结构是嵌套的。AMOVEIESETUP_FILTER结构有一个指针指向一个AMOVEIESETUP_PIN结构的一个数组,它们中的每个有一个指针指向AMOVEIESETUP_MEDIATYPE结构的一个数组。同样,这些结构提供足够信息使IFilterMapper2接口定位一个filter。它们不是一个filter的完全描述。例如,如果这个filter建立同样PIN的多个实例,你应该为这个PIN只声明一个AMOVIESETUP_PIN结构。同样,一个filter不被请求去支持它注册的媒体类型的每个合并;也不需要注册它支持的每个媒体类型。
在DLL中声明设置结构作为全局变量。下列例子显示有一个输出PIN的filter:
static const WCHAR g_wszName[] = L"Some Filter";
 
AMOVIESETUP_MEDIATYPE sudMediaTypes[] = {
    { &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24 },
    { &MEDIATYPE_Video, &MEDIASUBTYPE_RGB32 },
};
 
AMOVIESETUP_PIN sudOutputPin = {
    L"",            // Obsolete, not used.
    FALSE,          // Is this pin rendered?
    TRUE,           // Is it an output pin?
    FALSE,          // Can the filter create zero instances?
    FALSE,          // Does the filter create multiple instances?
    &GUID_NULL,     // Obsolete.
    NULL,           // Obsolete.
    2,              // Number of media types.
    sudMediaTypes   // Pointer to media types.
};
 
AMOVIESETUP_FILTER sudFilterReg = {
    &CLSID_SomeFilter,      // Filter CLSID.
    g_wszName,              // Filter name.
    MERIT_NORMAL,           // Merit.
    1,                      // Number of pin types.
    &sudOutputPin           // Pointer to pin information.
};
这个filter名字是作为一个静态全局变量声明,因为它将在任何地方再次使用。
3、 声明厂模板
下一个步骤是为你的filter声明厂模板。一个厂模板是一个C++类,它包含类厂的信息。在你的DLL中,声明一个CFactoryTempate对象的全局数组,在你DLL中每个filter的或COM组件的一个。这个数组必须命名为g_Template。关于厂模板的更多信息,查看how to create a dll
厂模板的m_pAMovieSetup_Filter成员是一个指向先前描述的AMOVIESETUP_FILTER结构的指针。下列例子显示一个厂模板,使用先前例子中给出的结构:
CFactoryTemplate g_Templates[] = {
    {
        g_wszName,                      // Name.
        &CLSID_SomeFilter,              // CLSID.
        CSomeFilter::CreateInstance,    // Creation function.
        NULL,
        &sudFilterReg                   // Pointer to filter information.
    }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
如果你不声明任何filter信息,m_pAMovieSetup_Filter可以为NULL。
 
4、 实现DllRegisterServer
最终步骤是实现DllRegisterServer函数。这个DLL包含组件必须导出这个函数。这个函数钭通过一个设置应用程序调用,或者用户运行regsvr32.exe工具。
下列代码例子显示一个DllRegisterServer最小化实现:
STDAPI DllRegisterServer(void)
{
    return AMovieDllRegisterServer2(TRUE);
}
在g_Template数组中为每个组件AMovieDllRegiseterServer2函数建立注册表入口。可是,这个函数有几个限制。第一,它分配每个filter到DirectShow filters类别(CLSID_LegacyAmFilterCategory),但不是每个filter属于这个类别。捕获filter和压缩filter,例如,有它们自己的类别。第二,如果你的filter支持一个硬件设备,你可以需要去注册两个额外信息。这个信息AMovieDLLRegisterServer2不处理:medium和pin category。一个medium定义在一个硬件设备中通讯的方法,如一个bus。这个PIN类别定义一个PIN的函数发。关于mediums的信息,查看在Microsoft Windows Driver Development Kit(DDK)中的KSPIN_MEDIUM。对于pin类别的一个列表,查看pin property set。
如果你想去指定一个类别,一个medium或者一个PIN类别,从DllRegisterServer中调用IFilterMapper2::RegiseterFilter方法。这个方法保持一个指向REGFILTER2结构的指针,这个结构指定关于这个filter的信息。
为了使问题变得稍微复杂,REGFILTER2结构为注册的PIN支持两个不同格式。dwVersion成员指定这个格式。
1) 如果dwVersion是1,这个PIN格式是AMOVIESETUP_PIN(先前描述)
2) 如果dwVersion是2,这个PIN格式是REGFILTERPINS2。
这个REGFILTERPINS2结构包含每个PIN mediums和PIN类别的入口。并且,它为一些项目使用位标志,这些项目是AMOVIESETUP_PIN声明作为布尔值。
下列例子显示如何从DllRegisterServer中调用IFilterMapper2::RegisterFilter。
REGFILTER2 rf2FilterReg = {
    1,              // Version 1 (no pin mediums or pin category).
    MERIT_NORMAL,   // Merit.
    1,              // Number of pins.
    &sudPins        // Pointer to pin information.
};
 
STDAPI DllRegisterServer(void)
{
    HRESULT hr;
    IFilterMapper2 *pFM2 = NULL;
 
    hr = AMovieDllRegisterServer2(TRUE);
    if (FAILED(hr))
        return hr;
 
    hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
            IID_IFilterMapper2, (void **)&pFM2);
 
    if (FAILED(hr))
        return hr;
 
    hr = pFM2->RegisterFilter(
        CLSID_SomeFilter,                // Filter CLSID.
        g_wszName,                       // Filter name.
        NULL,                            // Device moniker.
        &CLSID_VideoCompressorCategory,  // Video compressor category.
        g_wszName,                       // Instance data.
        &rf2FilterReg                    // Pointer to filter information.
    );
    pFM2->Release();
    return hr;
}
5、 注册filter的指导方针
filter注册信息决定在intellgent connect期间如何filter graph manager函数。因而,它影响每个为DirectShow而写的应用程序,不仅仅是使用你的filter的一个。你应该确保你的filter行为正确,通过下列这个指导方针:
1) 需要在注册表中的filter数据?对于许多自定义filters,没有必要使此filter对于filter mapper或者system device enumerator可见。如你注册这个DLL,你的应用程序可以使用CoCreateInstance建立这个filter。在这种情况下,简单从厂模板里忽略AMOVIESETUP_FILTER结构。(一个缺点是你的filter在GrahpEdit中是不可见的。为了得到这个,你可以使用IFilterMapper2::CreateCategory方法建立一个私有“测试”类别。你应该只为调试生成做这点)
2) 选择这个正确filter类别。默认”directshow filter” 是类别。如果可能,注册你的filter到一个指定的类别。当IFilterMapper搜索一个filter,它忽略任何类别值merit比MERIT_DO_NOT_USE或者更低的filter。
3) 对于一个PIN在AMOVIESETUP_MEDIATYPE信息中避免指向MEDIATYPE_None。MEDIASUBTYPE_None,或者GUID_NULL。IFilterMapper2对待这些作为一个通配符,它可以降低graph-builder处理。
4) 尽可能选择最低merit值。这里有一些指导方针:
Type of filter Recommended merit
Default renderer MERIT_PREFERRED. For standard media types, however, a custom renderer should never be the default.
Non-default renderer MERIT_DO_NOT_USE or MERIT_UNLIKELY
Mux MERIT_DO_NOT_USE
Decoder MERIT_NORMAL
Spitter, parser MERIT_NORMAL or lower
Special purpose filter; any filter that is created directly by the application MERIT_DO_NOT_USE
Capture MERIT_DO_NOT_USE
"Fallback" filter; for example, the Color Space Converter Filter
MERIT_UNLIKELY
5) 不要在“DirectShow Filters”中注册一个接受24位RGB类别,你的filter将妨碍color space converter filter。
6、 反注册一个filter
为了反注册一个filter,实现DllUnregisterServer函数。在这个函数里,用一个FALSE调用DirectShow AMovieDllRegisterServer2函数。如果你注册这个filter时调用IFilterMapper2::RegiseterFilter,在这里调用IFilterMapper2::UnregisterFilter方法。
下列例子显示如何反注册一个filter:
STDAPI DllUnregisterServer()
{
    HRESULT hr;
    IFilterMapper2 *pFM2 = NULL;
 
    hr = AMovieDllRegisterServer2(FALSE);
    if (FAILED(hr))
        return hr;
 
    hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER,
            IID_IFilterMapper2, (void **)&pFM2);
 
    if (FAILED(hr))
        return hr;
 
    hr = pFM2->UnregisterFilter(&CLSID_VideoCompressorCategory,
            g_wszName, CLSID_SomeFilter);
 
    pFM2->Release();
    return hr;
}