洞察 video 超能力系列——玩轉 flv


從2016年10月(Chrome 54)開始,Chrome再也不內置flash,而是改成用戶第一次訪問flash資源時提示安裝。從Chrome62開始,再也不提供「click to play的選項」,改成點擊視頻box後,左上方彈出html

這意味着,flash做爲過期的標準將被新技術所取代。前端


前言

從咱們能夠在網站上播放視頻開始,到h5播放器們如火如荼地發展以前,使用flash一直都是web播放視頻的不二之選。甚至於說得更加普遍一些,在html5成爲主流以前,網站的多媒體能力,包括動畫、遊戲和視頻一直都是adobe生態在掌控。那麼隨着html5標準的推出,這一切都成爲了過去式。html5

flv與直播方案

flv的全程是 Flash Video,顧名思義,就是專門給flash播放器提供的播放格式,這種格式具備結構簡單、清晰的優勢,最先出現是爲了解決flash導出的swf文件體積過大,不適合在web中播放的問題。隨着flash的逐漸淘汰,新的video標籤天然是不支持flv格式的,人們開始把web視頻的重點放在mp4或者hls上,可是隨着直播這種視頻形式的火熱興起,flv迎來了新一輪的生機。git

咱們來橫向對比一下可用的直播方案:github

flv播放實現·跳轉·清晰度切換web

能夠看到,flv因爲其編碼格式的特色,只須要一個MetaData 以及音視頻Track各自的Header就能夠在任意的時間點播放,極大地符合實時直播的需求,在GOP足夠小的狀況下甚至能夠達到0延遲,能夠說在現有的技術方案裏,http-flv是最理想的一個,正是所以,flv依然是web播放器不可或缺的一個格式支持。
算法

首先咱們經過一張圖瞭解一下前端播放flv的過程緩存

從上圖能夠看到,flv播放的過程實際上是從flvt中提取元信息、音視頻header以及數據,而後轉碼成fmp4盒子結構的過程,再經過MSE交給video的過程。由於flv的結構自己就是流式的,也就是說,它的數據被拆分到了不少個小的tag中,因此咱們能夠很方便地作數據的封裝,也就是說將一段flv tag數據封裝成一個獨立的moof_mdat盒子對,而後就能夠直接交給MSE處理,很是的方便。這裏咱們討論flv是如何處理加載、跳轉播放、重放及清晰度切換問題的服務器

數據加載 數據加載直播和點播兩種模式,首先來聊一下點播:針對點播的數據獲取,咱們採用Range這個參數做爲分段加載的控制參數
websocket

如上圖,Range這個參數向服務器描述了但願加載一個flv文件的 100000 到 3987705 個字節 這段數據,相應的,服務器也須要可以根據Range參數正確返回這段數據。這裏展現一個極簡的服務端代碼:

從上述代碼能夠看到,服務端是先是解析range,根據range切出文件分段,而後返回一個readableStream交給前端。

再看一下直播:直播跟點播是很是不同的數據流動結構,咱們看一下簡單的直播流程

能夠看到,直播的流程是一個 推流->服務端格式編碼-> 終端播放 的一個流程。那麼這裏就帶來了一個問題,咱們不能夠像點播時那樣去加載數據了。由於服務端實際上保存的是一個文件流,不斷有數據推到服務端,播放器也不存在緩存一段數據這樣的狀況,而是不斷向服務器請求,只要有新的流到達服務端,播放器就要拿到這一段進行解碼播放,達到推流和拉流同步進行的一個直播效果。爲了實現這種效果,在播放器內,咱們使用 fetch+ streamReader,簡單的實現以下:

從上述代碼咱們能夠看到,咱們經過一個reader遞歸地從流裏面讀取數據,再將數據交給解碼器進行處理。關於直播的數據加載還有更加先進的websocket方式,這裏再也不深刻探討,有興趣的同窗能夠自行查閱相關資料。

播放時跳轉 播放時點擊進度條跳轉(下稱seek)是一個很是高頻的操做,尤爲是在長視頻中。用戶遇到不想看的部分,或者想重看的片斷,都會點擊進度條觸發seek。舉個例子,一段視頻,可能用戶實際觀看的部分只佔不到50%, 若是咱們將整段視頻加載,那剩下加載的50%就浪費掉了。因此咱們要作的事情就是精確地加載用戶但願播放的部分,節約流量。解決方法以下:

  1. 獲取用戶將要跳轉到的時間點,下稱seekTime根據seekTime計算出離該時間點最近的一幀的位置,稱爲 startPos

  2. 以預加載時間爲30s 爲例, 計算出seekTime + 30s 這個時間點最近的幀位置 稱爲 endPos

  3. 咱們以 Range: startPos-endPos 爲請求參數,向服務端請求這一段數據

  4. 解碼播放

這裏提及來簡單,可是涉及到了一個跟flv格式緊密相關的問題,那就是flv的onMetaData信息裏是否具備keyframes這個屬性。咱們上述所提到的,基於時間點計算某一幀位置的算法,是徹底依賴keyframes這個屬性的,它記錄了flv中全部關鍵幀的時間點和文件偏移量,keyframes大概長這個樣子:

times中每個時間點都對應着同位置的fileposition的偏移量,正是基於這一點,咱們能夠計算須要加載的數據Range。值得注意的一點是,並非全部flv文件都攜帶了keyframes頭,flv文件缺失部分onMetaData屬性是很常見的事情。缺失了keyframes信息,咱們就沒辦法作跳轉了,所以咱們須要藉助額外的工具幫咱們補全flv的onMetaData。這裏推薦使用yamdi,一個輕量級的工具,有興趣的同窗能夠看一下它的使用。

清晰度切換 清晰度切換是一個很是重要的功能,爲何重要呢,由於用戶會根據網速的快慢,去選擇更清晰或者更流暢的視頻。假設沒有這個選項,用戶看視頻卡頓了無法切換清晰度,就只能憤憤地關掉窗口了。那麼多個清晰度的視頻源咱們如何作到無縫的切換呢?咱們分狀況來討論一下:

點播中的清晰度切換方案 點播的切換清晰度是比較容易實現的,流程以下

如圖,簡而言之,就是嘗試加載視頻B,經過從視頻B的onMetaData中提取關鍵幀信息,推算出當前應該加載哪個關鍵幀,而後加載這個位置以後的數據,直到數據加載到以後,將視頻B的數據交給MSE,同時,清除掉以前視頻A在buffer中的緩存。

直播中的清晰度切換方案 直播清晰度切換目前尚未發現無縫的方案,所以建議在切換時,直接重建解碼器以及MSE,關於這方面的問題咱們還在探究。更多內容請關注咱們的開源播放器,也歡迎來咱們github提issue。

相關文章
相關標籤/搜索