王朝网络
分享
 
 
 

数据流在Filter Graph里的流动

王朝厨房·作者佚名  2007-01-04
宽屏版  字体: |||超大  

数据流在Filter Graph里的流动

智慧的鱼(aoosang)

摘要:

1 directshow数据流动概述

数据总是存在内存块中的字节集合,每个buffer都被封装在一个叫做media sample的com组件,它引出了IMediaSample接口。这个sample一般都有一个叫做内存分配器(alloctor)的com对象来创建,这个对象具有IMemAllocator接口。每一个pin之间的连接都要指定一个allocator,有时也有几个连接同用一个allocator。

aspectratio="t">

每一个allocator都要创建一个media sample池,并且给每一个sample分配一个内存buffer。每当一个Filter需要一个buffer来填充数据,它就通过allocator的函数IMemAllocator::GetBuffer.来获得一个sample。如果分配器allocator正好有空闲的sample,GetBuffer立即返回一个指向该sample的指针。如果没有空闲的sample,该方法就阻塞,直到有一个sample可用为止。当该函数返回一个sample时,Filter就将数据填充到buffer里,设置好标识,然后就将sample传递给下一个Filter。

当一个renderfilter接收到一个sample时,它就检查该sample的时间戳,直到Fliter Graph的参考时钟表明该数据可用播放,Filter就开始播放该数据。当数据播放完毕,Filter释放sample,直到所有的Filter都释放对该sample的引用,该sample的引用计数为0时,这个sample才返回到sample池。

有时也许数据流的上游对buffer的填充比播放要快,即使这样,render Filter也要按照时间戳播放数据,这样sample池中的sample数量就少,从而填充的速度减慢。

上面描述了在流中只有一个allocator的情景,实际上,在每条数据流中总是有好几个allocator,当一个sample被释放的时候,也许此时有好几个allocator都在等着该sample,这就有新的问题了,也许有的alloctor永远都不能被分配sample,陷入互锁状态。下面的图就演示了这种情形,Decoder有数据需要压缩,因此它在等待Renderer释放sample,但是,Parser也在请求sample,它在等待decoder释放sample。

具体参加help

2 传输(Transports)

为了在过滤器图表中传送媒体数据,DirectShow过滤器需要支持一些协议,称之为传输协议(transport)。相连的过滤器必须支持同样的传输协议,否则不能交换媒体数据。

大多数的DirectShow过滤器把媒体数据保存在主存储器中,并通过引脚把数据提交给其它的过滤器,这种传输称为局部存储器传输(local memory transport)。虽然局部存储器传输在DirectShow中最常用,但并不是所有的过滤器都使用它。例如,有些过滤器通过硬件传送媒体数据,引脚只是用来提交控制信息,如IOverlay接口。

DirectShow为局部存储器传输定义了两种机制:推模式(push model)和拉模式(pull model)。在推模式中,源过滤器生成数据并提交给下一级过滤器。下一级过滤器被动的接收数据,完成处理后再传送给再下一级过滤器。在拉模式中,源过滤器与一个分析过滤器相连。分析过滤器向源过滤器请求数据后,源过滤器才传送数据以响应请求。推模式使用的是IMemInputPin接口,拉模式使用IAsyncReader接口,推模式比拉模式要更常用。

3 Samples和Allocators

当一个pin向另一个pin传递数据的时候,它并不是直接将内存块的指针传递下一个pin,实际上,它将传递一个管理内存的com对象的指针给下一个pin。这个com对象称为media sample。暴露了IMediaSample接口。接收pin通过调用IMediaSample的方法来对内存进行操作,比如方法IMediaSample::GetSize, IMediaSample::GetActualDataLength以及

IMediaSample::GetPointer。

Sample一般都是从源filter开始,通过输出pin传递到下一个filter的输入pin,一路传递下去一直到render filter。在拉模式中,输出pin通过调用输入pin上的IMemInputPin::Receive方法传递sample,输入pin或者在Receive函数同步处理数据,或者另外采用一个工作线程异步出来的方式。如果在Recive方法中需要等待资源的话,也可以阻塞。

另外一个com对象,叫做allocator,用来创建和管理sample的。暴露了IMemAllocator接口。当一个filter需要一个空的buffer的时候,就可以调用IMemAllocator::GetBuffer,该方法返回一个指向sample的指针。每一个pin连接都共享一个allocator,当两个pin连接的时候,他们会决定由哪个filter来提供allocator,通过pin还可以设置allocator的属性,例如,buffer的数量和大小。

下面的图表显示了allocator,sample和filter之间的关系。

Media sample引用计数

Allocator创建了一个sample池。因此 ,当某个Filter调用GetBuffer函数时,一些sample被使用,其他空闲的sample可以响应。Allocator通过引用计数来跟踪samples。Filter调用Getbuffer返回的sample的引用计数是1。当sample的引用计数为0时,sample就返回内存池,成为空闲的sample,可以再次响应Getbuffer的调用。如果所有的sample都处于繁忙状态,Getbuffer就会阻塞,直到有一个sample空闲。

例如,假设一个输入pin接到一个sample,如果它在Receive方法里同步的处理这个sample,没有增加该sample的引用计数,等到Receive返回后,输出pin就释放这个sample,引用计数为0,sample就返回到内存池中。 如果输入pin的线程还要处理该sample,引用计数增加1,成为2,输出pin返回,释放,计数成1。

当一个输入pin接收一个sample时,它可以将数据复制到另一个sample中,也可以将这个sample传递到下一个Filter。一个sample可以流遍整个filter graph。不过引用计数要保持大于0。 当一个输出pin调用了Release以后,就不应该再次使用该sample,因为也许下游还有filter正在使用该sample。输出pin必须调用GetBuffer获取新的sample。

这种机制减少 了内存分配的,因为buffer可以重用。也防止了数据没有被处理的sample被重新写入。

当一个Filter创建一个allocator的时候,allocator还没有保留任何的内存,如果这个时候有人Getbuffer,就会失败。只有当数据流开始的时候,输出pin调用IMemAllocator::Commit,提交allocator,现在才能分配内存。

当数据流停止的时候,pin就调用IMemAllocator::Decommit,来销毁allocator。在allocator再次committ之前,所有调用GetBuffer方法都会失败。当然,如果有一个GetBuffer阻塞调用在等待sample的时候,遇到Decommit方法,会立即返回一个错误码。

4 Filter的状态

Filter有三种状态,停止,暂停,运行。

过滤器图表管理器 控制着Filter的所有状态的转换。当应用程序调用IMediaControl::Run, IMediaControl::Pause, or IMediaControl::Stop时, 过滤器图表管理器就调用Filter相应的IMediaFilter方法。停止,运行状态的切换总是要经过暂停,因此,当一个应用程序对一个停止的Graph 调用RUN命令时,过滤器图表管理器 在run之前首先要暂停。

对于大多数的filter来说,running和paused状态是一样的。看下面的Graph

Source > Transform > Renderer

当一个Filter停止时,它拒绝发送给它的任何samples,源filter关闭他们的stream线程,其他filter也关闭他们创建的其他线程,pin decommit他们的内存分配器。

过滤器图表管理器按照逆流的方向来切换Filter的状态,从Renderer Filter到源filter,这种方式可以防止死锁。最关键的状态切换是暂停和停止之间。

从停止到暂停,当filter暂停时,它就做好了接收sample的准备,源filter是最后一个切换到暂停的。它开始创建streaming线程,发送sample,因为下游的filter的状态都已经切换到暂停了,所以,所有的filter都可以接收sample。只有当所有的flter都接收到sample,过滤器图表管理器才算完成了状态的切换

从暂停到停止。当一个filter停止时,它要释放它拥有的所有的samples。当图表管理器试图停掉上游的一个filter时,这个filter不会阻塞在Getbuffer和receive方法里,它会立即响应stop命令。上游的filter也许在执行stop命令前还会讲少量的sample传递下去,但是下游的filter会拒绝的,因为他们已经停止了。

5拉PULL模式

在IMemInputPin接口中,上游的flter决定了发送什么样的数据,然后将数据推给下游的filter。但在另外的场合,拉模式更适合。下游的filter向上游的filter请求数据,数据依然是从上游到下游,从输出pin到输入pin,但是下游的filter主导着数据的流动。这种类型的连接采用的是IAsyncReader接口

拉模式的典型应用的文件的回放,例如在一个AVI文件的回放graph中,Async File Source

Filter就担负着从文件中读取数据,然后将数据以字节流的方式发送给下面的filter。

作者简介:李强,目前暂时供职于山大联润信息科技有限公司,从事网络视频会议软件的开发,目前的感兴趣的方向,移动设备上多媒体的开发。aooang@hotmail.com 欢迎转载本文档,希望保留作者简介,以示对作者劳动的尊重。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有