Directshow中的时钟(Time and Clocks in Dshow)
智慧的鱼(aoosang)
摘要:本篇文档简单介绍了DirectShow中用来标记数据同步的时钟。
在 Filter Graph中,数据流就是依靠时钟来进行同步的,数据流中的每一个sample上都会标记一个时间戳,Video Renderer和Audio Renderer就根据sample上的时间戳来控制sample所携带的数据流的提交。如果到达Renderer的sample的时间比它携带的时间戳晚,则加快sample的播放,或者丢弃这个sample里的内容,如果sample上的时间戳的时间还未到,则sample要等待,一直等到sample时间戳的开始时间再开始播放。
下面我们先看看参考时钟。
1参考时钟:
参考时钟是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。
2 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
3时间戳
时间戳采用的是流时间,它在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接口
2 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的时间戳来自己决定播放速率
摘要:本篇文档简单介绍了DirectShow中用来标记数据同步的时钟。
在 Filter Graph中,数据流就是依靠时钟来进行同步的,数据流中的每一个sample上都会标记一个时间戳,Video Renderer和Audio Renderer就根据sample上的时间戳来控制sample所携带的数据流的提交。如果到达Renderer的sample的时间比它携带的时间戳晚,则加快sample的播放,或者丢弃这个sample里的内容,如果sample上的时间戳的时间还未到,则sample要等待,一直等到sample时间戳的开始时间再开始播放。
下面我们先看看参考时钟。
1参考时钟:
参考时钟是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。
2 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
3时间戳
时间戳采用的是流时间,它在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接口
2 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的时间戳来自己决定播放速率