6 Time and Clocks in DirectShow
6.1 Reference Clocks
参考时钟是
Filter Graph Manager
用来同步所有
Filter
的。任何一个暴露了
IReferenceClock
接口的对象都可以作为参考时钟。参考时钟可以是
Filter
提供,例如声卡就可以提供一个硬件的时钟。作为应变,
Filter Graph Manager
也可采用系统的时间。名义上,参考时钟的精确度在
100
纳秒,但实际上,没有那么精确。调用
IReferenceClock::GetTime
可以获取时钟的当前时间。时间的基准是开始计时的时间,根据实现的不同,
GetTime
的返回值不是绝对的。关键的是从时间开始的变化量。
尽管时钟的精确性还有所变动,但是
GetTime
方法返回的保证时间是增加的。也就是说,时钟不会倒退回去,比如,对硬件时钟进行了调整,
GetTime
方法就返回上次的时间,直到硬件时钟赶上。更多信息可参考
CBaseReferenceClock
类。
6.1.1 Default Reference Clock
当
Graph
运行的时候,
Filter Graph Manager
会自动选择一个参考时钟的,选择的法则如下
1
、如果应用程序选择了时钟,就采用应用程序选择的时钟
2
、如果
Graph
包含活动的源
Filter
,并且有
IReferenceClock
接口,那么就用这个时钟。
3
、如果
Graph
没有活动的源
Filter
,就用
Graph
中任何有
IReferenceClock
接口的
Filter
,选择的方法是从
Renderers
逆流向上,连接的
Filter
优先,没有连接的
Filter
次之。
4
、如果没有任何
Filter
符合条件,就采用系统参考时钟
System Reference Clock
6.1.2 Setting the Reference Clock
应用程序可以调用
Filter Graph Manager
的接口
IMediaFilter
的
SetSyncSource
方法来设置新的参考时钟。只有因为特殊原因需要设置其他时钟时才调用此函数。
如果你给
SetSyncSource
传递的参数为
NULL
,
Graph
不设置任何参考时钟。如果想恢复缺省的时钟,调用
IFilterGraph::SetDefaultSyncSource
。当
Graph
的参考时钟改变时,
Filter Graph Manager
调用
IMediaFilter::SetSyncSource
通知所有的每个
Filter
。
6.3 Clock Times
DirectShow
定义了两个相关的时间:参考时间和流时间。
·参考时间:是参考时钟返回的绝对时间
·流时间:和
Graph
最近异常开始运行的时间有关。
·当
Graph
正在运行,流时间就等于从开始时间减去开始时间
·当
Graph
暂停,流时间就等于它暂停开始的时间
·在进行
Seek
操作后,流时间重新设置为
0
·当
Graph
停止时,流时间没有定义。
当一个
Sample
具有时间戳
T
,就表示
Sample
应该在流时间
T
播放,因此流时间也叫播放时间。
应用程序调用
IMediaControl::Run
运行
Graph
时,
Filter Graph Manager
会调用每个
Filter
的
IMediaFilter::Run
方法。为了补偿调用每个
Filter
的时间延迟,
Filter Graph Manager
会指定一个稍微将来的时间进行补偿。
6.4 Time Stamps
时间戳定义了媒体
Sample
的开始和结束时间,用流时间表示。时间戳也叫播放时间。通过后面的知识你会了解到,并不是所有格式的数据流都采用同一种样式的时间戳。比如,并不是所有的
MPEG Sample
都有时间戳。在
MPEG Filter Graph
,时间戳并不应用在每帧上直到它们从解码器输出。
当
Render Filter
接收到
Sample
,根据
Sample
的时间戳进行提交。如果
Sample
达到晚了或者没有时间戳,
Filter
立即提交
Sample
。否则,在提交
Sample
前,
Filter
会一直等到
Sample
的开始时间。(通过调用
IReferenceClock::AdviseTime
方法等待开始时间。)
Source Filter
和
Parse Filter
有责任给处理的
Sample
设置正确的时间戳。使用如下规则:
·文件回放:第一个
Sample
的时间戳为
0
,随后的时间戳根据
Sample
的长度和播放的速率来确定。这些又由文件格式确定。解析
Filter
有责任计算正确的时间戳(例如
AVI Splitter
)。
·视频和音频的捕捉:每个
Sample
都打上开始时间,它等于捕捉的流时间。注意下面两点:
·预览
PIN
(与捕捉
PIN
相反)出来的视频帧没有时间戳。因为
Graph
延迟,打上捕捉时间的视频帧到达视频
Renerer
时总是有延迟。这样就会使
Renderer
丢帧以尝试质量控制。关于质量控制可参考
Quality-Control Management
。
·音频捕捉:音频捕捉
Filter
使用自己的缓冲,与音频驱动所使用的不同。音频捕捉驱动以固定间隔时间填充捕捉
Filter
的缓冲。间隔时间取决于驱动,但通常不超过
10
毫秒。在音频
Sample
上的时间戳反应了驱动填充捕捉
Filter
缓冲的时间。这些时间有稍微不准确,特别是如果应用程序使用更小的缓冲。但是,媒体时间能准确反应缓冲中音频
Sample
的数量。
·
Mux Filter:
根据输出数据流的格式,
Mux Filter
可能需要生成时间戳,也许不需要。比如
AVI
文件格式使用固定的帧率,没有时间戳,因此
AVI Mux Filter
假定
Sample
都是在大约正确的时间达到。如果到达时间比时间戳晚就会丢帧。而对于文件回放,新的时间戳是运行时生成的。
可以通过调用
IMediaSample::SetTime
来给
Sample
设置时间。
另一个可选功能是
Filter
可以给
Sample
指定
Media Time
。在视频流,
Media Time
表示帧数。在音频流,表示数据包中的
Sample
数量。比如,如果每个包有
1
秒的
444.1KHZ
的音频,第一个包的媒体开始时间是
0
媒体结束时间是
44100
。在支持
Seek
的流,总是与流的开始时间有关。比如,如果对一个
15FPS
的视频从开始定位到
2
秒。定位后的
Media Sample
的时间戳是
0
但是
Media Time
是
30
。
Renderer
和
Mux Filter
可通过检测间隔用
Media Time
判断帧或者
Sample
是否被丢弃。但是,
Filter
并不要求设置
Media Time
。设置
Media Time
可调用
IMediaSample::SetMediaTime.
6.5 Live Source
活动的
Live Filter
,就是推模式的源,实时的接收数据。视频捕捉和网络广播就是例子,活动源通常无法控制数据流得速率。
Filter
如果有下面的任意一个特征,通常被认为是
Live Filter
:
1
、
IAMFilterMiscFlags::GetMiscFlags
时返回
AM_FILTER_MISC_FLAGS_IS_SOURCE
,并且至少有一个输出
Pin
暴露了
IAMPushSource
接口
2
、
Filter
暴露
IKsPropertySet
接口,并且有个捕捉
Pin
(
PIN_CATEGORY_CAPTURE
),更多信息可参考
Pin Property Set.
如果
Live Source Filter
提供时钟,那么
Filter Graph Manager
首先采用它作为参考时钟。
6.5.1 Latency
Filter
的反映时间就是
Filter
处理
Sample
所花费的时间。对于
Live Source
,反应时间由
Sample
的内存大小决定。例如,假设一个
Filter
有一个
33ms
反应时间的视频源,一个
500ms
反应时间的音频源,每一个视频祯(
video frame
)比相应的音频
Sample
要早
470ms
,除非
Graph
进行补偿,否则视频和音频是不同步的。
Live Source
可以通过
IAMPushSource
接口来进行同步。
Filter Graph Manager
在应用程序调用
IAMGraphStreams::SyncUsingStreamOffset
方法后就不会对源进行同步。如果开启同步,
Filter Graph Manager
在每个
Source Filter
查询
IAMPushSource
接口,如果支持接口,
Filter Graph Manager
就用
IAMLatency::GetLatency
来得到
Filter
期望的反应时间。(
IAMPushSource
从
IAMLatency
继承)。通过组合这些反应值,
Filter Graph Manager
决定
Graph
的最大反应时间。然后调用
IAMPushSource::SetStreamOffset
给每个源
Filter
设置一个数据流偏移时间,
Filter
给它产生的
Sample
打时间戳的时候会加上偏移时间的。
这种方法主要用于实时预览。但是,实时捕捉设备的
(
比如摄像机
)
并不在
Sample
上设置时间戳。因此,在实时捕捉设备上用此方法,必须从捕捉
PIN
进行预览。可参考
DirectShow Video Capture Filter.
现在,
VFW Capture Filter
和
Audio Capture Filter.
都支持
IAMPushSource
接口。
6.5.2 Rate Matching
当
Render Filter
利用参考时钟安排播放顺序的时候,如果源
Filter
采用另一种时钟,在重放的时候就会发生故障。播放的速度大于源产生的速度,就会产生间隙停顿,或者播放速度小于源的产生速度,就会形成数据的堆积,直到
Graph
丢帧。源一般来说是无法控制数据的产生速度的,因此,播放速度要随着源的速度改变而改变。
现在,只有在音频播放
Filter
才能进行速率匹配,因为音频中的故障比视频中的更容易捕捉到。为了匹配播放速率,音频
Renderer
必须要选择一个匹配标准。使用如下规则:
·如果
Graph
没有使用参考时钟,没法进行速率匹配。(只要没有参考时钟
Sample
都是立即提交)
·如果
Graph
有参考时钟,
Renderer
的上一级必须要有一个活动的源。否则不进行匹配。
·如果上一级有
Live Source,
并且支持
IAMPushSource
接口,调用
GetPushSourceFlags
:
·返回
AM_PUSHSOURCECAPS_INTERNAL_RM
,表示
Source
有自己的匹配机制
·返回
AM_PUSHSOURCECAPS_NOT_LIVE
,表示不是
Live Source
,即使有
IAMPushSource
接口,因此
Audio Renderer
不匹配。
·返回
AM_PUSHSOURCECAPS_PRIVATE_CLOCK
,表示
Source
用私有时钟生成时间戳。此时,
Audio Renderer
用时间戳匹配。(如果没有时间戳,但是
Renderer
忽略
Flag
)
.
·如果
GetPushSourceFlags
返回
0
,播放
Filter
就根据
Graph
时钟和
Sample
的时间戳来自己决定播放速率。
·如果
Audio Renderer
不是
Graph
时钟并且
Sample
有时间戳,就与时间戳匹配。
·如果
Sample
没有时间戳,
Audio Renderer
尝试与进入
Audio Data
的频率匹配。
·如果
Audio Renderer
是
Graph
时钟,尝试与进入
Audio Data
匹配。
最后一种情况的原因是:如果Audio Renderer是参考时钟,Source Filter用同样的时钟生成时间戳,然后Audio Renderer不能与时间戳匹配。如果这么做了,就可能尝试与自己匹配,这样会引起时钟混乱。因此,Audio Renderer就与进入Audio Data的频率匹配。