前 言 视频会议以其方便、快捷、“面对面”交流的优点逐渐得到了人们的认可,许多企事业单位、教育单位,医疗单位都希望使用视频会议来代替传统的会议形式。在视频会议中,与会者之间主要传输的是音频数据和视频数据,其中的音频数据显得更为重要。因为会议中的大部分有用信息都包含在与会者的言语交流上,所以视频会议系统必须保证音频通信的流畅性和全双工,才能使视频会议更接近于真实的会议环境。 DirectX是Microsoft开发的专门用于开发游戏和多媒体软件的应用程序接口(API),包括了对二维和三维图像、声音、音乐和针对网络多人游戏的网络通信的强大支持。DirectX是一种标准的软件接口,所有主要的硬件供应商都提供支持DirectX的驱动设备,应用DirectX的软件可以在不同的硬件环境下正常运行。另一方面,DirectX能根据所使用的不同硬件,来选择适当的方式使用硬件加速能力,便于开发高质量的多媒体和游戏软件。在DirectX所提供的众多组件中,用于音频处理的是Direct Sound组件。为保证视频会议系统中语音的流畅性,需要采用Direct Sound中提供的Streaming Buffer(流式缓冲)机制来实现。而为了保证视频会议系统中的全双工音频通信,主要利用的则是Direct Sound中的混音机制来实现。
利用Streaming Buffer实现流畅的语音交流 Direct Sound中提供了两种缓冲机制,分别是Static Buffer(静态缓冲)和Streaming Buffer(流式缓冲)。Static Buffer指将一段完整的声音存入缓冲中;Streaming Buffer指的是并不将全部的数据读入缓冲,而是在播放声音时动态地读入,占用空间较小。一般来说,如果声音需要反复播放而且容量有限(如游戏音效),使用Static Buffer更有助于提高程序的效率;相反,如果是容量很大、实时性要求较高的音频数据流,则使用Streaming Buffer为佳。在视频会议系统中,如使用Static Buffer,则在向缓冲区写入新的音频数据时,声音的回放必然出现短暂停顿,使与会者的完整话语不能够连续播放,影响通话的流畅性,而Streaming Buffer可克服语音不连续的缺点。 Streaming Buffer提供了两个指针:Play Cursor(回放游标)和Write Cursor(写入游标),它们的值只是相对于缓冲区开头的偏移量而非的内存地址。其中Play Cursor总是指向下一个被输出的数据字节,而Write Cursor指向的地址则指明从哪个地方开始可以安全地写入新的音频数据而不影响回放。按回放音频数据的顺序来看,Write Cursor总是在Play Cursor之前,并且它们间保持着一定的间距,而这个间距会根据不同的系统状况而有所不同,实验表明这个间距大概是100~200字节左右。当开始对缓冲区中的音频数据进行循环模式回放时,总是在Play Cursor所指的地方开始。回放后Play Cursor和Write Cursor会保持它们的间距等速度前移,并且Play Cursor总是指向下一个被输出的数据字节。当回放到达缓冲区的结尾处时,Play Cursor将重新指向缓冲区的开头,如此循环下去。而当程序停止对Streaming Buffer中的音频数据进行回放时,Play Cursor则不再移动,并停留在下一个被输出的数据字节处,直到重新回放才会继续前移。另外,在Play Cursor和Write Cursor之间的区域被认为是即将要进行回放的数据,所以不能够对其做更新。在理解了Streaming Buffer的基本工作方式后,接下来详细阐述如何用Visual C++作具体实现,其中会涉及到一些Visual C++的函数,具体可参考Microsoft MSDN。 在程序中,设置一个大小为一帧音频数据的大小(一般相当于0.25秒的语音)的2倍的Streaming Buffer。并且在Streaming Buffer的正中间和结尾处分别设置标志一个触发事件。程序开始时,通过调用Play函数对Streaming Buffer中的数据进行循环回放。当Play Cursor到达正中间和结尾时,事件就会产生,就可以通过程序向缓冲区写入新一帧的音频数据。在写入新一帧音频数据的过程中,首先调用Lock函数锁定缓冲区中的部分,此时的Write Cursor被锁定不再前移,而Play Cursor将跟随着声音的回放继续前进;利用回放Play Cursor和Write Cursor间的音频数据的一段时间内,根据锁定时获得的lplpvAudioPtr1(此时的lplpvAudioPtr1指向的地方就是锁定时Write Cursor的所指的地方),lpdwAudioBytes1(可安全写入的音频数据大小)等与Streaming Buffer相关的参数lplpvAudioPtr2、lpdwAudioBytes2等信息,把数据在指定的地方写入缓冲区,然后调用Unlock函数解除对Write Cursor的锁定。这样,Write Cursor重新调整回与Play Cursor保持100~200字节间距的地方,继续对新的音频数据进行回放。上述这个过程在整个程序的运行过程中,不断地循环进行,如所示,实现了在对Streaming Buffer中旧一帧音频数据进行回放的同时写入新一帧的音频数据。 从理论上讲,这已经保证了音频回放的流畅性。但在实现过程中,由于操作的对象是一帧的音频数据,其回放的时间仅是0.25秒,所以必须考虑的一个问题是程序的反应速度问题。如果忽略由事件触发到真正用Lock函数锁定缓冲区的部分以进行新数据写入之间的时间,则这种实现方法没有任何问题。除了开始的两帧数据外,新的一帧数据会紧跟在前一帧数据之后,彼此之间没有重叠部分,也没有空隙存在,能很好地达到音频回放的流畅效果。但事实上,由事件触发到真正用Lock函数锁定缓冲区的部分以进行新数据的写入之间还必须经过线程监听到事件,分析事件对应的缓冲区,然后再触发相应的回调函数来进行 上面所述的新一帧音频数据的写入过程。而这一系列分析工作所占用的时间是会随系统当时的状况而变化的,是一个随机的时间长度,所以每次对缓冲区用Lock函数锁定缓冲区的部分时,Write Cursor所在的位置都会不同,这样就造成新的一帧数据并不一定会严格地紧跟在前一帧数据之后,它们之间可能会出现重叠部分,也可能会有空隙出现,不利于音频数据的连续播放。如果出现重叠部分,那么回放造成有部分的音频数据丢失;如果有空隙的出现,会造成语音的不连续或混乱。但经过调试,仔细分析了由事件触发到真正用Lock函数锁定缓冲区的部分以进行新数据写入之间的时间后,发现它对锁定时Write Cursor所在位置的偏差产生的波动不大,一般由此产生的重叠部分或空隙部分都在50字节左右,也就是说平均每帧数据中会有50字节的错误。在程序中,指定的一帧音频数据为2000字节(与0.25秒相对应),所以会有大概2.5%的音频数据会出错。如果以所采用的音频格式来计算,8KSPS(采样率)*8Bit(每个采样用8位表示)=64KBit/s=8KB/s,那么这2.5%的错误在每秒钟内对应的会是0.025s的音频数据,基本上人的听力是难以分辨的。所以在采用Streaming Buffer依然能很好地达到了音频的流畅性要求。 上面只阐述了音频回放的实现方法,但作为整个视频会议系统中的音频功能来说,还必须有音频采集部分跟它相配合。音频采集部分的实现方法与回放模块的基本原理是一样,都是利用Streaming Buffer来实现,故此处不再详述。
利用混音机制实现全双工音频通信 Direct Sound中有Primary Buffer(主缓冲区)和Secondary Buffer(辅助缓冲区)两个缓冲区。前面所述的Streaming Buffer属于Secondary Buffer。在初始化DirectSound时,它会自动创建一个Primary Buffer,这个主缓冲的作用就是进行混音并把混音结果送到输出设备。除了Primary Buffer外,程序至少还应该创建一个Secondary Buffer,辅助缓冲的作用是储存将要使用的声音,在不使用的时候可以释放掉,但Primary Buffer是不可释放的。用DirectSound实现同时播放多个声音,前提是硬件允许。其工作过程如下,当程序同时对多个Secondary Buffer中的音频数据进行回放时,Direct Sound会把这些来自于不同Secondary Buffer的声音在主缓冲区进行自动混音,然后通过输出设备输出,如所示。所以通常情况下,用户并不需要和主缓冲打交道,DirectSound会自行管理。视频会议系统的全双工音频通信功能,就是利用DirectSound的这一混音机制达到的。 视频会议中要实现的全双工音频通信功能并不仅限于两个与会者之间的全双工通信,而应该扩展为会议的每个者都能根据需要,同时听到其他与会者的讲话,以达到很好的讨论交流效果。为此,在程序中为每个与会者分配一个缓冲数组,数组的大小与一帧音频数据大小相同。系统开始运行后,程序一方面把各个与会成员的音频数据帧接收下来,然后根据用户标识把它们分别保存在相应的缓冲数组中,并按照时间顺序排列好,而且会为每个用户创建一个Streaming Buffer用于音频回放。另一方面,开始对各个用户对应的Streaming Buffer里的内容进行循环播放回放和更新,而更新的音频数据由各个用户的缓冲数组顺序提供。这样,不同用户的音频信息会自动被Direct Sound进行混音,并把混音的结果放到Primary Buffer中,再从输出设备输出,从而达到所需的效果。
结束语 DirectX技术大大方便了各种多媒体软件的开发,已经获得广泛采用。通过联合使用DirectX中提供的Streaming Buffer机制和混音机制,在视频会议系统中实现了流畅的全双工音频通信,使用效果令人满意。
|