DirectShow 过滤器的状态
过滤器有三种可能的状态:停止、暂停和运行。
暂停状态的目的是提示图形中的数据,以便run命令立即响应。过滤器图形管理器控制所有状态转换。
当应用程序调用IMediaControl::Run、IMediaControl::Pause或IMediaControl::Stop时,图形过滤管理器会在所有过滤器上调用相应的IMediaFilter方法。stopped和running之间的转换总是经过paused状态,因此如果应用程序调用在stopped图形上运行,则图形过滤管理器在调用stop前先调用Pause。
过滤器的状态数据示例
参见以下拓扑结构:
数据源(Source)过滤器 > 转换(Transform)过滤器 > 渲染(Renderer)过滤器
现在假设源过滤器不是实时捕获源。当源过滤器暂停时,它会创建一个线程,生成新数据并尽快将其写入媒体Sample。线程通过调用转换过滤器的输入pin上的IMemInputPin::Receive将样本“push”到下游。转换过滤器在源过滤器的线程上接收sample。它可以使用工作线程将sample投递到渲染器,但通常在同一线程上投递它们。渲染器暂停时,它会等待接收sample。在它收到一个sample后,它会无限期地阻塞和保持该sample。如果是视频渲染器,它会将sample显示为海报图像,并根据需要重新绘制图像。
此时,数据流已完全提示并准备好渲染。如果图形管理器保持暂停状态,那么 Sample将“堆积”在图形过滤管理器第一个Sample后,直到每个过滤器在Receive或IMemAllocator::GetBuffer中被阻塞。不过,没有数据丢失。一旦源线程被解除阻塞,它就会从阻塞点恢复。
源过滤器和转换过滤器忽略了从暂停到运行的传输—它们只是继续以尽可能快的速度处理数据。但是当渲染器运行时,它会开始渲染sample。首先,它渲染暂停时保存的样本。然后,每次它收到一个新sample时,它都会计算sample的渲染时间。渲染器将保留每个sample,直到呈现时间,此时渲染示例。当它等待呈现时间时,它要么在Receive方法中阻塞,要么在带有队列的工作线程上接收新样本。渲染器上游的过滤器不参与调度。
实时源(如捕获设备)是这种通用体系结构的一个例外。对于实时数据源,提前提示任何数据是不合适的。应用程序可能会暂停图形,然后在运行它之前等待很长时间。图形不应呈现“过时”的sample。因此,活动源在暂停时不生成sample,仅在运行时生成。为了将这个事实传递给过滤器图管理器,源过滤器的IMediaFilter::GetState方法返回VFW_S_CANT_CUE。此返回代码表示过滤器已切换到暂停状态,即使呈现器未接收到任何数据。
当一个过滤器停止时,它会拒绝接收任何新的sample。源过滤器关闭它们的流线程,其他过滤器关闭它们可能创建的所有工作线程。管脚解除了它们的分配器。
过滤器的状态转换
图形过滤器管理器按上游顺序执行所有状态转换,从渲染器开始,向后到源过滤器。这种排序对于防止sample丢失和防止锁是必要的。最关键的状态转换是暂停和停止之间的转换:
- 停止到暂停:当每个过滤器暂停时,它就可以从下一个过滤器接收sample。源筛选器是最后暂停的。它创建流线程并开始传递sample。因为所有下游过滤器都被暂停,所以没有过滤器拒绝任何sample。图形过滤器管理器直到图形中的每个渲染器都接收到一个sample(如前所述的活动源除外)后才完成转换。
- 暂停到停止:当一个过滤器停止时,它释放它持有的所有sample,这将取消阻止在GetBuffer中等待的所有上游过滤器。如果筛选器正在Receive方法中等待资源,它将停止等待并从Receive返回,这将取消对调用筛选器的阻止。因此,当Filter Graph Manager停止下一个上游过滤器时,该过滤器不会在GetBuffer或Receive中被阻塞,并且可以响应stop命令。上游过滤器可能会在收到stop命令之前提供一些额外的sample,但是下游过滤器只是拒绝它们,因为它已经停止了。