老虎说车 發表於 2021-1-28 14:37:00

Delphi的DirectShow开发概述

<p>第一部分:背景知识<br>&nbsp;<br>DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布。它经过DirectX 6.0中的DirectX Media发展而来,集成了DirectX家族中的其他成员(DirectDraw、DirectSound等),可以说是一位“集大成者”。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>DirectShow能做些什么? DirectShow为多媒体流的捕捉和回放提供了强有力的支持。运用DirectShow,可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。它广泛地支持各种媒体格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒体数据的回放变得轻而易举。另外,DirectShow直接支持DVD的播放,视频的非线性编辑,以及与数字摄像机的数据交换。更值得一提的是,DirectShow提供的是一种开放式的开发环境,每个功能模块都采取COM组件方式,称为Filter,开发者也可以开发自己的功能Filter来扩展DirectShow的应用。按照功能来划分,Filter分为3类:Source Filter, Transform Filter, Rendering Filter。前者负责获取数据,数据源可以是文件、数字摄像机等,然后将数据往下传输;中间者负责数据的格式转换,比如数据流的分离与合成、编码解码等,然后把数据继续往下传输;后者负责数据的去向——给声卡、显卡进行播放或者输出到文件存储。<br>&nbsp;<br>第二部分核心技术<br>&nbsp;<br>DirectShow的开发实际就是Filter的开发,DirectShow自身提供了,下面就是Filter概述。<br>&nbsp;<br>1、DirectShow的 Filter<br>Filter 一般分为下面几种类型。</p>
<p>  (1)源过滤器(source filter):源过滤器引入数据到过滤器图表中,数据来源可以是文件、网络、照相机等。不同的源过滤器处理不同类型的数据源。</p>
<p>  (2)变换过滤器(transform filter):变换过滤器的工作是获取输入流,处理数据,并生成输出流。变换过滤器对数据的处理包括编解码、格式转换、压缩解压缩等。</p>
<p>  (3)提交过滤器(renderer filter):提交过滤器在过滤器图表里处于最后一级,它们接收数据并把数据提交给外设。</p>
<p>  (4)分割过滤器(splitter filter):分割过滤器把输入流分割成多个输出。例如,AVI分割过滤器把一个AVI格式的字节流分割成视频流和音频流。</p>
<p>  (5)混合过滤器(mux filter):混合过滤器把多个输入组合成一个单独的数据流。例如,AVI混合过滤器把视频流和音频流合成一个AVI格式的字节流。</p>
<p>  过滤器的这些分类并不是绝对的,例如一个ASF读过滤器(ASF Reader filter)既是一个源过滤器又是一个分割过滤器。</p>
<p>2、关于Filter Graph Manager</p>
<p>  Filter Graph Manager也是一个com对象,用来控制Filter graph中的所有的filter,主要有以下的功能:</p>
<p>  1) 用来协调filter之间的状态改变,从而使graph 中的所有的filter的状态的改变应该一致。</p>
<p>  2) 建立一个参考时钟。</p>
<p>  3) 将filter 的消息通知返回给应用程序</p>
<p>  4) 提供用来建立 filter graph的方法。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简单描述,Graph就是各个Filter组成的一个流程图。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SourceFilter----|-----SpliterFilter-------------(Video-pin)&gt;-----TransFormFilter---&gt;VideoRender<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |---------------------(Audio-pin)-&gt;----ACMWraperFilter---&gt;DirectSoundFilter<br>&nbsp;<br>程序启动过程,先创建各个filter的com对象,然后使用FilterGraph.Addfilter加入到Graph中,然后把每个Filter按照<br>&nbsp;<br>数据流把OutPin和inpuin连接起来。最好启动FilterGraph.play即可。<br>&nbsp;</p>
<p>  Directshow是基于模块化,每个功能模块都采取COM组件方式,称为Filter。Directshow提供了一系列的标准的模块可用于应用开发,开发者也可以开发自己的功能Filter来扩展Directshow的应用。下面我们用一个例子来说明如何采取Filter来播放一个AVI的视频文件。</p>
<p>  1) 首先从一个文件中读取AVI数据,形成字节流。(这个工作由源Filter完成)</p>
<p>  2) 检查AVI数据流的头格式,然后通过AVI分割Filter将视频流和音频流分开。</p>
<p>  3) 解码视频流,根据压缩格式的不同,选取不同的decoder filters 。</p>
<p>  4) 通过Renderer Filter重画视频图像。</p>
<p>  5) 音频流送到声卡进行播放,一般采用缺省的 DirectSound Device Filter。<br>&nbsp;<br>&nbsp;<br>&nbsp;<br>状态改变,Graph中的filter的状态改变应该一致,因此,应用程序并将状态改变的命令直接发给filter,而是将相应的状态改变的命令发送给Filter graph Manager,由manager将命令分发给graph中每一个filter。Seeking也是同样的方式工作,首先由应用程序将seek命令发送到filter graph 管理器,然后由其分发给每个filter。</p>
<p>  参考时钟,graph中的filter都采用的同一个时钟,称为参考时钟(reference clock),参考时钟可以确保所有的数据流同步,视频桢或者音频桢应该被提交的时间称为presentation time.presentation time 是相对于参考时钟来确定的。Filter graph Manager应该选择一个参考时钟,可以选择声卡上的时钟,也可以选择系统时钟。</p>
<p>  Graph事件, Graph 管理器采用事件机制将graph中发生的事件通知给应用程序,这个机制类似于windows的消息循环机制。</p>
<p>  Graph构建的方法,graph管理器给应用程序提供了将filter添加进graph的方法,连接filter的方法,断开filter连接的方法。</p>
<p>  但是,graph 管理器没有提供如何将数据从一个filter发送到另一个filter的方法,这个工作是由filter在内部通过pin来独立完成的。<br>&nbsp;<br>3、媒体类型</p>
<p>  因为Directshow是基于com组件的,就需要有一种方式来描述filter graph每一个点的数据格式,例如,我们还以播放AVI文件为例,数据以RIFF块的形式进入graph中,然后被分割成视频和音频流,视频流有一系列的压缩的视频桢组成,解压后,视频流由一系列的无压缩的位图组成,音频流也要走同样的步骤。<br>&nbsp;<br>媒体类型是一种很普遍的,可以扩展的用来描述数字媒体格式的方法,当两个filter连接的时候,他们会就采用某一种媒体类型达成一致的协议。媒体类型定义了处于源头的filter将要给下游的filter发送什么样的数据,以及数据的physical layout。如果两个filter不能够支持同一种的媒体类型,那么他们就没法连接起来。</p>
<p>  对于大多数的应用来说,也许你不用考虑媒体类型,但是,有些应用程序中,你会直接应用到媒体类型的。</p>
<p>  媒体类型是通过AM_MEDIA_TYPE结构定义的。<br>&nbsp;<br>Filters通过pin的连接来传递数据,数据流是从一个filter的输出pin流向相连的filter的输入pin。输出pin常用的传递数据的方式是调用输入pin上的IMemInputPin::Receive方法。<br>&nbsp;<br>对于filter来说,可以有好几种方式来分配媒体数据使用的内存块,可以在堆上分配,可以在DirectDraw的表面,也可以采用GDI共享内存,还有其他的一些方法,在Directshow中用来进行内存分配任务的是内存分配器(allocator),也是一个COM对象,暴露了一个IMemAllocator接口。</p>
<p>  当两个pin连接的时候,必须有一个pin提供一个allocator,Directshow定义了一系列函数调用用来确定由哪个pin提供allocator,以及buffer的数量和大小。</p>
<p>  在数据流开始之前,allocator会创建一个内存池(pool of buffer),在开始发送数据流以后,源filter就会将数据填充到内存池中一个空闲的buffer中,然后传递给下面的filter。但是,源filter并不是直接将内存buffer的指针直接传递给下游的filter,而是通过一个media samples的COM对象,这个sample是allocator创建的用来管理内存buffer。Media sample暴露了IMediaSample接口,一个sample包含了下面的内容:</p>
<p>  一个指向没有发送的内存的指针。</p>
<p>  一个时间戳</p>
<p>  一些标志</p>
<p>  媒体类型。</p>
<p>  时间戳表明了presentation time,Renderer filter就是根据这个时间来安排render顺序的。标志是用来标示数据是否中断等等,媒体类型提供了中途改变数据格式的一种方法,不过,一般sample没有媒体类型,表明它们的媒体类型一直没有改变。</p>
<p>  当一个filter正在使用buffer,它就会保持一个sample的引用计数,allocator通过sample的引用计数用来确定是否可以重新使用一个buffer。这样就防止了buffer的使用冲突,当所有的filter都释放了对sample的引用,sample才返回到allocator的内存池,供重新使用<br>&nbsp;<br>基于Delphi的DirectShow开发概述2<br>&nbsp;<br>MajorType:主要类型;例如视频,音频,还是位流<br>subType&nbsp; :&nbsp; 辅助说明类型,例如视频中的YUV12,还是UYVY等等<br>formatType: 格式描述,更为细节的结构体。例如,视频大小,频率,帧率等,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可以使用FORMAT_VIDEOINFO(VIDEOINFOHEADER),FORMAT_WAVEFORMATEX(WAVEFORMATEX)结构体来描述<br>//PAMMediaType<br>当使用AM_MEDIA_TYPE数据结构来描述媒体类型的时候,如果MajorType,subType,formatType都指定了GUID,那么<br>这就是完全媒体类型。<br>&nbsp;<br>***************************************Filter的连接************************************<br>&nbsp;&nbsp; Filter的连接实际上就是Pin的连接。连接方向总是由上一级的Filter(UpStream Filter)的输出Pin指向下一级<br>Filter(DownStream Filter)的输入Pin。<br>&nbsp;<br>1.Filter连接过程<br>&nbsp; Pin也是一种COM接口。实现了IPIN的接口。一般通过调用(下面的函数来连接):<br>&nbsp; IFilterGraph.ConnectDirect,IGraphBuilder.Connect,IGraphBuilder.Render,IGraphBuilder.RenderFile<br>&nbsp;<br>&nbsp; { 下面就是个范例,一般Filter是在停止状态下连接的。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //连接&nbsp; source -&gt; MPEG1Spliter<br>&nbsp;&nbsp;&nbsp; Source.FindPin(StringToOLEStr('Output'), OutPin);<br>&nbsp;&nbsp;&nbsp; MPEG1Splitter.FindPin(StringToOLEStr('Input'), inPin);<br>&nbsp;&nbsp;&nbsp; hr := (FilterGraph1 as IGraphBuilder).Connect(OutPin, InPin);<br>&nbsp;&nbsp;&nbsp; if FAILED(hr) then begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShowMessage('Failed connect mpg Source -&gt; MPEG1Splitter');<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp; }<br>&nbsp;<br>2.FilterGraph构建的方法<br>&nbsp; 1)IFilterGraph.AddFilter&nbsp;&nbsp; 该参数提供一个Filter对象,将其加入到FilterGraph中.<br>&nbsp; 2)IFilterGraph.ConnectDirect&nbsp; 该参数提供输出Pin,输入Pin以及媒体类型,进行直接连接<br>&nbsp; 3)IGraphBuilder.AddSourceFilter 该参数提供源文件名,自动将一个SourceFilter加载到FilterGraph中<br>&nbsp; 4)IGraphBuilder.Connect 该参数提供输出Pin,输入Pin以及媒体类型,进行连接,如果失败,自动尝试在中间加入必要的格式转换Filter<br>&nbsp; 5)IGraphBuilder.Render 该参数提供输出Pin,自动间加入必要的Filter完成剩下的部分FilterGraph的构建(直到连到RenderFilter上)<br>&nbsp; 6)IGraphBuilder.Render 该参数提供源文件名,自动间加入必要的Filter完成这个文件的回放<br>&nbsp;<br>&nbsp; {<br>&nbsp;&nbsp;&nbsp; //下面范例,表示该FilterGraph中有6个Filter,他们都是由COM对象创建而来。<br>&nbsp;var<br>&nbsp;&nbsp;&nbsp; Source&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; MPEG1Splitter : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; MpegVcodec&nbsp;&nbsp;&nbsp; : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; AviDec&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; AviDest&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; Writer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IBaseFilter;<br>&nbsp;&nbsp;&nbsp; hr&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : HRESULT;<br>&nbsp;&nbsp;&nbsp; OutPin, InPin : IPin;<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_AsyncReader, nil, CLSCTX_INPROC,IID_IBaseFilter, Source); //典型的拉模式<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_MPEG1Splitter, nil, CLSCTX_INPROC,IID_IBaseFilter, MPEG1Splitter); //MPEG1格式<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_CMpegVideoCodec, nil, CLSCTX_INPROC,IID_IBaseFilter, MpegVcodec);&nbsp; //MPEG编码<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_AVIDec, nil, CLSCTX_INPROC,IID_IBaseFilter, AviDec);&nbsp;&nbsp;&nbsp; //AVI解码<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_AviDest, nil, CLSCTX_INPROC, IID_IBaseFilter, AviDest);&nbsp; //AVI目标<br>&nbsp;&nbsp;&nbsp; CoCreateInstance(CLSID_FileWriter, nil, CLSCTX_INPROC,IID_IBaseFilter, Writer);&nbsp; //写文件<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(Source, 'Source');<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(MPEG1Splitter, 'MPEG1Splitter');<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(MpegVcodec, 'MpegVcodec');<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(AviDec, 'AviDec');<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(AviDest, 'AviDest');<br>&nbsp;&nbsp;&nbsp;&nbsp; (FilterGraph1 as IFilterGraph).AddFilter(Writer, 'Writer');<br>&nbsp;&nbsp; end;<br>&nbsp; }&nbsp;<br>&nbsp;<br>3. 一般使用GraphEdit可以查看到目前正常安装在系统中的Filter,如果是安装在DirectShow目录下的可以通过指定CLSID<br>&nbsp;&nbsp; 用CoCreateInstance来创建。在其它目录下的,必须通过系统枚举来创建。<br>&nbsp;&nbsp; 系统提供了一个CLSID_SystemDeviceEnum,用CoCreateInstance创建,并获取ICreateDevEnum接口。然后<br>&nbsp;&nbsp; 使用ICreateDevEnum.CreateClassEnumerator为指定的类型目录创建一个枚举器,并获得IEnumMoniker接口。<br>&nbsp;&nbsp; 使用IEnumMoniker.next方法,媒体该目录下所有可用设备标识(Device Moniker),每个设备标识对象上都实现了Imoniker接口<br>&nbsp;&nbsp; 调用Imoniker.bindtoStorage之后就可以访问设备标识属性集。比如得到设备的显示名字。<br>&nbsp;&nbsp; 调用Imoniker.BindToObject可以将设备标识绑定成一个DirecshowFilter,然后调用IFilterGraph.addFilter加入FilterGraph<br>&nbsp;&nbsp; 参加工作。<br>&nbsp;&nbsp;<br>&nbsp;&nbsp; DirectShow通常有两个名字:显示名字例如:@device:cm:{33D9A760-90C8-11D0-BD43-00A0C911CE86}\xvid<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 友好名字例如:xvid mpeg4 decoder<br>&nbsp;&nbsp; {下面就是调用代码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; var<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; i, j: integer;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMoniker, MyMoniker: IMoniker;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PropBag: IPropertyBag;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AVariant: OleVariant;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLSID: TGUID;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Found: boolean;<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for i := 0 to SysDevEnum.CountCategories - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbCategories.Items.Add(SysDevEnum.Categories.FriendlyName); //SysDevEnum:TSysDevEnum;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Found := false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; j := 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyMoniker := Filter.BaseFilter.Moniker;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if MyMoniker = nil then exit;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyMoniker.BindToStorage(nil,nil,IPropertyBag, PropBag);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if PropBag.Read('CLSID',AVariant,nil) = S_OK then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CLSID := StringToGUID(AVariant)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else CLSID := GUID_NULL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for i := 0 to SysDevEnum.CountCategories - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SysDevEnum.SelectIndexCategory(i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if SysDevEnum.CountFilters &gt; 0 then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for j := 0 to SysDevEnum.CountFilters - 1 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if IsEqualGUID(CLSID, SysDevEnum.Filters.CLSID) then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMoniker := SysDevEnum.GetMoniker(j);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Found := AMoniker.IsEqual(MyMoniker) = S_OK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AMoniker := nil;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if Found then Break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if Found then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbCategories.ItemIndex := i;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbCategoriesChange(nil);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lbFilters.ItemIndex := j;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lbFiltersClick(nil);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PropBag := nil;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyMoniker := nil;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp;<br>&nbsp; PAMMediaType = ^TAMMediaType;<br>&nbsp; _AMMediaType = record<br>&nbsp;&nbsp;&nbsp; majortype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; subtype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; bFixedSizeSamples&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; bTemporalCompression : BOOL;<br>&nbsp;&nbsp;&nbsp; lSampleSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ULONG;<br>&nbsp;&nbsp;&nbsp; formattype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; pUnk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IUnknown;<br>&nbsp;&nbsp;&nbsp; cbFormat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ULONG;<br>&nbsp;&nbsp;&nbsp; pbFormat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Pointer;<br>&nbsp; end;<br>&nbsp;<br>Filter开发基础----基类分析<br>&nbsp;1)TBCBaseFilter<br>&nbsp; TBCBaseFilter = class(TBCUnknown, IBaseFilter, IAMovieSetup)<br>&nbsp; 是最基本Filter的基类,使用方法:<br>&nbsp; (1)声明一个新类继承自&nbsp; TBCBaseFilter<br>&nbsp; (2)在新类中定义一个Filter上Pin的实例。(Pin从TBCBasePin继承)<br>&nbsp; (3)实现纯虚函数TBCBaseFilter.GetPin,用于返回Filter上各个Pin的对象指针<br>&nbsp; (4)实现纯虚函数TBCBaseFilter.GetPinCount,用于返回Filter上Pin的数量<br>&nbsp; (5)考虑如何处理从输入Pin进来的Sample数据<br>&nbsp;<br>&nbsp;<br>&nbsp; 2)TBCBasePin<br>&nbsp;&nbsp; TBCBasePin实现了PIn接口,TBCBasePin设计了Pin的整个连接过程。也实现了IQualityControl质量控制接口。<br>&nbsp;&nbsp; 在TBCBasePin上实现了3个成员函数与Filter状态对应。<br>&nbsp;&nbsp;&nbsp; (1) Filter.Stopped&nbsp;&nbsp; &lt;-------------&gt; TBCBasePin.Inactive<br>&nbsp;&nbsp;&nbsp; (2) Filter.Spaused&nbsp;&nbsp; &lt;-------------&gt; TBCBasePin.active<br>&nbsp;&nbsp;&nbsp; (3) Filter.Running&nbsp;&nbsp; &lt;-------------&gt; TBCBasePin.Run<br>&nbsp;&nbsp;&nbsp; 在实际开发Filter时,有可能重写该3个函数,用来初始化和释放必要资源。实现方法:<br>&nbsp;&nbsp;&nbsp; (1)从TBCBasePin派生一个子类<br>&nbsp;&nbsp;&nbsp; (2)实现纯虚函数TBCBasePin.CheckMediaType,进行Pin连接时媒体类型检测<br>&nbsp;&nbsp;&nbsp; (3)实现纯虚函数TBCBasePin.GetMediaType,提供Pin上的首先媒体类型<br>&nbsp;&nbsp;&nbsp; (4)实现Ipin.BeginFlush和IPin.EndFlush两个函数<br>&nbsp;&nbsp;&nbsp; (5)可能需要重写的函数包括。TBCBasePin.Inactive,TBCBasePin.active,TBCBasePin.Run,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCBasePin.CheckConnect(连接的时候检查,如查询对方Pin上是否支持某个特殊接口),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCBasePin.BreakConnect(断开连接,并进行必要的资源释放),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCBasePin.CompleteConnect(完成连接时被调用,可以在这个函数中获得当前连接用的媒体类型等参数),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCBasePin.EndOfStream(当上流数据全部传送完毕后被调用,如果这是个TransformFilter则将继续往下送,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如是个RenderFilter,则需要向FilterGraph发送一个EC_COMPLETE事件)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCBasePin.Notify(直接响应质量控制,或者将质量控制消息往上一级Filter发送)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp; 3)TBCBaseInputPin和TBCBaseOutputPin<br>&nbsp;&nbsp;&nbsp; TBCBaseInputPin和TBCBaseOutputPin都是从TBCBasePin派生而来,<br>&nbsp;&nbsp;&nbsp; TBCBaseInputPin实现了ImeminputPin(用于推模式的数据传送)<br>&nbsp;&nbsp;&nbsp; TBCBaseOutputPin主要完成了传送数据所用的Sample管理器(Allocate)的协商,并重写了TBCBasePin.active(用于<br>&nbsp;&nbsp;&nbsp; 实际的Sample内存分配),TBCBasePin.inactvie(用于Sample内存的释放)。<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; TBCBaseInputPin使用方法(派生一个子类,并且至少重写如下函数):<br>&nbsp;&nbsp;&nbsp; (1) TBCBaseInputPin.BeginFlush<br>&nbsp;&nbsp;&nbsp; (1) TBCBaseInputPin.EndFlush<br>&nbsp;&nbsp;&nbsp; (1) TBCBaseInputPin.Receive<br>&nbsp;&nbsp;&nbsp; (1) TBCBaseInputPin.CheckMediaType(一般在输出Pin上实现该函数)<br>&nbsp;&nbsp;&nbsp; (1) TBCBaseInputPin.GetMediaType<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; TBCBaseOutputPin使用方法(派生一个子类,并且至少重写如下函数):<br>&nbsp;&nbsp;&nbsp; (1)重写TBCBasePin.CheckMediaType进行连接时的媒体类型检查<br>&nbsp;&nbsp;&nbsp; (2)实现纯虚函数TBCBaseOutputPin.DecideBufferSize,决定Sample内存的大小<br>&nbsp;&nbsp;&nbsp; (3)重写纯虚函数TBCBasePin.GetMediaType,提供Pin上的首先媒体类型<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 4)TBCMediaType<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCMediaType用于数据传输的Sample的实现类,TBCMediaType实现了IMediaSample2的接口,TBCMediaType封装了<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个指向一块内存的指针,通过TBCMediaType.GetPointer得到<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 5)TBCSourceStream = class(TBCBaseOutputPin)<br>&nbsp;&nbsp;&nbsp; 提供了“推”(Push)的能力,实现了一个线程(TBCAMThread),Sample数据就是靠这个线程向下一级Filter发送的。<br>&nbsp;&nbsp;&nbsp; 实现方法:<br>&nbsp;&nbsp;&nbsp; (1)从TBCSourceStream派生一个子类作为Pin<br>&nbsp;&nbsp;&nbsp; (2)实现纯虚函数TBCSourceStream.CheckMediaType,进行Pin连接时媒体类型检测<br>&nbsp;&nbsp;&nbsp; (3)实现纯虚函数TBCSourceStream.GetMediaType,提供Pin上的首先媒体类型<br>&nbsp;&nbsp;&nbsp; (4)实现TBCBaseOutputPin.DecideBufferSize,决定Sample内存大小,在Pin连接时会执行<br>&nbsp;&nbsp;&nbsp; (5)实现TBCSourceStream.FillBuffer,为即将传送出去的Sample填充数据<br>&nbsp;&nbsp;&nbsp; (6)可选地实现TBCSourceStream.OnThreadCreate,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCSourceStream.OnThreadDestroy,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TBCSourceStream.OnThreadStartPlay 等函数,进行适当时机的初始化,资源管理等操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 6)TBCTransformFilter<br>&nbsp;&nbsp;&nbsp; 实现了媒体类型的转换,主要实现如下函数:<br>&nbsp;&nbsp;&nbsp; (1)TBCTransformFilter.CheckInputType<br>&nbsp;&nbsp;&nbsp; (2)TBCTransformFilter.CheckTransForm<br>&nbsp;&nbsp;&nbsp; (3)TBCTransformFilter.DecideBufferSize<br>&nbsp;&nbsp;&nbsp; (4)TBCTransformFilter.GetMediaType<br>&nbsp;&nbsp;&nbsp; (5)TBCTransformFilter.TransForm<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 7)TBCTransinPlaceFilter<br>&nbsp;&nbsp;&nbsp; 是一个“就地”处理的转换Filter。<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 8)TBCVideoTransformFilter<br>&nbsp;&nbsp;&nbsp; 一个视频质量控制的基类。通过输入Pin上的Receive函数接收Sample时,能够根据质量消息决定是否丢帧,这个类<br>&nbsp;&nbsp;&nbsp; 主要是为开发AVI解码Filter而设计的。使用方法基本和TBCTransformFilter相同。<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 9)TBCBaseRenderer<br>&nbsp;&nbsp;&nbsp; 默认实现使用TBCRenderInutPin类的输入Pin。还实现了IMediaSeeking和IMediaPosition接口。使用方法:<br>&nbsp;&nbsp;&nbsp; (1)实现TBCBaseRenderer.CheckMediaType,用于检查输入Pin连接用的媒体类型<br>&nbsp;&nbsp;&nbsp; (2)实现TBCBaseRenderer.DoRenderSample,处理当前的Sample<br>&nbsp;&nbsp;&nbsp; 如果我们不处理Sample,需要写文件,基类可以选择从TBaseFilter,而此时输入Pin最好选择从TBCRenderInputIn<br>&nbsp;&nbsp;&nbsp; 派生类。<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; 10)TBCBaseVideoRenderer<br>&nbsp;&nbsp;&nbsp; 实现了VideoFilter基类,其中实现了IQualityControl用于视频质量控制,IQualProp用于在Filter属性页显示一些<br>&nbsp;&nbsp;&nbsp; 实时性能参数。使用方法与TBCBaseRenderer相同。<br>&nbsp;<br>基于Delphi的DirectShow开发概述3<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; Delphi设计Directshow其实也是比较简单的,看了前面两个概述,相信你也会明白一些了,问题就是你要有什么杨的需求,然后根据需求来选择合适的Filter基类,从基类(BassFilter)继承下来后,只需要覆盖指定的函数就可以了。<br>&nbsp;<br>&nbsp;&nbsp; 例如我们要设计一个屏幕(Desktop)捕获的的Filter,每秒捕获大概10帧(10副抓屏bitmap),这个明显需要实现Push下推的功能,因此我们可以选择从TBCSourceStream基类派生一个子类,然后实现如下几个函数即可:<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //GetMediaType函数是下级Filter在和本Filter连接时在下级InpuPin接口上调用的<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; function GetMediaType(iPosition: Integer; out MediaType: PAMMediaType): HResult; override;<br>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; //实现虚函数CheckMediaType,实现接受8, 16, 24 or 32 RGB位视频格式<br>&nbsp;&nbsp;&nbsp; //如果媒体格式不能接受,则返回E_INVALIDARG<br>&nbsp;&nbsp;&nbsp; function CheckMediaType(MediaType: PAMMediaType): HResult; override;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //在本Filter的OutPutPin接口和下级InputPin接口协商时调用的,主要用来协商每个Sample的大小<br>&nbsp;&nbsp;&nbsp; function DecideBufferSize(Allocator: IMemAllocator;Properties: PAllocatorProperties): HRESULT; override;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //设置视频媒体参数,在初始化Filter时调用<br>&nbsp;&nbsp;&nbsp; function SetMediaType(MediaType: PAMMediaType): HRESULT; override;<br>&nbsp;<br>&nbsp;&nbsp; //以上几个函数是在和下级Filter进行接口连接和协商时调用,FillBuffer函数则是由本Filter内置线程按一定时间<br>&nbsp;<br>&nbsp;&nbsp; //间隔调用,这里当然是把抓屏的Bitmap数据填充到Sample中,推给下一级。<br>&nbsp;&nbsp;&nbsp; function FillBuffer(Sample: IMediaSample): HResult; override;<br>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; //实现虚函数,质量控制功能<br>&nbsp;&nbsp;&nbsp; function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override; stdcall;<br>&nbsp;<br>&nbsp;<br>&nbsp;<br>具体代码分析如下:<br>&nbsp;<br>&nbsp; CLSID_PushSourceDesktop: TGUID = '{570757C1-D2D8-42D1-BA0C-24E1BED3F62F}'; //PushFilter注册名<br>&nbsp;<br>&nbsp; //Pin注册类型结构<br>&nbsp; TRegPinTypes = record<br>&nbsp;&nbsp;&nbsp; clsMajorType: PGUID;<br>&nbsp;&nbsp;&nbsp; clsMinorType: PGUID;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //Setup信息结构<br>&nbsp; sudPinTypes: TRegPinTypes =<br>&nbsp; (<br>&nbsp;&nbsp;&nbsp; //视频流的类型<br>&nbsp;&nbsp;&nbsp; clsMajorType: @MEDIATYPE_Video;<br>&nbsp;&nbsp;&nbsp; //所用可用类型<br>&nbsp;&nbsp;&nbsp; clsMinorType: @MEDIASUBTYPE_NULL<br>&nbsp; );<br>&nbsp;<br>&nbsp; //Filter注册Pin接口信息结构<br>&nbsp; TRegFilterPins = record<br>&nbsp;&nbsp;&nbsp; strName&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : PWideChar;<br>&nbsp;&nbsp;&nbsp; bRendered&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; bOutput&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; bZero&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; bMany&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; oFilter&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : PGUID;<br>&nbsp;&nbsp;&nbsp; strConnectsToPin : PWideChar;<br>&nbsp;&nbsp;&nbsp; nMediaTypes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : LongWord;<br>&nbsp;&nbsp;&nbsp; lpMediaType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : PRegPinTypes;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //定义实例<br>&nbsp;&nbsp; sudOutputPinDesktop: array of TRegFilterPins =<br>&nbsp; (<br>&nbsp;&nbsp;&nbsp; (<br>&nbsp;&nbsp;&nbsp; strName: 'Output'; // Pin名称<br>&nbsp;&nbsp;&nbsp; bRendered: FALSE;&nbsp; //是否是Render<br>&nbsp;&nbsp;&nbsp; bOutput: TRUE;&nbsp;&nbsp;&nbsp;&nbsp; //是否是输出接口<br>&nbsp;&nbsp;&nbsp; bZero: FALSE;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否允许为0<br>&nbsp;&nbsp;&nbsp; bMany: FALSE;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //是否有更多<br>&nbsp;&nbsp;&nbsp; oFilter: nil;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //连接的Filter<br>&nbsp;&nbsp;&nbsp; strConnectsToPin: nil;&nbsp;&nbsp;&nbsp; //连接的Pin<br>&nbsp;&nbsp;&nbsp; nMediaTypes: 1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //支持类型数量<br>&nbsp;&nbsp;&nbsp; lpMediaType: @sudPinTypes // Pin信息<br>&nbsp;&nbsp;&nbsp; )<br>&nbsp; );<br>&nbsp;<br>&nbsp; DefaultFrameLength: TReferenceTime = FPS_10; //由参考时钟确定FPS_10=1000000<br>&nbsp; PushDesktopName: WideString = '_ PushSource Desktop Filter'; //PushFilter友好名<br>&nbsp;<br>&nbsp; //Pin接口类封装,继承TBCSourceStream&lt;---TBCBaseOutputPin&lt;----TBCBasePin(TBCUnknown, IPin, IQualityControl)<br>&nbsp; TBCPushPinDesktop = class(TBCSourceStream)<br>&nbsp; protected&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FFramesWritten&nbsp; : Integer;&nbsp; //在播放文件的时候跟踪当前位置&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FZeroMemory&nbsp;&nbsp;&nbsp;&nbsp; : Boolean;&nbsp; //是否必须清零Buffer&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FSampleTime&nbsp;&nbsp;&nbsp;&nbsp; : TRefTime; //每个Sample一个时间戳<br>&nbsp;&nbsp;&nbsp; FFrameNumber&nbsp;&nbsp;&nbsp; : Integer;&nbsp; //已经显示了多少帧了<br>&nbsp;&nbsp;&nbsp; FFrameLength&nbsp;&nbsp;&nbsp; : TReferenceTime; //一帧的耗费时间<br>&nbsp;&nbsp;&nbsp; FScreenRect&nbsp;&nbsp;&nbsp;&nbsp; : TRect;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //包含需要捕获的视频框<br>&nbsp;&nbsp;&nbsp; FImageHeight,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //当前图像高<br>&nbsp;&nbsp;&nbsp; FImageWidth,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //当前图像宽&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FRepeatTime,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每帧之间重复时间 Time in msec between frames&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FCurrentBitDepth: Integer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //屏幕色彩位<br>&nbsp;&nbsp;&nbsp; FMediaType&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TAMMediaType;&nbsp;&nbsp; //媒体类型&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; FSharedState&nbsp;&nbsp;&nbsp; : TBCCritSec;&nbsp;&nbsp;&nbsp;&nbsp; //临界区在资源共享中实现线程同步<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; constructor Create(out hr: HResult; Filter: TBCSource);<br>&nbsp;&nbsp;&nbsp; destructor Destroy; override;<br>&nbsp;&nbsp;&nbsp; //实现虚函数,提供一个精确的媒体类型<br>&nbsp;&nbsp;&nbsp; function GetMediaType(iPosition: Integer; out MediaType: PAMMediaType): HResult; override;<br>&nbsp;&nbsp;&nbsp; //实现虚函数,实现接受8, 16, 24 or 32 RGB位视频格式<br>&nbsp;&nbsp;&nbsp; //如果媒体格式不能接受,则返回E_INVALIDARG<br>&nbsp;&nbsp;&nbsp; function CheckMediaType(MediaType: PAMMediaType): HResult; override;<br>&nbsp;&nbsp;&nbsp; function DecideBufferSize(Allocator: IMemAllocator;Properties: PAllocatorProperties): HRESULT; override;<br>&nbsp;&nbsp;&nbsp; function SetMediaType(MediaType: PAMMediaType): HRESULT; override;<br>&nbsp;&nbsp;&nbsp; function FillBuffer(Sample: IMediaSample): HResult; override;<br>&nbsp;&nbsp;&nbsp; //实现虚函数,质量控制功能<br>&nbsp;&nbsp;&nbsp; function Notify(Filter: IBaseFilter; q: TQuality): HRESULT; override; stdcall;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //PushFilter的类封装,继承自TBCSource&lt;-----TBCBaseFilter<br>&nbsp; //把桌面抓屏图像作为连续视频流<br>&nbsp; TBCPushSourceDesktop = class(TBCSource)<br>&nbsp; private<br>&nbsp;&nbsp;&nbsp; FPin: TBCPushPinDesktop;<br>&nbsp; public<br>&nbsp;&nbsp;&nbsp; constructor Create(ObjName: string; Unk: IUnKnown; out hr: HRESULT);<br>&nbsp;&nbsp;&nbsp; constructor CreateFromFactory(Factory: TBCClassFactory; const Controller: IUnknown); override;<br>&nbsp;&nbsp;&nbsp; destructor Destroy; override;<br>&nbsp; end;<br>&nbsp;<br>//TBCPushPinDesktop析构函数<br>constructor TBCPushPinDesktop.Create(out hr: HResult; Filter: TBCSource);<br>&nbsp; var<br>&nbsp;&nbsp;&nbsp; DC: HDC;<br>begin<br>&nbsp;&nbsp; inherited Create('_ Push Source Desktop', hr, Filter, 'Out');<br>&nbsp;&nbsp; FFramesWritten&nbsp;&nbsp; := 0;<br>&nbsp;&nbsp; FZeroMemory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; := False;<br>&nbsp;&nbsp; FFrameNumber&nbsp;&nbsp;&nbsp;&nbsp; := 0;<br>&nbsp;&nbsp; FFrameLength&nbsp;&nbsp;&nbsp;&nbsp; := FPS_5;<br>&nbsp;&nbsp; FSharedState&nbsp;&nbsp;&nbsp;&nbsp; := TBCCritSec.Create;<br>&nbsp;&nbsp; FCurrentBitDepth := 32;<br>&nbsp;<br>&nbsp; //这里关键是显示如何获取DIB图像,使用内存方式,把DIB图像插入到视频流<br>&nbsp;<br>&nbsp; //为了尽可能保持Samle采样,我们就需要从一个文件中读取图像,把它插入到发送下行接口流中<br>&nbsp;<br>&nbsp; //获取需要显示设备context上下文<br>&nbsp; DC := CreateDC('DISPLAY', nil, nil, nil);<br>&nbsp;<br>&nbsp; //获取主桌面窗口的尺寸<br>&nbsp; FScreenRect.Left&nbsp;&nbsp; := 0;<br>&nbsp; FScreenRect.Top&nbsp;&nbsp;&nbsp; := 0;<br>&nbsp; FScreenRect.Right&nbsp; := GetDeviceCaps(DC, HORZRES);<br>&nbsp; FScreenRect.Bottom := GetDeviceCaps(DC, VERTRES);<br>&nbsp;<br>&nbsp; //保持该尺寸,为后面填充Buffer使用<br>&nbsp; FImageWidth := FScreenRect.Right - FScreenRect.Left;<br>&nbsp; FImageHeight := FScreenRect.Bottom - FScreenRect.Top;<br>&nbsp;<br>&nbsp; //释放资源<br>&nbsp; DeleteDC(DC);<br>&nbsp;<br>&nbsp; hr := S_OK;<br>end;<br>&nbsp;<br>destructor TBCPushPinDesktop.Destroy;<br>begin<br>{$IFDEF DEBUG}<br>&nbsp; DbgLog(self, Format('Frames written %d', ));<br>{$ENDIF}<br>&nbsp; inherited;<br>end;<br>&nbsp;</p>
<p>//参考的视频格式,8, 16 (*2), 24 or 32 bits per pixel<br>//参考这些类型,选择更高的质量控制<br>// Therefore, iPosition =<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0&nbsp;&nbsp;&nbsp; Return a 32bit mediatype<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; Return a 24bit mediatype<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; Return 16bit RGB565<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; Return a 16bit mediatype (rgb555)<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; Return 8 bit palettised format<br>//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt;4&nbsp;&nbsp; Invalid<br>{<br>&nbsp; PVideoInfo = ^TVideoInfo;<br>&nbsp; tagVIDEOINFO = record<br>&nbsp;&nbsp;&nbsp; rcSource: TRect;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 我们实际需要使用的位(在整个窗口中的Sub窗口)<br>&nbsp;&nbsp;&nbsp; rcTarget: TRect;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 该视频音频去哪<br>&nbsp;&nbsp;&nbsp; dwBitRate: DWORD;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 近似位率<br>&nbsp;&nbsp;&nbsp; dwBitErrorRate: DWORD;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 错位率<br>&nbsp;&nbsp;&nbsp; AvgTimePerFrame: TReferenceTime;&nbsp;&nbsp; // 每帧的平均时间(100ns units)<br>&nbsp;&nbsp;&nbsp; bmiHeader: TBitmapInfoHeader;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //位图信息头,解码为RGB后可以形成一个Bitmpa图像<br>&nbsp;&nbsp;&nbsp; case Integer of<br>&nbsp;&nbsp;&nbsp; 0: (<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bmiColors: array of TRGBQuad //调色板<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; 1: (<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dwBitMasks: array of DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //真彩色掩码<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; 2: (<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TrueColorInfo: TTrueColorInfo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //两者都有<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp; end;<br>&nbsp;<br>&nbsp; PAMMediaType = ^TAMMediaType;<br>&nbsp; _AMMediaType = record<br>&nbsp;&nbsp;&nbsp; majortype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; subtype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; bFixedSizeSamples&nbsp;&nbsp;&nbsp; : BOOL;<br>&nbsp;&nbsp;&nbsp; bTemporalCompression : BOOL;<br>&nbsp;&nbsp;&nbsp; lSampleSize&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ULONG;<br>&nbsp;&nbsp;&nbsp; formattype&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : TGUID;<br>&nbsp;&nbsp;&nbsp; pUnk&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : IUnknown;<br>&nbsp;&nbsp;&nbsp; cbFormat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : ULONG;<br>&nbsp;&nbsp;&nbsp; pbFormat&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Pointer;<br>&nbsp; end;<br>}&nbsp;<br>function TBCPushPinDesktop.GetMediaType(iPosition: Integer; out MediaType: PAMMediaType): HResult;<br>var<br>&nbsp; pvi: PVIDEOINFO;<br>&nbsp; i: Integer;<br>begin<br>&nbsp; FFilter.StateLock.Lock;<br>&nbsp; try<br>&nbsp;&nbsp;&nbsp; if (MediaType = nil) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_POINTER;&nbsp; //指针错误<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; if (iPosition &lt; 0) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG; //无效的位置<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //是否从类型结束处开始 Have we run off the end of types?<br>&nbsp;&nbsp;&nbsp; if (iPosition &gt; 4) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := VFW_S_NO_MORE_ITEMS;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; MediaType.cbFormat := SizeOf(TVideoInfo);&nbsp; //视频类型<br>&nbsp;&nbsp;&nbsp; pvi := CoTaskMemAlloc(MediaType.cbFormat); //为媒体类型结构体分配内存<br>&nbsp;&nbsp;&nbsp; if (pvi = nil) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_OUTOFMEMORY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; ZeroMemory(pvi, MediaType.cbFormat);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; case iPosition of<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //返回32位格式的最高质量图像=RGB888<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biCompression := BI_RGB; //无压缩RGB格式<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biBitCount := 32;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biCompression := BI_RGB;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biBitCount := 24;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每像素16位=RGB565,把RGB掩码以DWord类型,放入调色板前三个地方<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for i := 0 to 2 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.TrueColorInfo.dwBitMasks := bits565;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biCompression := BI_BITFIELDS;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biBitCount := 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每像素16位=RGB555,把RGB掩码以DWord类型,放入调色板前三个地方<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for i := 0 to 2 do<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.TrueColorInfo.dwBitMasks := bits555;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biCompression := BI_BITFIELDS;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biBitCount := 16;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //每像素8位,可以不带调色板子<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biCompression := BI_RGB;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biBitCount := 8;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biClrUsed := iPALETTE_COLORS;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //任何视频格式都必须调整下面参数<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biSize := SizeOf(TBitmapInfoHeader);<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biWidth := FImageWidth;<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biHeight := FImageHeight;<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biPlanes := 1;<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biSizeImage := GetBitmapSize(@pvi.bmiHeader);<br>&nbsp;&nbsp;&nbsp; pvi.bmiHeader.biClrImportant := 0;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //清空源和目标框<br>&nbsp;&nbsp;&nbsp; SetRectEmpty(pvi.rcSource);<br>&nbsp;&nbsp;&nbsp; SetRectEmpty(pvi.rcTarget);<br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; //设置Majortype相关参数<br>&nbsp;&nbsp;&nbsp; MediaType.majortype&nbsp; := MEDIATYPE_Video;&nbsp; //主类型为视频格式<br>&nbsp;&nbsp;&nbsp; MediaType.formattype := FORMAT_VideoInfo; //格式类型为视频格式<br>&nbsp;&nbsp;&nbsp; MediaType.bTemporalCompression := False;<br>&nbsp;&nbsp;&nbsp; MediaType.bFixedSizeSamples := True;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; // 设置Subtype相关参数<br>&nbsp;&nbsp;&nbsp; MediaType.subtype := GetBitmapSubtype(@pvi.bmiHeader);<br>&nbsp;&nbsp;&nbsp; MediaType.pbFormat := pvi;<br>&nbsp;&nbsp;&nbsp; MediaType.lSampleSize := pvi.bmiHeader.biSizeImage;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; Result := S_OK;<br>&nbsp;<br>&nbsp; finally<br>&nbsp;&nbsp;&nbsp; FFilter.StateLock.UnLock;<br>&nbsp; end;<br>end;<br>&nbsp;<br>//检查我们是否支持该媒体类型<br>function TBCPushPinDesktop.CheckMediaType(MediaType: PAMMediaType): HResult;<br>var<br>&nbsp; pvi: PVIDEOINFO;<br>&nbsp; SubType: TGUID;<br>begin<br>&nbsp; //我们仅仅需要输出该视频<br>&nbsp; if not (IsEqualGUID(MediaType.majortype, MEDIATYPE_Video)) or<br>&nbsp; not (MediaType.bFixedSizeSamples) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //检查我们支持的子类型<br>&nbsp; SubType := MediaType.subtype;<br>&nbsp; if IsEqualGUID(SubType, GUID_NULL) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; if not (<br>&nbsp;&nbsp;&nbsp; IsEqualGUID(SubType, MEDIASUBTYPE_RGB8) or<br>&nbsp;&nbsp;&nbsp; IsEqualGUID(SubType, MEDIASUBTYPE_RGB565) or<br>&nbsp;&nbsp;&nbsp; IsEqualGUID(SubType, MEDIASUBTYPE_RGB555) or<br>&nbsp;&nbsp;&nbsp; IsEqualGUID(SubType, MEDIASUBTYPE_RGB24) or<br>&nbsp;&nbsp;&nbsp; IsEqualGUID(SubType, MEDIASUBTYPE_RGB32)<br>&nbsp;&nbsp;&nbsp; ) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; pvi := MediaType.pbFormat;<br>&nbsp;<br>&nbsp; if (pvi = nil) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //检查图像尺寸是否改变,那么返回错误,以便重新调整图像大小<br>&nbsp; if (pvi.bmiHeader.biWidth &lt;&gt; FImageWidth) or<br>&nbsp;&nbsp;&nbsp; (abs(pvi.bmiHeader.biHeight) &lt;&gt; FImageHeight) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; //不接受负高度,超出屏幕外的不支持<br>&nbsp; if (pvi.bmiHeader.biHeight &lt; 0) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; Result := S_OK; //接受该格式<br>end;<br>&nbsp;<br>// DecideBufferSize:在视频格式协商好后,每次都必须调用它,确定需要传输的的内存大小<br>function TBCPushPinDesktop.DecideBufferSize(Allocator: IMemAllocator;Properties: PAllocatorProperties): HRESULT;<br>var<br>&nbsp; pvi: PVIDEOINFOHEADER;<br>&nbsp; Actual: ALLOCATOR_PROPERTIES;<br>begin<br>&nbsp; if (Allocator = nil) or (Properties = nil) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_POINTER;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; FFilter.StateLock.Lock;<br>&nbsp; try<br>&nbsp;&nbsp;&nbsp; pvi := AMMediaType.pbFormat;&nbsp; //来自TBasePin接口的AMMediaType<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; Properties.cBuffers := 1; //内存块数量<br>&nbsp;&nbsp;&nbsp; Properties.cbBuffer := pvi.bmiHeader.biSizeImage; //内存块大小<br>&nbsp;&nbsp;&nbsp; Assert(Properties.cbBuffer &lt;&gt; 0); //确保该cbBuffer&lt;&gt;0,否则会引发异常<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //通过allocator分配查询我们需要的Sample内存大小<br>&nbsp;&nbsp;&nbsp; Result := Allocator.SetProperties(Properties^, Actual);<br>&nbsp;&nbsp;&nbsp; if Failed(Result) then Exit;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //实际需要的分配的内存和属性中定义的不匹配<br>&nbsp;&nbsp;&nbsp; if (Actual.cbBuffer &lt; Properties.cbBuffer) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_FAIL;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //确保我们的实际内存块数量为1<br>&nbsp;&nbsp;&nbsp; Assert(Actual.cBuffers = 1);<br>&nbsp;&nbsp;&nbsp; Result := S_OK;<br>&nbsp;<br>&nbsp; finally<br>&nbsp;&nbsp;&nbsp; FFilter.StateLock.UnLock;<br>&nbsp; end;<br>end;<br>&nbsp;<br>// SetMediaType,在两个Filter之间协商媒体类型时候,调用该函数<br>function TBCPushPinDesktop.SetMediaType(MediaType: PAMMediaType): HRESULT;<br>var<br>&nbsp; pvi: PVIDEOINFOHEADER;<br>begin<br>&nbsp; FFilter.StateLock.Lock;<br>&nbsp; try<br>&nbsp;&nbsp;&nbsp; //通过基类传递设置<br>&nbsp;&nbsp;&nbsp; Result := inherited SetMediaType(MediaType);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; if Succeeded(Result) then<br>&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pvi := AMMediaType.pbFormat;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pvi = nil) then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_UNEXPECTED;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 8-bit palettized,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // RGB565, RGB555,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // RGB24,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // RGB32<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if pvi.bmiHeader.biBitCount in then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //保存当前设定的媒体参数<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FMediaType := MediaType^;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FCurrentBitDepth := pvi.bmiHeader.biBitCount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //除此之外不支持其它媒体类型<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assert(False);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := E_INVALIDARG;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end;<br>&nbsp;&nbsp;&nbsp; end;<br>&nbsp;<br>&nbsp; finally<br>&nbsp;&nbsp;&nbsp; FFilter.StateLock.UnLock;<br>&nbsp; end;<br>end;<br>&nbsp;<br>//FillBuffer是在每次采集视频数据后调用,把数据写入到Stream中,传递给下一个Filter<br>//这个函数中为虚抽象函数,所以必须实现代码完成实际的视频数据填充,以便下传<br>function TBCPushPinDesktop.FillBuffer(Sample: IMediaSample): HResult;<br>var<br>&nbsp; pData: PByte;<br>&nbsp; cbData: Longint;<br>&nbsp; hDib: HBitmap;<br>&nbsp; pvih: PVIDEOINFOHEADER;<br>&nbsp; Start, Stop: REFERENCE_TIME;<br>&nbsp;<br>&nbsp; function min(v1, v2: DWord): DWord;<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; if v1 &lt;= v2 then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := v1<br>&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Result := v2;<br>&nbsp; end;<br>&nbsp;<br>begin<br>&nbsp; if (Sample = nil) then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := E_POINTER;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>&nbsp;<br>&nbsp; FSharedState.Lock;<br>&nbsp; try<br>&nbsp;&nbsp;&nbsp; //获取Sample中视频数据内存指针(PData获取)<br>&nbsp;&nbsp;&nbsp; Sample.GetPointer(pData);<br>&nbsp;&nbsp;&nbsp; cbData := Sample.GetSize;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //确认我们将要访问的视频格式,如果不合格将出现异常<br>&nbsp;&nbsp;&nbsp; Assert(IsEqualGUID(AMMediaType.formattype, FORMAT_VideoInfo));<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; pvih := AMMediaType.pbFormat;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //复制DIB位图数据到输出Buffer<br>&nbsp;&nbsp;&nbsp; //如果Sample的字节数大于真实图像字节,就限定复制的字节<br>&nbsp;&nbsp;&nbsp; pVih.bmiHeader.biSizeImage := min(pVih.bmiHeader.biSizeImage, cbData);<br>&nbsp;&nbsp;&nbsp; hDib := CopyScreenToBitmap(FScreenRect, pData, @pVih.bmiHeader);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; if (hDib &lt;&gt; 0) then&nbsp; DeleteObject(hDib);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //设置时间戳,可以给帧率回调函数使用<br>&nbsp;&nbsp;&nbsp; //如果这个是用AVI格式写的,那么我们就需要配置AVI Mux Filter 来设定每帧的平均时间<br>&nbsp;&nbsp;&nbsp; //当前时间<br>&nbsp;&nbsp;&nbsp; Start := FFrameNumber * FFrameLength;<br>&nbsp;&nbsp;&nbsp; Stop := Start + FFrameLength;<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; Sample.SetTime(@Start, @Stop);<br>&nbsp;&nbsp;&nbsp; Inc(FFrameNumber);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; //对于没有压缩的帧,都要设置为True,当作关键帧对待<br>&nbsp;&nbsp;&nbsp; Sample.SetSyncPoint(True);<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; Result := S_OK;<br>&nbsp;<br>&nbsp; finally<br>&nbsp;&nbsp;&nbsp; FSharedState.UnLock;<br>&nbsp; end;<br>end;<br>&nbsp;<br>//返回质量控制,由于这时源头,所以可以收到来自render之间的各个Filter的Notify,<br>//最终反应在Filter(来自哪个),Q质量控制参数,可以通过这两个参数来调整帧率和质量<br>function TBCPushPinDesktop.Notify(Filter: IBaseFilter; q: TQuality): HRESULT;<br>begin<br>&nbsp; Result := E_FAIL;<br>end;<br>&nbsp;</p>
<p>{<br>&nbsp; {$EXTERNALSYM CLSID_CVidCapClassManager} //BaseFilter目录类型<br>&nbsp; //CLSID_LegacyAmFilterCategory: TGUID = (D1:$083863F1;D2:$70DE;D3:$11D0;D4:($BD,$40,$00,$A0,$C9,$11,$CE,$86));<br>}<br>&nbsp;<br>initialization<br>&nbsp; TBCClassFactory.CreateFilter(TBCPushSourceDesktop, PushDesktopName,<br>&nbsp;&nbsp;&nbsp; CLSID_PushSourceDesktop, CLSID_LegacyAmFilterCategory,<br>&nbsp;&nbsp;&nbsp; MERIT_DO_NOT_USE, 1, @sudOutputPinDesktop<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp;<br>end.&nbsp;&nbsp;&nbsp;</p>
<p>//由FillBuffer先填充Sample信息,然后由DecideBufferSize来真实的为视频数据分配内存,供下级Filter使用<br>//加载顺序由下级Filter的InputPin接口调用本OutPutPin接口的GetMediaType,<br>//然后调用OutPutPin接口的SetMediaType设定媒体格式,<br>//调用DecideBufferSize,协商内存大小<br>//启动后,不断调用FillBuffer填充Sample</p>

</div>
<div id="MySignature" role="contentinfo">
    好的代码像粥一样,都是用时间熬出来的<br><br>
来源:https://www.cnblogs.com/jijm123/p/14339697.html
頁: [1]
查看完整版本: Delphi的DirectShow开发概述