当前位置:文档之家› 利用DirectShow开发自己的filter

利用DirectShow开发自己的filter

学习directshow已经有几天了,下面将自己的学习心得写下来,希望对其他的人有帮助。

Filter实质是个COM组件,所以学习开发Filter之前你应该对com的知识有点了解。

Com组件的实质是一个实现了纯虚指针接口的C++对象。

关于com的东西,这里不多讲。

一给vc配置DirectShow的开发环境无论开发Filter还是开发Dshow的应用程序都要配置一下开发环境的,其实就是包含一下dshow用到的头文件和动态库。

选择Tools菜单下面的Options。

在弹出的Option对话框配置如下图1 添加头文件选择动态库文件添加到工程中图2 添加动态库二创建工程以及Filter的入口函数创建工程一般情况下,创建Filter使用一个普通的Win32 DLL项目。

而且,一般Filter项目不使用MFC。

这时,应用程序通过CoCreateInstance函数Filter实例;Filter与应用程序在二进制级别的协作。

另外一种方法,也可以在MFC的应用程序项目中创建Filter。

在vc里新建一个工程,选择win32动态库,如下图图3图4这样生成了一个简单的DLL,只有一个Dllmain入口函数。

下面我要给这个filter添加入口函数了。

Filter是个基于DLL的com组件,所以一般的Filter都要实现下面几个入口函数首先定义导出函数要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建DLL文件时使用模块定义文件.Def。

使用导出函数关键字_declspec(dllexport)创建MyDll.dll就是在 .h文件中定义定义函数如下:为了用.def文件创建DLL,往该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码:LIBRARY MyFilter.axEXPORTSDllMain PRIVATEDllGetClassObject PRIVATEDllCanUnloadNow PRIVATEDllRegisterServer PRIVATEDllUnregisterServer PRIVATE其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。

我们可以在.def文件中的导出函数后加@n,如Max@1,Min@2,表示要导出的函数顺序号,在进行显式连时可以用到它。

该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。

然后要定义这些函数的实现了,其实这些工作dshow的基类里都已经替我们做好了,我们所要做的就拿来用就是了,最重要的三个函数的实现一般如下STDAPI DllUnregisterServer(){return AMovieDllRegisterServer2(FALSE);}extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved){return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);}其中DllEntryPoint 是在C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllentry.cpp定义的,如果感兴趣我们可以去看看它的定义。

AMovieDllRegisterServer2函数是在下面C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllsetup.cpp这个文件定义的,具体实现可以自己看看。

到了这里你恐怕要做点工作,还是要设置一下你的项目环境,否则恐怕你编译是通不过的,因为你用到了基类的一些东西,所以你要将你的dshow基类的定义和库文件包含进来。

首先包含#include Streams.h其次在Project –Setting菜单下配置自己的Filter输出的名字和连接的lib文件图5其中library modules里的包含的动态库如下c:\DX90SDK\Samples\C++\DirectShow\BaseClasses\debug\strmbasd.lib msvcrtd.lib quartz.lib vfw32.libwinmm.lib kernel32.lib advapi32.lib version.lib largeint.lib user32.lib gdi32.lib comctl32.lib ole32.lib olepro32.lib oleaut32.lib uuid.lib此时你编译一下,好像还是通不过,它提示有一个全局的用于实现COM接口的变量没有定义,不着急,下面我们就开始实现Filter的com接口。

三如何实现Filter 的类厂对象我们知道一个Filter是一个com组件,所以它com特性的实现其实在其基类中实现的,比如IUnknown 接口,我们直接从基类派生出我们的Filter后,它就支持com接口了,它就是一个com组件了。

所有的com组件为了实现二进制的封装,所以连创建的接口都封装了,因此每个com对象都有个类对象(也叫类厂对象,本身也是com对象,用来创建com组件)来创建com组件。

下面温习一下com组件的创建过程,其中涉及到几个函数1、当客户端要创建一个com组件时,它通过底层的COM API函数CoGetClassObject()使用SCM 的服务,这个函数请SCM把一个指针绑定到客户端请求的com组件的类对象上,其实在CoGetClassObject ()里它装载了该DLL的库,通过该dll的导出函数DllGetClassObject();DllGetClassObject根据客户端提供的com组件CLASSID,返回该com组件类对象的指针。

下面com组件的创建和SCM无关了。

2、客户端利用组件的类对象(类厂对象)的IClassFactory::CreateInstance方法创建com组件。

Filter在这里使用了一个类厂模板类来当作Filter的类厂对象。

下面看看类厂在DShow是怎么工作的。

类厂对象也是一个com组件。

本来DllGetClassObject是应该由我们自己完成一个函数,在directshow 基类里已经完成了,我们不用管它了。

它的功能就是来寻找这个DLL中的类厂对象,看是否有符合客户端请求的类厂对象。

DLL里声明了一个全局的类厂模板数组,当DllGetClassObject请求类厂对象的时候,它就搜索这个数组,看是否有和CLSID匹配的类厂对象。

当它找到一个匹配的CLSID,它就创建一个类厂对象,然后讲类厂指针返回给CoGetClassObject,然后客户端可以根据返回去的类厂指针,调用IClassFactory::CreateInstance方法创建组件,类厂就根据数组里定义的方法创建com组件。

factory template包含下列变量:const WCHAR * m_Name; // Nameconst CLSID * m_ClsID; // CLSIDLPFNNewCOMObject m_lpfnNew; // Function to create an instance of the componentLPFNInitRoutine m_lpfnInit; // Initialization function (optional)const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; // Set-up information (for filters)其中的两个函数指针m_lpfnNew and m_lpfnInit使用下面的定义你可以参照如下的方式定义你的类厂对象CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr){CMyFilter *pFilter = new CMyFilter(NAME("my Filter"), pUnk, pHr);if (pFilter== NULL){*pHr = E_OUTOFMEMORY;}return pFilter;}你可以声明自己的类厂数组如下:CFactoryTemplate g_Templates[1] ={{L"my filter", // Name&CLSID_MYFilter, // CLSIDCMyFilter::CreateInstance, // Method to create an instance of MyComponentNULL, // Initialization function&sudInfTee // Set-up information (for filters)}};int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);如果在这个com组件中你要支持多个filter,你可以在这个数组中继续添加就是了。

四如何实现自己的Filter在这里就要讲如何创建自己的Filter了,下面我们以写一个CTransformFilter为例1、选择一个基类,声明自己的类创建filter很简单,你只要根据自己的需要选择不同的基类Filter派生出自己的Filter,它就已经支持com特性了。

从逻辑上考虑,在写Filter之前,选择一个合适的Filter基类是至关重要的。

为此,你必须对几个Filter 的基类有相当的了解。

在实际应用中,Filter的基类并不总是选择CBaseFilter的。

相反,因为我们绝大部分写的都是中间的传输Filter(Transform Filter),所以基类选择CTransformFilter和CTransInPlaceFilter的居多。

如果我们写的是源Filter,我们可以选择CSource作为基类;如果是Renderer Filter,可以选择CBaseRenderer或CBaseVideoRenderer等。

总之,选择好Filter的基类是很重要的。

当然,选择Filter的基类也是很灵活的,没有绝对的标准。

相关主题