視頻直播相關問題

最近由於公司業務關係,需要一個在公網上能實時互動超清視頻的架構和技術方案。衆所周知,視頻直播用 CDN + RTMP 就可以滿足絕大部分視頻直播業務,我們也接觸了和測試了幾家 CDN 提供的方案,單人直播沒有問題,一旦涉及到多人互動延遲非常大,無法進行正常的互動交談。對於我們做在線教育的企業來說沒有互動的直播是毫無意義的,所以我們決定自己來構建一個超清晰(1080P)實時視頻的傳輸方案。


先來解釋下什麼是實時視頻,實時視頻就是視頻圖像從產生到消費完成整個過程人感覺不到延遲,只要符合這個要求的視頻業務都可以稱爲實時視頻。關於視頻的實時性歸納爲三個等級:


  • 僞實時:視頻消費延遲超過 3 秒,單向觀看實時,通用架構是 CDN + RTMP + HLS,現在基本上所有的直播都是這類技術。

  • 準實時: 視頻消費延遲 1 ~ 3 秒,能進行雙方互動但互動有障礙。有些直播網站通過 TCP/UDP + FLV 已經實現了這類技術,YY 直播屬於這類技術。

  • 真實時:視頻消費延遲 < 1秒,平均 500 毫秒。這類技術是真正的實時技術,人和人交談沒有明顯延遲感。QQ、微信、Skype 和 WebRTC 等都已經實現了這類技術。


市面上大部分真實時視頻都是 480P 或者 480P 以下的實時傳輸方案,用於在線教育和線上教學有一定困難,而且有時候流暢度是個很大的問題。在實現超清晰實時視頻我們做了大量嘗試性的研究和探索,在這裏會把大部分細節分享出來。


要實時就要縮短延遲,要縮短延遲就要知道延遲是怎麼產生的,視頻從產生、編碼、傳輸到最後播放消費,各個環節都會產生延遲,總體歸納爲下圖:



( 點擊圖片可以全屏縮放)


成像延遲,一般的技術是毫無爲力的,涉及到 CCD 相關的硬件,現在市面上最好的 CCD,一秒鐘 50 幀,成像延遲也在 20 毫秒左右,一般的 CCD 只有 20 ~ 25 幀左右,成像延遲 40 ~ 50 毫秒。


編碼延遲,和編碼器有關係,在接下來的小結介紹,一般優化的空間比較小。


我們着重針對網絡延遲和播放緩衝延遲來進行設計,在介紹整個技術細節之前先來了解下視頻編碼和網絡傳輸相關的知識和特點。


一、視頻編碼那些事


我們知道從 CCD 採集到的圖像格式一般的 RGB 格式的(BMP),這種格式的存儲空間非常大,它是用三個字節描述一個像素的顏色值,如果是 1080P 分辨率的圖像空間:1920 x 1080 x 3 = 6MB,就算轉換成 JPG 也有近 200KB,如果是每秒 12 幀用 JPG 也需要近 2.4MB/S 的帶寬,這帶寬在公網上傳輸是無法接受的。


視頻編碼器就是爲了解決這個問題的,它會根據前後圖像的變化做運動檢測,通過各種壓縮把變化的發送到對方,1080P 進行過  H.264 編碼後帶寬也就在 200KB/S ~ 300KB/S 左右。在我們的技術方案裏面我們採用 H.264 作爲默認編碼器(也在研究 H.265)。


1.1 H.264 編碼


前面提到視頻編碼器會根據圖像的前後變化進行選擇性壓縮,因爲剛開始接收端是沒有收到任何圖像,那麼編碼器在開始壓縮的視頻時需要做個全量壓縮,這個全量壓縮在 H.264 中 I 幀,後面的視頻圖像根據這個I幀來做增量壓縮,這些增量壓縮幀叫做 P 幀,H.264 爲了防止丟包和減小帶寬還引入一種雙向預測編碼的 B 幀,B 幀以前面的 I 或 P 幀和後面的 P 幀爲參考幀。H.264 爲了防止中間 P 幀丟失視頻圖像會一直錯誤它引入分組序列(GOP)編碼也就是隔一段時間發一個全量 I 幀,上一個 I 幀與下一個 I 幀之間爲一個分組 GOP。它們之間的關係如下圖:




PS:在實時視頻當中最好不要加入 B 幀,因爲 B 幀是雙向預測,需要根據後面的視頻幀來編碼,這會增大編解碼延遲。


1.2 馬賽克、卡頓和秒開


前面提到如果 GOP 分組中的P幀丟失會造成解碼端的圖像發生錯誤,其實這個錯誤表現出來的就是馬賽克。因爲中間連續的運動信息丟失了,H.264 在解碼的時候會根據前面的參考幀來補齊,但是補齊的並不是真正的運動變化後的數據,這樣就會出現顏色色差的問題,這就是所謂的馬賽克現象,如圖:




這種現象不是我們想看到的。爲了避免這類問題的發生,一般如果發現 P 幀或者 I 幀丟失,就不顯示本 GOP 內的所有幀,直到下一個 I 幀來後重新刷新圖像。但是 I 幀是按照幀週期來的,需要一個比較長的時間週期,如果在下一個 I 幀來之前不顯示後來的圖像,那麼視頻就靜止不動了,這就是出現了所謂的卡頓現象。如果連續丟失的視頻幀太多造成解碼器無幀可解,也會造成嚴重的卡頓現象。視頻解碼端的卡頓現象和馬賽克現象都是因爲丟幀引起的,最好的辦法就是讓幀儘量不丟


知道 H.264 的原理和分組編碼技術後所謂的秒開技術就比較簡單了,只要發送方從最近一個 GOP 的 I 幀開發發送給接收方,接收方就可以正常解碼完成的圖像並立即顯示。但這會在視頻連接開始的時候多發一些幀數據造成播放延遲,只要在接收端播放的時候儘量讓過期的幀數據只解碼不顯示,直到當前視頻幀在播放時間範圍之內即可。


1.3 編碼延遲與碼率


前面四個延遲裏面我們提到了編碼延遲,編碼延遲就是從 CCD 出來的 RGB 數據經過 H.264 編碼器編碼後出來的幀數據過程的時間。我們在一個 8 核 CPU 的普通客戶機測試了最新版本 X.264 的各個分辨率的延遲,數據如下:



從上面可以看出,超清視頻的編碼延遲會達到 50ms,解決編碼延遲的問題只能去優化編碼器內核讓編碼的運算更快,我們也正在進行方面的工作。


在 1080P 分辨率下,視頻編碼碼率會達到 300KB/S,單個 I 幀數據大小達到 80KB,單個 P 幀可以達到 30KB,這對網絡實時傳輸造成嚴峻的挑戰。


二、網絡傳輸質量因素


實時互動視頻一個關鍵的環節就是網絡傳輸技術,不管是早期 VoIP,還是現階段流行的視頻直播,其主要手段是通過 TCP/IP 協議來進行通信。但是 IP 網絡本來就是不可靠的傳輸網絡,在這樣的網絡傳輸視頻很容易造成卡頓現象和延遲。先來看看 IP 網絡傳輸的幾個影響網絡傳輸質量關鍵因素。


2.1 TCP 和 UDP


對直播有過了解的人都會認爲做視頻傳輸首選的就是 TCP + RTMP,其實這是比較片面的。在大規模實時多媒體傳輸網絡中,TCP 和 RTMP 都不佔優勢。TCP 是個擁塞公平傳輸的協議,它的擁塞控制都是爲了保證網絡的公平性而不是快速到達,我們知道,TCP 層只有順序到對應的報文才會提示應用層讀數據,如果中間有報文亂序或者丟包都會在 TCP 做等待,所以 TCP 的發送窗口緩衝和重發機制在網絡不穩定的情況下會造成延遲不可控,而且傳輸鏈路層級越多延遲會越大。


關於 TCP 的原理:

http://coolshell.cn/articles/11564.html

關於 TCP 重發延遲:

http://weibo.com/p/1001603821691477346388


在實時傳輸中使用 UDP 更加合理,UDP 避免了 TCP 繁重的三次握手、四次揮手和各種繁雜的傳輸特性,只需要在 UDP 上做一層簡單的鏈路 QoS 監測和報文重發機制,實時性會比 TCP 好,這一點從 RTP 和 DDCP 協議可以證明這一點,我們正式參考了這兩個協議來設計自己的通信協議。


2.2 延遲


要評估一個網絡通信質量的好壞和延遲一個重要的因素就是 Round-Trip Time(網絡往返延遲),也就是 RTT。評估兩端之間的 RTT 方法很簡單,大致如下:


  1. 發送端方一個帶本地時間戳 T1 的 ping 報文到接收端;

  2. 接收端收到 ping 報文,以 ping 中的時間戳 T1 構建一個攜帶 T1 的 pong 報文發往發送端;

  3. 發送端接收到接收端發了的 pong 時,獲取本地的時間戳 T2,用 T2 – T1 就是本次評測的 RTT。


示意圖如下:


 ( 點擊圖片可以全屏縮放)


上面步驟的探測週期可以設爲 1 秒一次。爲了防止網絡突發延遲增大,我們採用了借鑑了 TCP 的 RTT 遺忘衰減的算法來計算,假設原來的 RTT 值爲 rtt,本次探測的 RTT 值爲 keep_rtt。那麼新的 RTT 爲:


new_rtt = (7 * rtt + keep_rtt) / 8


可能每次探測出來的 keep_rtt 會不一樣,我們需要會計算一個 RTT 的修正值 rtt_var,算法如下:


new_rtt_var = (rtt_var * 3 + abs(rtt – keep_rtt)) / 4 


rtt_var 其實就是網絡抖動的時間差值。


如果 RTT 太大,表示網絡延遲很大。我們在端到端之間的網絡路徑同時保持多條並且實時探測其網絡狀態,如果 RTT 超出延遲範圍會進行傳輸路徑切換(本地網絡擁塞除外)。


2.3 抖動和亂序


UDP 除了延遲外,還會出現網絡抖動。什麼是抖動呢?舉個例子,假如我們每秒發送 10 幀視頻幀,發送方與接收方的延遲爲 50MS,每幀數據用一個 UDP 報文來承載,那麼發送方發送數據的頻率是 100ms 一個數據報文,表示第一個報文發送時刻 0ms, T2 表示第二個報文發送時刻 100ms . . .,如果是理想狀態下接收方接收到的報文的時刻依次是(50ms, 150ms, 250ms, 350ms….),但由於傳輸的原因接收方收到的報文的相對時刻可能是(50ms, 120ms, 240ms, 360ms ….),接收方實際接收報文的時刻和理想狀態時刻的差值就是抖動。如下示意圖:



( 點擊圖片可以全屏縮放)


我們知道視頻必須按照嚴格是時間戳來播放,否則的就會出現視頻動作加快或者放慢的現象,如果我們按照接收到視頻數據就立即播放,那麼這種加快和放慢的現象會非常頻繁和明顯。也就是說網絡抖動會嚴重影響視頻播放的質量,一般爲了解決這個問題會設計一個視頻播放緩衝區,通過緩衝接收到的視頻幀,再按視頻幀內部的時間戳來播放既可以了。


UDP 除了小範圍的抖動以外,還是出現大範圍的亂序現象,就是後發的報文先於先發的報文到達接收方。亂序會造成視頻幀順序錯亂,一般解決的這個問題會在視頻播放緩衝區裏做一個先後排序功能讓先發送的報文先進行播放。


播放緩衝區的設計非常講究,如果緩衝過多幀數據會造成不必要的延遲,如果緩衝幀數據過少,會因爲抖動和亂序問題造成播放無數據可以播的情況發生,會引起一定程度的卡頓。關於播放緩衝區內部的設計細節我們在後面的小節中詳細介紹。


2.4 丟包


UDP 在傳輸過程還會出現丟包,丟失的原因有多種,例如:網絡出口不足、中間網絡路由擁堵、socket 收發緩衝區太小、硬件問題、傳輸損耗問題等等。在基於 UDP 視頻傳輸過程中,丟包是非常頻繁發生的事情,丟包會造成視頻解碼器丟幀,從而引起視頻播放卡頓。這也是大部分視頻直播用 TCP 和 RTMP 的原因,因爲 TCP 底層有自己的重傳機制,可以保證在網絡正常的情況下視頻在傳輸過程不丟。基於 UDP 丟包補償方式一般有以下幾種:


報文冗餘


報文冗餘很好理解,就是一個報文在發送的時候發送 2 次或者多次。這個做的好處是簡單而且延遲小,壞處就是需要額外 N 倍(N 取決於發送的次數)的帶寬。


FEC


 Forward Error Correction即向前糾錯算法,常用的算法有糾刪碼技術(EC),在分佈式存儲系統中比較常見。最簡單的就是 A B 兩個報文進行 XOR(與或操作)得到 C,同時把這三個報文發往接收端,如果接收端只收到 AC,通過 A 和 C 的 XOR 操作就可以得到 B 操作。這種方法相對增加的額外帶寬比較小,也能防止一定的丟包,延遲也比較小,通常用於實時語音傳輸上。對於  1080P 300KB/S 碼率的超清晰視頻,哪怕是增加 20% 的額外帶寬都是不可接受的所以視頻傳輸不太建議採用 FEC 機制。


丟包重傳


丟包重傳有兩種方式,一種是 push 方式,一種是 pull 方式。Push 方式是發送方沒有收到接收方的收包確認進行週期性重傳,TCP 用的是 push 方式。pull 方式是接收方發現報文丟失後發送一個重傳請求給發送方,讓發送方重傳丟失的報文。丟包重傳是按需重傳,比較適合視頻傳輸的應用場景,不會增加太對額外的帶寬,但一旦丟包會引來至少一個 RTT 的延遲。


2.5 MTU 和最大 UDP


IP 網定義單個 IP 報文最大的大小,常用 MTU 情況如下:


超通道 65535

16Mb/s 令牌環 179144

Mb/s 令牌環 4464

FDDI 4352

以太網 1500

IEEE 802.3/802.2 1492

X.25 576

點對點(低時延)296


紅色的是 Internet 使用的上網方式,其中 X.25 是個比較老的上網方式,主要是利用 ISDN 或者電話線上網的設備,也不排除有些家用路由器沿用 X.25 標準來設計。所以我們必須清晰知道每個用戶端的 MTU 多大,簡單的辦法就是在初始化階段用各種大小的 UDP 報文來探測 MTU 的大小。MTU 的大小會影響到我們視頻幀分片的大小,視頻幀分片的大小其實就是單個 UDP 報文最大承載的數據大小。


分片大小 = MTU – IP 頭大小 – UDP 頭大小 – 協議頭大小;

IP 頭大小 = 20 字節, UDP 頭大小 = 8 字節。


爲了適應網絡路由器小包優先的特性,我們如果得到的分片大小超過 800 時,會直接默認成 800 大小的分片。


三、傳輸模型


我們根據視頻編碼和網絡傳輸得到特性對 1080P 超清視頻的實時傳輸設計了一個自己的傳輸模型,這個模型包括一個根據網絡狀態自動碼率的編解碼器對象、一個網絡發送模塊、一個網絡接收模塊和一個 UDP 可靠到達的協議模型。各個模塊的關係示意圖如下:



( 點擊圖片可以全屏縮放)


3.1 通信協議


先來看通信協議,我們定義的通信協議分爲三個階段:接入協商階段、傳輸階段、斷開階段。


接入協商階段:


主要是發送端發起一個視頻傳輸接入請求,攜帶本地的視頻的當前狀態、起始幀序號、時間戳和 MTU 大小等,接收方在收到這個請求後,根據請求中視頻信息初始化本地的接收通道,並對本地 MTU 和發送端 MTU 進行比較取兩者中較小的回送給發送方, 讓發送方按協商後的 MTU 來分片。示意圖如下:



( 點擊圖片可以全屏縮放)


傳輸階段:


傳輸階段有幾個協議,一個測試量 RTT 的 PING/PONG 協議、攜帶視頻幀分片的數據協議、數據反饋協議和發送端同步糾正協議。其中數據反饋協議是由接收反饋給發送方的,攜帶接收方已經接收到連續幀的報文 ID、幀 ID 和請求重傳的報文 ID 序列。同步糾正協議是由發送端主動丟棄發送窗口緩衝區中的報文後要求接收方同步到當前發送窗口位置,防止在發送主動丟棄幀數據後接收方一直要求發送方重發丟棄的數據。示意圖如下:



( 點擊圖片可以全屏縮放)


斷開階段:


就一個斷開請求和一個斷開確認,發送方和接收方都可以發起斷開請求。


3.2 發送


發送主要包括視頻幀分片算法、發送窗口緩衝區、擁塞判斷算法、過期幀丟棄算法和重傳。先一個個來介紹。


幀分片


前面我們提到 MTU 和視頻幀大小,在 1080P 下大部分視頻幀的大小都大於 UDP 的 MTU 大小,那麼就需要對幀進行分片,分片的方法很簡單,按照先連接過程協商後的 MTU 大小來確定分片大小(確定分片大小的算法在 MTU 小節已經介紹過),然後將 幀數據按照分片大小切分成若干份,每一份分片以 segment 報文形式發往接收方。


重傳


重傳比較簡單,我們採用 pull 方式來實現重傳,當接收方發生丟包,如果丟包的時刻 T1 + rtt_var< 接收方當前的時刻 T2,就認爲是丟包了,這個時候就會把所有滿足這個條件丟失的報文 ID 構建一個 segment ack 反饋給發送方,發送方收到這個反饋根據 ID 到重發窗口緩衝區中查找對應的報文重發即可。


爲什麼要間隔一個 rtt_var 才認爲是丟包了?因爲報文是有可能亂序到達,所有要等待一個抖動週期後認爲丟失的報文還沒有來才確認是報文丟失了,如果檢測到丟包立即發送反饋要求重傳,有可能會讓發送端多發數據,造成帶寬讓費和網絡擁塞。


發送窗口緩衝區


發送窗口緩衝區保存這所有正在發送且沒有得到發送方連續 ID 確認的報文。當接收方反饋最新的連續報文 ID,發送窗口緩衝就會刪除所有小於最新反饋連續的報文 ID,發送窗口緩衝區緩衝的報文都是爲了重發而存在。這裏解釋下接收方反饋的連續的報文 ID,舉個例子,假如發送方發送了 1. 2. 3. 4. 5,接收方收到 1.2. 4. 5。這個時候最小連續 ID = 2,如果後面又來了 3,那麼接收方最小連續 ID = 5。


擁塞判斷


我們把當前時間戳記爲 curr_T,把發送窗口緩衝區中最老的報文的時間戳記爲 oldest_T,它們之間的間隔記爲 delay,那麼


delay = curr_T - oldest_T


在編碼器請求發送模塊發送新的視頻幀時,如果 delay > 擁塞閾值 Tn,我們就認爲網絡擁塞了,這個時候會根據最近 20 秒接收端確認收到的數據大小計算一個帶寬值,並把這個帶寬值反饋給編碼器,編碼器收到反饋後,會根據帶寬調整編碼碼率。如果多次發生要求降低碼率的反饋,我們會縮小圖像的分辨率來保證視頻的流暢性和實時性。Tn 的值可以通過 rtt 和 rtt_var 來確定。


但是網絡可能階段性擁塞,過後卻恢復正常,我們設計了一個定時器來定時檢查發送方的重發報文數量和 delay,如果發現恢復正常,會逐步增大編碼器編碼碼率,讓視頻恢復到指定的分辨率和清晰度。


過期幀丟棄


在網絡擁塞時可能發送窗口緩衝區中有很多報文正在發送,爲了緩解擁塞和減少延遲我們會對整個緩衝區做檢查,如果有超過一定閾值時間的 H.264 GOP 分組存在,我們會將這個 GOP 所有幀的報文從窗口緩衝區移除。並將它下一個 GOP 分組的 I 的幀 ID 和報文 ID 通過 wnd sync 協議同步到接收端上,接收端接收到這個協議,會將最新連續 ID 設置成同步過來的 ID。這裏必須要說明的是如果頻繁出現過期幀丟棄的動作會造成卡頓,說明當前網絡不適合傳輸高分辨率視頻,可以直接將視頻設成更小的分辨率


3.3 接收


接收主要包括丟包管理、播放緩衝區、緩衝時間評估和播放控制,都是圍繞播放緩衝區來實現的,一個個來介紹。


丟包管理


丟包管理包括丟包檢測和丟失報文 ID 管理兩部分。丟包檢測過程大致是這樣的,假設播放緩衝區的最大報文 ID 爲 max_id,網絡上新收到的報文 ID 爲 new_id,如果 max_id + 1 < new_id,那麼可能發生丟包,就會將 [max_id + 1, new_id -1] 區間中所有的 ID 和當前時刻作爲 K/V 對加入到丟包管理器當中。如果 new_id < max_id,那麼就將丟包管理中的 new_id 對應的 K/V 對刪除,表示丟失的報文已經收到。當收包反饋條件滿足時,會掃描整個丟包管理,將達到請求重傳的丟包 ID 加入到 segment ack 反饋消息中併發往發送方請求重傳,如果 ID 被請求了重傳,會將當前時刻設置爲 K/V 對中,增加對應報文的重傳計數器 count,這個掃描過程會統計對包管理器中單個重發最多報文的重發次數 resend_count。


緩衝時間評估


在前面的抖動與亂序小節中我們提到播放端有個緩衝區,這個緩衝區過大時延遲就大,緩衝區過小時又會出現卡頓現象,我們針對這個問題設計了一個緩衝時間評估的算法。緩衝區評估先會算出一個 cache timer,cache timer 是通過掃描對包管理得到的 resend count 和 rtt 得到的,我們知道從請求重傳報文到接收方收到重傳的報文的時間間隔是一個 RTT 週期,所以 cache timer 的計算方式如下。


cache timer = (2 * resend_count+ 1) * (rtt + rtt_var) / 2


有可能 cache timer 計算出來很小(小於視頻幀之間間隔時間 frame timer),那麼 cache timer = frame timer,也就是說網絡再好,緩衝區緩衝區至少 1 幀視頻的數據,否則緩衝區是毫無意義的。


如果單位時間內沒有丟包重傳發生,那麼 cache timer 會做適當的縮小,這樣做的好處是當網絡間歇性波動造成 cache timer 很大,恢復正常後 cache timer 也能恢復到相對小位置,縮減不必要的緩衝區延遲。


播放緩衝區


我們設計的播放緩衝區是按幀 ID 爲索引的有序循環數組,數組內部的單元是視頻幀的具體信息:幀 ID、分片數、幀類型等。緩衝區有兩個狀態:waiting 和 playing,waiting 狀態表示緩衝區處於緩衝狀態,不能進行視頻播放直到緩衝區中的幀數據達到一定的閾值。Playing 狀態表示緩衝區進入播放狀態,播放模塊可以從中取出幀進行解碼播放。我們來介紹下這兩個狀態的切換關係:


  1. 當緩衝區創建時會被初始化成 waiting 狀態。

  2. 當緩衝區中緩衝的最新幀與最老幀的時間戳間隔 > cache timer 時,進入 playing 狀態並更當前時刻設成播放絕對時間戳 play ts。

  3. 當緩衝區處於 playing 狀態且緩衝區是沒有任何幀數據,進入 waiting 狀態直到觸發第 2 步。


播放緩衝區的目的就是防止抖動和應對丟包重傳,讓視頻流能按照採集時的頻率進行播放,播放緩衝區的設計極其複雜,需要考慮的因素很多,實現的時候需要慎重。


播放控制


接收端最後一個環節就是播放控制,播放控制就是從緩衝區中拿出有效的視頻幀進行解碼播放。但是怎麼拿?什麼時候拿?我們知道視頻是按照視頻幀從發送端攜帶過來的相對時間戳來做播放,我們每一幀視頻都有一個相對時間戳 TS,根據幀與幀之間的 TS 的差值就可以知道上一幀和下一幀播放的時間間隔,假如上一幀播放的絕對時間戳爲 prev_play_ts,相對時間戳爲 prev_ts,當前系統時間戳爲 curr_play_ts,當前緩衝區中最小序號幀的相對時間戳爲  frame_ts,只要滿足:


Prev_play_ts + (frame_ts – prev_ts) < curr_play_ts 且這一幀數據是所有的報文都收齊了


這兩個條件就可以進行解碼播放,取出幀數據後將 Prev_play_ts = cur_play_ts,但更新 prev_ts 有些講究,爲了防止緩衝延遲問題我們做了特殊處理。


如果 frame_ts + cache timer < 緩衝區中最大幀的 ts,表明緩衝的時延太長,則 prev_ts = 緩衝區中最大幀的 ts - cache timer。 否則 prev_ts = frame_ts。


四、測量


再好的模型也需要有合理的測量方式來驗證,在多媒體這種具有時效性的傳輸領域尤其如此。一般在實驗室環境我們採用 netem 來進行模擬公網的各種情況進行測試如果在模擬環境已經達到一個比較理想的狀態後會組織相關人員在公網上進行測試。下面來介紹怎麼來測試我們整個傳輸模型的。


4.1 netem 模擬測試


Netem 是 Linux 內核提供的一個網絡模擬工具,可以設置延遲、丟包、抖動、亂序和包損壞等,基本能模擬公網大部分網絡情況。


關於 netem 可以訪問它的官網:

https://wiki.linuxfoundation.org/networking/netem


我們在實驗環境搭建了一個基於服務器和客戶端模式的測試環境,下面是測試環境的拓撲關係圖:




我們利用 Linux 來做一個路由器,服務器和收發端都連接到這個路由器上,服務器負責客戶端的登記、數據轉發、數據緩衝等,相當於一個簡易的流媒體服務器。Sender 負責媒體編碼和發送,receiver 負責接收和媒體播放。爲了測試延遲,我們把 sender 和 receiver 運行在同一個 PC 機器上,在 sender 從 CCD 獲取到 RGB 圖像時打一個時間戳,並把這個時間戳記錄在這一幀數據的報文發往 server 和 receiver,receiver 收到並解碼顯示這幀數據時,通過記錄的時間戳可以得到整個過程的延遲。我們的測試用例是用 1080P 碼率爲 300KB/S 視頻流,在 router 用 netem 上模擬了以下幾種網絡狀態:


  1. 環路延遲 10m,無丟包,無抖動,無亂序

  2. 環路延遲 30ms,丟包 0.5%,抖動 5ms, 2% 亂序

  3. 環路延遲 60ms,丟包 1%,抖動 20ms, 3% 亂序,0.1% 包損壞

  4. 環路延遲 100ms,丟包 4%,抖動 50ms, 4% 亂序,0.1% 包損壞

  5. 環路延遲 200ms,丟包 10%,抖動 70ms, 5% 亂序,0.1% 包損壞

  6. 環路延遲 300ms,丟包 15%,抖動 100ms, 5% 亂序,0.1% 包損壞


因爲傳輸機制採用的是可靠到達,那麼檢驗傳輸機制有效的參數就是視頻延遲,我們統計 2 分鐘週期內最大延遲,以下是各種情況的延遲曲線圖:




從上圖可以看出,如果網絡控制在環路延遲在 200ms 丟包在 10% 以下,可以讓視頻延遲在 500ms 毫秒以下,這並不是一個對網絡質量要求很苛刻的條件。所以我們在後臺的媒體服務部署時,儘量讓客戶端到媒體服務器之間的網絡滿足這個條件,如果網路環路延遲在 300ms 丟包 15% 時,依然可以做到小於 1 秒的延遲,基本能滿足雙向互動交流。


4.2 公網測試


公網測試相對比較簡單,我們將 Server 部署到 UCloud 雲上,發送端用的是上海電信 100M 公司寬帶,接收端用的是河北聯通 20M 小區寬帶,環路延遲在 60ms 左右。總體測試下來 1080P 在接收端觀看視頻流暢自然,無抖動,無卡頓,延遲統計平均在 180ms 左右。


五、坑


在整個 1080P 超清視頻的傳輸技術實現過程中,我們遇到過比較多的坑。大致如下:


Socket 緩衝區問題


我們前期開發階段都是使用 socket 默認的緩衝區大小,由於 1080P 圖像幀的數據非常巨大(關鍵幀超過 80KB),我們發現在在內網測試沒有設置丟包的網絡環境發現接收端有嚴重的丟包,經查證是 socket 收發緩衝區太小造成丟包的,後來我們把 socket 緩衝區設置到 128KB 大小,問題解決了。


H.264 B 幀延遲問題


前期我們爲了節省傳輸帶寬和防丟包開了 B 幀編碼,由於 B 幀是前後雙向預測編碼的,會在編碼期滯後幾個幀間隔時間,引起了超過 100ms 的編碼延時,後來我們爲了實時性乾脆把 B 幀編碼選項去掉。


Push 方式丟包重傳


在設計階段我們曾經使用發送端主動 push 方式來解決丟包重傳問題,在測試過程發現在丟包頻繁發生的情況下至少增加了 20% 的帶寬消耗,而且容易帶來延遲和網絡擁塞。後來幾經論證用現在的 pull 模式來進行丟包重傳。


Segment 內存問題


在設計階段我們對每個視頻緩衝區中的幀信息都是動態分配內存對象的,由於 1080P 在傳輸過程中每秒會發送 400 - 500 個 UDP 報文,在 PC 端長時間運行容易出現內存碎片,在服務器端出現莫名其妙的 clib 假內存泄露和併發問題。我們實現了一個 memory slab 管理頻繁申請和釋放內存的問題。


音頻和視頻數據傳輸問題


在早期的設計之中我們借鑑了 FLV 的方式將音頻和視頻數據用同一套傳輸算法傳輸,好處就是容易實現,但在網絡波動的情況下容易引起聲音卡頓,也無法根據音頻的特性優化傳輸。後來我們把音頻獨立出來,針對音頻的特性設計了一套低延遲高質量的音頻傳輸體系,定點對音頻進行傳輸優化。


後續的工作是重點放在媒體器多點分佈、多點併發傳輸、P2P 分發算法的探索上,儘量減少延遲和服務帶寬成本,讓傳輸變的更高效和更低廉。


Q&A


提問:在優化到 500ms 方案中,哪一塊是最關鍵的?

袁榮喜:主要是丟包重傳 擁塞和播放緩衝這三者之間的協調工作最爲關鍵,要兼顧延遲控制和視頻流暢性。

提問:多方視頻和單方有哪些區別,用到了 CDN 推流嗎?

袁榮喜:我們公司是做在線教育的,很多場景需要老師和學生交談,用 CDN 推流方式延遲很大,我們這個視頻主要是解決多方通信之間交談延遲的問題。我們現在觀看放也有用 CDN 推流,但只是單純的觀看。我們也在研發基於 UDP 的觀看端分發協議,目前這部分工作還沒有完成。

發佈了23 篇原創文章 · 獲贊 23 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章