Dshow graph内的filter需要使用同一个参考时钟做同步工作。graph管理器要找到一个可用的参考时钟:用户定义时钟、渲染时钟,或者系统时钟。
媒体流的时间是基于参考时钟的,但和graph上一次运行的时钟有点不同(如果graph暂停了,媒体流的时间是不会走的)。如果媒体流sample进入到渲染时有一个时间戳T,那就是说时间T时候,媒体流应该进行渲染。这就是基本的音视频同步了。
通常音频硬件有一个晶振,而且不保证系统时间和音频硬件时间一致。所以通常进行音频渲染时候,渲染时钟提供给整个Dshow graph。如果音频渲染有1个sample的延时,或者音频时钟和系统时钟不一致,那么音频渲染会做流时间的同步调整。
音频渲染时钟一般继承CbaseReferenceClock类,会调用SetTimeDelta进行流时间的调整。在发送给主时钟进行时间调试前,是使用低通filter,所以没必要担心。
视频渲染使用时间戳决定sample的显示,是基于媒体流的时间,音频渲染可以改变这个媒体流时间,那麽音频和视频渲染会使用同一个时间线。
视频渲染和丢帧:
如果视频速度较慢,所有的视频帧已经渲染了,那么视频就根据sample的时间戳来决定渲染的时刻。这时,如果视频慢于音频,就会进行丢帧处理。
Dshow的音视频是根据2个因素进行同步的:
1、
2、
在视频渲染那层,进行丢帧处理是不太有效率(最好是解码前,或者解码后马上能判断出来)。如果使用了overlay flip的表面,这个消耗就比较小了。即使是使用Blit,消耗也比较小(渲染时间远小于解码时间)。
因此质量通知消息,需要获取从上层流filter/DMO送到render的状态和标志。视频渲染会发出质量通知消息(因为它的运行是实时的),把消息发送给上层流。如果上层流是解码器,那就可以处理这个消息。如果它处理不了,会继续往上传递这个消息。注意,视频渲染如果延时了,会一直丢帧。
下面是解码filter通过质量接口,进行丢帧处理:
HRESULT CDecoderFilterPin::Notify(IBaseFilter *pSender, Quality q)
{
}
else
{
if (has frame dropping algorithm)
}
}
return status
}
解码器需要根据当前时间(通过IQualityControl/IDMOQualityControl),决定是否进行丢帧。丢帧的算法根据当前情况而定,下面是个例子:
质量通知消息会告诉我们,距离上一次渲染,延时了的时间。简单的算法会一直丢帧,直到视频的帧是当前需要播放的为止。因此,这个追赶的速度会很快:
CatchupPTS = q.Late + q.Timestamp
Drop all frames until PTS_frame >= Catchup_PTS
当然,实际情况可能还会不大一样。
1、
2、
3、
在触发模式下,丢帧算法通常比较宽松。帧率播放比1.0的速度要快,解码器接受的sample不一定是关键帧。这个时候,只需要丢掉那些非关键性的帧,就能轻松的追赶上时间了。
http://blogs.msdn.com/medmedia/archive/2007/03/05/basics-of-a-v-synchronization-in-directshow.aspx