Filter原理
Filter概述
Filter是DirectShow中最近本的概念。DirectShow使用Filter Graph来管理Filter(管理者叫做Filter Graph Manager)。Filter Graph是Filter的“容器”,而Filter是Filter Graph中的最小功能模块。
Filter一般由一个或多个Pin组成,Filter之间通过Pin相互连接,构成一条顺序的链路。Filter根据实现功能的不同大致可分为3类:Source Filters、Transform Filters和Rendering Filters。另一种使用的判别方法是根据Filter包含的输入Pin或者输出Pin的数量来判断:仅含有输出Pin,没有输入Pin的Filter为Source Filter;既有输入Pin又有输出Pin的Filter为Transform Filter;仅有输入Pin,没有输出Pin的Filter为Rendering Filter。
Filter是一种COM组件。为了实现在Filter Graph中的统一操作,每个Filter上都至少实现了IBaseFilter接口。实现Filter的文件一般是一个DLL,扩展名可以使.dll,但更多是.ax。跟普通的COM组件一样,Filter的创建是通过API函数CoCreateInstance来完成的,代码如下:
STDAPI CoCreateInstance(
REFCLSID rclsid, //class identifier (CLSID) of the object
LPUNKNOWN pUnkOuter, //Pointer to controlling IUnknown
DWORD dwClsContext, //Context for running executable code
REFIID riid, //Reference to the identifier of the interface
LPVOID * ppv //the interface pointer requested in riid
);
其中,参数rclsid指定要创建的Filter的CLSID;因为绝大多数情况下创建的Filter不是被“聚合”(Aggregation)的,所以pUnkOuter指定为NULL;dwClsContext可以指定为CLSCTX_INPROC_SERVER,以创建进程内组件对象;riid在创建Filter成功后获得的接口的ID,一般为IID_IBaseFilter,也可以是其他特殊的接口;ppv用于获得接口对象的指针。
Filter必须加入到Filter Graph且接入到工作链路中才能发挥作用。如果想绕过Filter Graph而直接使用Filter实现的模块功能,微软公司提供了另一种解决方案,就是将Filter功能移植成DirectX媒体对象(DMO)。
Filter的注册
既然Filter是一种COM组件,使用前就必须先注册。Filter的注册程序为regsvr32.exe(唯一操作系统目录的system32子目录下)。假设现在有一个Filter文件,它的完整路径为C:\DSFilter\myFilter.ax,那么注册这个Filter的方法为:在命令行状态下,执行regsvr32 C:\DSFilter\myFilter.ax。随后会弹出一个对话框,告诉Filter注册是否成功。
注销Filter也是使用regsvr32.exe程序,方法为加命令行参数/u,即执行regsvr32 /u C:\DSFilter\myFilter.ax。另外一个命令行参数“/s”可以禁止执行regsvr32.exe后弹出提示对话框。
问:给出一个Filter的CLSID,如何判断这个Filter是否已经注册?
答:最简单的办法就是使用CoCreateInstance函数创建这个Filter,然后根据函数的返回值去判断。代码如下:
BOOL IsFilterRegistered(CLSID inFilterId)
{
IBaseFilter *pFilter = NULL;
if (SUCCEEDED(CoCreateInstance(inFilterId, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pFilter)))
{
pFilter->Release();
return TRUE;
}
return FALSE;
}
一般一个Filter项目都会包含一个.def文件,用于定义4个导出函数。代码如下:
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
这是COM规范的实现,其中,DllGetClassObject在创建Filter对象的时候被调用,根据CLSID返回对应的类工厂对象;DllCanUnloadNow用于判断是否可以从内存中卸载Filter DLL,即DLL中实现的所有COM对象是否都已经释放;DllRegisterServer和DllUnregisterServer完成COM组件的自注册功能,一般实现代码如下:
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2(FALSE);
}
AMovieDllRegisterServer2是DirectShow SDK提供的专门用于Filter注册的一个API函数,可以再SDK基类源代码的dllsrtup.cpp中找到这个函数的实现。
问:如何在应用程序中注册(或注销)某个Filter文件?
答:只要在应用程序中使用LoadLibrary装载这个Filter文件,并得到它的导出函数DllRegisterServer(或DllUnregisterServer)的入口地址,然后执行它就可以了。参考代码如下:
BOOL RegisterFilter(const char* inFilterAx)
{
typedef int(WINAPI *REGISTER_FUNC)(void);
REGISTER_FUNC MyFunc = NULL;
HMODULE hModule = ::LoadLibrary(inFilterAx);
if (hModule)
{
MyFunc = (REGISTER_FUNC)GetProcAddress(hModule, "DllRegisterServer");
BOOL pass = (MyFunc != NULL);
if (pass)
{
MyFunc();
}
::FreeLibrary(hModule);
return pass;
}
return FALSE;
}
Filter的注册信息一般包括两部分:基本的COM信息和Filter特有信息。前者是基本的COM组件所必需的信息,没有这部分内容,将无法成功创建Filter;后者是描述Filter的信息(包括Filter注册的类型目录、Filter上Pin的数量、支持的媒体类型等),这部分内容会被系统枚举器或者Filter映射器访问到,但并不是必需的。所谓“注册”,也就是向系统注册表的相应位置写入一些数据。
Filter的媒体类型
媒体类型实际上是DirectShow定义的一个数据结构AM_MEDIA_TYPE,代码如下:
typedef struct _AMMediaType
{
GUID majortype;
GUID subtype;
BOOL bFixedSizeSamples;
BOOL bTemporalCompression;
ULONG lSampleSize;
GUID formattype;
IUnknown *pUnk;
ULONG cbFormat;
BYTE *pbFormat;
}AM_MEDIA_TYPE;
从代码中可以看出,媒体类型主要用3部分来描述:majortype(主类型)、subtype(辅助说明类型)和formattype(格式细节类型)。这3部分各自用一个GUID来标识。它们的作用分别是:majortype定性地描述媒体类型,如制定这是一个视频(MEDIATYPE_Video)、音频(MEDIATYPE_Audio)或者字节流(MEDIATYPE_Stream)等;subtype辅助说明majortype,指明具体是哪种格式,例如,若majortype是视频,subtype可以进一步指明这是UYVY(MEDIASUBTYPE_UYVY)、YUY2(MEDIASUBTYPE_YUY2)、RGB24(MEDIASUBTYPE_RGB24)还是RGB32(MEDIASUBTYPE_RGB32)等,若majortype是音频,subtype可以进一步指明这是PCM格式(MEDIASUBTYPE_PCM)还是AC3格式(MEDIASUBTYPE_DOLBY_AC3)等;formattype指定了一种进一步描述格式细节的数据结构类型,格式细节描述的内容主要包括视频图像的大小、帧率,或者音频的采样频率、量化精度等参数,这个描述格式细节的数据块指针保存在pbFormat成员中。
AM_MEDIA_TYPE结构中的其他成员,如bFixedSizeSamples、bTemporalCompression和lSampleSize,它们都是可选参数,也就是说,Filter并不一定定义这些参数,在Filter中引用这些参数的值也并不总是“可信”的。这一点对于Filter开发人员来说尤为重要。
常见的媒体类型:
主类型majortype包括如下媒体类型:
MEDIATYPE_Video; //纯视频(数字)
MEDIATYPE_Audio; //纯音频(数字)
MEDIATYPE_AnalogVideo; //模拟视频,一般是视频采集卡输入的数据类型
MEDIATYPE_AnalogAudio; //模拟音频,一般是声卡采集输入的数据类型
MEDIATYPE_Text; //文字
MEDIATYPE_Midi; //Midi音乐
MEDIATYPE_Stream; //字节流,如(Pull模式)文件源的输出数据类型
MEDIATYPE_Interleaved; //数码摄像机输入的DV数据类型
MEDIATYPE_MPEG1SystemStream; //MPEG1的系统流
MEDIATYPE_MPEG2_PACK; //MPEG2的数据包
MEDIATYPE_MPEG2_PES; //MPEG2分组数据
MEDIATYPE_DVD_ENCRYPTED_PACK; //DVD播放用到的媒体类型
MEDIATYPE_DVD_NAVIGATION;
辅助说明类型subtype包括如下媒体类型:
MEDIASUBTYPE_YUY2; //各种YUV的格式
MEDIASUBTYPE_YVYU;
MEDIASUBTYPE_YUYV;
MEDIASUBTYPE_UYVY;
MEDIASUBTYPE_YVU9;
MEDIASUBTYPE_Y411;
MEDIASUBTYPE_RGB4; //各种RGB的格式
MEDIASUBTYPE_RGB8;
MEDIASUBTYPE_RGB565;
MEDIASUBTYPE_RGB555;
MEDIASUBTYPE_RGB24;
MEDIASUBTYPE_RGB32;
MEDIASUBTYPE_ARGB32; //包含Alpha通道的RGB32
MEDIASUBTYPE_Overlay; //Overlay Mixer Filter使用的媒体类型
MEDIASUBTYPE_MPEG1Packet; //MPEG1的数据包
MEDIASUBTYPE_MPEG1Payload; //MPEG1负载(通常表示视频)
MEDIASUBTYPE_MPEG1AudioPayload; //MPEG1音频负载
MEDIASUBTYPE_MPEG1System; //MPEG1系统流
MEDIASUBTYPE_MPEG1VideoCD; //VCD
MEDIASUBTYPE_MPEG1Video; //MPEG1视频数据
MEDIASUBTYPE_MPEG1Audio; //MPEG1音频数据
MEDIASUBTYPE_Avi; //AVI
MEDIASUBTYPE_Asf; //ASF
MEDIASUBTYPE_QTMovie; //QuickTime电影
MEDIASUBTYPE_PCM; //PCM
MEDIASUBTYPE_WAVE; //WAVE与PCM相同
MEDIASUBTYPE_dvsd; //DV
MEDIASUBTYPE_dvhd;
MEDIASUBTYPE_dvsl;
MEDIASUBTYPE_MPEG2_VIDEO; //MPEG2视频
MEDIASUBTYPE_MPEG2_PROGRAM; //MPEG2程序流
MEDIASUBTYPE_MPEG2_TRANSPORT; //MPEG2传输流
MEDIASUBTYPE_MPEG2_AUDIO; //MPEG2音频
MEDIASUBTYPE_DOLBY_AC3; //杜比的AC3音频格式
MEDIASUBTYPE_DVD_SUBPICTURE; //DVD的字图片流
MEDIASUBTYPE_DVD_LPCM_AUDIO; //DVD的线性PCM音频格式
MEDIASUBTYPE_DVD_NAVIGATION_PCI; //DVD播放用到的一些媒体类型
MEDIASUBTYPE_DVD_NAVIGATION_DSI;
MEDIASUBTYPE_DVD_NAVIGATION_PROVIDER;
格式细节类型formattype对应使用的数据结构代码如下:
FORMAT_None;
FORMAT_DvInfo; //描述DV数据,使用DVINFO数据结构
FORMAT_MPEGVideo; //描述MPEG1数据,使用MPEG1VIDEOINFO数据结构
FORMAT_MPEG2Video; //描述MPEG2数据,使用MPEG2VIDEOINFO数据结构
FORMAT_VideoInfo; //描述(一般的)视频数据,使用VIDEOINFOHEADER数据结构
FORMAT_VideoInfo2; //描述(扩展的)视频数据,使用VIDEOINFOHEADER2数据结构
FORMAT_WaveFormatEx;//描述音频数据,使用WAVEFORMATEX数据结构
当使用一个AM_MEDIA_TYPE数据结构描述媒体类型时,如果majortype、subtype和formattype都指定了一个特定的GUID值,那么称这种媒体类型为“完全指定的媒体类型”;这3个部分只要有一个指定为GUID_NULL,则称之为“不完全指定的媒体类型”。GUID_NULL具有“通配符”的作用。为了方便使用,DirectShow提供了另一个C++类——CMediaType来操作媒体类型。CMediaType类是从AM_MEDIA_TYPE数据结构中“公共”(public)派生而来的,这样既保留了直接访问数据成员的灵活性,又增加了一些有用的类成员接口,如媒体类型赋值、媒体类型比较、格式数据块内存的自动管理等。