天天看點

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&A參考閱讀

再來當一次技術搬運工,内容來自<code>高可用架構</code>,學霸君工程師袁榮喜的<code>如何實作1080P延遲低于500ms的實時超清直播傳輸技術</code>。

<a></a>

       導語:視訊直播是很多技術團隊及架構師關注的問題,在實時性方面,大部分直播是準實時的,存在 1-3 秒延遲。本文由袁榮喜向「高可用架構」投稿,介紹其将直播延遲控制在 500ms 的背後的實作。

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀
       袁榮喜,學霸君工程師,2015 年加入學霸君,負責學霸君的網絡實時傳輸和分布式系統的架構設計和實作,專注于基礎技術領域,在網絡傳輸、資料庫核心、分布式系統和并發程式設計方面有一定了解。

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

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

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

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

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

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

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

       成像延遲,一般的技術是毫無為力的,涉及到 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。它們之間的關系如下圖:

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

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

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

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

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

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

關于 TCP 的原理:

<a href="http://coolshell.cn/articles/11564.html" target="_blank">http://coolshell.cn/articles/11564.html</a>

關于 TCP 重發延遲:

<a href="http://weibo.com/p/1001603821691477346388" target="_blank">http://weibo.com/p/1001603821691477346388</a>

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

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

發送端方一個帶本地時間戳 T1 的 ping 封包到接收端;

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

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

示意圖如下:

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

       上面步驟的探測周期可以設為 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 ….),接收方實際接收封包的時刻和理想狀态時刻的內插補點就是抖動。如下示意圖:

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

       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 可靠到達的協定模型。各個子產品的關系示意圖如下:

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

       傳輸階段有幾個協定,一個測試量 RTT 的 PING/PONG 協定、攜帶視訊幀分片的資料協定、資料回報協定和發送端同步糾正協定。其中資料回報協定是由接收回報給發送方的,攜帶接收方已經接收到連續幀的封包 ID、幀 ID 和請求重傳的封包 ID 序列。同步糾正協定是由發送端主動丢棄發送視窗緩沖區中的封包後要求接收方同步到目前發送視窗位置,防止在發送主動丢棄幀資料後接收方一直要求發送方重發丢棄的資料。示意圖如下:

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

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

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

       前面我們提到 MTU 和視訊幀大小,在 1080P 下大部分視訊幀的大小都大于 UDP 的 MTU 大小,那麼就需要對幀進行分片,分片的方法很簡單,按照先連接配接過程協商後的 MTU 大小來确定分片大小(确定分片大小的算法在 MTU 小節已經介紹過),然後将 幀資料按照分片大小切分成若幹份,每一份分片以 segment 封包形式發往接收方。

       重傳比較簡單,我們采用 pull 方式來實作重傳,當接收方發生丢包,如果丢包的時刻 T1 + rtt_var&lt; 接收方目前的時刻 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 &gt; 擁塞門檻值 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 &lt; new_id,那麼可能發生丢包,就會将 [max_id + 1, new_id -1] 區間中所有的 ID 和目前時刻作為 K/V 對加入到丢包管理器當中。如果 new_id &lt; 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 狀态表示緩沖區進入播放狀态,播放子產品可以從中取出幀進行解碼播放。我們來介紹下這兩個狀态的切換關系:

當緩沖區建立時會被初始化成 waiting 狀态。

當緩沖區中緩沖的最新幀與最老幀的時間戳間隔 &gt; cache timer 時,進入 playing 狀态并更目前時刻設成播放絕對時間戳 play ts。

當緩沖區處于 playing 狀态且緩沖區是沒有任何幀資料,進入 waiting 狀态直到觸發第 2 步。

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

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

       Prev_play_ts + (frame_ts – prev_ts) &lt; curr_play_ts 且這一幀資料是所有的封包都收齊了

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

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

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

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

關于 netem 可以通路它的官網:

<a href="https://wiki.linuxfoundation.org/networking/netem" target="_blank">https://wiki.linuxfoundation.org/networking/netem</a>

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

       我們利用 Linux 來做一個路由器,伺服器和收發端都連接配接到這個路由器上,伺服器負責用戶端的登記、資料轉發、資料緩沖等,相當于一個簡易的流媒體伺服器。Sender 負責媒體編碼和發送,receiver 負責接收和媒體播放。為了測試延遲,我們把 sender 和 receiver 運作在同一個 PC 機器上,在 sender 從 CCD 擷取到 RGB 圖像時打一個時間戳,并把這個時間戳記錄在這一幀資料的封包發往 server 和 receiver,receiver 收到并解碼顯示這幀資料時,通過記錄的時間戳可以得到整個過程的延遲。我們的測試用例是用 1080P 碼率為 300KB/S 視訊流,在 router 用 netem 上模拟了以下幾種網絡狀态:

環路延遲 10m,無丢包,無抖動,無亂序

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

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

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

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

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

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

如何實作1080P延遲低于500ms的實時超清直播傳輸技術一、視訊編碼那些事二、網絡傳輸品質因素三、傳輸模型四、測量五、坑Q&amp;A參考閱讀

       從上圖可以看出,如果網絡控制在環路延遲在 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的實時超清直播傳輸技術/

<a href="http://windrunnerlihuan.com/2016/06/06/%E7%A7%BB%E5%8A%A8%E7%9B%B4%E6%92%AD%E6%8A%80%E6%9C%AF%E7%A7%92%E5%BC%80%E4%BC%98%E5%8C%96%E7%BB%8F%E9%AA%8C/">移動直播技術秒開優化經驗(含PPT)</a>

<a href="http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&amp;mid=2653547390&amp;idx=1&amp;sn=c241c46b7c9eba70cd0324117835270a&amp;scene=21#wechat_redirect" target="_blank">揭秘百萬人圍觀的Facebook視訊直播</a>

繼續閱讀