本文闡述了網易新聞客戶端在段視頻性能方面的優化思路、過程及結果,作到了小投入高產出算法
本文做者:Re(網易傳媒技術團隊)緩存
在過去的2018年,短視頻開始佔據互聯網愈來愈多的流量。爆紅的短視頻社交應用自沒必要說,經過短視頻來獲取新聞資訊、觀看音樂專輯、甚至在電商平臺觀看商品介紹也已經成爲愈來愈多用戶的選擇。安全
網易新聞客戶端做爲一個資訊類產品,也在逐漸增長視頻形式的內容來知足用戶的需求。而隨着視頻的內容的增長,視頻體驗也變得愈來愈重要。若是視頻的觀看給用戶形成了困擾,那麼就會增長流失用戶的風險。服務器
爲此咱們在過去的一年中對視頻的性能進行了持續的優化,雖然期間遇到過不少困難,但也所以收穫了寶貴的經驗。咱們但願將這個過程當中的經驗和思考分享出來,一方面是做爲一個階段性的總結,另外一方面也但願能對你們有所幫助和啓發。markdown
在這篇文章中,咱們將圍繞如下幾點來分享咱們的經驗與思考網絡
如何明確優化的方向與重點併發
客戶端上有哪些提高性能的手段框架
如何結合具體狀況進行技術選型性能
客戶端外咱們作了哪些優化優化
數據導向的重要性及對咱們的幫助
短視頻做爲網易新聞中的一個功能,是重點但不是所有。在優化上咱們沒有不停試錯的成本,因此抓住重點與方向很重要,這樣才能在有限的成本中達到最好的效果,一旦跑偏那麼會浪費大量的時間和人力。
系統地瞭解視頻的播放流程是有助於咱們把握方向的,站在一個全局的角度對視頻播放進行分析也更容易發現其中的問題。因此咱們先將須要優化的過程拆解成幾個獨立的過程,分析這些過程各自的特色,而後結合咱們的目標和條件去進行選擇性的優化。
生產流程
在瞭解視頻的播放流程前,咱們能夠先看看視頻是如何生成的。
數字化
曾經普遍流行的模擬攝像機輸出的是模擬信號,由於計算機只能處理0和1,因此須要將模擬信號轉換爲數字信號,這個過程就叫作數字化。雖然如今數字攝像機和手機已經成爲主流,能夠直接輸出數字信號,可是這些設備內部的光感元件本質上輸出的仍是模擬信號,因此這個過程仍是存在的,只是絕大多數狀況下咱們不須要去關心它。
編碼
原始數字視頻的數據量是至關大的。若是不進行壓縮,經過現有的網絡傳輸能力是難以流暢的在線播放的,即便是存儲也會變得很是不便。因此咱們須要經過壓縮來高效的存儲數字視頻的數據,這個過程就叫作編碼。常常看到的 H264(AVC)、H265(HEVC)就是常見的視頻編碼,AAC 則是一種常見的音頻編碼。
封裝
咱們一般意義上的「視頻」,其實包含了視頻數據和音頻數據,有時還會包含多音軌或者字幕。因此咱們須要一個容器可以將視頻和音頻等數據打包傳輸存儲,而且可以保證它們在時間上的同步。將以上這些元素打包進一個容器中的過程就叫作封裝,而咱們常常提到的 MP四、MKV、TS 等等這些其實都是一種封裝格式。
協議
這裏的流媒體協議指的是 HLS 、DASH 這些基於 HTTP 的動態自適應協議。在直播風頭過去後,這些流媒體協議仍然活躍在不少點播場景中。並非全部的視頻都須要而且能夠經過流媒體協議來下發,可是這些流媒體協議的動態自適應等特色在一些場景下能夠幫助咱們得到更大的優化空間。
播放流程
在瞭解了視頻的生產流程後,再看播放流程就相對簡單了。若是說視頻的生產是將數據一步步封裝組合的話,那麼視頻的播放就是將數據一步步拆解還原。
咱們將客戶端獲取視頻數據的過程也加進來,那麼一個視頻在手機上播放會經歷如下這些步驟。
讀取
獲取視頻的數據,數據的來源能夠是網絡、磁盤或者內存。
緩衝
通常狀況下,讀取的速度是大於播放器消費的速度的,咱們能夠將已經讀取但播放器還沒有消費的數據存儲在內存中,這樣當 I/O 速度有所波動時能夠必定程度上避免對觀看體驗的影響。這個過程就叫作緩衝。
解協議
若是咱們讀取的是流媒體協議,好比 HLS、DASH 這種,那麼咱們還須要進行解協議的操做。按照索引文件的規則去讀取相應的視頻文件,但若是咱們讀取的已是 MP4 這種容器文件的話,那麼就不存在這一步了。
解封裝
視頻、音頻等數據都封裝在了容器文件中,所以要想解析視頻和音頻就須要將它們從封裝容器中提取出來。按照封裝格式的規則進行數據提取的過程能夠稱做解封裝。
解碼
在解封裝後咱們拿到了視頻和音頻的編碼數據,接下來須要將它們解碼還原成視頻和音頻的原始數據。
渲染
最後咱們就能夠將視頻和音頻進行輸出從而進行觀看了。
在瞭解了基本的播放流程後,就須要結合目標進行具體分析,看看在這個播放的流程中哪些過程是值得咱們重點關注的。
在網易新聞客戶端上咱們是以秒開率、卡頓率、失敗率三個指標來衡量性能。
秒開率
秒開率的優化重點和首屏平均時間是不同的。秒開率不只僅要求的是速度,也衡量着播放器的穩定性。
當總體水平提高到必定階段後,首屏平均時間關注的仍然是中堅用戶,由於他們的佔比最大,優化的效果對於均值的提高最明顯;但秒開率更須要關注的是那些性能較差的用戶,由於只有這部分的用戶性能提高了,秒開率才能提升。
因此對於秒開率來講,「讀取」階段最爲關鍵。數據默認狀況下都是從網絡讀取,而網絡的時間是很是不可控的。惡劣的網絡環境下加載時間很容易就會超過1秒,所以這裏有着很大的優化空間。
卡頓率
卡頓率的計算方式雖然有不少種,可是衡量的東西都是同樣的。若是咱們的帶寬跟不上碼率,又沒有提早在內存中緩衝到足夠的數據的話,就會形成卡頓的升高。數據的讀取和緩衝的策略會對卡頓有着較明顯的影響。
失敗率
視頻播放失敗的緣由則有不少,多是網絡上的錯誤,也多是本地緩存文件的異常,又或者硬件解碼失敗等等。除了緩衝,理論上每一個步驟都有可能致使播放失敗。
綜合上面的分析,咱們能夠大體把重點與方向鎖定在讀取與緩衝階段,而其他階段則是當咱們須要優化失敗率或者遇到瓶頸時須要考慮的。
在肯定了重點與方向後,接下來就是方案了。要想提升秒開率,首先想到的會是預加載。
預加載做爲最多見的優化手段,能夠在網絡暢通、流量充裕的狀況下提早將數據緩存到本地,有效避免弱網時加載視頻的等待。在網易新聞客戶端上,預加載的各類策略能夠爲咱們帶來接近 10% 的秒開率絕對值的提高,能夠提及到了很是重要的做用。
不過雖然預加載簡單好用,在實現的過程當中仍是存在着不少咱們須要注意的細節,若是不加以注意不只會讓效果大打折扣,還會帶來不少問題與麻煩。
控制預加載的大小
不一樣於文章的文本數據,視頻中有視頻數據、音頻數據、元數據等信息,要大的多。
舉個例子來講的話,假設咱們如今但願預加載一個視頻時長 34 秒且清晰度較好,固定碼率是 800Kbps 左右,那麼這個視頻的大小就是 800Kbps / 8 * 34 / 1024 = 3.4M。消耗 3.4M 流量去加載一個不必定會被消費的內容是很浪費的,更況且實際使用中會發不少預加載請求,這個消耗將是巨大的。
磁盤消耗
過多的數據首先會佔用到大量磁盤的空間。若是預加載的文件在磁盤中佔用太多空間會直接影響到用戶的手機使用,但若是佔用的空間較少又會致使磁盤頻繁的刪除和寫入。
流量消耗
大量的數據在流量上也會是一筆很大的開銷。對於用戶來講,預加載會形成他們的流量浪費,因此咱們應該在 WiFi條件下才開啓預加載。對於公司來講數據量過多一樣會致使成本升高,這是由於不少時候咱們都會藉助商用 CDN 來提高性能,而 CDN 是按流量計費,因此流量的浪費就等於資金的浪費。
合適的大小
要避免這些問題,最好的辦法仍是控制每一個視頻預加載的數據量在一個合適的大小。這個大小的值取決於視頻的起播緩衝量,簡單來講就是須要加載多少時間的視頻數據後才能渲染出首屏畫面。咱們的預加載至少要大於這個起播緩衝量才能發揮做用。
若是一個播放器須要5s的數據才能起播,那麼咱們的預加載數據量就須要至少可以播放5s的視頻才行。不然當點擊視頻播放時,預加載的數據量不夠起播,那麼仍是得去網絡繼續下載數據,就又會受到網絡環境的影響了。
對於不一樣時長、不一樣編碼、不一樣播放器播放的視頻,這個須要下載的大小是不同的。咱們能夠在後臺提早計算好這個大小,經過接口告訴客戶端。
切換播放器的輸入流
既然要作到部分數據的預加載,那麼就不可避免的要涉及到流的切換。假設一個視頻總長度34秒,咱們預加載了前2秒,那麼咱們須要完成的任務就是前2秒的視頻去讀取本地文件,然後32秒的數據去經過網絡請求。
不少播放器都不支持在一次播放內進行流的切換,因此咱們須要本身去實現這個邏輯。但不少時候播放器爲了保證自身的穩定性都會進行封裝,不要說解碼、渲染等過程,即便是讀取過程也沒有暴露給外界。最多見的就是 Android 和 iOS 平臺上的系統播放器了,它們雖然使用簡單方便,可是不易擴展,內部對於咱們來講基本上就是一個黑盒,從讀取到渲染都是在播放器內部完成的。
若是咱們沒法去定製播放器的讀取流程,那麼切換播放器的輸入流就無從談起,預加載也就沒法落地。這時擺在咱們面前的只有兩條路,一條是選擇一個能夠支持定製讀取流程的播放器,另外一條則是對播放器進行「欺騙」的本地代理了。
本地代理
經過本地代理咱們可讓播放器覺得本身一直在讀取「網絡數據」,可是這個「網絡數據」其實是咱們的本地代理服務器提供的。咱們能夠在代理服務器處進行緩存、切換流等邏輯,這樣就能夠在不改變播放器行爲的前提下改變播放器的輸出結果。
要實現本地代理咱們須要在客戶端本地創建服務器,監聽 localhost 的指定端口,並將全部的視頻請求都指向這個端口。這樣本地代理服務器就能夠攔截客戶端的請求從而進行定製和改造了。
本地代理的優點在於對原有代碼幾乎是無入侵的,須要作的僅僅是將本來要傳給播放器的 url 包裝成指向本地代理服務器的 url 就好了。可是這個方案一樣存在着缺點,咱們須要處理許多細節問題,好比代理服務器中斷後的端口更新、拖拽形成的 range 請求的特殊處理、緩存文件存在但內容損壞時的容錯等等,稍有不慎可能就會形成視頻的播放失敗等問題。
提高預加載的命中率
光是實現預加載是不夠的,咱們還須要讓預加載發揮出最大價值。提升預加載的命中率能夠不只能提高性能,還能下降成本。
要實現這個目標咱們須要瞭解用戶在當前頁面接下來可能會消費什麼內容,而這個預測是須要創建在數據的基礎之上的。不一樣產品、不一樣業務下用戶的行爲均可能有所不一樣,所以咱們須要結合數據在具體場景下去具體分析。
好比網易新聞最一開始有兩個入口能夠進入視頻播放頁。分別是新聞欄目和視頻欄目。最一開始咱們想固然的在視頻欄目進行預加載,這樣進入視頻播放頁的時候就能夠直接使用已加載的數據,可是上線後效果並不理想。 分析過數據後咱們發現進入視頻播放頁的大部分流量其實都來自新聞欄目,而視頻欄目進入的佔比則不多。而在增長了新聞欄目的預加載後,數據確實獲得了明顯的提高。
相似的場景還有不少,好比保持預加載的順序和自動播放的順序相同、優先下載熱門的視頻等等。任什麼時候候咱們都應該優先預加載用戶最可能播放的內容。
下降預加載的反作用
雖然預加載帶來的好處顯而易見,可是若是處理很差也會帶來一些麻煩。
處理播放器和預加載請求的衝突
首先預加載做爲一個單獨的網絡請求,是有可能跟播放器的網絡請求衝突的。而衝突的結果,若是是不一樣的視頻,則會搶佔帶寬;若是是相同的視頻,根據你的實現,可能會沒命中緩存或者發生異常。
所以咱們須要作的是當播放器加載視頻時停掉預加載的請求,並去判斷是否有緩存文件。當播放器空閒時,再繼續隊列中的預加載。
限制預加載的資源佔用
若是預加載的併發數若是過多,會佔用過多帶寬和系統資源,影響到其餘的業務和功能。所以由一個單線程依次執行預加載隊列中的任務是比較合適的。
讓預加載請求和頁面生命週期綁定
當退出頁面時,咱們應該中止並清空隊列中屬於這個頁面的請求,由於這些視頻極可能已經沒有機會被播放了。
若是咱們只是暫時離開了一個頁面,那麼掛起這個頁面發起的預加載請求是比較合適的。由於極可能咱們進入的頁面會有新的預加載請求,繼續以前頁面的請求會讓咱們新的預加載請求沒法第一時間開始執行。
除此之外還有不少細節的問題咱們能夠進行處理,這些細節雖然提高有限,可是聚沙成塔也能有顯著的效果。
預加載本質上就是一種提早的緩存,只是其寫數據的時機和邏輯能夠不依賴於播放器。而緩存雖然與播放器息息相關,可是比預加載要更加泛用,好的緩存機制能夠帶來續播、重播等場景的體驗提高。
在優化緩存時咱們能夠參考其餘的緩存機制,好比圖片緩存等等。它們之間既有類似之處也有差別和不一樣,咱們能夠對於其中類似的部分能夠進行借鑑,而對於不一樣的部分進行優化改進。
緩存的切片
圖片緩存中咱們一般會以url做爲key,將完整的圖片數據做爲value保存起來。當所佔空間到達上限時經過一些淘汰算法進行部分緩存的清除,好比經常使用的LRU。但若是咱們在使用MP4視頻時仍然使用這種方式緩存,就可能遇到一些咱們不但願出現的場景。
如今用戶打開了一個已經緩存過的視頻,在觀看了幾秒鐘後就關閉了,這種狀況老是很常見,好比這是一個信息流中自動播放的視頻。這個時候視頻的最近使用時間被更新了,但其實它是一個用戶不感興趣的內容。 根據緩存策略,這個文件的清除順序會被日後排。前幾秒由於被播放過,日後被清除是能夠被接受的,可是後幾分鐘沒有看過的內容,由於跟前幾秒屬於同一個文件,就也被日後清除,顯然對其餘文件不太公平。這會影響到其餘緩存文件的存活時間。
因此咱們應該將本地的緩存文件切片,在緩存的key中增長一個時間片斷維度來區分同一個視頻的不一樣時間片斷,讓真正被最近使用的文件才最晚被清除。這能夠讓視頻得到更細的淘汰粒度,從而提升緩存的命中率。
每一個切片的大小沒有嚴格的要求,可是都有一個統一的上限,好比最可能是500KB。這會帶來另外一個好處就是在處理 seek 時邏輯會變得簡單。當發生拖拽時咱們只須要結束當前文件的寫入,新建立一個文件進行順序寫入操做就好了。
創建多級緩存模型
圖片緩存的另外一個特色,則是咱們常常會用到多級緩存模型。最基礎的三級緩存根據IO速度的不一樣,分爲內存、磁盤、網絡。有時候針對業務特色,可能會發展出四級等模型,根據重要性再次進行區分。
那麼視頻是否是也能夠借鑑一樣的思路呢。前面咱們已經分析過視頻的大小,將視頻完整的放在內存中確定不現實,但跟預加載同樣只加載前幾秒的數據仍是可行的。
首屏數據
經過前面的預加載和緩存切片,咱們至少能夠將一個完整的視頻文件分爲首屏數據和內容數據。
首屏數據即預加載下載的數據,即大於或等於起播時間長度的數據。 內容數據則是除首屏數據之外的全部數據,固然內容數據也是能夠再切分的,這裏咱們先簡單地將內容數據看成一個總體看待。
三級緩存
將首屏數據放到內存中能夠得到更快的IO速度,在遇到磁盤性能較差的手機或者磁盤IO密集的環境時也能將波動儘量下降。
視頻的秒開很大程度上取決與首屏數據的讀取速度,所以穩定快速的首屏數據獲取能夠進一步保證視頻的秒開。
四級緩存
若是咱們磁盤上的淘汰策略是LRU,則會將最近最少使用的文件切片清除。 但在不少狀況下,即便某個首屏數據沒有被使用過,它的重要性也可能比使用過的內容數據更高。由於用戶看過的視頻不必定會去看,可是頁面上看到的未播放過的視頻是有可能會去點擊的。
基於這種特色咱們能夠換一種淘汰算法,好比跟LRU策略相反,將播放過的視頻優先淘汰,由於可能咱們會認爲用戶對於看過的視頻不會再感興趣。但這種場景是不能一律而論的,好比重播場景這種方式就明顯不適用,因此具體的策略還得根據數據分析來進行決定。
若是咱們不想去考慮那些複雜的問題仍然使用LRU的話,在磁盤層再分出一級緩存出來演化成四級緩存是另一種方案。
這裏的二三級緩存雖然沒有速度的差別,可是在優先級上是有區分的。當磁盤空間不足時,會優先清空三級緩存中的內容。首屏數據獲得保留,從而能夠爲下次的播放提供秒開的條件。
不論幾級緩存,其目的都是同樣的,都是爲最重要的數據提供儘量快的IO速度。
在多級緩存模型中,能夠經過增長了內存緩存來獲取更快的IO速度。而在播放器內部其實也存在着一個基於內存的緩存,雖然它只能緩存播放器正在加載的視頻,可是保證了視頻播放的流暢性。咱們將這個播放器內部的內存緩存稱爲緩衝。
緩衝的控制在優化中一樣起到了舉足輕重的做用。網易新聞客戶端早期使用的是系統播放器,在Android 6.0 如下的系統播放器默認的起播緩衝量是5秒數據,這個時間在弱網下會大大增長首屏的時間,在替換播放器並優化了這個時間後咱們的秒開率直接提高到了 80%。在卡頓率上,各類緩衝策略的調整幫助咱們下降了1%的絕對值,至關於卡頓次數減小了30%。因此,緩衝的控制是咱們不能忽視的。
讓播放器能第一時間播放
單純的下降起播緩衝量好比從5s降到1s已經能夠看到明顯的效果了,可是咱們能夠將這個值計算的再精細些。
如今絕大部分的視頻編碼格式都是 H264,在 H264 的傳輸單元中包含三種幀,其中I幀具有完整的圖像信息能夠獨立解碼,P幀須要參考以前已經處理的幀,B幀則既須要參考以前的幀,也須要參考後面的幀。兩個I幀之間的圖像序列稱爲一個GOP(Group of Pictures)。
若是咱們能夠在轉碼時保證視頻的第一幀是I幀,那麼一個I幀的數據加上當前視頻格式的元數據,這些就是咱們起播視頻所須要的最小大小。
這個大小按照咱們以前說的能夠在後臺提早計算好,而後下發給客戶端,播放器在播放前配置好,從而保證視頻的第一時間渲染。
兩個起播緩衝量
「起播的緩衝量越低,咱們就能夠越早的渲染畫面,優化效果就越好」,這句話並非徹底正確的。
首先,當這個時間小於I幀的數據時,再小也是沒有效果的,由於解碼器無法還原出一個完整的畫面。
其次,起播緩衝量在不一樣場景下起到的做用是不同的。當首次播放時,較低起播緩衝量的緩衝量能夠幫助咱們快速打開視頻。但當緩衝區見底從新加載到足夠數據時,當即播放反而可能會給用戶更差的體驗。想象一下當你在高鐵上打開手機想經過視頻來打發時間,卻發現視頻播一幀卡一下,這種感受是很難受的。
若是咱們將起播緩衝量分爲首屏起播緩衝量和卡頓起播緩衝量的話,那麼前者大小能夠是咱們以前分析的首個I幀數據,然後者的配置則應該結合具體的用戶場景進行分析。
緩衝區的限制
在網易新聞中有着不少自動播放的場景來幫助用戶更低成本的觀看視頻,可是這不必定是對全部用戶都是有幫助的。雖然提供了關閉自動播放的選項,仍然會出現自動播放了用戶不感興趣內容的場景,當出現這種狀況時用戶都會選擇手動關閉掉視頻。
試想一下若是咱們的緩衝區沒有上限,發生上述場景時極可能在用戶思考的短短几秒內就已經加載完了完整的視頻。隨後用戶關閉它而且再也不觀看了,那麼後續下載的那些數據都將成爲浪費。
所以咱們不能在播放後就無止境的加載,而應該在加載到了足夠數據後暫停緩衝,當緩衝區的數據快要不足時在從新開啓這個任務。咱們能夠將這個停的節點做爲緩衝區的上限,將重啓緩衝任務的節點做爲緩衝區的下限。
緩衝模型
結合上面的分析,咱們瞭解到有四個節點是咱們比較關心的,經過這四個節點,能夠組成一個緩衝模型。
首屏起播緩衝量表示首次播放時只有到達這個數據量時才能開始渲染。卡頓起播緩衝量表示進入緩衝區數據見底致使畫面中止,從新加載到足夠數據容許播放時的緩衝量。緩衝區的上下限則分別定義了緩衝該什麼時候中止和從新開始。
緩衝的自適應
在前面咱們說到以I幀的數據量進行首屏起播,這是最理想的狀況。可是並非全部用戶在這個最理想的配置下都能得到最理想的體驗。
當弱網環境下,咱們很快的加載出了I幀的數據,渲染出了畫面,可是畫面很快就又停住了。這是由於咱們的帶寬跟不上碼率,因此後續的數據尚未下載下來,隨後用戶就會開始播一幀卡一下的場景了。
這時候咱們須要對不一樣網絡狀態下的播放進行鍼對性的調整。咱們可讓上面模型中的參數變得更加智能。好比當帶寬較低時,咱們能夠適當的延長首屏的起播緩衝量、當頻繁發生卡頓時,咱們須要適當增大卡頓的起播緩衝量等等。
瞭解了方案後剩下的就是實現了。對於一些播放器來說,這些可能只是一個簡單的參數配置問題;對於另外一些播放器來說,可能須要本身去實現這個模型;但還有那麼一部分播放器,你拿着優化方案卻根本無從下手,由於它們根本不支持定製。若是你是最後這種狀況,那麼或許你應該從新考慮技術的選型了。
技術選型是至關重要的。它不只決定了優化的上限,同時也影響着方案的實現。
之因此放在後面說,是由於若是一上來就講,未免會顯得有些空洞。若是沒有實際遇到問題,很難能體會到不一樣技術上的差別和特色。可是若是你完整的閱讀了前面的內容,應該已經可以理解這一點了。
在上面的優化中,感覺最明顯的就是播放器的選擇了。若是咱們的需求超出了播放器的能力範圍,那麼任何優化作起來都會變的很困難。因此咱們先來看看播放器的選型。 客戶端上能夠使用的播放器有不少,咱們能夠大體分爲如下三類
理論上講,越偏底層的播放器,上限也就越高,可是要承擔的風險也就越大。 因此在選擇時咱們應該結合本身的目標和條件,進行合適的選擇。
網易新聞的播放器
以網易新聞舉例,咱們當時所處的環境有如下一些特色。
在視頻優化上能投入的人力很是少。而且在優化的同時還得兼顧業務需求等等其餘事情,這意味大刀闊斧的底層改造很難在短時間看到成果。
業務迭代快。這就須要咱們的播放器須要足夠的穩定,有着很強的健壯性。若是常常性的 ANR 或者 Crash ,會浪費大量的時間排查與定位,大大下降業務效率。
咱們客戶端團隊中還缺乏經驗豐富的 C/C++ 開發。這種狀況下很難在底層快速實現定製化需求,同時也會有着很大的風險,也不利於以後的工做交接和維護。
這種狀況下咱們在 Android 平臺上選擇使用 ExoPlayer。做爲 Google 研發並普遍應用在國外諸多 App 上的播放器框架,ExoPlayer 有着穩定、可擴展等優勢。對上述提到的不少優化項好比預加載、緩存、緩衝模型都有着很好的支持,基於 Java 的實現也能讓咱們高效安全的進行定製與擴展,完美契合了咱們當前階段的需求。
在 iOS 平臺上咱們則繼續使用着系統播放器。平臺的優點讓 iOS 的播放表現比 Android 更加穩定,再加上系統對預加載、起播緩衝配置等特性的支持,基本能知足咱們當下的需求。因此 iOS 上咱們在不替換播放器的前提下進行了播放器預建立、本地代理等優化,在保證穩定性的前提下提高了性能。
這套方案雖然一樣存在着一些制約,可是對於現階段的咱們來講倒是最合適的。在知足咱們性能需求的同時,避免了成本問題與風險問題。
除了播放器,咱們還會遇到不少技術上的選擇,咱們須要作的是找到最切合本身業務的技術。
若是應用常常播放番劇電影這種長視頻,那麼使用MP4格式會由於moov信息過大而消耗太多的解析時間。這時能夠考慮使用 HLS 或者 DASH 等流媒體協議下發切片文件。
若是視頻播放時常常會有滿屏的禮物動畫,那麼當動畫的計算和軟解碼的播放器一塊兒搶佔CPU資源時就很容易在低端手機上卡頓。這個時候使用硬解碼會更加的合適。
若是沒有完善的兼容方案就盲目的上線H265,極可能原本之前能播放視頻的用戶如今都沒法進行觀看了。即便用戶能播放出來,也可能由於解碼時間變長了而成爲負優化。
相似這些場景還有不少,一句話總結的話,就是技術的選型不能脫離業務的場景。
咱們上面討論了不少客戶端上的優化,可是說到底客戶端只是整個視頻播放過程當中的一個環節。
除了客戶端,視頻的播放還會通過視頻的生成、轉碼、CDN的調度等過程。在這些過程當中存在着一些咱們須要注意的地方,它們極可能會在較大程度上影響着視頻的性能,可是卻沒有太多優化成本。
MP4 文件由一個個 box 組成,其中有一個叫 moov 的 box,存儲着播放 MP4 視頻必不可少的信息。 在下發 MP4 資源時咱們要確保這個 moov 在文件的前面。由於在不少時候,生成的 MP4 的 moov 會默認追加在文件末尾,這時若是播放器直接播放會在找不到 moov 時二次請求文件末尾,從而影響播放速度。
若是咱們的視頻生成來源是不可控的,在後臺進行視頻的統一轉碼是頗有必要的。
若是視頻資源是經過 HLS 或者 DASH 協議下發的切片文件,那麼咱們能夠考慮在切片文件上進行優化。
默認轉碼下,每一個切片的時長和碼率都是相同的。咱們能夠將首個切片的時長和碼率下降,從而讓首屏的數據變小,獲取更快的首屏打開速度。在首屏以後讓切片時長和碼率隨着進度的增長從低到高逐漸提高,使用戶可以播放體驗從快速趨向於平穩。
預加載能夠下降客戶端的首屏時間,一樣也能夠下降 CDN 回源形成的延時。若是 CDN 提早從源站預取了熱門的視頻,就能夠保證邊緣節點第一時間提供數據。而且在咱們前幾個切片文件縮小後,將策略調整爲預取熱門視頻的前幾個切片文件能夠在相同的存儲空間下緩存更多的熱門視頻。
CDN 有時會由於成本問題沒法保證全部的視頻文件都緩存在固態硬盤上,這時咱們須要優先在固態硬盤上緩存每一個視頻的首個切片,避免硬盤的IO速度對視頻秒開的影響。前幾個切片文件縮小也有助於咱們在固態硬盤上存放更多的視頻,更大程度的進行覆蓋。
在優化早期咱們的視頻業務只使用一家 CDN,而咱們和 CDN 又常常會同時進行一些優化或調整,因此當性能出現波動時,會出現多個變量同時存在的場景,很難去定位性能影響的緣由。尤爲是出現一些難以解釋且難以定位的問題時,會花費至關多的時間去排查分析。因而咱們選擇接入多家 CDN,這不只在必定程度上幫助了咱們定位問題,在容災等方面也讓咱們有所受益。
在接入多 CDN 之後,咱們發現不一樣 CDN 的技術細節是有所差別的,對不一樣場景下的優化經驗也不同。好比一樣的視頻經過不一樣 CDN 下發,在不一樣平臺、不一樣清晰度等環境下的數據表現會出現各有優劣的狀況。根據這個特色,咱們綜合歷史數據的表現去動態調度用戶訪問的 CDN 資源,從而讓用戶能過得到最好的體驗。
除此之外的優化還有不少,相比端上的優化,客戶端外的優化更多的是須要溝通與配合。當咱們可以明確共同的目標、積極地互相配合、高效地溝通與解決問題,那麼取得的效果將會比單方的努力要好的多。
有句古話叫作「兵馬未動,糧草先行」。若是將開展優化項目比做行軍打仗,那麼數據收集就是戰爭中的糧草。沒有糧草戰爭沒法進行下去,而沒有數據支撐優化工做也將沒法展開下去。數據能夠說是優化的前提。
沒有數據支撐,咱們不知道本身的優化是否起到了效果,也不知道本身的思路方向是否正確。 經過直觀感覺去衡量優化結果是很危險的,它極可能讓你產生錯誤的判斷,從而得出錯誤的結論。因此若是你打算開始作優化,進行性能的埋點上報是首先須要作的。它能夠確保你的第一次優化效果也可以經過數據體現出來。
經過數據咱們能夠了解到哪些工做起到了做用,哪些優化又是可有可無的。當咱們抓住了重點,就能夠朝着這個方向進行突破而不會跑偏。能夠說數據不只能幫助咱們衡量結果,也能指導咱們從此的方向。
進行數據的上報只是第一步。網易新聞客戶端在早期雖然上報了數據,但在後臺只有一個簡單的平臺展現。條件篩選須要提早約定好、數據在顯示上也會有所延時。那時候出了問題咱們不能第一時間發現,多維度的分析也須要自行跑腳本去處理原始數據。
後來咱們的後臺小夥伴對性能平臺進行了改造,支持了多維度條件的篩選,數據的實時計算等功能。在那之後,出現問題咱們能夠第一時間從網絡狀態、系統版本、渠道等各個維度進行排查,大大提升了咱們的效率。
舉例來講,有一天咱們發現秒開率從早上7點開始就斷崖式降低。咱們經過多維度的篩選,定位到一秒率的降低集中在同一個視頻上,而這個視頻有着遠遠高出其餘視頻的播放量。經過分析這個視頻,咱們發現這個視頻是當天的熱點視頻,其播放量和其餘產品數據是相吻合的,而由於這個視頻自己的碼率較高,致使首屏加載速度較慢,從而拉低了平均的秒開率。
如今,網易新聞客戶端短視頻的秒開率已經達到了 94%,卡頓率 1.4%,失敗率 0.14%。在這個過程當中有過各類因素的限制,也有過各類意外的發生,可是咱們都一一克服並實現了進一步的提高。咱們的優化雖是從0開始,但還遠遠沒有達到1的程度,所以咱們的優化之路也將仍將繼續。2019年,咱們會在優化的道路上繼續探索,在咱們力所能及的範圍內爲用戶帶來最好的體驗。