1. 程式人生 > >優化短視訊實現“秒播”技術分析

優化短視訊實現“秒播”技術分析

短視訊無疑是2017年最火的事情之一,套用業內術語就是很大的風口之一。

短視訊也確實很有魅力,可能迎合了人們時間碎片化下的精神娛樂需求,或者現在追求“短平快”的大環境,我也有點短視訊中毒,沒事經常光顧某幾個短視訊APP,以至於冷落了某頭條和某易新聞基本很少點開了,這些時間加起來holy bible估計都能讀好幾遍了。當然這是一篇技術文章,其他心理學,社會學問題,產品問題就不在這裡討論了,咱也沒那個水平。

言歸正傳,我們也推出了短視訊相關的產品。
在短視訊的體驗中,起播速度無疑是最影響體驗的指標之一,因為短視訊很短,十幾秒到幾分鐘不等,如果一個十幾秒的視訊,載入時間都要3秒,肯定是一個很壞的體驗;所以在產品定義之初,起播速度就設定了控制在1秒左右,大部分在1秒內,也就是業內說的“秒播”,這需要對播放流程進行優化。
艾瑪,終於繞到主題上了。

談具體的優化之前我們先看下一個短視訊播放的大概流程是怎樣的?

再通過拆解流程,找到可以優化的模組或點,最終才能連成線,並給出優化方案。

如上圖所示,移動裝置的播放器通過某個視訊url的域名,通過DNS服務請求到IP地址,通過這個IP地址與視訊伺服器建立TCP連線,然後在連線之上建立http協議,最終請求到資料,給到播放器進行解析音視訊解碼顯示,使用者看到畫面聽到聲音,一個起播流程結束了。

為了更好的發現可優化的地方,我們對上圖進行了拆解,結合實際情況給出了下邊這張圖

圖中藍色的部分是可以優化的,但由於實際情況是,flyme只在客戶端接入了內容,內容都是放在CP的伺服器上的,雖然有優化空間但是flyme這邊優化不到,但在後邊我們也會介紹有哪些優化空間可以操作。這個是行業現狀,不過可以多接入幾家內容選擇豈不是更好。
圖中灰色部分是不能優化的,在流程上沒有優化空間,而且這部分容易受到網路情況的影響,所以我們後續提到的優化,是基於大多數normal的網路情況的,雖然部分邏輯對極端網路情況也適用,但是這不是我們討論的重點。
圖中綠色的部分是可以優化並且在專案現實中可以實施的。

下面對這些專案逐一進行介紹。

Domain name: 域名解析

耗時原因:

DNS請求包會先發到本地DNS伺服器,如果查不到,會遞迴到根域名伺服器,這個過程是比較耗時的,當然如果你請求過了,或者期間有其他人請求過相同的域名,那域名伺服器就會有快取,再次請求的時候就很快了;但是一般快取的週期很短,需要有人不停地請求才能保持更新,所以具有很大的不確定性。

解決方案:

有兩方面可以做
1.注意請求使用的IP協議版本,做播放的肯定都繞不過ffmpeg,在ffmpeg裡為了相容性,DNS請求的IP協議版本設定為AF_UNSPEC,這樣在請求的時候會先請求IPv6的地址,如果沒有再請求IPv4的地址,是很保險,但是在實際的專案中,沒有IPv6的地址,造成一直遞迴到根域名伺服器也查不到IPv6地址,極大的浪費了時間,可以使用AF_INET指定請求IPv4地址,節省一半以上的時間,首次請求或快取過期後請求,耗時大概在大幾十毫秒到100毫秒左右,可以通過監測getaddrinfo函式的耗時的到。

hints.ai_family = AF_INET;
getaddrinfo(hostname, portstr, &hints, &ai);
2.預置或預解析域名IP地址,100毫秒還是很大的一筆時間對於秒內播放來說,這個方案就是提前把域名解析出來,用的時候直接使用IP地址,但是這種方案有侷限性,可能適合特定的音視訊直播,對於短視訊播放地址比較多樣來說操作起來有一定難度,而且還存在CP切流和更換接入CP的情況,所以這個100毫秒目前只能先放在這裡了。

Socket cache:socket buffer

耗時原因:

TCP connection在客戶端的具體操作中基本都是通過socket實現的,在socket中有一個緩衝區的概念,傳送端先把資料寫到緩衝區,接收端資料也是先經過緩衝區,再從緩衝區讀出,移動裝置作為接收端,接收端緩衝區設定的太小,影響效率,接收端緩衝區設定的太大,會短時間內吃掉頻寬,如果頻寬不夠會引起網路傳輸問題,還會造成流量的浪費。這些都會影響首屏資料的及時送達。

解決方案:

根據實際情況調整接收端緩衝區大小,通過計算和測試資料得到一個比較合理的值。可以在ffmpeg的network和tcp裡進行調整,這是比較低層的修改了,為了通用性可以擴充套件http/tcp的options並通過ffmpeg提供的AVDictionary機制在avformat API這一層進行透傳相關設定引數。
av_dict_set(&avdictionary, “param”, “value of param”);
setsockopt(fd, SOL_SOCKET, SO_RCVBUF,&len,sizeof(len));

Probe buffer

耗時原因:

在播放端,一開始並不知道要播放的視訊的相關資訊,比如封裝格式,解析度,音視訊編碼等資訊,需要先讀一段資料進來,再對這段資料進行探測,得出相應的資訊,而存放這段探測資料需要一個buffer。這個buffer設定的太小可能導致分析不出資訊導致重新探測,設定的太大就會增加收流的時間從而影響了首屏的播放,太小太大都會引入延遲。

解決方案:

根據實際情況調整這個buffer,通過計算和測試資料得到一個比較合理的值。可以通過ffmpeg的AVFormatContext結構體的probesize和max_analy_duration把對buffer的限制透傳下去。
可以通過觀察avformat_find_stream_info這個函式來評價探測耗時。
AVFormatContext->probesize = n;
AVFormatContext->max_analyze_duration = m*AV_TIME_BASE;

Probe list

耗時原因:

同樣是探測的流程,一開始播放端並不知道這段資料是什麼格式,需要根據自己支援的格式通過探測得出一個分數,然後依據這個分數給出相應的格式,類似於android的sniff,所以如果ffmpeg設定的支援的格式越多這個探測list就越長,相應的探測時間也就越長。
而對於短視訊來說,CP的內容格式基本是確定的,基本都是MP4+H264/H265(不常見)+AAC。所以很多格式的探測是不必要的。

解決方案:

對於沒有用的格式在ffmpeg build config裡移除,只保留需要的格式,比如mp4,最大限度的減小probe list。
可以通過觀察avformat_find_stream_info這個函式來評價探測耗時。
disable avi
disable asf
disalbe mkv
and so on…

Player buffer

耗時原因:

對於非直播類的播放器,業內的一般做法是都會在player內設計一個緩衝buffer,這是為了播放流暢性和音視訊同步的需要,尤其是在網路不穩定或較差的情況下,這個緩衝buffer顯得尤為重要。
一般這個緩衝buffer有按照幀數設定的,也有設定為1-2秒的,也有設定為3-5秒的,因為一般的播放比如線上電影,電視劇考慮的是整個播放過程幾十分鐘,甚至幾個小時的體驗,在開始緩衝個幾秒是可以接受的,但是在短視訊的場景下這是不可接受的。

解決方案:

策略性的優化,保證視訊第一時間輸出,把緩衝機制移到首屏播放之後,當然這裡也要照顧到音訊,保證音視訊的同步,有些取捨要做。
這裡其實是很重要的一個環節,Android的nuplayer框架設計上受限於這些因素起播速度就遠達不到要求,又搞了個exoplayer,但是不做二次開發exoplayer還是不能滿足需求。
這個需要根據自己的播放框架來做設計,我們使用的是自研的Normandie播放框架,該框架已經上線將近兩年,支援了多個音視訊的業務,這裡就不詳細展開了。

MP4 Size:解析度/QP/I幀位置

耗時原因:

解析度這個不難理解,如果視訊檔案的解析度很高,那它一幀的資料會很大,相應的傳輸時間就會變長。所以在內容產生的時候選擇合適的解析度錄製或轉碼為合適的解析度也是為播放端的負載在考慮,移動端720P左右足夠了對於個人秀之類的短視訊,內容聚合類的短視訊解析度可以更低。
QP指的是影象質量,同一個解析度的影象可以有很多級的QP,它是跟編碼密切相關的,QP越高影象質量越高位元速率也越高,相應的傳輸時間也就越長。同樣,不是QP越高越好,對於不是不同場景快速切換的720P視訊,3M和5M位元速率的區別不大。選擇合適的QP在畫面質量和傳輸上找到一個平衡。

I幀位置,指的是視訊I幀在視訊檔案開頭的位置,播放器為了防止花屏之類的問題出現,一般在開始播放或seek時都會找到第一個I幀進行解碼,一般視訊檔案一秒有25-30幀,很明顯I幀放在第一幀和放在最後一幀對秒播是有影響的。

解決方案:

根據實際情況,在產品服務鏈條中選擇合適的解析度/QP。
把I幀放在檔案開頭第一幀的位置。

MP4 MOOV box position & Http re-connection

耗時原因:

如果在起播過程中發生了http re-connection耗時肯定會增加,而且http connection的耗時基本是不可優化的,所以要避免http re-connection的發生。
但是mp4作為主流的短視訊封裝格式,它的MOOV box在檔案中的位置直接影響了會否發生http re-connection,直接點說就是MOOV box放在檔案的後面也就是MDAT box後,會產生http-reconnection,引入延時。

解決方案:

1.在上傳mp4檔案的時候把MOOV box放在前面
2.在mp4檔案上傳到伺服器之後,重新封裝,把MOOV box放到前面

如果是傳統音視訊出身的工程師應該很清楚MOOV box,這裡做下簡單的介紹。
mp4是由很多box組成的,其中MOOV box裡存放的是音視訊編碼之類的對播放很重要的資訊,先要拿到這些資訊才能播放。所以
MOOV box放在檔案的前面通過一次http請求就可以直接讀取到,解析,繼續讀取音視訊資料播放。
MOOV box放在檔案的後邊,但是播放器不知道它放在後面,需要先發起一次http請求讀開頭部分資料,發現沒有MOOV box,它會seek到檔案後邊位置,讀取MOOV box,讀完之後再seek到檔案前邊位置,從開始位置讀取音視訊幀資料播放,每發生一次seek就重新發起一次http connetion請求。
所以MOOV box放在後邊比放在前邊多了2次http connection,用時是放在前邊方案的3倍。下邊是兩個短視訊一個moov box放在了前邊,一個放在了後邊,放在了後邊的通過NmdPlayer的log可以都看到會發生re-connection,請求檔案最後一段資料來獲取moov box。

Server/CDN

耗時原因:

CDN節點部署,路由策略
快取還是拉流
都會對延時產生影響

解決方案:

server進行相關的優化。

TCP connection

耗時原因:

協議耗時,比如TCP的握手機制等,在穩定的網路下耗時基本是固定的,在較差網路下耗時會較長

解決方案:

CDN骨幹網路的部署可以改善這種情況。

Http connection

耗時原因:

協議耗時,在穩定的網路下耗時基本是固定的,在較差網路下耗時會較長

解決方案:

CDN骨幹網路的部署可以改善這種情況。

對於做技術的同學來說,並沒有多麼高深的東西,需要做的只是抓住問題的核心,把一個大問題拆解成一個個小問題,然後完成一個個小目標,最後水到渠成。其實很多事情都是這樣的,你就把它看成一個技術問題,把它拆解成一個個小問題,完成一個個小目標,比如先賺它1個億。

我們也完成了一個小目標,通過優化大多數的短視訊起播速度都落在1秒內,達到了業內的“秒播”水平。

One more thing…

追求體驗的極致,一直是我們追求的目標,而每個功能每個細節體驗的極致累加起來就是最終產品的極致。
1.通過上邊的分析,我們認為還有至少100~200ms的優化空間。
2.另外我們下一步計劃用大資料來監控沒有落在1秒內的情況,根據資料分析報告推動對這種情況的優化。
這是我們下一個小目標。

很早之前錄的一個視訊,線上播放解析度960x720的短視訊,這算是比較高的解析度了,以快手,抖音等為代表的個人秀類差不多是這個解析度,抖音可能低一些;以西瓜等為代表的內容聚合類的,解析度要更低些大多640x360或480P。

S70901-18153290
http://v.youku.com/v_show/id_XMzU0MTg3MzE0NA==.html

 

優化前(未解耦的框架android框架)

 

slow
http://v.youku.com/v_show/id_XMzU0MTg3NTU5Ng==.html

 

優化後(解耦的框架normandie+短視訊定製優化)

 

fast
http://v.youku.com/v_show/id_XMzU0MTg3NTE0NA==.html