WebRTC 音訊抗弱網技術(上)

融雲RongCloud發表於2022-07-13

人均社恐,鍾愛語聊。以語聊房為代表的語音社交產品解鎖陌生人社交新方式,也不斷講述著新的出圈故事。關注【融雲全球網際網路通訊雲】瞭解更多

而聲音卡頓、斷斷續續、快進、慢放等現象會嚴重影響使用者體驗,直接導致使用者離開,這些都是弱網引起的常見問題。
本文主要從音訊應用的角度來分析常用的弱網對抗技術,主要有如下幾種:

  • 前向糾錯技術(FEC、RED 等)
  • 後向糾錯技術(ARQ、PLC 等)
  • 編碼器抗弱網特性(本文重點關注 OPUS 編碼器的特性)
  • 抗抖動技術(JitterBuffer)

我們將用上、下兩篇文章,結合 WebRTC 中使用或支援的音訊抗弱網技術,對以上幾類技術做分析,以實現音訊通訊服務在弱網環境下高可用。

上篇主要分享前向糾錯技術、後向糾錯技術及 OPUS 編解碼抗弱網特性;下篇專題分享 WebRTC 使用的抗抖動模組 NetEQ。

前向糾錯技術

FEC

前向糾錯技術,最典型的就是 FEC 技術了。
傳送端:生成冗餘包來對抗傳輸過程中丟包的問題;
接收端:對收到的冗餘包和正常包來重新恢復傳輸過程中丟失的包。

FEC 分帶內和帶外兩種,WebRTC 中視訊是通過帶外 FEC(ULPFEC[1]、FLEXFEC[2])來產生冗餘包,音訊則是通過 OPUS 帶內 FEC 來生成冗餘包。

帶內 FEC 由於會佔用一部分編碼位元速率,所以對音訊的音質會有所降低。帶外 FEC 不會影響音質,但會額外佔用網路頻寬,各有優缺點。

FEC 典型的編碼方式有 XOR 和 Reed Solomon[3]。WebRTC 的帶外 FEC 使用的是 XOR 編碼方式(ULPFEC和FLEXFEC),其特點是計算量相對少,但其抗丟包能力有限。

在 WebRTC 中,帶外 FEC,不論是 ULPFEC,還是 FLEXFEC 都是根據 MASK 掩碼來確定 FEC 包和被保護的源 RTP 包的對映關係,其中定義了兩種型別的掩碼,RandMask 和 BurstMask,前者在隨機丟包中保護效果要好些;後者則是對突發導致連續丟包效果會好些,但是不論哪種,都有其缺點;這裡以 7-4 掩碼(即 7 個原始包,將生成 4 個冗餘包)舉例:

#define kMaskBursty7_4 \
0x38, 0x00, \
0x8a, 0x00, \
0xc4, 0x00, \
0x62, 0x00

將上面十六進位制按照二進位制展開如下:

包序號: S1 S2 S3 S4 S5 S6 S7
R1: 0 0 1 1 1 0 0 原始包S3,S4,S5被冗餘包R1保護
R2: 1 0 0 0 1 0 1 ==> 原始包S1,S5,S7被冗餘包R2保護
R3: 1 1 0 0 0 1 0 原始包S1,S2,S6被冗餘包R3保護
R4: 0 1 1 0 0 0 1 原始包S2,S3,S7被冗餘包R4保護

上面的掩碼錶示根據 S1-S7 共 7 個原始包,傳送端將生成 4 個冗餘包 R1-R4,其中:

R1 包保護 S3,S4,S5 三個原始包
R2 包保護 S1,S5,S7 三個原始包
R3 包保護 S1,S2,S6 三個原始包
R4 包保護 S2,S3,S7 三個原始包

從上也可以看出,每個原始包都有被冗餘包保護;當包丟失了,一般可以通過冗餘包和收到的原始包來進行恢復,比如傳送端傳送了 S1-S7、R1-R4 共 11 個包,接收端收到了 S1、S3、S5、S7、R1、R2、R3、R4 共 8 個包,丟失了 S2、S4、S6 三個包;則 S2、S4、S6 修復過程如下:

S2 可以被 R4、S3、S7 修復,即 S2 = R4 XOR S3 XOR S7 

S4 可以被 R1、S3、S5 修復,即 S4 = R1 XOR S3 XOR S5 

S6 可以被 R3、S1、S2 修復,即 S6 = R3 XOR S1 XOR S2

但是也有些包無法修復,比如丟失了 S1、S2、S7,則無法恢復,原因如下:

根據掩碼保護關係可知,S1 的恢復可以通過 R2、S5、S7 或者 R3、S2、S6;但因為 S7 和 S2 丟失,要恢復 S1,需要先恢復 S2 或 S7

同樣,S2 可以通過 R3、S1、S6 恢復,但因為 S1 丟失,則需要先恢復 S1 

同理,S6 可以通過 R3、S1、S2 恢復,但是需要先恢復 S1、S2 

所以,經過上面的分析可知 S1、S2、S7 均⽆法恢復

同理,要是丟失了 S3、S5、S7,也無法恢復,這是 WebRTC 中採用掩碼來確定冗餘包和原始包之間的保護關係的技術缺點。

即對於(M 個原始包 + N 個冗餘包)一組包,有小於等於 N 個包丟失時,可能無法恢復丟失包的情況。

Reed Solomon 編碼則可以做到對於(M 個原始包 + N 個冗餘包)一組包,有小於等於 N 個包丟失,都可以將丟失的包恢復。

RS FEC 主要是使用範德蒙矩陣或者柯西矩陣來進行編解碼[4],柯西矩陣效果比範德蒙矩陣計算量少,效能更優;但不論上面何種矩陣,它們都具有⼀個特性就是可逆,且任意子矩陣可逆,這就保證了在丟失小於等於 N 個包時,RS 能將其恢復。

下面以範德蒙矩陣做簡要說明。以(7,4)為例,即 7 個原始包產生 4 個冗餘包,原始包為 S1、S2、S3、S4、S5、S6、S7,冗餘包為(R1、R2、R3、R4)。原始包和冗餘包的關係如下:
微信圖片_20220706142632.png
圖說

其中上面的範德蒙矩陣為 A,如下所示:
微信圖片_20220706142637.png
圖說

單位矩陣表示如下:
微信圖片_20220706142641.png
圖說

假設 S2 、S4 兩資料包丟失了,則將公式 1 中的單位矩陣對應的行刪除,則有如下:
微信圖片_20220706142645.png
圖說

公式 2 左側的矩陣記為 B,如下:
微信圖片_20220706142649.png
圖說

根據範德蒙矩陣可逆特點, 所以 B 也是一個可逆矩陣,記為 B,則恢復包過程其實主要就是求解 B' 矩陣的過程,對公式 2 做如下推導,即可求解原始包,如下所示:
微信圖片_20220706142653.png
圖說

即 (S1、S2、S3、S4、S5、S6、S7)中任何一個包都可以通過矩陣 B' 和收到的包進行恢復。所以 RS 的保護能力更強。

RED[5]

RED 也是前向糾錯的⼀種方式,傳送端通過主動傳送冗餘碼,來在⼀定程度上抵抗包在傳輸網路丟失的問題。解碼端可以通過冗餘包恢復丟失的包,RED 的標準規範在 RFC2198 中定義,可用在視訊和音訊冗餘包生成,WebRTC 音訊在 m96 上開啟了 RED 方式。

RED 的 payload 中不但包含當前包,還包含了歷史包,這樣 payload 在⼀定程度上具有冗餘資訊,起到抗丟包的作用。

下面簡要介紹下 RED 的封裝格式:RED block head

0                   1                   2                   3  
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1  
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
   |F| block PT | timestamp offset | block length | 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
  
   F: 1表⽰當前block後還有其它block, 0表⽰當前block為最後⼀個block 
   block PT: 表⽰當前block 的payload type 
   timestamp offset: 表⽰當前包時間戳相對於rtp head的時間戳的偏移 
   block length: 表⽰當前block的⻓度,不包括當前block header⻓度 
  
   0 1 2 3 4 5 6 7 
   +-+-+-+-+-+-+-+-+ 
   |0| Block PT | 
   +-+-+-+-+-+-+-+-+ 
   表⽰最後⼀個block

下面是一個 RED 包的示例:

0 1 2 3 
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 |V=2|P|X| CC=0 |M| PT | sequence number of primary | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 | timestamp of primary encoding | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 | synchronization source (SSRC) identifier | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 |1| block PT=7 | timestamp offset | block length | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
 |0| block PT=5 | | 
 +-+-+-+-+-+-+-+-+ + 
 | | 
 + LPC encoded redundant data (PT=7) + 
 | (14 bytes) | 
 + +---------------+ 
 | | | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 
 | | 
 + + 
 | | 
 + + 
 | | 
 + + 
 | DVI4 encoded primary data (PT=5) | 
 + (84 bytes, not to scale) + 
 / / 
 + + 
 | | 
 + + 
 | | 
 + +---------------+ 
 | | 
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

 該 rtp 包是⼀個 RED 封裝,包含兩個 block, ⼀個 block type 為 7, ⼀個 block type 為 5;即該 rtp 包包含了兩中型別的資料包。

WebRTC 中使用 RED 包來生成 audio 冗餘包,其原理大致如下:
微信圖片_20220706142658.png
圖說

上圖中,傳送端除了傳送當前包,還會攜帶之前一個包做為冗餘包,當上圖的 RED4 包丟失,即 4,3 包丟掉時,後續的 RED5 包到達,包含了 5,4 包,結合之前 RED3 包(包含了 3, 2 包),可以恢復丟失的包。


後向糾錯技術

ARQ

ARQ 為丟包重傳技術,接收端通過向傳送端請求重發丟失的包來恢復丟失的包。

這個相對於前向糾錯技術來講,延時偏高,在延時小的情況下,是個比較合適的選擇。

原理如下所示:
微信圖片_20220706142705.png

資料包 3 第⼀次傳送時,接收端沒有收到,便向傳送端發起 3 的重傳請求(WebRTC 中使⽤ NACK RTCP 包),傳送端收到了接收的重傳請求後,則再次重發報文 3。

下面是對 WebRTC 中使用的 NACK[6] RTCP 格式的簡單介紹,NACK RTCP 在 RFC4585 中有介紹,NACK 屬於反饋訊息,即 Feedback Message,格式如下:

 0                  1                   2                   3 
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
   |V=2|P| FMT | PT | length | 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
   | SSRC of packet sender | 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
   | SSRC of media source | 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
   : Feedback Control Information (FCI) : 
   : : 
  
   Figure 3: Common Packet Format for Feedback Messages

PT 有兩種大型別:‍

Name | Value | Brief Description 
 ----------+-------+------------------------------------ 
 RTPFB | 205 | Transport layer FB message 
 PSFB | 206 | Payload-specific FB message

NACK 對應的 FCI 訊息格式如下:

0                 1                   2                   3 
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
| PID | BLP | 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

            Figure 4: Syntax for the Generic NACK message 

NACK的PT=RTPFB 且 FMT=1 
PID表⽰當前重傳請求的第⼀個seqnum 
BLP為16位,代表PID所指的seqnum後連續的16個seqnum的重傳請求情況, 1表⽰當前位對應的se qnum丟失,接收端對其進⾏了重傳請求, 0表⽰未對該位對應的seqnum做重傳請求 
NACK中可以攜帶多個FCI端

PLC

PLC 稱之為丟包隱藏技術,位於接收端,也即解碼端;解碼端根據歷史語音幀,對其進行訊號分析,通過線性預測係數進行 LPC 建模,來預測丟失的語音幀,這項技術的可行性是基於語音的短時語音相似性。其優點是,不佔用額外頻寬;PLC 技術可以處理較小的丟包率(<15%)。

NetEQ 中的丟包隱藏是根據上一語音幀的線性預測係數 PLC 來建模,根據歷史語音訊號重建語音訊號然後載入一定的隨機噪聲;

連續丟包隱藏時,均使用同一個線性預測係數 LPC 重建語音訊號,注意這⾥需要減少連續重建訊號間的相關性,因此丟包隱藏產生的資料包能量遞減;

最後為了語音連續,需要做平滑處理。當需要進行丟包補償時,從儲存最近 70ms 的語音緩衝區中取出最新的一幀資料並計算該幀的 LPC 係數即可

WebRTC 的 NetEQ 模組和 OPUS 解碼器都具有 PLC 的功能,要是 Decoder 支援 PLC,優先使用解碼器的 PLC 功能,否則使用 NetEQ 的 PLC 功能,下一篇文章在介紹 NetEQ 模組時,會進行較詳細說明。


編碼器 OPUS 抗弱網特性[7]

OPUS 不但是開源且無專利的編解碼器, 而且相比其它編解碼器來說,效能十分優越。這也是 WebRTC 音訊通常使用它的原因。

下面對 OPUS 的一些特性進行說明,這些特性在對抗弱網上都有非常大的幫助。

⽀持全頻帶頻寬

OPUS 支援的位元速率可以從窄帶 6kbps 到⾼品質立體聲 510kbps,下面這幅圖說明 OPUS 從窄帶到高品質寬頻都能覆蓋,且相同位元速率下,品質更高。
微信圖片_20220706142712.png
圖說

OPUS 支援動態位元速率調準

可以無縫調節碼高低,同等位元速率下,OPUS 的音效品質更高;同時在丟包情況下,當丟包率大於一定範圍時,會將編碼模式轉換成為 SILK 模式,即低位元速率模式,以適應網路情況。

> //設定位元速率接⼝,可以通過該接⼝動態調整位元速率 
> WebRTCOPUS_SetBitRate 
>  
> /* When FEC is enabled and there's enough packet loss, use SILK */ 
> if (st->silk_mode.useInBandFEC && st->silk_mode.packetLossPercentage > (128-vo ice_est)>>4) 
>       st->mode = MODE_SILK_ONLY;

OPUS 延時更低

OPUS 結合了兩種編解碼技術,SILK(用於語音)和CELT(用於音樂),具有低延遲優勢。

這對於用作低延遲音訊通訊鏈路的一部分是必不可少的, OPUS 可以以犧牲語音質量為代價將演算法延遲減少到 5 毫秒。

現有的音樂編解碼器(例如 MP3、Vorbis 和 HE-AAC)具有 100 毫秒或更多的延遲,而 OPUS 的延遲要低得多,但在質量上與位元率相當,如下圖所示:
微信圖片_20220706142718.png
圖說

OPUS 支援帶內 FEC

OPUS 支援帶內 FEC 功能,在使用 FEC 後,可以根據丟包率來生成冗餘包,提高音訊的抗丟包能力。

OPUS 的帶內 FEC 功能使用方式類似 RED 方法,即傳送當前包時,會攜帶上一個包的內容,只不過是上一個包是使用低位元速率編碼來產生冗餘包的,類似下面的方式:

|1| | -> |2|1| -> |3|2| -> |4|3| -> |5|4| -> |6|5|

下面是 OPUS 和 FEC 相關的幾個介面:


//使能內建FEC
WebRTCOPUS_EnableFec
//向OPUS傳遞丟包率 
WebRTCOPUS_SetPacketLossRate 

//根據丟包率及useInBandFEC來判斷是否開啟低位元速率編碼,即利⽤低位元速率編碼來上⼀幀語⾳幀,⽣成 冗餘包 
st->silk_mode.LBRR_coded = decide_fec(st->silk_mode.useInBandFEC, 
             st- >silk_mode.packetLossPercentage, st->silk_mode.LBRR_coded, st->mode, &st->bandwidth, equiv_rate); 
             
//根據是否⽀持FEC,來分配SILK rate 
static int compute_silk_rate_for_hybrid(int rate, int bandwidth, int frame20ms, int vbr, int fec) 


/* Low-Bitrate Redundancy (LBRR) encoding. 
Reuse all parameters but encode excitation at lower bitrate */ 
static OPUS_INLINE void silk_LBRR_encode_FLP( 
    silk_encoder_state_FLP *psEnc, /* I/O Encoder state FLP */ 
    silk_encoder_control_FLP *psEncCtrl, /* I/O Encoder control FLP */ 
    const silk_float xfw[], /* I Input signal */ 
    OPUS_int condCoding /* I The type of conditional coding used so far for this frame */ 
)

這裡需要指出的是,OPUS 內建的 FEC 包只在 SILK 模式下生成,CELT 編碼模式下是不生成冗餘包的。

if (st->mode == MODE_CELT_ONLY) 
   redundancy = 0; 

 if (redundancy) 
 { 
     redundancy_bytes = compute_redundancy_bytes(max_data_bytes, st- >bitrate_bps, frame_rate, st->stream_channels); 
     if (redundancy_bytes == 0) 
        redundancy = 0; 
 }

WebRTC 中 FEC 的功能開啟是通過 SDP 協商來完成的,如下所示:

a=rtpmap:111 OPUS/48000/2 
a=fmtp:111 minptime=10;useinbandfec=1

下圖是 OPUS 開啟 FEC 和沒開啟 FEC 的效果對比圖[8]
微信圖片_20220706142722.png
圖說

從圖中可以看出,FEC 開啟後,在 20% 丟包情況下,音訊 MOS 值提升還是非常明顯的。

OPUS 解碼端支援 PLC

OPUS 解碼端支援丟包隱藏,其原理是根據語音訊號具有短時相似性的特點,利用上一幀正常或恢復的語音訊號,對其進行訊號分析,重建和預測當前丟失的語音幀。

int WebRTCOPUS_Decode(OPUSDecInst* inst, const uint8_t* encoded, 
                      size_t encoded_bytes, int16_t* decoded, 
                      int16_t* audio_type) { 
  int decoded_samples; 

  if (encoded_bytes == 0) { 
    *audio_type = DetermineAudioType(inst, encoded_bytes); 
    decoded_samples = WebRTCOPUS_DecodePlc(inst, decoded, 1); 
  } else { 
   ... 
  }

OPUS 語音功能支援 DTX

當不是音樂模式時,即在 VoIP 模式下,當檢測到某個時間期間內沒有說話聲時,為了節省頻寬,可以將開啟 DTX。

這個時候,在沒有檢測到通話聲音時,OPUS 會定期 400ms 傳送靜音包,達到降低頻寬的目的,WebRTC 預設沒有開啟這個特性,要開啟 DTX,只需要 SDP 協商時,在 a=ftmp 這一行中加入 usedtx=1 即可開啟。

WebRTCOPUS_EnableDtx  
WebRTCOPUS_

OPUS 本⾝具有很多抗弱網的特性,這些特性再配合丟包重傳,可以使音訊具備很強的抗弱網能力。


本文主要結合實際弱網處理工作經驗,從前向糾錯、後向糾錯及 OPUS 編碼器本身特性等方面,對音訊弱網一些常用技術做簡要說明和總結。

弱網處理還有一個關鍵的抗抖動技術,將在該系列的下一篇文章中詳細介紹。

參考資料:

[1]: https://datatracker.ietf.org/doc/html/rfc5109

[2]: https://datatracker.ietf.org/doc/html/draft-ietf-payload-flexible-fec-scheme-03

[3]:‍https://tex2e.github.io/rfctranslater/html/rfc5510.html‍

[4]:https://www.scirp.org/pdf/6-2.16.pdf

[5]:https://datatracker.ietf.org/doc/html/rfc2198

[6]https://tex2e.github.io/rfc-translater/html/rfc4585.html

[7]:https://ja.wikipedia.org/wiki/OPUS_(%E9%9F%B3%E5%A3%B0%E5%9C%A7%E7%B8%AE)

[8]:https://www.OPUScodec.org/static/presentations/OPUS_voice_aes135.pdf

相關文章