两个Filter之间的连接都要指定一个allocator,有时也有几个Filter连接同用一个allocator。每一个allocator都要创建一个media sample池,并且给每一个sample分配一个内存buffer。每当一个Filter需要一个buffer来填充数据,它就通过allocator的函数IMemAllocator::GetBuffer.来获得一个sample。如果分配器allocator正好有空闲的sample,GetBuffer立即返回一个指向该sample的指针。如果没有空闲的sample,该方法就阻塞,直到有一个sample可用为止。当该函数返回一个sample时,Filter就将数据填充到buffer里,设置好标识,然后就将sample传递给下一个Filter。
下面描述了在流中只有一个allocator的情景,实际上,在每条数据流中总是有好几个allocator,当一个sample被释放的时候,也许此时有好几个allocator都在等着该sample,这就有新的问题了,也许有的alloctor永远都不能被分配sample,陷入互锁状态。下面的图就演示了这种情形,Decoder有数据需要压缩,因此它在等待Renderer释放sample,但是,Parser也在请求sample,它在等待decoder释放sample。
DirectShow为局部存储器传输定义了两种机制:推模式(push model)和拉模式(pull model)。在推模式中,源过滤器生成数据并提交给下一级过滤器。下一级过滤器被动的接收数据,完成处理后再传送给再下一级过滤器。在拉模式中,源过滤器与一个Render过滤器相连。Render过滤器向源过滤器请求数据后,源过滤器才传送数据以响应请求。推模式使用的是IMemInputPin接口,拉模式使用IAsyncReader接口,推模式比拉模式要更常用。
当一个pin向另一个pin传递数据的时候,它并不是直接将内存块的指针传递下一个pin,实际上,它将传递一个管理内存的com对象的指针给下一个pin。这个com对象称为media sample。暴露了IMediaSample接口。接收pin通过调用IMediaSample的方法来对内存进行操作,比如方法IMediaSample::GetSize, IMediaSample::GetActualDataLength以及IMediaSample::GetPointer。
另外一个com对象,叫做allocator,用来创建和管理sample的。暴露了IMemAllocator接口。当一个filter需要一个空的buffer的时候,就可以调用IMemAllocator::GetBuffer,该方法返回一个指向sample的指针。每一个pin连接都共享一个allocator,当两个pin连接的时候,他们会决定由哪个filter来提供allocator,通过pin还可以设置allocator的属性,例如,buffer的数量和大小。
Allocator创建了一个sample池。因此 ,当某个Filter调用GetBuffer函数时,一些sample被使用,其他空闲的sample可以响应。Allocator通过引用计数来跟踪samples。Filter调用Getbuffer返回的sample的引用计数是1。当sample的引用计数为0时,sample就返回内存池,成为空闲的sample,可以再次响应Getbuffer的调用。如果所有的sample都处于繁忙状态,Getbuffer就会阻塞,直到有一个sample空闲。
|