DTV、DVD、直播卫星(DBS)和数字有线等技术采用压缩方法来为消费者提供品质极高的节目。随着音频视频内容传送方面先进的视音频压缩标准的引入,人们对音频和视频间的时序关系的关注程度也在不断提高。
音视频信号要同步
数字广播系统的基本目标之一,就是向观众提供实现合理同步的视频和音频数据内容。提出这样一个目标的原因,是从制作到收看的整个传播链条中的每种数字音频和视频部件,都会给通过该部件的信号带来一定的延迟。音频和视频信号上出现的延迟一般是不相等的。每个元件都很有可能造成其输出信号出现音视频同步方面的错误。
音频和视频流的解码必须按照一定的先后顺序完成,这样才能确保“对上口形”。音频和视频解码器可以通过相互独立的处理器来实现,或者,如果有相应的处理能力的话,也可以将它们集成到单个处理器上。无论何种情况,为了保证音视频信号的同步,必须采取某种形式的A/V同步技术。若音频和视频解码后的数据流无法保证同步,则听到的音效将超前于或滞后于原本对应的视频帧。这当然是观众不可接受的。
媒体数据通道和延迟
典型的基于媒体的数字系统是由一组编码器和解码器构成的。系统的编码器部分执行压缩功能,而系统解码器功能则执行解压缩功能,以供观众观看。
媒体数据(Meda)在编码器系统和解码器系统间传送,该数据同时包含了音频和视频分量。因为音频和视频分量本身的特性并不相同,两个子系统的同步化方法也不同。
一般来说,由于音频流通过系统时出现的延迟极低,因此无需添加音频延迟补偿措施。另一方面,视频的处理所需的时间和计算能力都大大超过音频。
与音频信号类似,视频信号一旦被数字化并被缓冲后,对该信号的处理操作所花费的时间将长于在模拟域进行的相同操作。因为大多数视频效果无法在模拟域实现,就必须采用数字处理,于是,必然出现一定的“系统”延迟。
关注一下如下问题将很有意义:音频和视频的信号处理是如何影响到系统的总延迟的?视频处理的时间之所以要长于音频处理的,是因为待处理的数据量更大,而且对处理的要求也更高。视频处理所需的工作量高于音频信号处理的工作量的特点,又使得视频信号相对于音频信号会出现延迟。于是,视频和音频分量从编码过程开始就“脱离了同步”。
业界标准在不断推进
对于任何延迟超过几个ms的视频装置,补偿都是必不可少的,否则观众就会觉察到延迟的存在,视频内容的收看质量就会受到影响。为了保证这一现象不至于发生,音频通道必须出现同样长度的延迟。ITU则进一步推荐了可以采取的技术,该技术作为ITU-R BT.1377的一部分推出。具体来说,该技术是对音频和视频的帧进行标记,以指示处理过程带来的延迟。
人类的感官对于听觉滞后于视觉的现象的容忍度要更高一些,因为这一滞后现象是我们每天都司空见惯的。国际电信联盟(ITU)于1998年发布了ITU-R BT.1359。它基于如下研究结果:当非同步误差处在音频超前视频45ms和滞后视频125ms这样的范围内时,A/V同步误差能够被可靠地检测出来。这仅仅是检测方面的标准,而可接受的范围和推荐的最大值则要宽得多。总而言之,ITU的推荐认为,从观众或听众捕捉节目内容的角度来看,可容忍的范围应该分别不大于90ms(音频超前于视频)和185ms(音频滞后于视频)。在现实中,这一范围对于真正可接受的性能来说也许太宽了,所以一般遵循更小的公差。
随着数字媒体广播的增长,人们认为对ITU R BT.1359-1的市场要求对于DTV广播的音频和视频同步来说已经不合适了。先进电视系统委员会(Advanced Television System Committee,ATSC)是一个关于数字电视标准的产业性组织。其“实施方案”分委员会经过调查,提交了一份“实施方案分委员会”发现报告,IS/191 ATSC实施方案分委员会发现报告:广播操作中的声音和视觉的相对时序”。该分委员会发现,在所有的工作条件下,在DTV编码装置的输入点,声音节目都应该与视频节目严格保持同步。基于该发现,他们推荐声音节目超前于视频节目的时间绝对不能超过15ms,而且滞后于视频节目的时间也不能长于45ms。
音频解码更重要
MPEG规范提供了可绝对保证A/V同步的恰当工具。每个音频和视频帧都有一个能让解码器同步对声音和图像进行重建的PTS。这些PTS值由MPEG编码器中的复用器来赋予。解码器在这些PTS之前接收到音频和视频数据,于是便能利用这些值来同步地播放视频和音频。
MPEG位流中需要插入播放时间印记,插入的间隔不超过700ms。MPEG系统目标解码器模型容忍最高为1s的解码器缓冲延迟。在传输流中,用来呈现应该同步播放的声音和图像的音频、视频播放单元可以在时间上分离,时间差长达1s。为了产生同步化的输出,接收机必须恢复出解码器的系统时间时钟(STC)并使用播放时间印记(PTS)来向观众播放音频-视频内容,其相应的公差在PTS所标注的时间点的正负15ms内。
音频解码被认为更为重要,因为音频中的任何间断都非常明显,而人们对视频就不那么敏感(例如,视频帧的丢失就不那么容易被观看者所察觉)。视频解码器的调节比音频解码器的调节更为容易,因为视频场每1/60s(16.6ms)才发送一次。这样的调节必须不至于造成视频场的遗漏或丢失。建议定期对音频和视频STC值进行同步化处理,频率最低为每秒1次。
在解码器实例中,音频缓冲器和视频的帧可以在外部存储中生成。随着这些输出缓冲数据被写入到外部容量较大的存储中,如SDRAM或者DDR,来自于编码器流的时间印记就与每个缓冲器和帧“关联”起来。此外,处理器需要跟踪自己的时基。在每个解码的视频帧和音频缓冲器都送出到显示器之前,处理器要执行时间检查并找出来自每个缓冲器的数据之间恰当的匹配关系。要用一个DMA控制器实现这一任务有多种方式,但最佳的方法是拥有已建立的描述符,并根据哪个数据帧的时间能与目前的处理器时间相匹配,来将指针DMA的写入指针调整到恰当的描述符。
音频和视频是多媒体系统中的一个关键部件。数字音频视频的制作、分发和广播系统是由数字处理、压缩、解压缩和传输等构成的一个复杂阵列。音频和视频各自所出现的延迟可能是不相等的,这些延迟会影响到音频视频的同步。应该采取措施来确保每一级的输出段给出的音频和视频信号都能同步到很小的公差范围内,以满足业界的标准。
编码器系统中的A/V同步
数字媒体编码和处理单元包括了视频和音频捕捉装置,用于对来自于真实世界的信息进行采样,并将该信息变换到数字域。捕获装置输出必须直接连接到视频和音频数据缓冲器。中心部件是视频编码器、音频编码器单元,它们可以将数字数据变换成压缩过的信息。复用器负责将来自于两个通道的数据混编在一起,然后发送到传输信道中。
在帧尺寸给定的情况下视频和音频的编码需要一定的时间,复用器必须得到关于这一时间的长短的确切信息。该延迟取决于设备的制造商,但其具体的量值对于能否正确地给内容赋予播出时间印记来说极为关键。A/V同步所遇到的许多问题都可以归结为没有恰当地考虑这些延迟的影响,或者干脆没有进行设定。总的音频视频同步误差是链条中的各个同步误差的代数和。如果计算正确的话,调整后的PTS值应该能在音频/视频数据到达视频音频处理单元前就对延迟进行补偿。
在许多情况下,国际电影与电视工程师协会(the Society of Motion Picture and Television Engineers)的时间印记可以施加到音频和视频编码器上,可以被复用器用来计算出确切的PTS值,从而将编码器延迟作为误差源而予以去
DirectShow音视频同步解决方案。
多媒体处理,不可避免地要解决音视频的同步问题。DirectShow是怎么来实现的呢?我们一起来学习一下。
大家知道,DirectShow结构最核心的部分是Filter Graph Manager:向下控制Graph中的所有Filter,向上对应用程序提供编程接口。其中,Filter Graph Manager实现的很重要一个功能,就是同步音视频的处理。简单地说,就是选一个公共的参考时钟,并且要求给每个Sample都打上时间戳,Video Renderer或Audio Renderer根据Sample的时间戳来控制播放。如果到达Renderer的Sample晚了,则加快Sample的播放;如果早了,则Renderer等待,一直到Sample时间戳的开始时间再开始播放。这个控制过程还引入一个叫Quality Control的反馈机制。
下面,我们来看一下参考时钟(Reference Clock)。所有Filter都参照于同一个时钟,才能统一步调。DirectShow引入了两种时钟时间:Reference time和Stream time。前者是从参考时钟返回的绝对时间(IReferenceClock::GetTime),数值本身的意义取决于参考时钟的内部实现,利用价值不大;后者是两次从参考时钟读取的数值的差值,实际应用于Filter Graph内部的同步。Stream time在Filter Graph不同状态的取值为:
1. Filter Graph运行时,取值为当前参考时钟时间减去Filter Graph启动时的时间(启动时间是通过调用Filter上的IMediaFilter::Run来设置的);
2. Filter Graph暂停时,保持为暂停那一刻的Stream time;
3. 执行完一次Seek操作后,复位至零;
4. Filter Graph停止时,取值不确定。
那么,参考时钟究竟是什么东西呢?其实,它只是一个实现了IReferenceClock接口的对象。也就是说,任何一个实现了IReferenceClock接口的对象都可以成为参考时钟。在Filter Graph中,这个对象一般就是一个Filter。(在GraphEdit中,实现了参考时钟的Filter上会显示一个时钟的图标;如果同一个Graph中有多个Fiter实现了参考时钟,当前被Filter Graph Manager使用的那个会高亮度显示。)而且大多数情况下,参考时钟是由Audio Renderer这个Filter提供的,因为声卡上本身带有了硬件定时器资源。接下来的问题是,如果Filter Graph中有多个对象实现了IReferenceClock接口,Filter Graph Manager是如何做出选择的呢?默认的算法如下:
1. 如果应用程序设置了一个参考时钟,则直接使用这个参考时钟。(应用程序通过IMediaFilter:: SetSyncSource设置参考时钟,参数即为参考时钟;如果参数值为NULL,表示Filter Graph不使用参考时钟,以最快的速度处理Sample;可以调用IFilterGraph:: SetDefaultSyncSource来恢复Filter Graph Manager默认的参考时钟。值得注意的是,这时候的IMediaFilter接口应该从Filter Graph Manager上获得,而不是枚举Graph中所有的Filter并分别调用Filter上的这个接口方法。)
2. 如果Graph中有支持IReferenceClock接口的Live Source,则选择这个Live Source。
3. 如果Graph中没有Live Source,则从Renderer依次往上选择一个实现IReferenceClock接口的Filter。如果连接着的Filter都不能提供参考时钟,则再从没有连接的Filter中选择。这一步算法中还有一个优先情况,就是如果Filter Graph中含有一个Audio Render的链路,则直接选择Audio Renderer这个Filter(原因上面已经提及)。
4. 如果以上方法都找不到一个适合的Filter,则选取系统参考时钟。(System Reference Clock,通过CoCreateInstance创建,CLSID为CLSID_SystemClock。)
我们再来看一下Sample的时间戳(Time Stamp)。需要注意的是,每个Sample上可以设置两种时间戳:IMediaSample::SetTime和IMediaSample::SetMediaTime。我们通常讲到时间戳,一般是指前者,它又叫Presentation time,Renderer正是根据这个时间戳来控制播放;而后者对于Filter来说不是必须的,Media time有没有用取决于你的实现,比如你给每个发出去的Sample依次打上递增的序号,在后面的Filter接收时就可以判断传输的过程中是否有Sample丢失。我们再看一下IMediaSample::SetTime的参数,两个参数类型都是REFERENCE_TIME,千万不要误解这里的时间是Reference time,其实它们用的是Stream time。还有一点,就是并不是所有的Sample都要求打上时间戳。对于一些压缩数据,时间戳是很难打的,而且意义也不是很大(不过压缩数据经过Decoder出来之后到达Renderer之前,一般都会打好时间戳了)。时间戳包括两个时间,开始时间和结束时间。当Renderer接收到一个Sample时,一般会将Sample的开始时间和当前的Stream time作比较,如果Sample来晚了或者没有时间戳,则马上播放这个Sample;如果Sample来得早了,则通过调用参考时钟的IReferenceClock::AdviseTime等待Sample的开始时间到达后再将这个Sample播放。Sample上的时间戳一般由Source Filter或Parser Filter来设置,设置的方法有如下几种情况:
1. 文件回放(File playback):第一个Sample的时间戳从0开始打起,后面Sample的时间戳根据Sample有效数据的长度和回放速率来定。
2. 音视频捕捉(Video and audio capture):原则上,采集到的每一个Sample的开始时间都打上采集时刻的Stream time。对于视频帧,Preview pin出来的Sample是个例外,因为如果按上述方法打时间戳的话,每个Sample通过Filter链路传输,最后到达Video Renderer的时候都将是迟到的;Video Renderer通过Quality Control反馈给Source Filter,会导致Source Filter丢帧。所以,Preview pin出来的Sample都不打时间戳。对于音频采集,需要注意的是,Audio Capture Filter与声卡驱动程序两者各自使用了不同的缓存,采集的数据是定时从驱动程序缓存拷贝到Filter的缓存的,这里面有一定时间的消耗。
3. 合成(Mux Filters):取决于Mux后输出的数据类型,可以打时间戳,也可以不打时间戳。
大家可以看到,Sample的时间戳对于保证音视频同步是很重要的。Video Renderer和Audio Renderer作为音视频同步的最终执行者,需要做很多工作。我们或许要开发其它各种类型的Filter,但一般这两个Filter是不用再开发的。一是因为Renderer Filter本身的复杂性,二是因为微软会对这两个Filter不断升级,集成DirectX中其它模块的最新技术(如DirectSound、DirectDraw、Direct3D等)。
最后,我们再来仔细看一下Live Source的情况。Live Source又叫Push source,包括Video /Audio Capture Filter、网络广播接收器等。Filter Graph Manager是如何知道一个Filter是Live Source的呢?通过如下任何一个条件判断:
1. 调用Filter上的IAMFilterMiscFlags::GetMiscFlags返回有AM_FILTER_MISC_FLAGS_IS_SOURCE标记,并且至少有一个Output pin实现了IAMPushSource接口。
2. Filter实现了IKsPropertySet接口,并且有一个Capture output pin(Pin的类型为PIN_CATEGORY_CAPTURE)。
Live Source对于音视频同步的影响主要是以下两个方面:Latency和Rate Matching。Latency是指Filter处理一个Sample花费的时间,对于Live Source来说DirectShow音视频同步解决方案。,主要取决于使用缓存的大小,比如采集30fps的视频一般采集完一帧后才将数据以一个Sample发送出去,则这个Filter的Latency为33ms,而Audio一般缓存500ms后才发送一个Sample,则它的Latency就为500ms。这样的话,Audio与Video到达Renderer就会偏差470ms,造成音视频的不同步。默认情况下,Filter Graph Manager是不会对这种情况进行调整的。当然,应用程序可以通过IAMPushSource接口来进行Latency的补偿,方法是调用IAMGraphStreams::SyncUsingStreamOffset函数。Filter Graph Manager的实现如下:对所有实现IAMPushSource接口的Filter调用IAMLatency::GetLatency得到各个Source的Latency值,记下所有Latency值中的最大值,然后调用IAMPushSource::SetStreamOffset对各个Source设置偏移值。
这样,在Source Filter产生Sample时,打的时间戳就会加上这个偏移量。Rate Matching问题的引入,主要是由于Renderer Filter和Source Filter使用的是不同的参考时钟。这种情况下,Renderer对数据的播放要么太快,要么太慢。另外,一般Live Source不能控制输出数据的速率,所以必须在Renderer上进行播放速率的匹配。因为人的听觉敏感度要大于视觉敏感度,所以微软目前只在Audio Renderer上实现了Rate Matching。实现Rate Matching的算法是比较复杂的,这里就不再赘述。
看到这里,大家应该对DirectShow是如何解决音视频同步问题的方案有一点眉目了吧。深层次的研究,还需要更多的测试、Base class源码阅读,以及DirectShow相关控制机制的理解,比如Quality Control Management等。