再來當一次技術搬運工,內容來自高可用框架
,學霸君工程師袁榮喜的如何實現1080P延遲低於500ms的實時超清直播傳輸技術
。
html
導語:視頻直播是不少技術團隊及架構師關注的問題,在實時性方面,大部分直播是準實時的,存在 1-3 秒延遲。本文由袁榮喜向「高可用架構」投稿,介紹其將直播延遲控制在 500ms 的背後的實現。linux
袁榮喜,學霸君工程師,2015 年加入學霸君,負責學霸君的網絡實時傳輸和分佈式系統的架構設計和實現,專一於基礎技術領域,在網絡傳輸、數據庫內核、分佈式系統和併發編程方面有必定了解。
最近因爲公司業務關係,須要一個在公網上能實時互動超清視頻的架構和技術方案。衆所周知,視頻直播用 CDN + RTMP 就能夠知足絕大部分視頻直播業務,咱們也接觸了和測試了幾家 CDN 提供的方案,單人直播沒有問題,一旦涉及到多人互動延遲很是大,沒法進行正常的互動交談。對於咱們作在線教育的企業來講沒有互動的直播是毫無心義的,因此咱們決定本身來構建一個超清晰(1080P)實時視頻的傳輸方案。算法
先來解釋下什麼是實時視頻,實時視頻就是視頻圖像從產生到消費完成整個過程人感受不到延遲,只要符合這個要求的視頻業務均可以稱爲實時視頻。關於視頻的實時性概括爲三個等級:shell
市面上大部分真實時視頻都是 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)。網絡
前面提到視頻編碼器會根據圖像的先後變化進行選擇性壓縮,由於剛開始接收端是沒有收到任何圖像,那麼編碼器在開始壓縮的視頻時須要作個全量壓縮,這個全量壓縮在 H.264 中 I 幀,後面的視頻圖像根據這個I幀來作增量壓縮,這些增量壓縮幀叫作 P 幀,H.264 爲了防止丟包和減少帶寬還引入一種雙向預測編碼的 B 幀,B 幀之前面的 I 或 P 幀和後面的 P 幀爲參考幀。H.264 爲了防止中間 P 幀丟失視頻圖像會一直錯誤它引入分組序列(GOP)編碼,也就是隔一段時間發一個全量 I 幀,上一個 I 幀與下一個 I 幀之間爲一個分組 GOP。它們之間的關係以下圖:
PS:在實時視頻當中最好不要加入 B 幀,由於 B 幀是雙向預測,須要根據後面的視頻幀來編碼,這會增大編解碼延遲。
前面提到若是 GOP 分組中的P幀丟失會形成解碼端的圖像發生錯誤,其實這個錯誤表現出來的就是馬賽克。由於中間連續的運動信息丟失了,H.264 在解碼的時候會根據前面的參考幀來補齊,可是補齊的並非真正的運動變化後的數據,這樣就會出現顏色色差的問題,這就是所謂的馬賽克現象,如圖:
這種現象不是咱們想看到的。爲了不這類問題的發生,通常若是發現 P 幀或者 I 幀丟失,就不顯示本 GOP 內的全部幀,直到下一個 I 幀來後從新刷新圖像。可是 I 幀是按照幀週期來的,須要一個比較長的時間週期,若是在下一個 I 幀來以前不顯示後來的圖像,那麼視頻就靜止不動了,這就是出現了所謂的卡頓現象。若是連續丟失的視頻幀太多形成解碼器無幀可解,也會形成嚴重的卡頓現象。視頻解碼端的卡頓現象和馬賽克現象都是由於丟幀引發的,最好的辦法就是讓幀儘可能不丟。
知道 H.264 的原理和分組編碼技術後所謂的秒開技術就比較簡單了,只要發送方從最近一個 GOP 的 I 幀開發發送給接收方,接收方就能夠正常解碼完成的圖像並當即顯示。但這會在視頻鏈接開始的時候多發一些幀數據形成播放延遲,只要在接收端播放的時候儘可能讓過時的幀數據只解碼不顯示,直到當前視頻幀在播放時間範圍以內便可。
前面四個延遲裏面咱們提到了編碼延遲,編碼延遲就是從 CCD 出來的 RGB 數據通過 H.264 編碼器編碼後出來的幀數據過程的時間。咱們在一個 8 核 CPU 的普通客戶機測試了最新版本 X.264 的各個分辨率的延遲,數據以下:
從上面能夠看出,超清視頻的編碼延遲會達到 50ms,解決編碼延遲的問題只能去優化編碼器內核讓編碼的運算更快,咱們也正在進行方面的工做。
在 1080P 分辨率下,視頻編碼碼率會達到 300KB/S,單個 I 幀數據大小達到 80KB,單個 P 幀能夠達到 30KB,這對網絡實時傳輸形成嚴峻的挑戰。
實時互動視頻一個關鍵的環節就是網絡傳輸技術,無論是早期 VoIP,仍是現階段流行的視頻直播,其主要手段是經過 TCP/IP 協議來進行通訊。可是 IP 網絡原本就是不可靠的傳輸網絡,在這樣的網絡傳輸視頻很容易形成卡頓現象和延遲。先來看看 IP 網絡傳輸的幾個影響網絡傳輸質量關鍵因素。
對直播有過了解的人都會認爲作視頻傳輸首選的就是 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 協議能夠證實這一點,咱們正式參考了這兩個協議來設計本身的通訊協議。
要評估一個網絡通訊質量的好壞和延遲一個重要的因素就是 Round-Trip Time(網絡往返延遲),也就是 RTT。評估兩端之間的 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 超出延遲範圍會進行傳輸路徑切換(本地網絡擁塞除外)。
UDP 除了延遲外,還會出現網絡抖動。什麼是抖動呢?舉個例子,假如咱們每秒發送 10 幀視頻幀,發送方與接收方的延遲爲 50MS,每幀數據用一個 UDP 報文來承載,那麼發送方發送數據的頻率是 100ms 一個數據報文,表示第一個報文發送時刻 0ms, T2 表示第二個報文發送時刻 100ms . . .,若是是理想狀態下接收方接收到的報文的時刻依次是(50ms, 150ms, 250ms, 350ms….),但因爲傳輸的緣由接收方收到的報文的相對時刻多是(50ms, 120ms, 240ms, 360ms ….),接收方實際接收報文的時刻和理想狀態時刻的差值就是抖動。以下示意圖:
咱們知道視頻必須按照嚴格是時間戳來播放,不然的就會出現視頻動做加快或者放慢的現象,若是咱們按照接收到視頻數據就當即播放,那麼這種加快和放慢的現象會很是頻繁和明顯。也就是說網絡抖動會嚴重影響視頻播放的質量,通常爲了解決這個問題會設計一個視頻播放緩衝區,經過緩衝接收到的視頻幀,再按視頻幀內部的時間戳來播放既能夠了。
UDP 除了小範圍的抖動之外,仍是出現大範圍的亂序現象,就是後發的報文先於先發的報文到達接收方。亂序會形成視頻幀順序錯亂,通常解決的這個問題會在視頻播放緩衝區裏作一個前後排序功能讓先發送的報文先進行播放。
播放緩衝區的設計很是講究,若是緩衝過多幀數據會形成沒必要要的延遲,若是緩衝幀數據過少,會由於抖動和亂序問題形成播放無數據能夠播的狀況發生,會引發必定程度的卡頓。關於播放緩衝區內部的設計細節咱們在後面的小節中詳細介紹。
UDP 在傳輸過程還會出現丟包,丟失的緣由有多種,例如:網絡出口不足、中間網絡路由擁堵、socket 收發緩衝區過小、硬件問題、傳輸損耗問題等等。在基於 UDP 視頻傳輸過程當中,丟包是很是頻繁發生的事情,丟包會形成視頻解碼器丟幀,從而引發視頻播放卡頓。這也是大部分視頻直播用 TCP 和 RTMP 的緣由,由於 TCP 底層有本身的重傳機制,能夠保證在網絡正常的狀況下視頻在傳輸過程不丟。基於 UDP 丟包補償方式通常有如下幾種:
報文冗餘很好理解,就是一個報文在發送的時候發送 2 次或者屢次。這個作的好處是簡單並且延遲小,壞處就是須要額外 N 倍(N 取決於發送的次數)的帶寬。
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 的延遲。
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 可靠到達的協議模型。各個模塊的關係示意圖以下:
先來看通訊協議,咱們定義的通訊協議分爲三個階段:接入協商階段、傳輸階段、斷開階段。
主要是發送端發起一個視頻傳輸接入請求,攜帶本地的視頻的當前狀態、起始幀序號、時間戳和 MTU 大小等,接收方在收到這個請求後,根據請求中視頻信息初始化本地的接收通道,並對本地 MTU 和發送端 MTU 進行比較取二者中較小的回送給發送方, 讓發送方按協商後的 MTU 來分片。示意圖以下:
傳輸階段有幾個協議,一個測試量 RTT 的 PING/PONG 協議、攜帶視頻幀分片的數據協議、數據反饋協議和發送端同步糾正協議。其中數據反饋協議是由接收反饋給發送方的,攜帶接收方已經接收到連續幀的報文 ID、幀 ID 和請求重傳的報文 ID 序列。同步糾正協議是由發送端主動丟棄發送窗口緩衝區中的報文後要求接收方同步到當前發送窗口位置,防止在發送主動丟棄幀數據後接收方一直要求發送方重發丟棄的數據。示意圖以下:
就一個斷開請求和一個斷開確認,發送方和接收方均可以發起斷開請求。
發送主要包括視頻幀分片算法、發送窗口緩衝區、擁塞判斷算法、過時幀丟棄算法和重傳。先一個個來介紹。
前面咱們提到 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。這裏必需要說明的是若是頻繁出現過時幀丟棄的動做會形成卡頓,說明當前網絡不適合傳輸高分辨率視頻,能夠直接將視頻設成更小的分辨率。
接收主要包括丟包管理、播放緩衝區、緩衝時間評估和播放控制,都是圍繞播放緩衝區來實現的,一個個來介紹。
丟包管理包括丟包檢測和丟失報文 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 狀態表示緩衝區進入播放狀態,播放模塊能夠從中取出幀進行解碼播放。咱們來介紹下這兩個狀態的切換關係:
播放緩衝區的目的就是防止抖動和應對丟包重傳,讓視頻流能按照採集時的頻率進行播放,播放緩衝區的設計極其複雜,須要考慮的因素不少,實現的時候須要慎重。
接收端最後一個環節就是播放控制,播放控制就是從緩衝區中拿出有效的視頻幀進行解碼播放。可是怎麼拿?何時拿?咱們知道視頻是按照視頻幀從發送端攜帶過來的相對時間戳來作播放,咱們每一幀視頻都有一個相對時間戳 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 來進行模擬公網的各類狀況進行測試,若是在模擬環境已經達到一個比較理想的狀態後會組織相關人員在公網上進行測試。下面來介紹怎麼來測試咱們整個傳輸模型的。
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 上模擬瞭如下幾種網絡狀態:
由於傳輸機制採用的是可靠到達,那麼檢驗傳輸機制有效的參數就是視頻延遲,咱們統計 2 分鐘週期內最大延遲,如下是各類狀況的延遲曲線圖:
從上圖能夠看出,若是網絡控制在環路延遲在 200ms 丟包在 10% 如下,可讓視頻延遲在 500ms 毫秒如下,這並非一個對網絡質量要求很苛刻的條件。因此咱們在後臺的媒體服務部署時,儘可能讓客戶端到媒體服務器之間的網絡知足這個條件,若是網路環路延遲在 300ms 丟包 15% 時,依然能夠作到小於 1 秒的延遲,基本能知足雙向互動交流。
公網測試相對比較簡單,咱們將 Server 部署到 UCloud 雲上,發送端用的是上海電信 100M 公司寬帶,接收端用的是河北聯通 20M 小區寬帶,環路延遲在 60ms 左右。整體測試下來 1080P 在接收端觀看視頻流暢天然,無抖動,無卡頓,延遲統計平均在 180ms 左右。
在整個 1080P 超清視頻的傳輸技術實現過程當中,咱們遇到過比較多的坑。大體以下:
咱們前期開發階段都是使用 socket 默認的緩衝區大小,因爲 1080P 圖像幀的數據很是巨大(關鍵幀超過 80KB),咱們發如今在內網測試沒有設置丟包的網絡環境發現接收端有嚴重的丟包,經查證是 socket 收發緩衝區過小形成丟包的,後來咱們把 socket 緩衝區設置到 128KB 大小,問題解決了。
前期咱們爲了節省傳輸帶寬和防丟包開了 B 幀編碼,因爲 B 幀是先後雙向預測編碼的,會在編碼期滯後幾個幀間隔時間,引發了超過 100ms 的編碼延時,後來咱們爲了實時性乾脆把 B 幀編碼選項去掉。
在設計階段咱們曾經使用發送端主動 push 方式來解決丟包重傳問題,在測試過程發如今丟包頻繁發生的狀況下至少增長了 20% 的帶寬消耗,並且容易帶來延遲和網絡擁塞。後來幾經論證用如今的 pull 模式來進行丟包重傳。
在設計階段咱們對每一個視頻緩衝區中的幀信息都是動態分配內存對象的,因爲 1080P 在傳輸過程當中每秒會發送 400 - 500 個 UDP 報文,在 PC 端長時間運行容易出現內存碎片,在服務器端出現莫名其妙的 clib 假內存泄露和併發問題。咱們實現了一個 memory slab 管理頻繁申請和釋放內存的問題。
在早期的設計之中咱們借鑑了 FLV 的方式將音頻和視頻數據用同一套傳輸算法傳輸,好處就是容易實現,但在網絡波動的狀況下容易引發聲音卡頓,也沒法根據音頻的特性優化傳輸。後來咱們把音頻獨立出來,針對音頻的特性設計了一套低延遲高質量的音頻傳輸體系,定點對音頻進行傳輸優化。
後續的工做是重點放在媒體器多點分佈、多點併發傳輸、P2P 分發算法的探索上,儘可能減小延遲和服務帶寬成本,讓傳輸變的更高效和更低廉。
提問:在優化到 500ms 方案中,哪一塊是最關鍵的?
袁榮喜:主要是丟包重傳 擁塞和播放緩衝這三者之間的協調工做最爲關鍵,要兼顧延遲控制和視頻流暢性。
提問:多方視頻和單方有哪些區別,用到了 CDN 推流嗎?
袁榮喜:咱們公司是作在線教育的,不少場景須要老師和學生交談,用 CDN 推流方式延遲很大,咱們這個視頻主要是解決多方通訊之間交談延遲的問題。咱們如今觀看放也有用 CDN 推流,但只是單純的觀看。咱們也在研發基於 UDP 的觀看端分發協議,目前這部分工做尚未完成。
from:http://windrunnerlihuan.com/2016/09/18/如何實現1080P延遲低於500ms的實時超清直播傳輸技術/