《DirectShow开发指南》学习笔记_3

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)派生而来的,这样既保留了直接访问数据成员的灵活性,又增加了一些有用的类成员接口,如媒体类型赋值、媒体类型比较、格式数据块内存的自动管理等。

猜你喜欢

转载自blog.csdn.net/Small_SaltedFish/article/details/81740092
今日推荐