Walker.Xuhtml
短視頻無疑是2017年最火的事情之一,套用業內術語就是很大的風口之一。android
短視頻也確實頗有魅力,可能迎合了人們時間碎片化下的精神娛樂需求,或者如今追求「短平快」的大環境,我也有點短視頻中毒,沒事常常光顧某幾個短視頻APP,以致於冷落了某頭條和某易新聞基本不多點開了,這些時間加起來holy bible估計都能讀好幾遍了。固然這是一篇技術文章,其餘心理學,社會學問題,產品問題就不在這裏討論了,咱也沒那個水平。緩存
言歸正傳,咱們也推出了短視頻相關的產品。
在短視頻的體驗中,起播速度無疑是最影響體驗的指標之一,由於短視頻很短,十幾秒到幾分鐘不等,若是一個十幾秒的視頻,加載時間都要3秒,確定是一個很壞的體驗;因此在產品定義之初,起播速度就設定了控制在1秒左右,大部分在1秒內,也就是業內說的「秒播」,這須要對播放流程進行優化。
艾瑪,終於繞到主題上了。服務器
如上圖所示,移動設備的播放器經過某個視頻url的域名,經過DNS服務請求到IP地址,經過這個IP地址與視頻服務器創建TCP鏈接,而後在鏈接之上創建http協議,最終請求到數據,給到播放器進行解析音視頻解碼顯示,用戶看到畫面聽到聲音,一個起播流程結束了。網絡
圖中藍色的部分是能夠優化的,但因爲實際狀況是,flyme只在客戶端接入了內容,內容都是放在CP的服務器上的,雖然有優化空間可是flyme這邊優化不到,但在後邊咱們也會介紹有哪些優化空間能夠操做。這個是行業現狀,不過能夠多接入幾家內容選擇豈不是更好。
圖中灰色部分是不能優化的,在流程上沒有優化空間,並且這部分容易受到網絡狀況的影響,因此咱們後續提到的優化,是基於大多數normal的網絡狀況的,雖然部分邏輯對極端網絡狀況也適用,可是這不是咱們討論的重點。
圖中綠色的部分是能夠優化而且在項目現實中能夠實施的。框架
DNS請求包會先發到本地DNS服務器,若是查不到,會遞歸到根域名服務器,這個過程是比較耗時的,固然若是你請求過了,或者期間有其餘人請求過相同的域名,那域名服務器就會有緩存,再次請求的時候就很快了;可是通常緩存的週期很短,須要有人不停地請求才能保持更新,因此具備很大的不肯定性。socket
有兩方面能夠作
1.注意請求使用的IP協議版本,作播放的確定都繞不過ffmpeg,在ffmpeg裏爲了兼容性,DNS請求的IP協議版本設置爲AF_UNSPEC,這樣在請求的時候會先請求IPv6的地址,若是沒有再請求IPv4的地址,是很保險,可是在實際的項目中,沒有IPv6的地址,形成一直遞歸到根域名服務器也查不到IPv6地址,極大的浪費了時間,可使用AF_INET指定請求IPv4地址,節省一半以上的時間,首次請求或緩存過時後請求,耗時大概在大幾十毫秒到100毫秒左右,能夠經過監測getaddrinfo函數的耗時的到。tcp
hints.ai_family = AF_INET;
getaddrinfo(hostname, portstr, &hints, &ai);
2.預置或預解析域名IP地址,100毫秒仍是很大的一筆時間對於秒內播放來講,這個方案就是提早把域名解析出來,用的時候直接使用IP地址,可是這種方案有侷限性,可能適合特定的音視頻直播,對於短視頻播放地址比較多樣來講操做起來有必定難度,並且還存在CP切流和更換接入CP的狀況,因此這個100毫秒目前只能先放在這裏了。函數
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));
在播放端,一開始並不知道要播放的視頻的相關信息,好比封裝格式,分辨率,音視頻編碼等信息,須要先讀一段數據進來,再對這段數據進行探測,得出相應的信息,而存放這段探測數據須要一個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;
一樣是探測的流程,一開始播放端並不知道這段數據是什麼格式,須要根據本身支持的格式經過探測得出一個分數,而後依據這個分數給出相應的格式,相似於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,這是爲了播放流暢性和音視頻同步的須要,尤爲是在網絡不穩定或較差的狀況下,這個緩衝buffer顯得尤其重要。
通常這個緩衝buffer有按照幀數設置的,也有設置爲1-2秒的,也有設置爲3-5秒的,由於通常的播放好比在線電影,電視劇考慮的是整個播放過程幾十分鐘,甚至幾個小時的體驗,在開始緩衝個幾秒是能夠接受的,可是在短視頻的場景下這是不可接受的。
策略性的優化,保證視頻第一時間輸出,把緩衝機制移到首屏播放以後,固然這裏也要照顧到音頻,保證音視頻的同步,有些取捨要作。
這裏實際上是很重要的一個環節,Android的nuplayer框架設計上受限於這些因素起播速度就遠達不到要求,又搞了個exoplayer,可是不作二次開發exoplayer仍是不能知足需求。
這個須要根據本身的播放框架來作設計,咱們使用的是自研的Normandie播放框架,該框架已經上線將近兩年,支持了多個音視頻的業務,這裏就不詳細展開了。
分辨率這個不難理解,若是視頻文件的分辨率很高,那它一幀的數據會很大,相應的傳輸時間就會變長。因此在內容產生的時候選擇合適的分辨率錄製或轉碼爲合適的分辨率也是爲播放端的負載在考慮,移動端720P左右足夠了對於我的秀之類的短視頻,內容聚合類的短視頻分辨率能夠更低。
QP指的是圖像質量,同一個分辨率的圖像能夠有不少級的QP,它是跟編碼密切相關的,QP越高圖像質量越高碼率也越高,相應的傳輸時間也就越長。一樣,不是QP越高越好,對於不是不一樣場景快速切換的720P視頻,3M和5M碼率的區別不大。選擇合適的QP在畫面質量和傳輸上找到一個平衡。
I幀位置,指的是視頻I幀在視頻文件開頭的位置,播放器爲了防止花屏之類的問題出現,通常在開始播放或seek時都會找到第一個I幀進行解碼,通常視頻文件一秒有25-30幀,很明顯I幀放在第一幀和放在最後一幀對秒播是有影響的。
根據實際狀況,在產品服務鏈條中選擇合適的分辨率/QP。
把I幀放在文件開頭第一幀的位置。
若是在起播過程當中發生了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。
CDN節點部署,路由策略
緩存仍是拉流
都會對延時產生影響
server進行相關的優化。
協議耗時,好比TCP的握手機制等,在穩定的網絡下耗時基本是固定的,在較差網絡下耗時會較長
CDN骨幹網絡的部署能夠改善這種狀況。
協議耗時,在穩定的網絡下耗時基本是固定的,在較差網絡下耗時會較長
CDN骨幹網絡的部署能夠改善這種狀況。
對於作技術的同窗來講,並無多麼高深的東西,須要作的只是抓住問題的核心,把一個大問題拆解成一個個小問題,而後完成一個個小目標,最後水到渠成。其實不少事情都是這樣的,你就把它當作一個技術問題,把它拆解成一個個小問題,完成一個個小目標,好比先賺它1個億。
咱們也完成了一個小目標,經過優化大多數的短視頻起播速度都落在1秒內,達到了業內的「秒播」水平。
追求體驗的極致,一直是咱們追求的目標,而每一個功能每一個細節體驗的極致累加起來就是最終產品的極致。
1.經過上邊的分析,咱們認爲還有至少100~200ms的優化空間。
2.另外咱們下一步計劃用大數據來監控沒有落在1秒內的狀況,根據數據分析報告推進對這種狀況的優化。
這是咱們下一個小目標。
很早以前錄的一個視頻,在線播放分辨率960x720的短視頻,這算是比較高的分辨率了,以快手,抖音等爲表明的我的秀類差很少是這個分辨率,抖音可能低一些;以西瓜等爲表明的內容聚合類的,分辨率要更低些大多640x360或480P。
http://v.youku.com/v_show/id_...
http://v.youku.com/v_show/id_...