本文首發(fā)地址:開源實踐網(wǎng):RTMP直播累積延時和網(wǎng)絡抖動的優(yōu)化(卷一原理)
問題一 累積延時
對于交互性要求較高的直播業(yè)務來說取董,采集推流端和觀看端的延時太高是不可接受的。在 直播協(xié)議的選擇:RTMP vs. HLS 一文中提到了采用 RTMP 協(xié)議做直播業(yè)務,一般可以將延時控制在 1-3s 或者更低喂柒。但是如果在直播中發(fā)生卡頓婴程、播放暫停等情況時庄敛,也會不斷積累推流端和觀看端的延時伟叛。這種累積延時要怎么優(yōu)化呢略就?
一兄墅、優(yōu)化切換前后臺帶來的累積延時
在直播場景中踢星,有一種情況是切換前后臺造成累積延時。這里舉個例子:在前臺時隙咸,直播視頻在播放沐悦,然后退到后臺,此時暫停播放器五督,音視頻數(shù)據(jù)繼續(xù)緩存藏否,當回到前臺時,繼續(xù)從剛才退出時的視頻流數(shù)據(jù)開始播放充包,而實際的直播現(xiàn)在已經(jīng)不在這個時間點了秕岛。這段前后臺切換的時間里,就積累了一段延時误证。
對于這種延時改怎么處理呢继薛?
- 第一種方案是播放器采用視頻同步音頻的策略,然后退到后臺時保持音頻繼續(xù)播放(在 iOS 平臺需要開啟 App 的 Background Audio 能力來配合)愈捅。這樣可以保持音頻一直播放不產(chǎn)生延時遏考,而當回到前臺時,視頻同步音頻直接切換到最新時間戳即可蓝谨。
- 第二種方案是回到前臺時重新刷新直播灌具,重連直播流,這樣即可消滅累積延時譬巫。但是這種方案的問題是重連直播流的過程需要一定的時間咖楣,這樣回到前臺時會有卡頓,或者出現(xiàn)黑屏芦昔,尤其是當你的首屏加載優(yōu)化不夠時诱贿,這個卡頓或黑屏時間會較長。所以這種方案在你的首屏加載優(yōu)化的比較好的情況下可以采用。此外珠十,你可以退到后臺時截取視頻當前幀貼圖到直播間上料扰,從而當切回前臺時,防止黑屏焙蹭,優(yōu)化體驗晒杈,實踐效果還是不錯的。
二孔厉、優(yōu)化卡頓帶來的累積延時
在理想的情況下:網(wǎng)絡狀況良好拯钻;采集推流端、流媒體服務器撰豺、播放端均吞吐正常無阻塞说庭,可以不配置緩沖區(qū)。這時候推流端到播放端的延時將會很小郑趁,基本上就是網(wǎng)絡傳輸?shù)暮臅r刊驴。
但是在實際情況中,我們多多少少會遇到網(wǎng)絡不佳或網(wǎng)絡抖動的情況寡润,在這種網(wǎng)絡環(huán)境下捆憎,如果沒有緩沖策略,直播將發(fā)生卡頓梭纹。為了解決卡頓躲惰,通常會根據(jù)具體情況在采集推流端、流媒體服務器变抽、播放端增加緩沖策略础拨,而一旦發(fā)生緩沖,就意味著推流端到播放端的延時绍载。當卡頓情況多次出現(xiàn)诡宗,這樣的延時就會累積。
此外击儡,從 RTMP 協(xié)議層面上來講塔沃,累積延時本身是它的一個特征,因為 RTMP 是基于 TCP阳谍,所以不會丟包蛀柴,在網(wǎng)絡情況不佳的情況下超時重傳策略、緩沖策略等自然會帶來累積延時矫夯。
所以鸽疾,優(yōu)化卡頓帶來的累積延時首先是要優(yōu)化整個直播鏈條的網(wǎng)絡狀況去減少卡頓。從這個角度出發(fā)训貌,我們可以采用的策略包括:
- 使用 CDN 分發(fā)網(wǎng)絡制肮。
- 合理采用 CDN 邊緣節(jié)點推流。
- 推流端、播放端使用 HTTPDNS 選擇網(wǎng)絡狀況最好的節(jié)點接入弄企。
- 推流端實現(xiàn)碼率自適應策略,在網(wǎng)絡狀況不佳的情況下区拳,降低推流碼率來降低上行帶寬壓力拘领。
- 流媒體服務器提供多檔位直播流服務,與此同時樱调,播放端實現(xiàn)直播流多檔位切換策略约素,在網(wǎng)絡狀況不佳的情況下,切換到低檔位直播流來降低下行帶寬壓力笆凌。
問題二 網(wǎng)絡抖動的優(yōu)化
除了這些外圣猎,我們還可以優(yōu)化各端的緩沖策略來降低累積延時。直播鏈條各端的緩沖區(qū)通常都是為了防止網(wǎng)絡抖動以及端上性能抖動產(chǎn)生卡頓乞而,但是各緩沖區(qū)的數(shù)據(jù)越多送悔,通常也意味著累積延時越大。所以在各端上爪模,我們還可以嘗試這些策略:
- 在「卡頓」和「累積延時」這兩項體驗指標上尋找一個平衡點欠啤,在各端設(shè)置合適的緩沖區(qū)大小。
- 在各端實現(xiàn)一些丟幀策略屋灌,當緩沖區(qū)超過一定閾值時洁段,開始丟幀。
- 在播放端的緩沖區(qū)過大時共郭,嘗試斷開重連祠丝。
- 服務端推流可以分高,中除嘹,低三種質(zhì)量的流写半,根據(jù)拉流端網(wǎng)絡質(zhì)量動態(tài)的切換
問題三 如何優(yōu)化弱網(wǎng)下推流端
推流端是整個數(shù)據(jù)的起點,如果推流端因為網(wǎng)絡原因推到服務端的視頻是卡頓的尉咕,那么接收端的視頻肯定也是卡頓的污朽,所有推流端出現(xiàn)問題,一般都是批量問題龙考。幸運的是蟆肆,在具體的業(yè)務中,推流端一般能夠保證設(shè)備性能和網(wǎng)絡質(zhì)量晦款。但如果不能保證炎功,在弱網(wǎng)環(huán)境下,為了保證推流的流暢性和低延時缓溅,我們需要弄一些策略使推流更流暢蛇损,一般有如下策略:
(1) 降幀率
網(wǎng)絡發(fā)送層發(fā)現(xiàn)發(fā)送速度過慢,反饋給camera采集模塊,通過抽幀的方式來降幀率淤齐,降低整體發(fā)送的碼率
(2) 降編碼碼率
網(wǎng)絡發(fā)送層發(fā)現(xiàn)發(fā)送速度過慢股囊,反饋給視頻編碼模塊,通過動態(tài)調(diào)整編碼器碼率更啄,來減小視頻編碼的輸出碼率稚疹。Android上的MediaCodec在4.3+版本上都是支持動態(tài)調(diào)整碼率的; x264以及ios上的VideoToolbox也是支持的
(3) 使用H.265編碼推流
使用H.265編碼推流可以節(jié)省40%帶寬祭务,可惜的是并不是所有手機都支持用H.265編碼格式播放内狗,所以需要針對手機型號進行推流。
(4) 丟幀
上面兩種方法义锥,反射弧比較長一點柳沙,它們從pipeline上來看,操作的模塊比較前面拌倍,生效比如慢一點赂鲤,取決于各個模塊間的緩沖的大小。丟幀策略的話直接作用于pipeline的末端柱恤,立即生效蛤袒。
RTMP發(fā)送線程循環(huán)從一個緩沖隊列里面讀取幀,然后發(fā)送膨更。為了方便作丟幀處理妙真,encoder采用baseline的profile,這樣荚守,緩沖隊列里面只存在I幀和P幀
如下 mVideoCount是這個緩沖隊列里面視頻幀的個數(shù)珍德,mVideoCapbility是這個緩沖隊列總的大小
if(mVideoCount >= mVideoCapbility/2){
dropFrame(0.3f);
} else if(mVideoCount >= mVideoCapbility/3) {
dropFrame(0.1f);
} else if(mVideoCount >= mVideoCapbility/4) {
dropFrame(0.05f);
} else if(mVideoCount >= mVideoCapbility/5) {
dropFrame(0.02f);
}
dropFrame的參數(shù)是丟幀的百分比,意思是相臨兩個gop之前丟掉的P幀的百分比矗漾。為了播放端不花屏锈候,從一個GOP的最后面的P幀開始丟