版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、深入浅出 DirectShow FilterFilter 概述Filter 是一个 COM 组件,由一个或多个 Pin 组成。 Pin 也是一个 COM 组件。 Filter 文件的扩展名为.ax,但也可以是.dll。Filter根据其包含In put pi n或Output pin 的情况(或在 Filter Graph 的位置),大致可分为三类: Source Filter( 仅有 Output pin) 、Transform Filter( 同时具有 Input pin 和 Output pin) 和 Renderer Filter( 仅有 Input pin) 。一般情况下,创建 Fi
2、lter 使用一个普通的 Win32 DLL 项目。而且,一般 Filter 项目不 使用 MFC 。这时,应用程序通过 CoCreateInstance 函数 Filter 实例; Filter 与应用程序在 二进制级别的协作。另外一种方法,也可以在 MFC 的应用程序项目中创建 Filter 。这种情 况下, Filter 不需注册为 COM 组件, Filter 与应用程序之间的协作是源代码级别的;创建 Filter 实例,不再使用 CoCreateInstance 函数,而是直接 new 出一个 Filter 对象,如下: m_pFilterObject = new CFilterCl
3、ass();/ make the initial refcount 1 to match COM creationm_pFilterObject ->AddRef();因为 Filter 的基类实现了对象的引用计数, 所以即使在第二种情况下, 对创建后的 Filter 对象的操作也完全可以遵循 COM 标准。Filter 是一个独立功能模块,最好不要将 Filter 依赖于其他第三方的 DLL 。因为 Filter 具有 COM 的位置透明性特点, Filter 文件可以放在硬盘的任何位置,只要位置移动后重新 注册。但此时,如果 Filter依赖其他DLL,则Filter对该DLL的定位
4、就会出现问题。Filter 不能脱离 Filter Graph 单独使用。所以,如果你想绕过 Filter Graph 直接使用Filter 实现的模块功能,请将你的 Filter 移植成 DMO ( DirectX Media Object )。2. Filter 的注册Filter 是 COM 组件,所以在使用前一定要注册。 Filter 的注册程序为 regsvr32.exe如果带上命令行参数 /u ,表示注销;如果带上是 /s ,表示不弹出任何注册 / 注销成功与否的 提示对话框。 如果你想在 Build Filter 项目的时候进行自动注册, 请在 VC 的 Project sett
5、ings 的 Custom Build 页如下设置:Description: Register filterCommands: regsvr32 /s /c $(TargetPath)echo regsvr32 exe.time > $(TargetDir)$(T argetName).trgOutputs: $(TargetDir)$(TargetName).trgFilter 的注册信息包括两部分:基本的 COM 信息和 Filter 信息。注册信息都存放在注 册表中。前者的位置为: HKEY_CLASSES_ROOTCLSIDFilter ,后者的位置为:。COM信息标示了 Fil
6、ter是一个标准的可以通过 CoCreateInstance 函数创建的 COM 组件, Filter 信息标示了我们 通过 Graphedit 看到的描述这个 Filter 的信息。如果你不想让 Graphedit 看到(或者让 Filter 枚举器找到)你写的 Filter ,你完全可以不注册 Filter 信息。而且不用担心,你这么做也完 全不会影响 Filter 的功能。屏蔽注册 Filter 信息的方法也很简单。 因为 CBaseFilter 实现了 IAMovieSetup 接口的 两个函数: Register 和 Unregister 。我们只需重载这两个函数, 直接 retur
7、n S_OK 就行了。(注意:lAMovieSetup 是用以注册Filter信息部分的接口,但已经废弃,仅在AMovieDllRegisterServer 和 AMovieDllUnregisterServer 调用才会用到。新写的 Filter 注册函数一般使用 AMovieDllRegisterServer2 ,这个函数不使用 lAMovieSetup 接口。 如 果想要不注册 Filter 信息,最好自己实现 Filter 的两个导出函数: DllRegisterServer 和DllUnregisterServer ,其中只使用 RegisterAllServers 函数注册 Ole
8、 Server 。)Filter 的 Merit 值。这个值是微软的“智能连接”函数使用的。在 Graphedit 中,当我 们加入一个 Source Filter 后,在它的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 = 0x100
9、050Merit 值只有大于 MERIT_DO_NOT_USE 的时候才有可能被“智能连接”使用; Merit 的 值越大,这个 Filter 的机会就越大。3. Filter 之间 Pin 的连接过程Filter 只有加入到 Filter Graph 中并且和其它 Filter 连接成完整的链路后,才会发挥作 用。 Filter 之间的连接(也就是 Pin 之间的连接),实际上是连接双方的一个 Media type 的协商过程。连接的方向总是从 Output pin 指向 Input pin 。连接的大致过程为:如果调 用连接函数时已经指定了完整的 Media type ,则用这个 Medi
10、a type 进行连接,成功与否 都结束连接过程;如果没有指定或不完全指定了 Media type ,则进入下面的枚举过程。枚 举欲连接的 Input pin 上所有的 Media type ,逐一用这些 Media type 与 Output pin 进 行连接(如果连接函数提供了不完全 Media type ,则要先将每个枚举出来的 Media type 与它进行匹配检查) ,如果 Output pin 也接受这种 Media type ,则 Pin 之间的连接宣告成 功;如果所有 Input pin 上枚举的 Media type , Output pin 都不支持, 则枚举 Outpu
11、t pin上的所有 Media type ,并逐一用这些 Media type 与 Input pin 进行连接。 如果 Input pin 接受其中的一种 Media type ,则 Pin 之间的连接到此也宣告成功;如果 Output pin 上的 所有 Media type , Input pin 都不支持,则这两个 Pin 之间的连接过程宣告失败。每个 Pin 都可以实现 GetMediaType 函数来提供该 Pin 上支持的所有 Preferred Media type (但一般只在 Output pin 上实现, Input pin 主要实现 CheckMediaType 看是否
12、支 持当前提供的 Media type 就行了)。连接过程中, Pin 上枚举得到的所有 Media type 就 是这里提供的。在 CBasePin 类中有一个 protected 的成员变量 m_bTryMyTypesFirst ,默认值为 false 。在我们定制 Filter 的 Output pin 中改变这个变量的值为 true ,可以定制我们自己的 连接过程(先枚举 Output pin 上的 Media type )。当 Pin 之间的连接成功后,各自的 pin 上都会调用 CompleteConnect 函数。我们可 以在这里取得一些连接上的 Media type 的信息,以
13、及进行一些计算等。在 Output pin 的 CompleteConnect 实现中,还有一个重要的任务,就是协商 Filter Graph 运行起来后 Sample 传输使用的内存配置情况。这同样是一个交互过程:首先要询问一下 Input pin 上 的配置要求,如果 Input pin 提供内存管理器( Allocator ),则优先使用 Input pin 上的内 存管理器; 否则, 使用 Output pin 自己生成的 内存管理器。我们一般 都要实 现 DecideBufferSize 来决定存放 Sample 的内存大小。注意:这个过程协商完成之后,实际 的内存并没有分配,而要等
14、到 Output pin 上的 Active 函数调用。4. Filter Media type概述Media type 一般可以有两种表示: AM_MEDIA_TYPE 和 CMediaType 。前者是一个 Struct ,后者是从这个 Struct 继承过来的类。每个 Media type 有三部分组成: Major type 、 Subtype 和 Format type 。这三个部 分都使用 GUID 来唯一标示。 Major type 主要定性描述一种 Media type ,比如指定这是 一个 Video ,或 Audio 或 Stream 等; Subtype 进一步细化 Me
15、dia type ,如果 Video 的 话可以进一步指定是 UYVY 或 YUY2 或 RGB24 或 RGB32 等; Format type 用一个 Struct 更进一步细化 Media type 。如果 Media type 的三个部分都是指定了某个具体的 GUID 值,则称这个 Media type 是完全指定的;如果 Media type 的三个部分中有任何一个值是 GUID_NULL ,则称这个 Media type 是不完全指定的。 GUID_NULL 具有通配符的作用。常用的 Major type:MEDIATYPE_Video;MEDIATYPE_Audio;MEDIAT
16、YPE_AnalogVideo; / Analog captureMEDIATYPE_AnalogAudio;MEDIATYPE_Text;MEDIATYPE_Midi;MEDIATYPE_Stream;MEDIATYPE_Interleaved; / DV camcorderMEDIATYPE_MPEG1SystemStream;MEDIATYPE_MPEG2_PACK;MEDIATYPE_MPEG2_PES;MEDIATYPE_DVD_ENCRYPTED_PACK;MEDIATYPE_DVD_NAVIGATION;常用的 Subtype:MEDIASUBTYPE_YUY2;MEDIASUB
17、TYPE_YVYU;MEDIASUBTYPE_YUYV;MEDIASUBTYPE_UYVY;MEDIASUBTYPE_YVU9;MEDIASUBTYPE_Y411;MEDIASUBTYPE_RGB4;MEDIASUBTYPE_RGB8;MEDIASUBTYPE_RGB565;MEDIASUBTYPE_RGB555;MEDIASUBTYPE_RGB24;MEDIASUBTYPE_RGB32;MEDIASUBTYPE_ARGB32; / Contains alpha valueMEDIASUBTYPE_Overlay;MEDIASUBTYPE_MPEG1Packet;MEDIASUBTYPE_MP
18、EG1Payload; / Video payloadMEDIASUBTYPE_MPEG1AudioPayload; / Audio payloadMEDIASUBTYPE_MPEG1System; / A/V payloadMEDIASUBTYPE_MPEG1VideoCD;MEDIASUBTYPE_MPEG1Video;MEDIASUBTYPE_MPEG1Audio;MEDIASUBTYPE_Avi;MEDIASUBTYPE_Asf;MEDIASUBTYPE_QTMovie;MEDIASUBTYPE_PCM;MEDIASUBTYPE_WAVE;MEDIASUBTYPE_dvsd; / DV
19、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_DV
20、D_NAVIGATION_PROVIDER;常用的 Format type :FORMAT_NoneFORMAT_DvInfo DVINFOFORMAT_MPEGVideo MPEG1VIDEOINFOFORMAT_MPEG2Video MPEG2VIDEOINFOFORMAT_VideoInfo VIDEOINFOHEADERFORMAT_VideoInfo2 VIDEOINFOHEADER2FORMAT_WaveFormatEx WAVEFORMATEX5. Filter 之间的数据传送Filter 之间的数据是通过 Sample 来传送的。 Sample 是一个 COM 组件,拥有自己的
21、 一段数据缓冲。 Sample 由 Allocator 统一管理。如下图所示:1 完整类型如果媒体类型每一个部分都定义的很完成, 那么 pin 就严格按照定义的类型进行连接。 如果不匹配,连接失败。2 部分媒体类型如果媒体类型的机构中, major type, subtype, or format type 的值为 GUID_NULL , 这个值是一个通配符号。任何类型都可以匹配。3 没有媒体类型如果 filter 图表管理器传递过来一个 NULL 的指针,这个 pin 就可以和任意的类型的 媒体类型匹配。Filter 之间数据传送的方式有两种: Push 模式和 Pull 模式。所谓 Pus
22、h 模式,即 Source filter 自己能够产生数据,并且一般在它的 Output pin 上 有独立的子线程负责将数据发送出去,常见的情况如 WDM 模型的采集卡的 Live Source Filter ;而所谓 Pull 模式,即 Source filter 不具有把自己的数据送出去的能力, 这种情况下, 一般 Source filter 后紧跟着接一个 Parser Filter 或 Splitter Filter ,这种 Filter 一般在 Input pin 上有个独立的子线程, 负责不断地从 Source filter 索取数据, 然后经过处理后将数据传 送下去,常见的情况
23、如 File source 。 Push 模式下, Source filter 是主动的; Pull 模式下, Source filter 是被动的。而事实上,如果将上图 Pull 模式中的 Source filter 和 Splitter Filter看成另一个虚拟的 Source filter ,则后面的 Filter 之间的数据传送也与 Push 模式完全相同。那么,数据到底是怎么通过连接着的 Pin 传送的呢?首先来看 Push 模式。在 Source filter 后面 Filter 的 Input pin 上,一定实现了一个 IMemInputPin 接口,数据正是通过上 一级 F
24、ilter 调用这个接口的 Receive 方法进行传送的。值得注意的是,数据从 Output pin 通过 Receive 方法调用传送到 Input pin 上,并没有进行内存拷贝, 它只是一个相当于数据 到达的“通知” 。再看一下 Pull 模式。 Pull 模式下的 Source filter 的 Output pin 上,一定 实现了一个 IAsyncReader 接口;其后面的 Splitter Filter ,就是通过调用这个接口的 Request 方法或者 SyncRead 方法来获得数据。 Splitter Filter 然后像 Push 模式一样,调 用下一级 Filter
25、 的 Input pin 上的 IMemInputPin 接口 Receive 方法实现数据的往下传送。一个 DirectShow 的应用程序, 至少会有两条线程: 主线程和 Filter 用于数据传送的子 线程。既然是多线程,就不可避免会出现线程同步问题。 Filter 的状态改变都在主线程中完 成, Filter 的数据相关操作都在数据线程中调用。各线程一些主要函数调用参考如下:Streaming thread(s): IMemInputPin:Receive, IMemInputPin:ReceiveMultiple, IPin:EndOfStream, IMemAllocator:Ge
26、tBuffer.Application thread: IMediaFilter:Pause, IMediaFilter:Run, IMediaFilter:Stop, IMediaSeeking:SetPositions, IPin:BeginFlush, IPin:EndFlush.Either: IPin:NewSegment.这些函数切忌混合调用,否则会引起线程的死锁。另外值得注意的是, BeginFlush 和 EndFlush 属于主线程调用,而不是数据线程调用。6. Transform filter 和 Trans-in-place filter 的区别首先,这两种 Filter
27、 是有共同点的,因为 Trans-in-place filter 本身就是从 Transform filter 中继承过来的。 其次, 我们要明白的是, Trans-in-place filter “尽力” 使自己的 Input pin 和 Output pin 使用相同的 Allocator ,以免去一次 Sample 数据的 memcpy 。我们说“尽力”,就是说 Trans-in-place filter 也未必能够实现它的初衷。 (如果 Trans-in-place filter 使用的 Allocator 是 ReadOnly 的,而 Trans-in-place filter 又要
28、修改 Sample 的数 据,则 Trans-in-place filter 的 Input pin 和 Output pin 将不得不使用不同的 Allocator 。)Trans-in-place filter 有一个 protected 的成员变量 m_bModifiesData ,默认值为 true 。 如 果 你 确 信 定 制 Trans-in-place filter 不 需 要 修 改 Sample 数 据 , 则 将 m_bModifiesData 赋值为 false ,这样可以保证 Input pin 和 Output pin 使用相同的 Allocator 。Trans-
29、in-place filter 的 实 现 主 要 体 现 在 以 下 三 个 函 数 : CTransInPlaceFilter:CompleteConnect 、 CTransInPlaceInputPin:GetAllocator 和 CTransInPlaceInputPin:NotifyAllocator 。 CompleteConnect 中 进 行 必 要 的 重 连Reconnect ),保证 Trans-in-place filter的 Input pin 和 Output pin 使用相同的 Media type 。 GetAllocator 能够取得 Trans-in-p
30、lace filter 下一级 Filter 的 Input pin 上的Allocator 。 NotifyAllocator “尽力”使 Trans-in-place filter的 Input pin 和 Output pin使用同一个 Allocator 。7. IMediaSeeking 的实现IMediaSeeking 的实现在 Filter 上,但应用程序应该从 Filter Graph Manager 上得到 这个接口。在 Filter 级别, Filter Graph Manager 首先从 Renderer filter 开始询问上一级 Filter 的 Output pi
31、n 是否支持 IMediaSeeking 接口。如果支持, 则返回这个接口;如果不 支持,则继续往上一级 Filter 询问,直到 Source filter 。一般在 Source filter 的 Output pin上实现 IMediaSeeking 接口。(如果是 File source ,一般在 Parser Filter 或 Splitter Filter 实现这个接口。 )对于 Filter 开发者来说,如果我们写的是 Source filter ,就要在 Filter 的Output pin 上实现 IMediaSeeking 接口;如果写的是 Transform filter
32、 ,只需要在 Output pin 上将用户的接口请求往上传递给上一级 Filter 的 Output pin ;如果写的是 Renderer Filter ,需要在 Filter 上将用户的接口请求往上传递给上一级 Filter 的 Output pin 。注意:为了保证 Seek 操作后 Stream 的同步性,如果实际实现 IMediaSeeking 接口 的 Filter 有多个 Output pin ,一般仅有一个 pin 支持 Seek 操作。对于你定制的 Transform filter ,如果有多个 Input pin ,你需要自己决定当 Output pin 接收到 IMed
33、iaSeeking 接 口请求时选择哪一条路径往上继续请求。应用程序能够在任何时候( running, paused or stopped )对 Filter graph 执行 Seek 操 作。但当 Filter graph 正在 running 的时候, Filter graph manager会先 pause 住,执行完 Seek 操作后,再重新 run 起来。IMediaSeeking 可以有如下几种 Seek 的时间格式:TIME_FORMAT_FRAME Video frames.TIME_FORMAT_SAMPLE Samples in the stream.TIME_FORMA
34、T_FIELD Interlaced video fields.TIME_FORMAT_BYTE Byte offset within the stream.TIME_FORMAT_MEDIA_TIME Reference time (100-nanosecond units).但实现这个接口的 Filter 未必支 持所有的这 些格式。 一般 Filter 都会支持 TIME_FORMAT_MEDIA_ TIME , 当 使 用 其 它 的 格 式 时 , 最 好 调 用 IMediaSeeking:IsFormatSupported 进行一下确认。对于 Filter ,不赞成使用 IMed
35、iaPosition 接口。 IMediaPosition 是用以支持 Automation的(比如 VB 里面使用 DirectShow ), IMediaSeeking 不支持 Automation8. Filter 的状态转换Filter 有三种状态: stopped, paused, running 。 paused 是一种中间状态, stopped 状态到 running 状态必定经过 paused 状态。 paused 可以理解为数据就绪状态, 是为了快 速切换到 running 状态而设计的。在 paused 状态下,数据线程是启动的,但被 Renderer filter 阻塞了
36、。paused 与 running 两者间的状态转换, 对于 Source filter 和 Transform filter 可以忽 略不计,而对于 Renderer filter (特别是 Video renderer / Audio renderer )情形稍有不 同。 Renderer 首先处理那个 paused 状态下 Hold 的 Sample ,当接收到新的 Sample 时, 判断 Sample 上的时间戳。如果时间未到, Renderer 会 Hold 住这个 Sample 进行等待。Filter graph manager 以从下到上的顺序对 Filter 进行状态转换,
37、即从 Renderer filter 一直回溯到 Source filter 。这个顺序能够有效地避免 Sample 的丢失以及 Filter graph 的 死锁。Stopped to paused: 首先从 Renderer 开始进行 paused 状态的转换。这时, Filter 调 用自己所有 Pin 的 Active 函数进行初始化 (一般 Pin 在 Active 中进行 Sample 内存的分配, 如果是 Source filter 还将启动数据线程) ,使 Filter 处于一种就绪状态。 Source filter 是最 后一个完成到就绪状态转换的Filter。然后,Sour
38、ce filter启动数据线程,往下发送Sample 。当 Renderer 接收到第一个 Sample 后就阻塞住。 当所有的 Renderer 实现了状态转换, Filter graph manager 才认为状态转换完成Paused to stopped: 当 Filter 进入 stopped 状态时,调用自己所有 Pin 的 Inactive 函 数(一般 Pin 在 Inactive 中进行 Sample 内存的释放,如果是 Source filter 还将终止数据 线程)。释放所有 Hold 的 Sample ,以使上一级 Filter 的 GetBuffer 脱离阻塞; 终止所
39、有在Receive 中的等待, 以使上一级 Filter 的 Receive 函数调用返回。 Filter 在 stopped 状态下 拒绝接受任何 Sample 。这样从 Renderer filter 往上一级一级脱离阻塞,当到达 Source filter 的时候,可以确保数据线程终止。9. EndOfStream 问题当 Source filter 的所有数据都已经发送出去, 则会调用下一级 Filter 的 Input pin 上的 IPin:EndOfStream ,直到 Renderer filter 。当这个 Renderer filter 的所有 Input pin 都被 调
40、用了 EndOfStream ,则向 Filter graph manager 发送一个 EC_COMPLETE 事件。仅当 Filter graph 中的所有 Stream 都发送了 EC_COMPLETE 事件, Filter graph manager 才 会将这个事件发送给应用程序。在我们定制的 Filter 中,如果接收到了上一级 Filter 传过来的 EndOfStream ,则说明 上面的数据已经全部传送完毕, Receive 方法不须再接收数据。 如果我们对数据进行了缓冲, 则应确认缓冲中的所有数据都被处理完并往下发送了,然后再往下调用EndOfStream 。Pull模式下
41、,一般是 Splitter filter 或 Parser filter 发送 EndOfStream ,而且方向是往下的, Source filter 上不会收到这样的通知。10. BeginFlush 、EndFlush 、 NewSegment 问题典型的情况,当进行 MediaSeeking 之后,会调用 BeginFlush 、 EndFlush 。一般在 Input pin 上实现这两个函数。对于 Filter 开发者来说, Filter 在被调用 BeginFlush 时需要做以下工作:调用下一级Filter的BeginFlush,使其不再接收新的Sample ;拒绝接收上一级
42、Filter的数据,包括 Receive调用和EndOfStream调用;如果上一级 Filter正在阻塞等待空的Sample,此时需要让它脱离阻塞(通过析构Allocator );确保数据流线程脱离阻塞状态。Filter 在被调用 EndFlush 时需要做以下工作:确保所有等待缓存的Sample被丢弃;确保Filter上已经缓存的数据被丢弃;清除没有发出去的 EC_COMPLETE事件(如果这是一个Ren dered in put pin );调用下一级 Filter 的 EndFlush。还有一点:如果你必须在定制的 Filter 中为每个 Sample 打 Time stamp ,那么
43、记住在MediaSeeking 之后出去的 Sample 的 Time stamp 应该从 0 开始重打。Segment 是一段时间内具有相同的 Playback rate 的一组 Sample ,以 NewSegment 函数调用来表示这个 Segment 的开始。 NewSegment 一般在开始新的 Stream 的时候, 或者用户进行了 MediaSeeking 之后,由 Source filter ( Push 模式下)或 Parser/Splitter filter ( Pull 模式下)发起,并往下层层调用,一直到 Renderer filter 。在我们定制的 Filter 中
44、可以利用 NewSegment 传递下来的信息, 特别是对于 Decoder 对于 Audio renderer 也是一个典型例子, 它根据 Playback rate 和 Audio 实际的采样频率 来对声卡产生输出。11. Quality Control 问题Filter 之间的数据传送,有时候过快,有时候过慢。 DirectShow 使用 Quality Control 来解决这个问题,即IQualityControl 接口的两个函数(SetSink和Notify )。一般,Renderer filter 在 Filter 上实现这个接口,而其他 Filter 在 Output pin
45、上实现这个接口。上图为一般的 Quality Control 的处理过程。而能够调整发送速度的 IQualityControl 接口一般在 Source filter ( pull 模式下为 parser/splitter filter )上实现, Transform filter 只是将 Quality Message 往上一级 Filter 传递。应用程序可以实现自己的 Quality Control Manager ,然后通过调用 SetSink 方法设 置给 Filter 。上述的处理过程就改变了, Quality Message 直接发送给自定义的 Manager 。12. 对运行过
46、程中 Media type 改变的支持我们可以从 CBaseInputPin:Receive 中可以看到, Input pin 每次在接收 Sample 的 之前,一般都会进行 CheckStreaming (如果当前 Filter 已经 Stop 或正在 Flush 或发生了 RuntimeError ,则拒绝接收 Sample ),然后将当前的 Sample 属性保存到 protected 的 m_SampleProps 成员变量中。描述 Sample 属性的是一个 AM_SAMPLE2_PROPERTIES 的结构,它有一个标记来表明当前 Sample 的 Media type 是否已经
47、改变。 如果 Media type 改变了,则进行 CheckMedaiType 看我们的 Filter 是否仍然支持它。如果不支持,则发出 一个 RuntimeError ,并发送 EndOfStream 。一个健全的 Filter 应该能够对运行时 Media type 的改变做出处理。在我们的 Receive 方 法 实 现 中 , 我 们 可 以 通 过 if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) 来判断 Meida type 是否已经改变;如果改变,我们需要 根据新的 Media type 进行必要的初始化。一个
48、典型的案例:当 Camcorder 输入时, Audio 的 Media type 可能改变。比如, Filter 连接时 Media type 使用了 MEDIATYPE_PCM ,而在运行时又换成了 MEDIATYPE_WAVE ; 或者连接时 Audio 的采样频率时 44.1K ,而在运行时却变成了 48K ;或者 Camcorder 的 带子上本身保存了混合的 44.1K 和 48K 的 Audio 。创建一个 filter 实例CTransformFilter1 、选择所要创建的 filter 的用途,据此来选择基类。基类可以从CTransInPlaceFilter 、 CVide
49、oTransformFilter和 CBaseFilter 中来选取。( 1 ) CTransInPlaceFilter 提供了本地处理 Sample 的机制( Sample 可以认为是存储一 个视频帧的结构) ,当一个 trans-in-place filter 收到一个 sample 时,你可以通过重载它 的 Transform ()函数来修改其中的数据, trans-in-place filter 会在 Transform ()函数 执行完后直接把这个 sample 传递给下一个 filter 。( 2 ) CTransformFilter 完成的功能与 CTransInPlaceFil
50、ter 一样,它们的区别就是 CTransformFilter 总是把上游 filter 传递过来的 sample 复制一份,并把复制后的 sample 传递给下一个 filter 。当然,你可以通过重载 Transform ()函数来控制这个过程,包括修 改其中的数据(这也是自己写 filter 的原因)。( 3 ) CVideoTransformFilter与 CTransformFilter 一样,只是多加了质量控制功能。(4 )以上三个 filter 都继承于 CBaseFilter ,所以如果想对 filter 进行更多的控制,就要直 接从 CBaseFilter 来继承,但是所要做
51、的工作也最多。在这个例子中,我选择 CTransformFilter ,因为 CTransInPlaceFilter 太简单了, dx9sdk 中的例子 NullNull 就是一个完整的 CTransInPlaceFilter 的框架, 并且只是一个框架, 什么 工作也没有做,如果要用的话直接修改就可以用了。2、在 vc 中选择 win32 dll ,创建一个 dll ,名字随便取,这里我取 SplitFilter ,选择空 dll 。3、然后,创建一个类 CSplitFilter ,继承自CTransformFilter ,当然要选择 public 方式。4、为 filter 生成一个CLS
52、ID ,可以使用 Guidgen ,它的用法是在命令行中打 Guidgen ,然后回车, Guidgen 就执行了,单击 New GUID 就会生成一个新的 GUID ;单击 Copy 就可以把新生成的 GUID复制到剪贴板上。然后在 SplitFilter.h 文件上粘贴进来,最后是这个样子:/ GUID/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(<<name>>,0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c)
53、;然后把其中的 <<name>> 换成自己设置的名字,如下:/ 3DCD790F-B7A0-429a-B9E1-3CE3255D8D1CDEFINE_GUID(CLSID_SplitFilter,0x3dcd790f, 0xb7a0, 0x429a, 0xb9, 0xe1, 0x3c, 0xe3, 0x25, 0x5d, 0x8d, 0x1c);然后在 SplitFilter.cpp 中加入#include <initguid.h>再来修改构造函数,形式如下:CSplitFilter:CSplitFilter() : CTransformFilter(NAM
54、E("SplitFilter"), 0, CLSID_SplitFilter)5、处理媒体类型首先要明白一点,两个 filter 连接,也就是两个 filter 的输出 pin 和输入 pin 在进行连接,这个工作是由输出 pin 发起的, 由输入 pin 来检查媒体类型是否匹配, 并决定是否接受 这个连接。在 CTransformFilter 中协商媒体类型的工作是由 CTransformFilter 来完成的, 这个工作本来是应该由 pin 来完成的,但是 CTransformFilter 中的 pin 只是简单的调用CTransformFilter 中相应的函数而已,
55、所以我们所有的工作只是重载 CTransformFilter 中 的三个虚函数而已:(1)实现CheckInputType(const CMediaType *mtIn) (不要问我如何添加这个函数,如果这 个都不会的话趁早别看 dshow 了,赶紧去看 vc 的书去)。这个函数是由输入 pin 来调用, 当上游输出 pin 要来进行连接的时候, 我们的 filter 的输入 pin 就会调用这个函数来检查是 否支持上游输出 pin 的媒体类型。其中的 CMediaType 类是对 AM_MEDIA_TYPE 结构的 封装, AM_MEDIA_TYPE 包含了有关的媒体类型,具体可查阅 dxs
56、dk 文档。因为我这里只 是写一个框架来为大家演示,功能只是传递 sample ,并不对数据有任何的修改,所以任何 的格式都可以,所以不管什么格式我们都应该返回ok ,实际函数如下:HRESULT CSplitFilter:CheckInputType(const CMediaType *mtIn) / Everything is good.return S_OK;( 2 )实现 GetMediaType(int iPosition, CMediaType *pMediaType)前面我们已经讲了输入 pin 接受连接的时候用的函数,那么这个函数呢就是输入 pin 进行连接的时候用的, 输出
57、pin 进行连接的时候首先要有一个支持的媒体类型列表, 这个函 数就是来生成这个列表的。实际上我们也可以直接返回一个 ok 了事,但是这样做有点太不 负责任,我们虽然什么工作都不做,但是还是要把例行的检查做完了,这样老板( filter graph )看到了也会很满意,你们是不是也很赞同?首先, 我们要确定输入 pin 是否已经连接了, 如果输入 pin 没有连接, 那我们和下面的?画饼是不filter 连接了也没有意义, 因为上游没有数据传过来, 我们拿什么给后面的 filter 能充饥的:)ASSERT(m_pInput->IsConnected();然后,看看 pin 的位置是否正确if (iPosition < 0)return E_INVALIDARG;这个函数最后的结果是这样的:HRESULT CSplitFilter:GetMediaType(int iPosition, CMediaType *pMediaType)/ Is the input pin connectedif(m_pInput->IsConnected() = FALSE)return E_UNEXPECTED;/ This should never happenif(iPosition &
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024-2025学年河南省郑州市中原区八年级(上)期末数学试卷-20251114224749
- 2026年翻译专业面试技巧与考点预测
- 2026年能源行业托管项目人员招聘题库与解析
- 2026年银行柜员岗位招聘面试题集
- 2026年法务专员招聘面试问题与答案
- 2026年IT部主管技术面试题含答案
- 一年级数学(上)计算题专项练习汇编
- 焦炉煤气冷凝净化工保密意识评优考核试卷含答案
- 2026年摄影师岗位面试题集
- 家务服务员操作技能评优考核试卷含答案
- 《增值税法》实施解析及应对指南(2026版)课件
- 伤口护理中的营养支持策略
- 汽车美容销售话术与技巧
- 2025年征信报告模板样板个人版模版信用报告详细版(可修改编辑)
- 培训课件:分布式调相机对大规模新能源汇集的支撑作用
- 【《铜电解阳极泥处理各工序及工艺分析案例》7400字】
- 化工设备新员工培训课件
- 防漏电安全工作培训课件
- 分包工程监理方案(3篇)
- DB51∕T 2791-2021 川西高原公路隧道设计与施工技术规程
- 行政单位预算管理课件
评论
0/150
提交评论