播放器性能優化之路

下面是播放的完整流程:node

播放器性能優化之路

  • 播放器加載一個網絡url,首先要進行網絡請求,網絡如何優化,涉及到網絡優化的方方面面。
  • 網絡拉取回來數據以後,識別一下當前視頻的具體封裝格式,這個能夠正式流式視頻,也能夠是普通視頻,優化的手段有點不一樣。
  • 識別到具體的封裝格式,按照封裝格式的要求,開始解析封裝格式,解析其中的音頻流、視頻流、字幕流等等。
  • 音頻流要解碼成音頻原始數據,視頻流要解碼成視頻原始數據。
  • 解碼過程當中注意音視頻同步。
  • 音頻播放,同時視頻開始渲染。

1、播放痛點

根據咱們平時的開發實踐,咱們總結出播放過程當中常見的幾類問題:緩存

  • 播放失敗率高
  • 播放首幀慢
  • 播放卡頓
  • 播放器佔用CPU、內存太高

面對這些問題,咱們急切須要知道兩方面的數據:性能優化

  • 怎麼監控這些問題
  • 怎麼解決這類問題

這兩個問題是有有遞進關係的,「怎麼監控這些問題」就是爲了更好地「解決這類問題」。服務器

2、監控手段

咱們得知上面的痛點,在發生這些問題時,咱們要收集相應的數據分析這類問題,否則開發者一頭霧水,不知道播放過程的數據信息,解決問題全靠運氣。網絡

一、網絡加載監控

播放視頻首要的是網絡加載,網絡請求是一個複雜的過程,全鏈路的點太多,將全鏈路的全部點收集起來,能夠在播放器中加上網絡的全鏈路監控:app

播放器性能優化之路

這樣咱們對網絡的總體加載狀況有了全面的把握,發生網絡加載問題,也知道是哪一個點出現了問題,分析解決問題有了更加全的數據。ide

二、播放器全鏈路監控

開篇就分析了播放器的完整流程,其實開發者也很是須要當前播放器的運行狀態:性能

播放器的工做狀態也能夠拆解一下:優化

播放器性能優化之路

播放器發生狀態異常,開發者能夠明確獲知播放器當前所處的狀態。編碼

每一個狀態均可能發生異常,發生異常都有具體的緣由。利用播放器狀態、播放器出錯狀況構建一個較爲完善的播放監控體系。

三、播放器流暢度監控

播放卡頓,就是播放過程當中發生loading,UI直接顯示轉圈,這對用戶體驗的損害是巨大的,用戶在不斷的吐槽中默默地卸載了咱們的app。卡頓的主要緣由是網絡情況很差,很小的一部分緣由是源的問題。

  • 卡頓的次數
  • 卡頓的時長
  • 卡頓時的網速

單次播放平均卡頓次數和卡頓時間是咱們衡量播放流暢度的重要指標。

若是是源的問題,例如出現播放視頻的時候,進度條在走,可是畫面不走,就是視頻解碼出現問題,可是又沒有出錯,只是解碼出的數據有問題。

解碼出的數據有問題,有兩種狀況:原始數據就存在問題,這種狀況下基本沒法優化;另外一種狀況下是解碼線程異常。

  • MediaCodec發生異常
  • 解碼線程異常錯誤

監控發生問題時系統codec的具體狀態,而後上報,便於分析問題。

3、播放成功率優化

播放失敗的緣由不少,使用播放器播放視頻,最終都會在Player.onError回調中通知開發者播放失敗了,最多返回一個錯誤碼,對應一個播放錯誤。

總結而言,播放錯誤主要分爲下面幾類:

  • 網絡加載錯誤:網絡請求發生問題,多是網絡請求的任何一個階段。
  • 視頻格式識別錯誤:不支持當前的格式,或者當前格式識別出錯。
  • 解碼出錯:不支持當前視頻、音頻解碼致使的出錯,或者系統codec異常致使的問題。
  • 文件的IO異常:讀取緩存文件發生問題。

網絡加載錯誤通常要視狀況而定,網絡超時要作好超時重試機制。

視頻格式支持使用ffmpeg能解決基本上全部的視頻格式的識別和處理工做。

MediaCodec解碼受到手機硬件的制約,解碼有時候會出錯,出錯能夠切換到軟解碼。

4、播放性能優化

一、複用連接:

平時刷信息流視頻的時候,其實不少視頻的域名都是相同的,這些連接都是能夠複用的,網絡建連的時間須要30ms到200ms不等,若是能複用連接,這部分的時間是能夠節省下來的。

二、預加載

一個播放器實例持有的數據很是大,player初始化的時候會初始化MediaCodec,MediaCodec對應底層的AVCodec,操做底層的/dev/codec-node,Android系統規定了系統最大持有的MediaCodec實例是16個,固然每一個手機會有所不一樣,但總的來講不會有大的不一樣,就是MediaCodec的實例個數是有限的,不可能無限建立實例。

咱們預加載多個播放器實例的時候,就會建立多個codec實例,超過codec實現限制,系統codec就沒法正常工做,極易形成OOM或者ANR。

咱們再平時解決問題的時候常常發現media.codec進程致使SystemServer卡死的。通常都是media.codec使用不當形成的。

那如今可否預加載不起播放器實例?

咱們預加載的目的是爲了請求視頻資源,其實只須要網路模塊就能夠的。

本地代理能夠實現將播放器的網絡模塊獨立出來:

播放器性能優化之路

  • 播放器不直接和視頻源服務器交互,中間經過本地代理層交互。
  • 本地代理層的網絡加載模塊是獨立於播放器的,能夠是播放器發起請求,也能夠是其餘的外部調用發起請求。
  • 最終setDataSource到播放器的url是一個http://127.0.0.1:port的請求,本地代理層會經過Socket向這個url中發送數據,播放器能夠直接解析數據流。跟正常的播放流程徹底同樣。

這樣咱們能夠全局持有一個播放器實例:既能夠作到預加載,並且能夠解決播放器佔用資源過多的問題。一舉多得。

三、指定封裝格式和解碼格式:

針對一些視頻,咱們已經明確知道它們的封裝格式和音視頻的編解碼格式,那咱們就能夠提早告知播放器這些信息,播放器直接使用特定的封裝格式去嗅探,直接起特定的解碼器去解碼。

例如信息流視頻基本上都是MP4的封裝格式,H264的視頻編碼,AAC的音頻編碼。

這樣咱們能夠節省嗅探和MediaCodec檢索的時間。

四、MP4視頻優化:

MP4格式的視頻解析出來以下:

其中moov中包含着MP4文件特有的屬性數據,mdat是具體的音頻和視頻數據,MP4格式規定,只有解析出moov數據以後,才能解析出mdat中具體的音頻和視頻數據。

可是moov有時候在mdat以前,有時間在mdat以後,如上,moov在mdat以前,那麼咱們順序請求就沒有問題。

播放器性能優化之路

可是若是moov在mdat以後,咱們順序請求就播放不出來,這時候須要起雙IO緩衝加載:一個從頭開始檢索moov,另外一個從末尾檢索moov,雖找到moov,就能夠先解析出moov,而後解析mdat,播放視頻了。

可是雙IO畢竟比較耗時,若是能在服務器提早將MP4視頻的moov移到mdat以前,就能夠提高MP4的首幀。

五、流式視頻優化:

除了MP4視頻,還有一些流式點播的視頻,例如HLS格式,這些視頻是一個一個的ts分片組成的。針對這些視頻首幀的優化,我建議直接將前幾個ts的分片的數據壓縮,例如針對一個3s的ts視頻,原來的分辨率是1280 720,如今能夠壓縮到320 180的大小,數據量大大下降,這樣首幀就能快速加載下來。

六、邊下邊播

咱們在播放視頻的時候,最好能邊播放邊緩存到本地,這樣我二次打開這個視頻的時候,就能夠不用請求了,直接複用本地的數據。

邊下邊播可使用本地代理來完成。

七、video-id複用緩存

咱們實行邊下邊播以後,二次打開能夠複用緩存了,可是咱們是根據視頻的url來複用的,如今信息流視頻的url常常變化的,即便是同一個視頻,半小時視頻url就發生了變化。

那這樣的複用效率不是很低嗎?

還好如今信息流都是傳過來一個video-id,這個video-id不會隨着視頻url變化而變化,只要是同一個視頻,video-id不會變化,那咱們能夠利用這個video-id來實現一次緩存,屢次複用。

5、其餘播放體驗優化建議

一、播放的時候出現丟幀

播放時丟幀主要是直播應用會出現,當出現播放丟幀的狀況,服務器應該主動推流低碼率的流,防止客戶端出現丟幀嚴重甚至卡住不走的狀況。

二、播放畫面出現鋸齒

播放視頻的時候出現鋸齒,這時候通常是兩種狀況:

  • 視頻清晰度較高,MediaCodec對搞清的視頻解碼支持地較弱,畫面細膩感不強。建議切換到軟解碼開始解碼,軟解碼使用CPU解碼,對細節的支持度很強,甚至8K的視頻都能很好的解析。
  • 使用GLSurfaceView替換SurfaceView或者TextureView,GLSurfaceView經過OpenGL繪製紋理實現視頻細節的細膩繪製,對視頻畫面支持效果很好。

做者:Li Tianpeng

相關文章
相關標籤/搜索