作者:王宇(騰訊音視訊進階架構師)
自我介紹下,畢業以來加入騰訊,一直從事用戶端研發,身處網際網路公司,踏着網際網路的浪潮,一直在浪尖行走,從最早的PC QQ,到移動時代的手Q,再到騰訊物聯的嵌入式,以及最近這兩年直播領域風起雲湧,接下來主要給大家介紹下QQ視訊直播的架構及原理。
今天主要給大家介紹三個方面的内容,視訊直播的架構及基礎介紹;
直播中的一些關鍵技術:主要包括播放控制問題,性能與品質等;直播APP的整體解決方案。
首先來看下直播前背景的大緻架構,通過推流App或者第三方推流器将資料推送給背景伺服器;
伺服器将收到的資料進行轉碼發送給DC;
觀看者從OC上拉取對應的直播流進行觀看;
目前主要采用RMTP、HLS、FLV三種直播格式。接下來我們重點看下直播過程中用戶端架構。
這是直播用戶端的大體框。
左側是推流端:音視訊采集;預處理;音視訊編解碼;資料封裝網絡發送;
右側是播放端:網絡收資料;音視訊解碼;視訊渲染與音頻播放。
整個過程就是一個線性的流水化過程,流程看起來還是比較簡潔的。
視訊編碼,就是一個壓縮的過程,如何将視訊資料壓縮後便于網絡傳輸,這裡有幾個緯度:
1.每一幀資料的幀内壓縮,也就是這裡的I幀,能夠獨立還原出圖像資料,一般比較大;
2.連續的兩幀畫面之間存在相似性,參考前面一幀資料,隻記錄差異部分,這樣資料量就比較小,也就是這裡的P幀,采用的是前向預測編碼;
3.更進一步,如果參考前後兩幀資料記錄差異部分,這樣資料量會更小壓縮率最高,也就是這裡的B幀,采用雙向預測内插編碼。
麥克風采集到的原始PCM資料一般比較大,以44.1k,雙聲道為例,碼率大概有1M多,不做壓縮的話很難傳輸,音頻編碼,就是将采集到的PCM資料,根據不同的音頻格式進行壓縮,當然目的也是為了在保證音質的前提下盡量壓縮音頻資料,目前采用的是AAC來壓縮。
流媒體音視訊資料封裝格式,原始的編碼之後的音視訊資料不太利于網絡傳輸,目前有多種封裝格式:
1.RTMP協定,需要做拆包與組包,将每個資料幀拆成小分片加上協定頭發送,一般用于推流;
2.FLV格式,比較簡單每個資料幀加協定頭,一般用于播放;
3.HLS格式,将一組音視訊資料組合在一起,通過m3u8文體來關聯,相當于一個個的小檔案,适用于網頁播放。
直播的架構及基本原理就這麼簡潔,但是要做到好的體驗卻不是一件很簡單的事情,基本上幾個人花一兩個月就能做出來,但是要優化出一個好的效果需要長期的積累與打磨。
接下來主要看下直播過程中的一些關鍵問題,主要包括:首屏秒開、流暢與延遲控制、實時音畫同步等等。
當我們觀看一個直播的時候不能等半天才出畫面,體驗顯然很差,我們期望的是能夠立馬看到畫面,即首屏秒開。
首先來看下解碼過程:推流端上傳P幀,播放端拉到一個P幀,如果幀連續,能立馬解出來正常顯示;如果沒有之前的參考幀,而僅僅隻收到了一個P幀,解析不出來不能顯示,直到遇到一個關鍵幀;如果要做到資料來了就能立馬顯示,對于首次打開就必須要能收到一個I幀,那麼問題來了,服務端如何保證傳回給播放端的第一幀為I幀?
假設有一個人正在觀看,推流端上傳一個P幀,伺服器将其發送給播放端,播放端正常播放,同時伺服器将其放到一個緩存隊列裡面;有了這樣一個臨近資料的緩存隊列,當有一個新的觀看者來播放的時候,就可以直接從緩存隊列裡面取最新的一個GOP一次性傳回給觀看端,此時播放端就可以立馬解析出I幀渲染,這樣就能做到秒開。
流暢與時延控制,對播放而言,來資料就播放,這個問題應該就這麼簡單,但是..
播放有兩類需求:
1.遊戲場景下更關注流暢性;
2.主播場景下更關注低延遲;
于是就有了流暢與極速兩種模式,流暢就是緩沖播放,極速就是來資料就播放,緩存大的時候就做速播;看起來還不錯,但是..
實際使用發現流暢模式會帶來延遲大的問題,而極速模式卡頓的機率又比較高;有沒有一種更好的方案?這裡問題的根源在于網絡是抖動的,要保證低延遲的同時又能流暢播放,其實就是在流暢與低延遲之間做平衡;應該怎麼設計?
對于播放端而言,網絡來資料,自己播放,這就是一個生産消費模型,正常情況是生産多少,消費多少,但網絡抖動引發夾頓與資料堆積進而造成時延。
這裡統計過去一段時間内的資料,有兩個名額:
1.待播放資料的大小反應時延,即這裡的過去一段時間内的AvgCache;
2.過去一段時間内的網絡抖動大小決定了待播放資料抖動的大小,也即決定了卡頓,即這裡的Jitter,Jitter随時間推移而修正。
平均cache太大即延遲大,為了消除延遲就需要快速消耗掉一部分資料,這裡要消耗掉多少資料呢?
理想情況下是要調整到紅色曲線所示的,加速MinCache的資料,這樣既能保證不觸發夾頓又能做到最小延遲,但是這太理想化了,這裡需要更謹慎一些,于是做了以下調整:
1.引入最小門檻值,如圖所示最多加速到這個程度;
2.不是一次加速到最佳狀态,設定最大加速時長。
總結下,統計資料決策加速;計算調整時長,恒定加速;如此循環,就能調整到一個最佳狀态,這就是自适應播放控制政策。
這裡加速有三種政策:
1.直接丢棄部分資料;
2.短時的快速播放;
3.長時的較快速的播放,讓人察覺不到在速播。
這裡問題的根本就是要多消耗一部分資料,毫無疑問不論是丢棄、快速加速、慢速加速都是有損的,三種方案各有優劣,瞬時的大損傷好,還是長時的低損傷好?這裡還是看個人喜好,蘿蔔白菜各有所愛,最終還是要看使用者回報來選擇。既然做了速播,那不可避免會遇到音畫不同步的問題,如何解決?
音畫如何做同步?
經典的音畫同步有三種方案:音頻同步到視訊;音視訊同步到外部時鐘;視訊同步到音頻;
方案一音頻很難去頻繁的做調速,是以直接pass了;
方案二在卡頓的時候需要做時間修正,做加減速的時候保持步調一緻不是很好處理;
方案三視訊同步到音頻比較自然,視訊能夠很友善做調速處理,目前大多推薦的也是方案三,實作起來也比較簡單,接下來看下整個播放控制方案。
音視訊jitter隊列分開友善取資料,控制器根據jitter大小及目前政策來決定加速還是正常播放;
音頻按需播放,在最末端,音頻将PTS同步給視訊,視訊據此修正目前幀的播放時機,解決音畫不同步問題;
引入一個負回報機制來保證解碼後的資料不會太大;
聲音如何加速,聲音處理有變調變速,變調不變速,變速不變調,這裡采用了變速不變調算法來對聲音進行加速。
來看下實際效果,這分别是兩種損傷模型下的效果,左邊的是瞬時高損傷,右邊的是長時低損傷;可以感受下兩種的不同效果。
高分辨率下性能如何保證,360p一般都能達到比較好的性能,那麼問題來了,720P推流與播放、1080P播放性能如何保證?主要包括以下幾個方面:
1.整個系統采用多線程處理,推流與播放都是流水化的過程,每個處理流程都采用獨立線程,每秒幀率完全由單一處理過程的最大耗時決定,而不是整個流程的處理時間。
2.多核手機已經普及,采用軟編軟解能夠在手機性能達标的情況下完成高幀率編解碼。
3.目前手機基本都支援硬編硬解,對于高分辨率采用硬體編解碼,可以顯著降低CPU占用,提升性能。
4.能用GPU處理的盡量用GPU來做,如本地渲染與鏡像處理、格式轉換等。
5.采用ARM指令集優化來提升性能,比如視訊裁剪、旋轉等算法。
既然做直播SDK,那麼有問題如何定位?直播品質如何監控?
這裡問題主要包括兩大類:
1.開發測試過程中遇到的問題;
2.使用者現網問題;這裡直播品質主要包括CPU占用率、卡頓率、播放延遲、上下行帶寬等等。
使用者遇到的問題如何怎麼定位,LOG肯定是第一手資料,如何友善快捷的擷取到LOG?
這裡有一套LOG提取系統,三個步驟:擷取位址軟色、問題重制、提取log分析;這個就是我們的LOG提取系統。
為了監控直播品質,這裡做了完備的資料上報及分析系統,基本上涵蓋了各種關鍵性名額,既能反應直播的各種性能友善優化,同時也能輔助定位各種問題;這個是播放端的整體統計資料,下行帶寬、卡頓率、緩沖大小、CPU占用率,這是一個宏觀統計資料,反應了目前直播觀看端的一個品質。
有了直播品質監控,更精細一些我們可以做一些營運分析,比如說,最近我們内部對一個遊戲直播的流暢性做了一個營運統計分析,把外網使用者分成了三組,通過配置不同的播放端參數來統計品質,最後我們得出了幾個比較有意思的結論:
這是對直播推流端做的一個品質評估,根據一些名額對每項進行打分,然後給出一個綜合得分來評估目前直播推流端的品質。
這個是直播背景的整套大體架構。
上面主要是完成視訊的推流與播放,包含了接入叢集,計算叢集及CDN加速網絡;
下面主要是帳号、消息等基礎服務,及社互動動系統,我們使用這些背景服務建構了小直播的App。
小直播用戶端App及業務背景源碼對客戶開放,提供了直播App的整套內建解決方案,基于此可以很友善的完成一款直播App的開發。