现在的位置: 首页 > 综合 > 正文

CE基本的音视频同步方法

2013年08月31日 ⁄ 综合 ⁄ 共 2127字 ⁄ 字号 评论关闭
 

Dshow graph内的filter需要使用同一个参考时钟做同步工作。graph管理器要找到一个可用的参考时钟:用户定义时钟、渲染时钟,或者系统时钟。

 

媒体流的时间是基于参考时钟的,但和graph上一次运行的时钟有点不同(如果graph暂停了,媒体流的时间是不会走的)。如果媒体流sample进入到渲染时有一个时间戳T,那就是说时间T时候,媒体流应该进行渲染。这就是基本的音视频同步了。

 

通常音频硬件有一个晶振,而且不保证系统时间和音频硬件时间一致。所以通常进行音频渲染时候,渲染时钟提供给整个Dshow graph。如果音频渲染有1个sample的延时,或者音频时钟和系统时钟不一致,那么音频渲染会做流时间的同步调整。

 

音频渲染时钟一般继承CbaseReferenceClock类,会调用SetTimeDelta进行流时间的调整。在发送给主时钟进行时间调试前,是使用低通filter,所以没必要担心。

 

视频渲染使用时间戳决定sample的显示,是基于媒体流的时间,音频渲染可以改变这个媒体流时间,那麽音频和视频渲染会使用同一个时间线。

 

视频渲染和丢帧:

如果视频速度较慢,所有的视频帧已经渲染了,那么视频就根据sample的时间戳来决定渲染的时刻。这时,如果视频慢于音频,就会进行丢帧处理。

 

Dshow的音视频是根据2个因素进行同步的:

1、  音频渲染控制了Dshow的媒体流时间

2、  IqualityControl和IDMOQualityControl接口,负责丢帧处理

 

在视频渲染那层,进行丢帧处理是不太有效率(最好是解码前,或者解码后马上能判断出来)。如果使用了overlay flip的表面,这个消耗就比较小了。即使是使用Blit,消耗也比较小(渲染时间远小于解码时间)。

 

因此质量通知消息,需要获取从上层流filter/DMO送到render的状态和标志。视频渲染会发出质量通知消息(因为它的运行是实时的),把消息发送给上层流。如果上层流是解码器,那就可以处理这个消息。如果它处理不了,会继续往上传递这个消息。注意,视频渲染如果延时了,会一直丢帧。

 

下面是解码filter通过质量接口,进行丢帧处理:

HRESULT CDecoderFilterPin::Notify(IBaseFilter *pSender, Quality q)

{

       if (quality sink has been set)           // m_pQSink

       {

              status = Pass Notify on the quality sink   (base sender is the decoder filter now)

}

else

{

if (has frame dropping algorithm)

              {

                     status = Call decoder filter to do frame dropping

              }

              else

              {

                     if (upstreamQualityControl)

                     {

                           status = Pass Notify() on to upstream quality control interface (base sender is the decoder filter now)

                     }

                     else

                     {

                            status = not handled;

                     }

}

}

return status

}

 

解码器需要根据当前时间(通过IQualityControl/IDMOQualityControl),决定是否进行丢帧。丢帧的算法根据当前情况而定,下面是个例子:

质量通知消息会告诉我们,距离上一次渲染,延时了的时间。简单的算法会一直丢帧,直到视频的帧是当前需要播放的为止。因此,这个追赶的速度会很快:

CatchupPTS = q.Late + q.Timestamp

Drop all frames until PTS_frame >= Catchup_PTS

 

当然,实际情况可能还会不大一样。

1、  如果B帧到了,开始通过丢弃B帧来追赶时间。

2、  如果解码器需要进行输出转换的(如颜色转换),可以通过丢弃转换过程,来追赶时间。

3、  如果上述都不是,就要丢弃P帧了,那需要一直等待到下一个I帧的到来。这会导致用户体验差,因为每个I帧之间的相距时间可能很远。

 

在触发模式下,丢帧算法通常比较宽松。帧率播放比1.0的速度要快,解码器接受的sample不一定是关键帧。这个时候,只需要丢掉那些非关键性的帧,就能轻松的追赶上时间了。

http://blogs.msdn.com/medmedia/archive/2007/03/05/basics-of-a-v-synchronization-in-directshow.aspx

抱歉!评论已关闭.