代雯蕾 黄桂金
华中科技大学电信系 武汉430074
摘 要 在VoIP(Voice Over IP,基于IP的话音)服务当中,媒体的采集/播放质量将直接影响到通话质量。本文详细描述了基于VoIP的媒体采集/播放的设计实现,以及需要注意的一些问题。
关键词 VoIP RTP(Realtime Transport Protocol,实时传输协议) 音频 视频
1 背景
随着宽带互联网的普及和技术的巨大进步,VoIP技术已经成为了能够与传统电话服务和PBX产品的相匹敌的技术。VoIP终端将音频、视频模拟信号进行抽样、编码处理成压缩帧,然后转换为IP包进行传输,从而达到了在IP网上进行多媒体通信的目的。本文将从VoIP对媒体采集/播放的要求出发,分析可能影响采集/播放的性能的几个因素并提出解决方法。
2 VoIP的媒体采集/播放要求
VoIP通常采用G.723.1或G.728等音频编码方式,要求能够提供采样频率为8000Hz,每样本点为13比特以上的采集能力。因此,音频采集需输出指定时间间隔(20~200ms,缺省值为20 ms)、采样频率为8000Hz、每样本点为16比特的音频数据。对应的,音频播放将得到一定时间间隔、采样频率为8000Hz、每样本点为16比特的音频数据。
视频编码通常采用H261和H263两种方式,要求以1/30秒的时间间隔提供一个完整的一帧符合CIF(Common Intermediate Format,画面大小为352 * 288)、QCIF(Quarter-CIF,176 * 144)或者SQCIF(sub-QCIF,128 * 96)格式的原始视频数据。对应的,视频播放将得到固定时间间隔、完整的一帧符合CIF、QCIF或者SQCIF格式的视频数据。
3 VoIP的媒体封装要求
原始媒体流经过编码后,按一定规则被切割成段,继而进行RTP封装。RTP包带有一个固定包头:
其中,采集模块关心的只是timestamp字段。 timestamp为时间戳,表示音频/视频数据的采样时刻,其初值随机。对于音频数据,其分辨率为1/8000秒,对于视频数据,要求其分辨率为1/90000秒。
4 可能影响系统性能的几个因素及解决方法
评价系统的性能,必须从两个方面来考虑,即处理时间和占用的内存空间。下面就可能影响系统性能的几个因素进行讨论,并提出解决方法。
4.1 采集/播放与其他模块之间的数据交互
采集的媒体数据将发送至音频/视频编码部分,为了避免反复申请/释放内存,编码模块应尽可能地在采集模块发送的数据块内就地编码,而不是另申请一块数据存放编码后的数据(在视频编码中,若某帧与上一帧差别太大,该帧编码后的数据量可能无法装入一个RTP包当中,必须分为多个RTP包发送,这种情况下,另外申请空间无法避免)。同时,由于编码后的数据还会做RTP封装,为了避免反复拷贝,采集模块发送至编码模块的数据块应为RTP包头留有余地。
考虑到编码算法(如H.263,G.728,G.723.1)需要从采集模块得到时间戳,原始数据长度,视频帧号等信息,所以需要定制一个结构来存放这些数据,称之为编码信息结构。因此,采集模块发送到编码模块的数据块内,首先存放的是一个联合(Union),该联合中包括RTP包头结构和编码信息结构。接下来存放的才是原始媒体数据。
根据编码方式的不同,RTP包头的长度也可能不同,如G.728仅有一个RTP固定包头,而H.263和H.261需先添加一个RTP载荷头,再添加固定包头。出于对可扩展性的考虑,在数据块的结构中需有一个字段来指示“有用的”数据相对与本数据块的偏移量。
4.2 音频采集/播放模块的内部结构
采集模块为音视频会议提供数据源,因此音视频数据采集的实时性非常重要。在实现方法上,采用系统开销较小的方式。以Windows系统为例,可以采用Waveform Audio SDK来完成音频采集,用VFW SDK来完成视频采集,而不选用DirectShow。同样地,音视频的播放也可通过上述两类SDK来完成。
由于人类对声音比较敏感,所以为了保证音频采集/播放的连续性与均匀性,音频采集和播放都应申请一个环形缓冲区以存放待编码/播放的音频数据块地址。之所以存放数据块地址而不直接将音频数据填入,是因为若直接填入数据,采集侧每次将数据发送至编码模块时,需要将数据从缓冲区内拷出;同时播放侧每次从同步模块接收音频数据时,亦需要将数据从消息中拷入缓冲区。这样做对性能有一定的影响。为了减少拷贝,可在缓冲区内仅存放数据块的地址,在音频采集模块向音频编码模块,或者是同步模块向音频播放发送数据时,只需指出该数据块的地址及音频数据偏移量即可,而不需要重复拷贝。
在实时操作系统系统环境下,音频采集/播放模块可分为两个独立的任务或线程,即音频采集和音频播放。其中,音频采集线程还拥有一个子线程,即数据发送线程,该线程的任务是实时地将采集完毕的音频数据发送至音频编码模块;音频播放线程也拥有一个子线程,即数据播放线程,该线程的任务是实时地将音频数据从播放缓冲区取出,送至音频驱动以供播放。
音频采集模块在收到采集启动消息后,将待采集数据块(为了保证采集的连续性,这里需要是多个数据块)的地址告知驱动,并通知驱动开始采集;当一块数据块采集完成后,数据发送线程将采集的数据取出发送给音频编码实体,并申请下一块数据送给驱动。音频播放模块在收到播放启动消息后,并不马上播放,而是先将同步模块发来的数据填入播放(环形)缓冲区。在填到一个门限值后,从缓冲区的第一块数据开始,将多个数据块的地址送往驱动,并通知驱动开始播放;每当一块数据播放完成后,数据播放线程都取出下一块数据送给驱动。
4.3 线程的同步与互斥
由于音频的采集和播放都是由两个线程协作完成的,因此线程间的同步与互斥是重点。在音频采集线程和数据发送线程之间,音频播放线程和数据播放线程之间需要进行同步,也就是说,当一个数据块采集/播放完毕时,需要以某种方式告知数据发送/播放线程去取下一个数据块。
对于VoIP的应用来说,通知的实时性最为重要。首先考虑回调函数方式,它的实时性最高,但在一些系统中(如Windows),在回调函数中调用音频驱动的某些函数可能会引起死锁;其次是事件通知方式,即在数据发送线程中,等待“采集完成”事件的发生,一旦采集完成,该事件即被置为“有事件”状态,这种方式的实时性较好,只是获取数据块索引的方式略为麻烦(因为送往驱动待采集\播放的数据不止一块);采集完成后,驱动亦可向指定的线程发送消息,但由于消息机制的实时性不高,所以不作考虑。因此,一般采用事件进行通知的方式来完成同步和互斥。
5 时间戳的产生
根据协议规定,音频时间戳的分辨率为1/8000秒,视频时间戳的分辨率为1/90000秒,因此需要采用高精度的计数器(其分辨率不低于1/90000秒)。由于音视频时间戳要求的分辨率不同,在没有两个计数器的情况下,可以用一个计数器来得到两种时间戳。
首先获取计数器频率Freq。在启动采集时,查询计数器的初使值,保存在一个静态变量当中。接下来通过MD5算法产生一个32bit的随机数赋予timeStamp作为第一个RTP包的时间戳。每当有数据块采集完成,查询当前计数器的值,计算与计数器初始值的差值CntGap,将这个差值与计数器周期相乘得到实际时间差timeGap,即:
timeGap = (double)( (double) CntGap / Freq),将这个时间差除以RTP时间戳的周期1/8000(1/90000),再加上时间戳的初值,即得到本次采集的时间戳。
6 结束语
由于音/视频数据的采集/播放性能直接关系到通话质量,所以高性能的VoIP终端离不开高性能的媒体采集/播放模块。本文提出了影响音/视频数据的采集/播放性能的一些因素,并讨论了以Windows操作系统为例的设计方法。在其他平台上开发VoIP终端时,采集/播放模块与操作系统的接口由于实际平台的不同将会有所差异,但本方案提出的模块内部结构,与周围模块的接口等与平台无关,因此仍有一定的参考价值。
----《中国数据通信》
|