ID #22275

DirectShow快速入门之参考时钟

  摘要:本篇文档简单介绍了DirectShow中用来标记数据同步的时钟。

  在 Filter Graph中,数据流就是依靠时钟来进行同步的,数据流中的每一个sample上都会标记一个时间戳,Video Renderer和Audio Renderer就根据sample上的时间戳来控制sample所携带的数据流的提交。如果到达Renderer的sample的时间比它携带的时间戳晚,则加快sample的播放,或者丢弃这个sample里的内容,如果sample上的时间戳的时间还未到,则sample要等待,一直等到sample时间戳的开始时间再开始播放。

  下面我们先看看参考时钟。

  参考时钟

  参考时钟是Filter用来同步Filter 图表管理器中的所有的Filter的。

  任何一个引出IReferenceClock 接口的对象都可以作为参考时钟。参考时钟可以是Filter提供(例如声卡就可以提供一个硬件的时钟),当然,可靠的时钟就是采用系统的时间。

  名义上,参考时钟的精确度在100纳秒,但实际上,没有那么精确。调用IReferenceClock::GetTime可以获取时钟的当前时间。

  尽管时钟的精确性还有所变动,但是GetTime方法返回的保证时间是增加的。也就是说,时钟不会倒退回去,比如,对硬件时钟进行了调整,GetTime方法就返回上次的时间。

  如果Filter Graph中有多个对象实现了IReferenceClock接口,Filter Graph Manager是如何做出选择的呢?默认的算法如下:

  当Graph运行的时候,Filter图表管理器会自动选择一个参考时钟的,选择时钟的法则如下

  1、如果应用程序选择了时钟,就采用应用程序选择的时钟

  2、如果Graph包含一个活动的源Filter(Live Source),并且这个filter有IReferenceClock接口,那么就用这个时钟。

  3、如果Graph中不含有任何活动的源Filter,就选用graph中任何暴露IReferenceClock接口的Filter,选择的方法是从Renderers逆流向上,连接的filter优先,没有连接的filter次之。

  4、如果没有任何filter符合条件,就采用系统参考时钟System Reference Clock

  设置参考时钟

  如果你想为graph设置新的时钟,应用程序可以调用图表管理器的接口IMediaFilter::SetSyncSource方法来选择一个参考时钟。

  如果你给SetSyncSource传递的参数为NULL,Graph就不设置任何的参考时钟了。如果想恢复缺省的时钟,调用IFilterGraph::SetDefaultSyncSource方法。

  当graph的参考时钟改变时,Graph通过the reference clock changes, the Filter Graph Manager notifies each filter by calling its IMediaFilter::SetSyncSource通知所有的Filter。

  Clock Times

  Directshow定义了两个相关的时间,参考时间Reference time和 数据流时间Stream time

  前者是从参考时钟返回的绝对时间(IReferenceClock::GetTime),数值本身的意义取决于参考时钟的内部实现,利用价值不大;后者是两次从参考时钟读取的数值的差值,实际应用于Filter Graph内部的同步

  当Filter graph正在运行,流时间的值为当前参考时钟时间减去Filter Graph启动的时间,也就是一个差值,当Filter graph暂停,流时间就等于它暂停时的stream时间,当Filter graph停止时,流时间不确定。

  当一个sample具有时间戳t,就意味着这个sample应该在流时间t播放,因此,流时间也叫播放时间。

  当应用程序通过IMediaControl::Run来运行graph时,在graph内部也调用了IMediaFilter::Run

  时间戳

  时间戳采用的是流时间,它在sample标上开始和结束时间。时间戳也叫播放时间,通过后面的知识你会了解到,并不是所有格式的数据流都采用同一种样式的时间戳。

  当renderer Filter接收到sample,它会根据sample的时间戳进行排序,等到该sample的播放时间到了,就开始播放该sample,从到达到开始播放的时间,可以通过IReferenceClock::AdviseTime获得。如果sample来晚了,或者sample没有时间戳,filter就立即播放sample。如果Sample来得早了,则通过调用参考时钟的IReferenceClock::AdviseTime等待Sample的开始时间到达后再将这个Sample播放。Sample上的时间戳一般由Source Filter或Parser Filter来设置,设置的方法有如下几种情况:

  1、文件回放

  第一个sample的时间戳为0,随后的时间戳根据sample的大小和播放的速率来确定。这都由分解文件的Filter来计算和确定,例如AVI Splitter

  2、视频和音频的捕捉

  所有的sample在捕捉的时候就被打上时间戳了,时间等于流时间

  3、混和Filter

  根据输出数据流的格式,混和filter也许需要时间戳,也许不需要,

  可以通过调用IMediaSample::SetTime来给sample设置时间

  4、活动的源 Live Source

  活动的源Filter,就是推模式的源,实时的接收数据。视频捕捉和网络广播就是例子,活动得源无法控制数据流得速率。

  下面的Filter通常被认为是活动的源filter

  ·Filter调用IAMFilterMiscFlags::GetMiscFlags方法时返回AM_FILTER_MISC_FLAGS_IS_SOURCE,并且至少有一个输出pin暴露了IAMPushSource接口。

  ·Filter暴露IKsPropertySet接口,并且有个捕捉pin(PIN_CATEGORY_CAPTURE),如果活动的源能够提供参考时钟,那么Graph首先采用。

  反应时间(Latency)

  过滤器(filter)的反应时间就是Filter处理sample所花费的时间。对于活动的源filter,反应时间由容纳sample的内存大小决定。例如,假设一个filter有一个视频源具有33ms的反应时间,一个音频源有500ms的反应时间,每一个视频祯(video frame)比相应的音频sample要早470ms,除非graph进行补偿,否则视频和音频是不同步的。

  活动的源可以通过IAMPushSource接口来进行同步。除非应用程序调用IAMGraphStreams::SyncUsingStreamOffset方法对源进行同步,一般来说Filter图表管理器是不会对源进行同步的。图表管理器对源进行同步时,向各个源filter查询IAMPushSource 接口,如果filter支持IAMPushSource ,图表管理器就会通过IAMLatency::GetLatency来得到filter期望的反应时间。注:IAMPushSource继承与 IAMLatency。通过组合这些反应值,filter图表管理器Graph最大反应时间,然后调用IAMPushSource::SetStreamOffset给每个源filter设置一个数据流偏移时间,当filter给它产生的sample打时间戳的时候,要加上偏移时间的。

  现在,VFW Capture filter 和 Audio Capture filter.都支持IAMPushSource接口。

  速率匹配(Rate Matching)

  当renderer Filter利用参考时钟安排播放顺序的时候,如果源filter采用另一种时钟,在重放的时候就会发生故障。播放的速度大于源产生的速度,就会产生间隙停顿,或者播放速度小于源的产生速度,就会形成数据的堆积,造成内存出错。源一般来说是无法控制数据的产生速度的,因此,播放速度要随着源的速度改变而改变。

  现在,只有在音频播放filter才能进行速率匹配,因为音频中的glitches比视频中的更容易捕捉到。为了匹配音频播放速率,要注意下列事情。

  1、如果graph没有使用参考时钟,没法进行速率匹配。

  2、上游要有一个活动的源

  3、源filter的输出pin要支持IAMPushSource接口,当请求IAMPushSource::GetPushSourceFlags.要返回下面的值

  AM_PUSHSOURCECAPS_INTERNAL_RM

  AM_PUSHSOURCECAPS_NOT_LIVE

  AM_PUSHSOURCECAPS_PRIVATE_CLOCK

  4、如果GetPushSourceFlags返回0,播放filter就根据graph时钟和sample的时间戳来自己决定播放速率


2006-10-08 00:00
阅读:
I'm VC , Just U know Y
本站部分文章来源于互联网,版权归原作者所有。

延伸阅读:

VC下利用多线程实现文件的快速检索

ADO VC++ Extensions入门

软件框架的利器、TangramMini组件应用教程一:入门

我的Visual C++入门之路

VC精华教程:MFC入门系列(二)