語音通訊是實時通訊,影響語音質量的因素不少,大體可把這些因素分紅兩大類:一類是回聲噪聲等周圍環境因素致使語音質量差,另外一類是丟包延時等網絡環境因素致使語音質量差。這兩類因素因爲成因不同,解決方法也不同。下面就講講用哪些方法來提升語音質量。html
首先看因爲周圍環境因素致使語音質量差的解決方法。這類方法主要是用信號處理算法來提升音質,不一樣的因素有不一樣的處理算法,用回聲消除算法把回聲消除掉,用噪聲抑制算法把噪聲抑制住,用自動增益控制算法把音量調整到一個指望的值。這些都是信號處理領域比較專業的算法,好在如今webRTC已經開源,也包括這些算法(AEC/ANS/AGC)。咱們只要把這些算法用好就有很是不錯的效果。這些算法的調試中AEC相對複雜一些,我在前面的文章中(音頻處理之回聲消除及調試經驗)專門寫過怎麼調試,有興趣的能夠去看一看。ANS/AGC相對簡單,先在Linux下作一個小應用程序驗證算法的效果,有可能要調一下參數,找一個相對效果較好的值。驗證算法的過程也是熟悉算法怎麼使用的過程,對後面把算法應用到方案中是有好處的。web
再來看因爲網絡環境因素致使語音質量差的解決方法。網絡環境因素主要包括延時、亂序、丟包、抖動等,又有多種方法來提升音質,主要有抖動緩衝區(Jitter Buffer)、丟包補償(PLC)、前向糾錯(FEC)、重傳等,下面分別一一介紹。算法
1,Jitter Buffer緩存
Jitter Buffer主要針對亂序、抖動因素,主要功能是把亂序的包排好序,同時把包緩存一些時間(幾十毫秒)來消除語音包間的抖動使播放的更平滑。我在前面的文章(音頻傳輸之Jitter Buffer設計與實現)中專門寫過Jitter Buffer 的設計和實現,有興趣的能夠去看一看。網絡
2,FEC編碼
FEC(Forward Error Correction,中文譯爲前向糾錯)主要針對丟包這種因素。它是一種經過增長冗餘數據對丟失的數據包進行恢復的信道編碼算法(至於信道編碼,想了解原理的朋友能夠找相關文章看,這裏就不講了。再說我講也講很差,我掌握信源編碼(語音編碼就是信源編碼的一種),對信道編碼只是瞭解)。具體地說就是由發送端對原始數據包進行 FEC 編碼,生成冗餘數據包(原始數據包和冗餘數據包的數量比例是固定的)。接收端收到後,經過冗餘數據包和原始數據包來恢復出丟失或者出錯的數據包。比較成熟的FEC編解碼算法有RS(Reeds-Solomon) 算法、Raptor 算法和 Tornado 算法,我當時用的就是RS算法。語音上利用FEC來作補償主要是在發端對發出的RTP包(幾個爲一組,稱爲原始包)FEC編碼生成冗餘包發給收端,收端收到冗餘包後結合原始包用FEC解碼獲得原始的RTP包從而把丟掉的RTP包補上。至於生成幾個冗餘包,這取決於接收方反饋過來的丟包率。例如原始包5個爲一組,丟包率爲30%,通過FEC編碼後須要生成兩個冗餘包,把這7個包都發給對方。對方收到原始包和冗餘包的個數和只要達到5個就能夠經過FEC完美復原出5個原始包,這5個原始包中丟掉的就經過這種方式補償出來了。下圖展現了這個過程,發送端有5個原始RTP包,經過FEC編碼生成2個冗餘RTP包。因爲生成兩個冗餘包, 算法最多可以恢復 2 個丟失的原始包。發送端把原始包和冗餘包發出去,在傳輸過程當中假設第 2 號和4號原始包丟失了。接收端接收到原始包和冗餘包後,經過原始包一、三、5和 冗餘包1 、2就能夠把已經丟失的第 2 號和4號原始數據包恢復出來。設計
原始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的包後就作冗餘包處理。code
FEC不依賴與語音包內的payload,對於丟失的包能精確的復原出來。可是它也有缺點,一是它要累積到指定數量的包才能精確的復原,這就增長了時延;二是它要產生冗餘包發送給對方,增長了流量。htm
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下能夠用,在蜂窩網絡下建議慎用。
以上就是我用過的提升音質的方法。還有其餘方法,我沒實踐過,就不寫了,寫出來也是紙上談兵。歡迎你們補充其餘的方法。