音視頻播放過程當中的問題解決(播放質量優化)

1、概述

本文主要介紹音視頻播放過程當中的一些問題,以及針對具體問題的優化方法。前端

2、異常狀況及緣由簡介

  • 卡頓,上行鏈路或者下行鏈路網絡帶寬不足播放,設備性能不足,視頻流時間戳問題
  • 花屏,有可能出現整幅畫面的模糊或馬賽克,sps/pps參數設置錯誤,或者P幀丟失或解碼失敗致使局部畫面花屏。
  • 綠屏,sps/pps獲取失敗或錯誤,沒法渲染的畫面有些用黑色填充,有些用綠色填充,有些用上一幀畫面填充。視頻參數改變, 而解碼端的SPS&PPS信息未及時從新獲取更新,會致使畫面沒法正常渲染,繼而致使綠屏的現象出現
  • 跳播,體現爲播放過程當中音頻或視頻的不連續——>即時間戳的不連續。主要因爲緩衝管理、音畫同步、一些主動的丟幀策略或性能瓶頸形成。
  • 拖拽無效,seek的位置不合法,seek位置計算錯誤(進度條錯誤、時間戳錯誤或者整個視頻文件時長錯誤)
  • 拖拽偏差過大,GOP太大
  • 黑屏,有聲音但畫面爲黑屏。
  • 切碼率異常(無痕切換),通常表現爲沒法渲染,或花屏,或跳播,或黑屏之類的。其實都是在不一樣碼流切換時一些信息設置錯誤/沒有重置。
  • 斷流,除了設置斷流閾值,超過閾值重連,沒想到啥好辦法。但重連的實現過程有許多細節要處理。
  • 音畫不一樣步,同步算法處理不當,致使音頻時間戳和視頻時間戳差值過大。但也有物理意義上的音畫不一樣步,如音頻源距離麥克風太遠,而音速也比光速慢不少(離麥克風別太遠)。或者採集過程當中的音頻處理模塊耗時過長(沒啥辦法)。
  • 聲音異常,好比變聲,噪聲,回聲,聲音不連續這些現象。首先除了噪聲抑制、回聲消除、自動增益、重採樣等這些模塊的處理不當,還可能有采集或播放的編解碼、渲染參數錯誤問題。
  • 播放失敗,緣由有不少。分發流中入口錯誤、播放後端接口返回錯誤、業務層建立播放container失敗、建立播放器錯誤、流狀態錯誤、播放url無效、DNS解析錯誤,以上都屬於業務(第三方)相關的錯誤。播放層面可能致使播放失敗的錯誤包括CDN節點鏈接失敗、讀packet錯誤、解碼錯誤、播放端網絡中斷、流服務器宕機或負載太高、源流中斷等。咱們在實際工業場景中通常認爲閾值時間內能夠恢復的錯誤,不算播放失敗。
  • 首幀時間長,播放器初始化時間,後端接口耗時,dns解析耗時,cdn節點調度,metadata解析時間,解碼器初始化時間,播放隊列開始往渲染器送數據的緩衝閾值,都會致使首幀時間過長。
  • 延遲大,主要是採集端編碼耗時、上行傳輸鏈路耗時(若是是tcp會有ack和重傳機制,耗時更多)、轉碼耗時、分發到cdn節點耗時、下行鏈路耗時。
  • 累積延遲,通常指上行或下行採用基於TCP協議的流媒體協議傳輸,因爲網絡抖動以及TCP的重傳機制,會致使延遲不斷累加增大。
  • 手機發熱,採集端較爲常見,播放端碼率較高時也會常見。其實是CPU的使用率太高,或內存使用率太高,全景和VR場景也有可能顯卡使用率太高。好比緩衝設計不合理、頻繁複制數據、算法時間複雜度太高、編解碼過於依賴CPU計算、渲染數據量過大、渲染過程當中的轉換計算量過大、編碼算法與機器配置不匹配、有一些數據精度太高等。
  • 錄製視頻播放異常,通常是錄製時音視頻編碼參數設置錯誤,或者時間戳異常。
  • 播放閃屏,頻繁出現穩定畫面和融合後的畫面的切換,給用戶一種畫面抖動的感受。

3、優化方法

針對每一種異常狀況或優化點,逐個解決及優化:android

1. 卡頓

  • 上行優化,解決源流的卡頓。好比推流端設備性能配置優化、推流端性能優化、硬編、推流邊緣CDN節點就近選擇。
  • 抖動緩衝,在卡頓和延遲中作平衡。
  • 下載鏈路優化,選擇更近更優的CDN邊緣節點。
  • 針對播放嚴重卡頓的用戶,或CDN負載太高的狀況,直播後端下發消息,強制客戶端降碼率。客戶端自己在檢測到持續卡頓後也能夠強制降碼率。
  • 播放端採用硬解,優化解碼性能。
  • 底層預估網絡變化,從新創建與CDN的鏈接,並沒有痕切換(鏈路切換)。
  • TCP協議和UDP協議的挑選和結合。TCP協議可靠,但在網絡抖動時會大量重傳,形成更嚴重的擁塞。UDP協議對網絡抖動的適應更好,但可能會有包亂序和包丟棄的問題
  • 可變碼率,如轉碼平臺和播放端能夠支持真正的碼率自適應流,應重點考慮。不然也能夠採用折中方案,客戶端預估網絡變化,播放器底層創建與CDN低清碼流的鏈接,獲取低清晰度數據並解碼,提供給上層渲染(假的分層編碼實現方式),待網絡恢復後又切換回源碼流。這樣不用從協議或轉碼、解碼方式上進行大改,只是有可能加大CDN負載,播放SDK的下載和解碼、同步邏輯會較爲複雜。
  • 分層編碼,直播場景下實現難度較大,但也值得考慮,在點播播放時應重點考慮。主要是對轉碼和解碼算法的要求不一樣。
  • 插入假數據,好比插入少許平穩的音頻幀,以及在畫面變化較小時插入幀。實現難度較大,首先對執行插幀的閾值須要及時判斷,不然會形成體驗不好,或來不及插幀,須要考慮時間戳的修改及音畫同步,判斷在哪些點能夠插音頻幀,哪些點能夠插視頻幀,在下載鏈路恢復正常後如何切換,再次校準時間戳。這部分能夠參考回聲消除時,遠端信號與近端信號不一樣步時,如何爲遠端信號插幀。
  • 時間戳錯亂的問題(好比視頻時間戳回退),這種狀況下若是直接按照嚴格的音畫同步策略,可能會直接丟棄一些視頻幀不進行渲染,這樣畫面就暫停(卡頓)了,直到時間戳恢復正常。這時候能夠增大緩衝區,調整時間戳爲單調遞增。

2. 花屏

  • 碼率太低致使的馬賽克,除了給當前分辨率匹配合適的碼率,沒別的辦法
  • 顯卡性能瓶頸,優化渲染模塊的代碼邏輯,好比全景和VR之類的播放,能夠局部更新
  • 獲取sps/pps解碼信息失敗或者錯誤,對於可伸縮編碼或多碼流的狀況下,若是分辨率等數據變化但未及時刷新,就可能花屏(有的機型表現爲綠屏)。解碼信息的修改主要來源於轉碼服務,(好比在flv中,默認只有剛開始播放時下載的video header中才帶有sps/pps信息,中途修改了編碼信息,默認並不會再次發送video header),感受服務器若是隻是轉封裝而不是轉碼,很容易有這樣的問題,若是服務器作了轉碼,能夠在源流切換解碼信息時加入AVCDecorderConfigurationRecord這種幀。
  • 部分參考幀丟失,有多是採集端/播放端存在丟幀策略,並且丟棄的幀被參考,也有多是流媒體服務器的接收緩衝區SO_SNDBUF過小,網絡抖動恢復時接收的一些幀被丟棄,且這些幀被參考,致使局部畫面花屏。在UDP場景下,固然也有多是傳輸的不可靠性致使丟包。
  • 解碼器初始化參數錯誤或處理邏輯錯誤,致使解碼失敗,且解碼失敗的幀被參考,推流端編碼器初始化參數錯誤也會致使編碼異常。
  • 有些android機型的硬編硬解兼容性不夠好,當編碼/解碼異常時未檢測出來,這個只能根據歷史經驗設置黑名單
  • 給渲染模塊傳遞的參數不是實際參數,這個是bug了
  • 拖拽或從新設置解碼器參數時,沒有清空解碼隊列

3. 綠屏

  • 獲取sps/pps解碼信息失敗或者錯誤,就可能綠屏。(同花屏)
  • 硬解時ios videotoolbox沒法解析被切分爲多個NALU單元的幀,由於ios硬解碼器認爲這樣的幀是不完整的。播放端能夠在送入videotoolbox前將本幀的多個NALU中的data(具體參考H264中nalu格式)所有複製到AVPacket中,再塞給videotoolbox。注意:當採集端開啓mutli-slice會致使源流中部分視頻幀切分爲多個slice,存儲在不一樣的NALU中,因此有這個問題

4. 跳播

  • 服務端緩衝管理和性能瓶頸致使,緩衝已滿、流媒體服務器來不及處理緩衝中的數據,就會丟棄部分源流中的數據,致使播放端出現跳播的體驗。若是是視頻或直播回放錄製過程當中硬盤讀寫跟不上CPU處理產生的數據,也有可能會直接丟棄部分幀,致使跳播。
  • 播放端CPU佔用太高,好比使用率太高或訪問頻率太高,出現性能瓶頸,可能直接丟棄部分數據。
  • 音畫同步致使,若是出現時間戳回退,播放器默認會直接丟棄回退過程當中的幀。或音頻和視頻時間戳差別過大,爲了同步參考時鐘,也可能丟棄部分幀。
  • 主動丟幀策略致使,爲了平衡編解碼/網絡傳輸/播放速度/延遲採起丟幀策略,可能致使跳播,好比連續丟棄有關鍵聲音信號的音頻幀,或者連續丟棄視頻幀。最爲明顯的是丟棄GOP時。
  • UDP傳輸不可靠致使亂序和丟包,若是外部參考時鐘對應的幀已經到了,就會丟棄中間的幀。能夠開發UTCP的機制,參考TCP作驗證包序,以及像SRT那樣客戶端主動發送NACK請求重傳

5. 拖拽無效

ffmpeg中seek的實現能夠參考最後一部分中的文件how to seek in mp4/mkv/ts/flv。 【注:我的經驗在沒有關鍵幀列表的flv中拖拽會很慢很慢很慢。】一般seek是由用戶拖動前端顯示的進度條,播放器上層計算出seekTime=(進度條位置/總進度條長度)*視頻總時長,而後傳遞到播放內核。若是seekTime不在實際的視頻總時長內,就會致使seek無效,沒有bug通常不會有這問題。向前seek時,因爲數據未緩衝,若是下載速度過慢,某個時間段內一直沒有緩衝到數據,也有可能出發播放器內部的重啓機制,將本次seek取消。ios

6. 拖拽偏差過大

seek的原理是,找seekTime位置最近的I幀。GOP=n*fps,這個n值越大,seekTime距離實際定位點越遠(平均來講)。【注意seek操做後要清空解碼緩衝隊列】
若是是視頻總時長較長,那麼精準seek的需求不是很迫切,只要GOP別極其大就行。若是視頻總時長較小,或者是畫面切換很頻繁的場景(好比戶外運動),那麼可能須要精準seek。
能夠先定位到seekTime的前一個I幀,解碼I幀及其後的數據,直到seekTime的時間戳所屬的幀出現,才放入播放隊列。(這樣可能從體驗上會感受seek耗時過長,但通常還好,由於這種場景GOP也不用設特別大)web

7. 黑屏

  • 源流沒有圖像,多是推流端採集圖像失敗或者編碼失敗,能夠在編碼模塊增長檢測邏輯。
  • 持續解碼失敗,播放端有可能對特定的格式不支持,這是須要完善回調機制,通知上層解碼失敗。底層能夠自行嘗試重置解碼器,或從新創建鏈接。
  • 部分推流端編碼和封裝格式不標準,這也是源流問題。
  • 推流端業務層切換純音頻推流/音視頻推流的模式,若是一開始是純音頻流,後來切成音視頻流,由於播放器默認不會從新初始化解碼器,則會致使播放端黑屏。能夠在播放器內部增長是否有新的幀格式出現的探測,加入播放過程當中從新初始化解碼器的邏輯。或者服務端下發通知。

8. 切碼率異常

通常表現爲沒法渲染,或花屏,或跳播,或黑屏之類的。其實都是在不一樣碼流切換時一些信息設置錯誤/沒有重置。算法

9. 斷流

斷流沒法避免地會對播放體驗形成影響,只能儘量快速恢復,並保證恢復後的正常播放後端

  • 斷流檢測的週期設置(針對源流中斷或播放端下載鏈路等不一樣狀況)
  • 關閉原有stream鏈接,清除一些沒必要要的數據,關閉avformat
  • 從新打開流、獲取音視頻解碼數據
  • 重建stream鏈接並開始讀取音視頻數據
  • 此過程當中find_stream_info重置解碼參數可能致使恢復後倍速播放等,最好保持原解碼參數不變。具體原理未知 todo

10. 音畫不一樣步

參考音畫同步原理及實現性能優化

11. 聲音異常

  • 音頻預處理部分的工做,可使用webrtc的音頻處理模塊,比speex效果要好不少,sdk體積的增長也能夠接受。參考連接: 噪聲抑制 回聲消除 混音 靜音檢測
  • 採集或播放的編解碼、採樣率、渲染參數錯誤問題。工程實現上須要注意這些問題的處理。
  • 跳播致使的聲音不連續,參考跳播的處理。
  • 丟幀致使的聲音不連續或變調,主要是丟幀策略的調整不當致使。好比丟幀頻率很高,那麼會有一頓一頓的體驗,若是一次性丟棄不少時序相連的音頻幀,會有跳播的體驗,若是不丟音頻只丟視頻,聲音可能變調,使用音頻變速不變調算法如WSOLA。

12. 播放失敗

  • 由於致使播放失敗的緣由可能有不少,因此最最重要的是日誌收集的完善。
  • 對於上層業務(第三方)相關的錯誤,經過日誌分析推動相關方修改。
  • CDN節點鏈接失敗,能夠嘗試一次DNS解析返回多個ip,客戶端在某個ip重試失敗後,使用其餘ip創建鏈接
  • 讀packet錯誤,若是不是AVERROR_EOF這種,通常都直接丟棄packet了但頻率較高或連續的讀packet錯誤會致使用戶播放體驗受嚴重影響,而導致用戶在播放異常狀態下退出直播間(這種咱們認爲是播放失敗),當讀packet錯誤累計必定量時能夠從新創建鏈接,防止沒法恢復正常播放。出現AVERROR_EOF錯誤就直接重連。
  • 解碼錯誤,通常的解碼錯誤會體現爲渲染異常,處理方式參考讀packet錯誤
  • 播放端網絡中斷,其實和前面的讀packet錯誤是部分重合,仍舊是有超時重連,加大超時時間,達到閾值就換鏈路重連的策略
  • 流服務器宕機或負載太高,日誌上報只能起到過後分析的做用。主要是經過流服務自己的監控來實現整個集羣的穩定性
  • 源流中斷,有多是推流端異常關閉,或上行鏈路中斷,能夠在推流端作一些錯誤處理。

13. 首幀時間長

  • 預設播放格式,若是url中已經帶有音視頻的一些解碼參數,能夠預設iformat,提早開始解碼
  • DNS預解析,能夠由業務層在初始化播放器的同時,向調度服務器發送請求
  • CDN調度優化,下載鏈路優化
  • flv metadata解析簡化,由於flv的audio和video tag中其實已經帶有不少信息了,上層也能夠在初始化播放器時預設一些信息,因此能夠一邊read_frame一邊解析metadata,而無需等待。
  • 播放起始過程當中等待和同步的優化,好比減少packets buffer隊列長度,prepare過程當中不進行任何等待(start-on-prepared),強制刷新

14. 延遲大(直播)

  • 採集端網絡自適應,網絡環境極其差時丟幀,設計要求高,須要考慮對源流數據連續性的影響。關於採集端和播放端的丟幀,其實能夠單獨寫一篇。若是丟棄I幀,則需丟整個GOP,若是丟棄P幀,考慮GOP中靠後的P幀,B幀能夠直接丟棄。注:網絡直播app中的採集端通常不編碼B幀。
  • 採集端編碼性能優化及代碼邏輯優化,減小編碼延時,能夠設置mutli slice
  • 採集端採用實時性更高的協議,好比RTP或自定義UDP
  • 採集端使用可變碼率VBR,當畫面變化較少或網絡帶寬不夠時,下降碼率,其餘時候保持或升高碼率
  • 轉碼平臺CDN節點調度優化,儘量保證與採集端創建鏈接時間短,網絡傳輸高效穩定
  • 轉碼平臺轉碼性能優化,減小解碼、再編碼形成的延遲,拆分實時性和壓縮比需求不一樣的任務
  • CDN分發後下載節點調度優化,性能和負載優化
  • 播放端網絡自適應,丟幀,相似採集端的丟幀策略。通常丟棄音頻幀後,用視頻幀與音頻幀比對時間戳,再基於解碼渲染正常的前提,抉擇丟棄的P幀編號。若是是已知解碼所需的分辨率等信息,那麼能夠考慮直接丟棄解碼前的幀(解碼前丟幀若是操做不當,會形成跳播和花屏/綠屏)
  • 播放端倍速播放,音頻會變調,須要進行變速不變調的處理
  • 播放端解碼性能的優化
  • 播放端抖動緩衝(實現與卡頓的平衡)
  • 播放端採用實時性更高的協議
  • 播放端採用可變碼率
  • 播放端採用分層編碼流

15. 累積延遲(直播)

  • 平滑丟幀和快速丟幀兩種方式

16. 手機發熱

目前記錄到的:服務器

  • CPU使用率太高,主要是計算複雜度太高,數據精度過大,算法要求機器配置較高,軟編的時候很常見,矩陣轉換之類的操做多,全景的時候很常見
  • CPU使用頻率太高,核數較少,線程切換極爲頻繁。在覈少的機型上,有時候單線程解碼會比多線程解碼效率更高。
  • 內存使用率太高,緩衝隊列設計不合理,頻繁複制數據之類的
  • 顯卡使用率太高,超清或全景/VR之類的會存在顯卡使用率太高的狀況,能夠下降部分畫面的刷新率

17. 錄製視頻播放異常

  • pts寫入時校準
  • 一些關鍵的解碼信息不能寫錯

18. 閃屏

這部分我沒有遇到過,網上資料提到是指視頻圖像融合,或者播放器卡頓時中加入一些默認的圖片,致使畫面抖動(也就是閃屏)。連麥時的畫面合成,或者加logo,或者播放隊列插幀,都要注意平滑過渡。markdown

4、檢測和監控

  • 播放端日誌上報
  • 針對streamid實現從推流、轉碼、分發、下載、播放的全鏈路監控

5、參考

  1. 直播問題分析總結
  2. ffmpeg-how to seek in mp4/mkv/ts/flv
相關文章
相關標籤/搜索