視訊直播秒開背後的技術與優化經驗

迅達雲SpeedyCloud發表於2017-04-10

視訊是如何播放的

視訊要播放它肯定是有視訊資料,把視訊資料放到編碼器,然後編碼器把這個視訊資料解碼出來,解成圖片,然後播放到顯示器上,這是一個基本的播放流程。一般來講,大家現在主流的用H.264編碼。對於H.264編碼來說,我們會有三個不同的幀,所謂幀是什麼呢?就是你看到的每一個影象。我們看到動態的視訊,大家知道電影最開始用膠片拍的時候,每秒是25幀,是每秒25個圖片在切換。對於H.264來講,我們常見的有I幀,P幀,和B幀。

視訊直播秒開背後的技術與優化經驗

1.I幀,I-Frame也有人會叫Inter Frame,那麼它的意義是什麼?

它是一個自描述幀,你可以理解為它就類似一個jpg圖片,它裡頭所有的資料,你解出來之後,它就是一整張圖片。

無其他幀引用,它不需要去做前置和後置的引用。

它壓縮比是最小的,因為它要包括整個圖片所有的資料在裡頭。

2.P幀,P-Frame也就是說預測幀,它的預測幀是怎麼回事呢?大家有沒有用過版本管理軟體,比如gitSVN,這樣可能大家會比較好理解,P幀就是保留變的部分,不變的部分你去上一個或者幾個幀裡面找就行。P幀只是負責向前引用,也就是任何一個P幀,它只看它往前的這些幀的資料。P幀的好處是什麼呢?因為它只存一些變化資訊,所以它大概的壓縮比是I幀的50%。這個資料哪來的?大家可以去翻一下維基百科,那裡會有一些介紹。

3.B幀,B-Frame,前後雙向引用預測。

B幀比較特別,它要引用前面P幀某一部分的影象資料同時B幀後面的資料也會引用,這個是B幀的特點,它要引用前面的資料,也要引用後面的資料。那麼它的優勢就是壓縮比比P幀還大,大概是I幀的25%,也就是我們B幀用的特別多的話,它會把視訊的大小降的比較低,因為它的壓縮比更大一些。

I幀,B幀,P幀它是怎麼組成一個視訊流呢?我們管這個東西叫Group Of Picture,簡稱叫GoP

視訊解碼器,看到GoP它是怎麼放呢?那很簡單,編碼器會有一個緩衝,然後它會保留從I幀開始,當然現在說是I幀,其實這個I幀還有個特殊的型別。從I幀開始,他會把資料快取到解碼器的Buffer裡,當他遇到下一個P幀,或者再下一個B幀的時候,它會從它Buffer裡找到它之前引用的那個幀,然後把這個資料解出來,最終播放到顯示器上。那麼緩衝區開始的第一個幀肯定是I幀,這個毋庸置疑。另外還有一個比較特別的點,B幀和P幀並不會只引用當前GoP裡的幀,他可能會往前去引用上一個GoP中的幀。既然它有這麼一個特性的話,我們什麼時候去清空這個緩衝區呢?這裡就要介紹一個新的概念,叫IDR幀。

IDR

IDR幀是I幀,但I幀並不一定是IDR幀,所謂IDR幀是什麼?它就是拿到這個幀之後,播放器可以直接從這個幀開始往後播放,它保證後面的P幀和B幀的引用不會跨越這個IDR幀,那麼看到IDR幀,編碼器就可以把當前的Buffer清空,從當前這IDR幀開始解碼往Buffer裡邊放,後續幀就可以從Buffer裡的資料引用,然後解碼,也就是說編碼器可以從任何一個IDR幀開始解碼。大家可以聯想到,當我播放一個視訊檔案的時候,我可以拖動,但是我拖動的任何一個點,它肯定是一個IDR幀,當然它也是I幀,但是並不一定說每一個I幀我都能讓它作為一個拖動的點。

IDR幀有時也有它不太學術的叫法:關鍵幀。在做編解碼程式的時候,我們可能會看到FFmpeg的資料結構裡會標著PTSDTS,那麼PTSDTS是什麼呢?

PTSDTS是什麼

視訊直播秒開背後的技術與優化經驗

PTSPresentation Time Stamp也就說這個幀什麼時候會放在顯示器上;DTS就是Decode Time Stamp,就是說這個幀什麼時候被放在編碼器去解。那麼如果全是I幀和P幀,PTSDTS都是單調遞增的,那麼如果我們有B幀,會出現什麼情況?因為大家都知道,對於B幀來講,它會引用前面的幀和後面的幀。

我們看這個例子,就是當B幀進來的時候,因為它要引用後面的P幀,也要引用前面的I幀,可以看到DTS的順序,一三四二,然後PTS順序,一二三四。B幀它會根據它編碼時候的特性,它會自動的把它的DTS時間戳往後挪,把它引用的幀先放到前面去,等它引用的幀解完了,資料解完了之後,才會把B幀去解,否則的話,我先把B幀放進去,它引用後面的P幀,P幀的資料還沒有,B幀解不出來。所以說這點上給大家講一下,B幀,P幀,I幀它們整個放在一個視訊流裡面,它的解碼順序和編碼順序,當然後續的話,我們可能會根據這個有一些開放性的思考。

為什麼直播會等待

對於直播來講,它是一個流,它不像點播,大家都從0秒開始,任何一個視訊檔案,0秒第一個幀肯定都是關鍵幀。那麼對於直播來講,我是一個隨機的時間點接到這個視訊流進行播放,那麼我接入的這個時間點的幀有可能拿到的第一個幀的資料是I幀,也有可能是B幀,也有可能是P幀。這是一個隨機的。在這種情況下,我們大概率會出現一個黑屏的狀態。因為我拿到的是個P幀,對於P幀來講,解碼器面那個Buffer是空的,它不知道這個P幀如何進行解碼,所以它只能丟棄這個幀。

對於直播來講,我一秒鐘的幀數是固定的,只能等到我下一個關鍵幀到來的時候,我才能開始去播放。當然正好趕巧了的話,接入那瞬間得到的資料正好是個I幀。就可以達到秒開的效果。

關鍵幀緩衝如何工作

視訊直播秒開背後的技術與優化經驗

其實是在cache伺服器上,它會去預先解一下這個幀,然後去看它到底是個I幀,還是個B幀,還是個P幀,當它發現是I幀的時候,它會放在它的程式的記憶體裡頭,當你每一次開啟這個視訊流的時候,cache伺服器會把記憶體中的I幀傳送給客戶端比如當前播放到了P幀,那我把P幀前面的I幀和P幀全波放到cache的記憶體裡,然後當客戶端接入之後先把記憶體裡的資料傳送給客戶端解碼器,然後再從這個B幀往後給。對於這個解碼器來講,它很舒服,它接到第一個資料流的第一個包肯定是I幀,那麼它就可以直接播放了。

如何去做到秒開?

視訊直播秒開背後的技術與優化經驗

沒有什麼好的方法,任何人做這種事時候都是一個笨的方法,就是去看文件,沒有什麼捷徑。大家可以翻閱FLV視訊檔案格式文件,它會告訴你package裡頭,它任何一個Video裡的package有一個Video TagHeader,對它是有一個Frame TypeFrame Type如果把它解出來,它是1的時候,它管這個叫key Frame,我們們看後面這個AVCa seekable Frame你可以理解為它是個IDR幀,它並不一定就是I幀。

幾個問題

開放性的解決問題,GoP Cache是從當前的這個GoP幀開還是從上一個GoP開始?

這個問題比較有意思的是我從上一個GoP放的話,我拿過來的肯定是直接可以放了。因為有時我也預見過一些比較特殊的編碼,會導致我從當前這個GoP的第一個I幀拿播放器放出來,我這樣可能會提高編碼器的相容性,但是它會有一個問題,就是如果GoP開的特別大的話,那麼我的延時自然而然就會上去。因為我上一個GoP如果是十秒,等於說我拿的是十秒之前的資料,也就是你看到的是10秒之前他說的話,他做得表情,他做的動作。那麼我從當前這個GoP開始,它肯定是有一定的延遲,但是不會大到超過你整個GoP。對於視訊直播來講,我們GoP size多少合適,換一句話講如果GoP size設成0,所有都是I幀,不存在任何GoP cache問題,但是位元速率來講會很高,因為所有的都是I幀,我們知道它壓縮比會比較低,那麼反過來,就是我需要設一個GoPSize是多少呢?

視訊直播的GoP Size設定成多少合適?

一般來講,對於手機直播,一到兩秒可能是比較合適的,因為它本身的GoP時間也不會很長,我這邊緩衝,一旦出現問題大概一到兩秒這個視訊也能出來。有一個不太好的地方就是它位元速率會稍微高一些,也就說同樣的東西,如果我把GoP改成十秒,我可能是500K,但是我改成一秒,有可能變成一個六七百K的樣子,這個還是跟編碼有關係,具體的比例是多少,可能跟實際相關。

另外如果是點播的話,不關心首屏開啟時間,只要是客戶端下來速度快,CDN給力,那麼我可能要求更小的範圍。告訴大家一個實踐過程中得出的結果,大家用過OBS?比如說做主播的話,大家用OBS會比較多,OBS它有一個問題就是它預設的話,如果你不調它的特性,GoP就是10秒,10秒的意思就是說GoP size。如果比較點背的話,看的是10秒之前的,如果是比較大的話,它的位元速率,碼流的大小會小點,但是延遲會稍微高一些,CDN開了幾個cache,有些情況下,我們也可以做些轉碼,強行把它的GoP size壓小,整個CDN層面上加一個轉碼的話,它可能會增高這個延遲,這塊一個開放性問題,大家可以根據自己的場景去思考,這個GoP Size配成多大比較合適。

你在視訊直播中到底用不用B幀?

我有時候在搜相關的資料,也有做直播的不用B幀,所以這一塊我並沒有什麼結論,就是說給大家一個點,讓大家去想一想。鑑於B幀之前看的DTSPTSPPT,也知道B幀在解碼的時候,它是要打亂每一個幀傳入解碼器的順序,如果丟包或者一些特殊情況,它可能會影響解碼器的執行的特點

一個數學故事

我不知道有多少人聽說過這個事,就是1943年以前,二戰的時候,因為英國被德國打的都已經找不著北了,他希望美國在後面支援英國,然後去做一些物資上的支援,那麼就會有很多的商船,運輸船把它的物資源源不斷的從美國送到英國,德國人反制方式是什麼?它的狼群潛艇在大西洋裡逛。對於軍事學家來講,他們可能想不出來一些什麼好的辦法,他們就請來一些數學家問我這個商船怎麼開比較合適,能保證一個是我的損失最小,另外一個我物資運送最大,碰到狼群的次數越低。這些數學家根據概率統計的概率論,然後得出一個結論,就是一定數量的船隊編隊規模越小,編次流越多與敵人相遇的概率就越大,換句話來講,就是我這個船隊肯定是攢足了,比如說我原來有一百艘船的貨運量,那麼我每一次發一艘船過去,那麼我可能遇到狼群的概率會大一些,但是我把這一百艘船變成一個編隊,然後我把我的護航編隊做到足夠好,我把我這一群送過去,我碰到的狼群概率很低的情況下,我可能效率更高一些。

資料包與網路抖動之間的鬥爭

我們從這個數學故事發散去考慮一個問題,我們把視訊的資料保管好,運維的資料保管好,當成我們的運輸艦,把網路走動,丟包,我們想成是一個狼群,就是德國的潛艇在整個大西洋上跑。我們就可以根據這個數學結論去考慮一個特殊的點,我們的發包節奏是什麼樣的。

因為我發一個包,比如說我發出一個資料包過去,你可以理解為,我從美國發了一批貨運船,帶了一行護行艦隊去往英國,那麼我們到底是有資料就往外發,還是我們攢一波資料之後往外發?這個就我剛才說的,你發包頻率應該是有講究的,看怎麼去發比較合適。

另外,可不可以換一種思路,使用類似TCP慢起動這樣的演算法,我最開始為了保證首屏時間,我以最快的發包速率往上發,等我發到一定的程度的時候,換成慢包率傳送,把時間拉長一點,拉倒一個可以接受的範圍,然後逐漸的去調整這個東西,當然這可能是比較傳統的,樸素的方式。最終的效果呢大家還得自己去根據這個特性自己想,這也是一個開放性的問題。

相關文章