语音通信是实时通信,影响语音质量的因素很多,大致可把这些因素分成两大类:一类是回声噪声等周围环境因素导致语音质量差,另一类是丢包延时等网络环境因素导致语音质量差。这两类因素由于成因不一样,解决方法也不一样。下面就讲讲用哪些方法来提高语音质量。

 

首先看由于周围环境因素导致语音质量差的解决方法。这类方法主要是用信号处理算法来提高音质,不同的因素有不同的处理算法,用回声消除算法把回声消除掉,用噪声抑制算法把噪声抑制住,用自动增益控制算法把音量调整到一个期望的值。这些都是信号处理领域比较专业的算法,好在现在webRTC已经开源,也包括这些算法(AEC/ANS/AGC)。我们只要把这些算法用好就有非常不错的效果。这些算法的调试中AEC相对复杂一些,我在前面的文章中(音频处理之回声消除及调试经验)专门写过怎么调试,有兴趣的可以去看一看。ANS/AGC相对简单,先在Linux下做一个小应用程序验证算法的效果,有可能要调一下参数,找一个相对效果较好的值。验证算法的过程也是熟悉算法怎么使用的过程,对后面把算法应用到方案中是有好处的。

 

再来看由于网络环境因素导致语音质量差的解决方法。网络环境因素主要包括延时、乱序、丢包、抖动等,又有多种方法来提高音质,主要有抖动缓冲区(Jitter Buffer)、丢包补偿(PLC)、前向纠错(FEC)、重传等,下面分别一一介绍。

 

1,Jitter Buffer

Jitter Buffer主要针对乱序、抖动因素,主要功能是把乱序的包排好序,同时把包缓存一些时间(几十毫秒)来消除语音包间的抖动使播放的更平滑。我在前面的文章(音频传输之Jitter Buffer设计与实现)中专门写过Jitter Buffer 的设计和实现,有兴趣的可以去看一看。

 

2,FEC

FEC主要针对丢包这种因素。FEC属于信道编码,想了解原理的朋友可以找相关文章看,这里就不讲了。再说我讲也讲不好,我掌握信源编码(语音编码就是信源编码的一种),对信道编码只是了解。语音上利用FEC来做补偿主要是在发端对发出的RTP包(几个为一组,称为原始包)FEC编码生成冗余包发给收端,收端收到冗余包后结合FEC解码得到原始的RTP包从而把丢掉的RTP包补上。至于生成几个冗余包,这取决于收方反馈过来的丢包率。例如原始包5个为一组,丢包率为30%,经过FEC编码后需要生成两个冗余包,把这7个包都发给对方。对方收到原始包和冗余包的个数和只要达到5个就可以通过FEC完美复原出5个原始包,这5个原始包中丢掉的就通过这种方式补偿出来了。原始RTP包有包头和payload,冗余包中还要加上一个FEC头(在RTP头和payload中间),FEC头结构如下:

                                                      

其中Group first Sequence number是指这一组原始包中第一个的sequence number,original count是指一组原始包的个数,redundant count是指生成的冗余包的个数,Redundant index是指第几个冗余包。冗余包有自己的payload type 和sequence number,要在SIP的SDP中告诉对方冗余包的payload type是多少,对方收到这个payload type的包后就做冗余包处理。

 

FEC不依赖与语音包内的payload,对于丢失的包能精确的复原出来。但是它也有缺点,一是它要累积到指定数量的包才能精确的复原,这就增加了时延;二是它要产生冗余包发送给对方,增加了流量。

 

3,PLC

PLC也主要针对丢包因素。它本质上是一种信号处理方法,利用前面收到的一个或者几个包来近似的产生出当前丢的包。产生补偿包的技术有很多种,比如基音波形复制(G711 Appendix A PLC用的就是这种技术)、波形相似叠加技术(WSOLA)、基音同步叠加(PSOLA)技术等,这些都很专业,有兴趣可以找相关的文章看看。对codec而言,如果支持PLC功能,如G729,就不需要再在外部加PLC功能了,只需要对codec做相应的配置,让它的PLC功能使能。如果不支持PLC功能,如G711,就需要在外部实现PLC。

 

PLC对小的丢包率(< 15%)有比较好的效果,大的丢包率效果就不好了,尤其是连续丢包,第一个丢的包补偿效果还不错,越到后面丢的包效果越差。

 

把Jitter Buffer、FEC、PLC结合起来就可以得到如下的接收侧针对网络环境因素的提高音质方案:

                                                            

从网络收到的RTP包如是原始包不仅要PUT进JB,还要PUT进FEC。如是冗余包则只PUT进FEC,在FEC中如果一组包中原始包的个数加上冗余包的个数达到指定值就开始做FEC解码得到丢失的原始包,并把那些丢失的原始包PUT进JB。在需要的时候把语音帧从JB中GET出解码并有可能做PLC。

 

4,重传

重传也主要针对丢包这种因素,把丢掉的包再重新传给对方,一般都是采用按需重传的方法。我在用重传的方法时是这样做的:接收方把收到的包排好序后放在buffer里,如果收到RTP包头中的sequence number能被5整除(即模5),就统计一下这个包前面未被播放的包有哪些没收到(即buffer里相应位置为空), 采用比特位的方式记录下来(当前能被5整除的包的前一个包用比特位0表示,丢包置1,不丢包置0,比特位共16位(short型),所以做多可以看到前16个包是否有丢包),然后组成一个控制包(控制包的payload有两方面信息:当前能被5整除的包的sequence number(short型)以及上面组成的16位的比特位)发给对方,让对方重发这些包。接收方收到这个控制包后就能解析出哪些包丢了,然后重传这些包。在控制包的payload里面也可以把每个丢了的包的sequence number发给对方,这里用比特位主要是减小payload大小,省流量。

 

在实际使用中重传起的效果不大,主要是因为经常重传包来的太迟,已经错过了播放窗口而只能主动丢弃了。它是这些方法中效果最差的一个。

 

5,RFC2198

RFC2198是RTP Payload for Redundant Audio Data(用于冗余音频数据的RTP负载格式),用了它后在当前RTP包中不仅可以承载当前语音的payload,还可以承载前几个包的payload,承载以前包的个数越多,在高丢包率的情况下效果越好,但是延时也就越大,同时消耗的流量也就越多。相比于FEC,它消耗的流量更多,因为FEC用一组RTP包编码生一个或多个成冗余包,而它一个RTP包就带一个或多个以前包的payload。在有线网络或者WIFI下可以用,在蜂窝网络下建议慎用。

 

以上就是我用过的提高音质的方法。还有其他方法,我没实践过,就不写了,写出来也是纸上谈兵。欢迎大家补充其他的方法。

posted on 2017-12-17 20:02 davidtym 阅读() 评论() 编辑 收藏
版权声明:本文为talkaudiodev原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:http://www.cnblogs.com/talkaudiodev/p/8053062.html