深入浅出DirectshowFilter.docx
- 文档编号:14978856
- 上传时间:2023-06-28
- 格式:DOCX
- 页数:39
- 大小:35.11KB
深入浅出DirectshowFilter.docx
《深入浅出DirectshowFilter.docx》由会员分享,可在线阅读,更多相关《深入浅出DirectshowFilter.docx(39页珍藏版)》请在冰点文库上搜索。
深入浅出DirectshowFilter
深入浅出DirectShowFilter
Filter概述
Filter是一个COM组件,由一个或多个Pin组成。
Pin也是一个COM组件。
Filter文件的扩展名为.ax,但也可以是.dll。
Filter根据其包含Inputpin或Outputpin的情况(或在FilterGraph的位置),大致可分为三类:
SourceFilter(仅有Outputpin)、TransformFilter(同时具有Inputpin和Outputpin)和RendererFilter(仅有Inputpin)。
一般情况下,创建Filter使用一个普通的Win32DLL项目。
而且,一般Filter项目不使用MFC。
这时,应用程序通过CoCreateInstance函数Filter实例;Filter与应用程序在二进制级别的协作。
另外一种方法,也可以在MFC的应用程序项目中创建Filter。
这种情况下,Filter不需注册为COM组件,Filter与应用程序之间的协作是源代码级别的;创建Filter实例,不再使用CoCreateInstance函数,而是直接new出一个Filter对象,如下:
m_pFilterObject=newCFilterClass();
//maketheinitialrefcount1tomatchCOMcreation
m_pFilterObject->AddRef();
因为Filter的基类实现了对象的引用计数,所以即使在第二种情况下,对创建后的Filter对象的操作也完全可以遵循COM标准。
Filter是一个独立功能模块,最好不要将Filter依赖于其他第三方的DLL。
因为Filter具有COM的位置透明性特点,Filter文件可以放在硬盘的任何位置,只要位置移动后重新注册。
但此时,如果Filter依赖其他DLL,则Filter对该DLL的定位就会出现问题。
Filter不能脱离FilterGraph单独使用。
所以,如果你想绕过FilterGraph直接使用Filter实现的模块功能,请将你的Filter移植成DMO(DirectXMediaObject)。
2.Filter的注册
Filter是COM组件,所以在使用前一定要注册。
Filter的注册程序为regsvr32.exe。
如果带上命令行参数/u,表示注销;如果带上是/s,表示不弹出任何注册/注销成功与否的提示对话框。
如果你想在BuildFilter项目的时候进行自动注册,请在VC的Projectsettings的CustomBuild页如下设置:
Description:
Registerfilter
Commands:
regsvr32/s/c$(TargetPath)
echoregsvr32exe.time>$(TargetDir)\$(TargetName).trg
Outputs:
$(TargetDir)\$(TargetName).trg
Filter的注册信息包括两部分:
基本的COM信息和Filter信息。
注册信息都存放在注册表中。
前者的位置为:
HKEY_CLASSES_ROOT\CLSID\FilterClsid\,后者的位置为:
HKEY_CLASSES_ROOT\CLSID\Category\Instance\FilterClsid\。
COM信息标示了Filter是一个标准的可以通过CoCreateInstance函数创建的COM组件,Filter信息标示了我们通过Graphedit看到的描述这个Filter的信息。
如果你不想让Graphedit看到(或者让Filter枚举器找到)你写的Filter,你完全可以不注册Filter信息。
而且不用担心,你这么做也完全不会影响Filter的功能。
屏蔽注册Filter信息的方法也很简单。
因为CBaseFilter实现了IAMovieSetup接口的两个函数:
Register和Unregister。
我们只需重载这两个函数,直接returnS_OK就行了。
(注意:
IAMovieSetup是用以注册Filter信息部分的接口,但已经废弃,仅在AMovieDllRegisterServer和AMovieDllUnregisterServer调用才会用到。
新写的Filter注册函数一般使用AMovieDllRegisterServer2,这个函数不使用IAMovieSetup接口。
如果想要不注册Filter信息,最好自己实现Filter的两个导出函数:
DllRegisterServer和DllUnregisterServer,其中只使用RegisterAllServers函数注册OleServer。
)
Filter的Merit值。
这个值是微软的“智能连接”函数使用的。
在Graphedit中,当我们加入一个SourceFilter后,在它的pin上执行“Render”,会自动连上一些Filter。
Merit的值参考如下:
MERIT_PREFERRED=0x800000,
MERIT_NORMAL=0x600000,
MERIT_UNLIKELY=0x400000,
MERIT_DO_NOT_USE=0x200000,
MERIT_SW_COMPRESSOR=0x100000,
MERIT_HW_COMPRESSOR=0x100050
Merit值只有大于MERIT_DO_NOT_USE的时候才有可能被“智能连接”使用;Merit的值越大,这个Filter的机会就越大。
3.Filter之间Pin的连接过程
Filter只有加入到FilterGraph中并且和其它Filter连接成完整的链路后,才会发挥作用。
Filter之间的连接(也就是Pin之间的连接),实际上是连接双方的一个Mediatype的协商过程。
连接的方向总是从Outputpin指向Inputpin。
连接的大致过程为:
如果调用连接函数时已经指定了完整的Mediatype,则用这个Mediatype进行连接,成功与否都结束连接过程;如果没有指定或不完全指定了Mediatype,则进入下面的枚举过程。
枚举欲连接的Inputpin上所有的Mediatype,逐一用这些Mediatype与Outputpin进行连接(如果连接函数提供了不完全Mediatype,则要先将每个枚举出来的Mediatype与它进行匹配检查),如果Outputpin也接受这种Mediatype,则Pin之间的连接宣告成功;如果所有Inputpin上枚举的Mediatype,Outputpin都不支持,则枚举Outputpin上的所有Mediatype,并逐一用这些Mediatype与Inputpin进行连接。
如果Inputpin接受其中的一种Mediatype,则Pin之间的连接到此也宣告成功;如果Outputpin上的所有Mediatype,Inputpin都不支持,则这两个Pin之间的连接过程宣告失败。
每个Pin都可以实现GetMediaType函数来提供该Pin上支持的所有PreferredMediatype(但一般只在Outputpin上实现,Inputpin主要实现CheckMediaType看是否支持当前提供的Mediatype就行了)。
连接过程中,Pin上枚举得到的所有Mediatype就是这里提供的。
在CBasePin类中有一个protected的成员变量m_bTryMyTypesFirst,默认值为false。
在我们定制Filter的Outputpin中改变这个变量的值为true,可以定制我们自己的连接过程(先枚举Outputpin上的Mediatype)。
当Pin之间的连接成功后,各自的pin上都会调用CompleteConnect函数。
我们可以在这里取得一些连接上的Mediatype的信息,以及进行一些计算等。
在Outputpin的CompleteConnect实现中,还有一个重要的任务,就是协商FilterGraph运行起来后Sample传输使用的内存配置情况。
这同样是一个交互过程:
首先要询问一下Inputpin上的配置要求,如果Inputpin提供内存管理器(Allocator),则优先使用Inputpin上的内存管理器;否则,使用Outputpin自己生成的内存管理器。
我们一般都要实现DecideBufferSize来决定存放Sample的内存大小。
注意:
这个过程协商完成之后,实际的内存并没有分配,而要等到Outputpin上的Active函数调用。
4.FilterMediatype概述
Mediatype一般可以有两种表示:
AM_MEDIA_TYPE和CMediaType。
前者是一个Struct,后者是从这个Struct继承过来的类。
每个Mediatype有三部分组成:
Majortype、Subtype和Formattype。
这三个部分都使用GUID来唯一标示。
Majortype主要定性描述一种Mediatype,比如指定这是一个Video,或Audio或Stream等;Subtype进一步细化Mediatype,如果Video的话可以进一步指定是UYVY或YUY2或RGB24或RGB32等;Formattype用一个Struct更进一步细化Mediatype。
如果Mediatype的三个部分都是指定了某个具体的GUID值,则称这个Mediatype是完全指定的;如果Mediatype的三个部分中有任何一个值是GUID_NULL,则称这个Mediatype是不完全指定的。
GUID_NULL具有通配符的作用。
常用的Majortype:
MEDIATYPE_Video;
MEDIATYPE_Audio;
MEDIATYPE_AnalogVideo;//Analogcapture
MEDIATYPE_AnalogAudio;
MEDIATYPE_Text;
MEDIATYPE_Midi;
MEDIATYPE_Stream;
MEDIATYPE_Interleaved;//DVcamcorder
MEDIATYPE_MPEG1SystemStream;
MEDIATYPE_MPEG2_PACK;
MEDIATYPE_MPEG2_PES;
MEDIATYPE_DVD_ENCRYPTED_PACK;
MEDIATYPE_DVD_NAVIGATION;
常用的Subtype:
MEDIASUBTYPE_YUY2;
MEDIASUBTYPE_YVYU;
MEDIASUBTYPE_YUYV;
MEDIASUBTYPE_UYVY;
MEDIASUBTYPE_YVU9;
MEDIASUBTYPE_Y411;
MEDIASUBTYPE_RGB4;
MEDIASUBTYPE_RGB8;
MEDIASUBTYPE_RGB565;
MEDIASUBTYPE_RGB555;
MEDIASUBTYPE_RGB24;
MEDIASUBTYPE_RGB32;
MEDIASUBTYPE_ARGB32;//Containsalphavalue
MEDIASUBTYPE_Overlay;
MEDIASUBTYPE_MPEG1Packet;
MEDIASUBTYPE_MPEG1Payload;//Videopayload
MEDIASUBTYPE_MPEG1AudioPayload;//Audiopayload
MEDIASUBTYPE_MPEG1System;//A/Vpayload
MEDIASUBTYPE_MPEG1VideoCD;
MEDIASUBTYPE_MPEG1Video;
MEDIASUBTYPE_MPEG1Audio;
MEDIASUBTYPE_Avi;
MEDIASUBTYPE_Asf;
MEDIASUBTYPE_QTMovie;
MEDIASUBTYPE_PCM;
MEDIASUBTYPE_WAVE;
MEDIASUBTYPE_dvsd;//DV
MEDIASUBTYPE_dvhd;
MEDIASUBTYPE_dvsl;
MEDIASUBTYPE_MPEG2_VIDEO;
MEDIASUBTYPE_MPEG2_PROGRAM;
MEDIASUBTYPE_MPEG2_TRANSPORT;
MEDIASUBTYPE_MPEG2_AUDIO;
MEDIASUBTYPE_DOLBY_AC3;
MEDIASUBTYPE_DVD_SUBPICTURE;
MEDIASUBTYPE_DVD_LPCM_AUDIO;
MEDIASUBTYPE_DVD_NAVIGATION_PCI;
MEDIASUBTYPE_DVD_NAVIGATION_DSI;
MEDIASUBTYPE_DVD_NAVIGATION_PROVIDER;
常用的Formattype:
FORMAT_None
FORMAT_DvInfoDVINFO
FORMAT_MPEGVideoMPEG1VIDEOINFO
FORMAT_MPEG2VideoMPEG2VIDEOINFO
FORMAT_VideoInfoVIDEOINFOHEADER
FORMAT_VideoInfo2VIDEOINFOHEADER2
FORMAT_WaveFormatExWAVEFORMATEX
5.Filter之间的数据传送
Filter之间的数据是通过Sample来传送的。
Sample是一个COM组件,拥有自己的一段数据缓冲。
Sample由Allocator统一管理。
如下图所示:
1完整类型
如果媒体类型每一个部分都定义的很完成,那么pin就严格按照定义的类型进行连接。
如果不匹配,连接失败。
2部分媒体类型
如果媒体类型的机构中,majortype,subtype,orformattype的值为GUID_NULL,这个值是一个通配符号。
任何类型都可以匹配。
3没有媒体类型
如果filter图表管理器传递过来一个NULL的指针,这个pin就可以和任意的类型的媒体类型匹配。
Filter之间数据传送的方式有两种:
Push模式和Pull模式。
所谓Push模式,即Sourcefilter自己能够产生数据,并且一般在它的Outputpin上有独立的子线程负责将数据发送出去,常见的情况如WDM模型的采集卡的LiveSourceFilter;而所谓Pull模式,即Sourcefilter不具有把自己的数据送出去的能力,这种情况下,一般Sourcefilter后紧跟着接一个ParserFilter或SplitterFilter,这种Filter一般在Inputpin上有个独立的子线程,负责不断地从Sourcefilter索取数据,然后经过处理后将数据传送下去,常见的情况如Filesource。
Push模式下,Sourcefilter是主动的;Pull模式下,Sourcefilter是被动的。
而事实上,如果将上图Pull模式中的Sourcefilter和SplitterFilter看成另一个虚拟的Sourcefilter,则后面的Filter之间的数据传送也与Push模式完全相同。
那么,数据到底是怎么通过连接着的Pin传送的呢?
首先来看Push模式。
在Sourcefilter后面Filter的Inputpin上,一定实现了一个IMemInputPin接口,数据正是通过上一级Filter调用这个接口的Receive方法进行传送的。
值得注意的是,数据从Outputpin通过Receive方法调用传送到Inputpin上,并没有进行内存拷贝,它只是一个相当于数据到达的“通知”。
再看一下Pull模式。
Pull模式下的Sourcefilter的Outputpin上,一定实现了一个IAsyncReader接口;其后面的SplitterFilter,就是通过调用这个接口的Request方法或者SyncRead方法来获得数据。
SplitterFilter然后像Push模式一样,调用下一级Filter的Inputpin上的IMemInputPin接口Receive方法实现数据的往下传送。
一个DirectShow的应用程序,至少会有两条线程:
主线程和Filter用于数据传送的子线程。
既然是多线程,就不可避免会出现线程同步问题。
Filter的状态改变都在主线程中完成,Filter的数据相关操作都在数据线程中调用。
各线程一些主要函数调用参考如下:
Streamingthread(s):
IMemInputPin:
:
Receive,IMemInputPin:
:
ReceiveMultiple,IPin:
:
EndOfStream,IMemAllocator:
:
GetBuffer.
Applicationthread:
IMediaFilter:
:
Pause,IMediaFilter:
:
Run,IMediaFilter:
:
Stop,IMediaSeeking:
:
SetPositions,IPin:
:
BeginFlush,IPin:
:
EndFlush.
Either:
IPin:
:
NewSegment.
这些函数切忌混合调用,否则会引起线程的死锁。
另外值得注意的是,BeginFlush和EndFlush属于主线程调用,而不是数据线程调用。
6.Transformfilter和Trans-in-placefilter的区别
首先,这两种Filter是有共同点的,因为Trans-in-placefilter本身就是从Transformfilter中继承过来的。
其次,我们要明白的是,Trans-in-placefilter“尽力”使自己的Inputpin和Outputpin使用相同的Allocator,以免去一次Sample数据的memcpy。
我们说“尽力”,就是说Trans-in-placefilter也未必能够实现它的初衷。
(如果Trans-in-placefilter使用的Allocator是ReadOnly的,而Trans-in-placefilter又要修改Sample的数据,则Trans-in-placefilter的Inputpin和Outputpin将不得不使用不同的Allocator。
)
Trans-in-placefilter有一个protected的成员变量m_bModifiesData,默认值为true。
如果你确信定制Trans-in-placefilter不需要修改Sample数据,则将m_bModifiesData赋值为false,这样可以保证Inputpin和Outputpin使用相同的Allocator。
Trans-in-placefilter的实现主要体现在以下三个函数:
CTransInPlaceFilter:
:
CompleteConnect、CTransInPlaceInputPin:
:
GetAllocator和CTransInPlaceInputPin:
:
NotifyAllocator。
CompleteConnect中进行必要的重连(Reconnect),保证Trans-in-placefilter的Inputpin和Outputpin使用相同的Mediatype。
GetAllocator能够取得Trans-in-placefilter下一级Filter的Inputpin上的Allocator。
NotifyAllocator“尽力”使Trans-in-placefilter的Inputpin和Outputpin使用同一个Allocator。
7.IMediaSeeking的实现
IMediaSeeking的实现在Filter上,但应用程序应该从FilterGraphManager上得到这个接口。
在Filter级别,FilterGraphManager首先从Rendererfilter开始询问上一级Filter的Outputpin是否支持IMediaSeeking接口。
如果支持,则返回这个接口;如果不支持,则继续往上一级Filter询问,直到Sourcefilter。
一般在Sourcefilter的Outputpin上实现IMediaSeeking接口。
(如果是Filesource,一般在ParserFilter或SplitterFilter实现这个接口。
)对于Filter开发者来说,如果我们写的是Sourcefilter,就要在Filter的Outputpin上实现IMediaSeeking接口;如果写的是Transformfilter,只需要在Outputpin上将用户的接口请求往上传递给上一级Filter的Outputpin;如果写的是RendererFilter,需要在Filter上将用户的接口请求往上传递给上一级Filter的Outputpin。
注意:
为了保证Seek操作后Stream的同步性,如果实际实现IMediaSeeking接口的Filter有多个Outputpin,一般仅有一个pin支持Seek操作。
对于你定制的Transformfilter,如果有多个Inputpin,你需要自己决定当Outputpin接收到IMediaSeeking接口请求时选择哪一条路径往上继续请求。
应用程序能够在任何时候(running,pausedorstopped)对Filtergraph执行Seek操作。
但当Filtergraph正在running的时候,Filtergraphmanager会先pause住,执行完Seek操作后,再重新run起来。
IMediaSeeking可以有如下几种Seek的时间格式:
TIME_FORMAT_FRAMEVideoframes.
TIME_FORMAT_SAMPLESamplesinthestream.
TIME_FORMAT_FIELDInterlacedvideofields.
TIME_FORMAT_BYTEByteoffsetwithinthestream.
TIME_FORMAT_MEDIA_TIMEReferencetime(100-nanosecondunits).
但实现这个接口的Filter未必支持所有的这些格式。
一般Filter都会支持TIME_FORMAT_MEDIA_TIME,当使用其它的格式时,最好调用IMediaSeeking:
:
IsFormatSupported进行一下确认。
对于Filter,不赞成使用IMediaPosition接口。
IMediaPosition是用以支持Automation的(比如VB里面使用DirectShow),IMediaSeeking不支持Automation。
8.Filter的状态转换
Filter有三种状态:
stopped,paused,running。
paused是一种中间状态,stopped状态到running状态必定经过paused状态。
paused可以理解为数据就绪状态,是为了快速切换到running状态而设计的。
在paused状态下,数据线程是启动的,但被Rendererfilter阻塞了。
paused与running两者间的状态转换,对于Sour
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入浅出 DirectshowFilter