360視頻云前端團(tuán)隊圍繞HEVC前端播放及解密實現(xiàn)了一套基于WebAssembly、WebWorker的通用模塊化Web播放器,在LiveVideoStackCon2019深圳的演講中360奇舞團(tuán)Web前端技術(shù)經(jīng)理胡尊杰對其架構(gòu)設(shè)計、核心原理,具體痛點問題的解決方式進(jìn)行了詳細(xì)剖析。
文 / 胡尊杰
整理 / LiveVideoStack
奇舞團(tuán)是360集團(tuán)最大的大前端團(tuán)隊,同樣也是TC39和W3C會員,擁有Web前端、服務(wù)端、Android、iOS、設(shè)計、產(chǎn)品、運營等崗位人員,旗下的開源框架和技術(shù)品牌有SpriteJS、ThinkJS、MeshJS、Chimee、QiShare、聲享、即視、奇字庫、眾成翻譯、奇舞學(xué)院、奇舞周刊、泛前端分享等。
奇舞團(tuán)支持的業(yè)務(wù)基本上涵蓋了360大部分業(yè)務(wù)線。我個人最開始的時候也曾帶隊負(fù)責(zé)360核心安全平臺的Web前端支持,包括大家耳熟能詳?shù)陌踩l(wèi)士、殺毒軟件等。隨著公司的業(yè)務(wù)發(fā)展,后面也負(fù)責(zé)了IoT業(yè)務(wù)前端支持,最近兩年主要配合360視頻云的一些Web前端支持工作?;贖EVC的播放器,實際上就是來源于我們最近做的一個叫QHWWPlayer的播放器。HEVC并不是一個新鮮事物,但對于我們團(tuán)隊來說,Web前端的HEVC播放器一直是個亟待優(yōu)化的領(lǐng)域。雖然移動終端或PC端HEVC播放器已經(jīng)遍地開花,但在Web端仍舊有很多地方需要改進(jìn)。包括現(xiàn)存一系列智能硬件產(chǎn)品,也在固件采集端已經(jīng)應(yīng)用了HEVC的編碼,不過如果想讓其在Web端呈現(xiàn)并達(dá)到用戶需求仍需加倍的努力。本次分享將從以下幾個維度展開,希望能給大家?guī)硪欢ǖ膮⒖純r值。
上圖展示了HEVC在瀏覽器端的支持情況,其中紅色代表不支持的瀏覽器對應(yīng)版本,綠色代表對HEVC具有良好的支持,青色代表無法保證瀏覽器可以很好地支持HEVC。總體上來說HEVC在瀏覽器端并不是一個得到廣泛支持的靠譜方案。
一般情況下,PC端瀏覽器都給我們提供了相應(yīng)的API,如果我們的業(yè)務(wù)場景是支持HEVC的瀏覽器,可嘗試有效利用瀏覽器的原生能力。
基于瀏覽器原生video,配置source時指定解碼器,告知瀏覽器當(dāng)前視頻采取的是哪一種編碼方案。如果瀏覽器自身有能力進(jìn)行解碼那么其自然會走入“支持HEVC”的邏輯分支當(dāng)中。
也可以另外通過JS實現(xiàn)檢測功能,JS也提供了相應(yīng)API——canPlayType來判斷當(dāng)前瀏覽器環(huán)境是否支持HEVC解碼。
但如果以上流程無法得到有效支持呢?這也是本次分享我們討論的重點。
瀏覽器端視頻解碼總共有以上三種方案,首先就是前文我們提到的基于瀏覽器原生能力的播放,例如基于video標(biāo)簽拉流、解碼以及渲染播放,整個過程完全由瀏覽器實現(xiàn)。第二種方案是首先通過JS來下載視頻流、對視頻流進(jìn)行解封裝與轉(zhuǎn)封裝處理,最后再通過瀏覽器提供的相關(guān)API,交由瀏覽器原生video進(jìn)行解碼與渲染播放。如開源社區(qū)當(dāng)中的HLS.JS或FLV.JS等就是基于該思路。
但是HEVC不能僅靠解封裝與轉(zhuǎn)封裝來實現(xiàn),因為其本質(zhì)上在解碼層就不支持。因此第三種方案就是:JS下載的視頻流首先經(jīng)由解封裝(解密)處理,并在接下來進(jìn)行解碼,解碼完成后渲染播放。如果我們這里轉(zhuǎn)成瀏覽器普遍支持的解碼格式并讓video標(biāo)簽進(jìn)行播放,盡管理論上可行,但成本顯然是非常高的,并且中間存在一個無端的浪費。因此這里通常直接采用瀏覽器端Canvas+WebAudio API實現(xiàn)視頻與音頻的渲染,而不再使用瀏覽器原生video能力。這里如果使用純?yōu)g覽器原生的JS,由于 JS天生單線程執(zhí)行的弱勢,會導(dǎo)致整個處理的效率比較差。
近期,萬維網(wǎng)標(biāo)準(zhǔn)化委員會正式推出了WebAssembly規(guī)范。一方面我們可以借助WebAssembly高于JS的能力,實現(xiàn)更加出色的大規(guī)模數(shù)據(jù)處理與解碼,另一方面基于WebAssembly,我們也能方便地將傳統(tǒng)媒體處理中基于C或C++開發(fā)的一些媒體處理能力集成在瀏覽器端執(zhí)行,并且可通過JS來調(diào)用API。對于熟悉傳統(tǒng)Web前端開發(fā)的我們來說,這也是一個值得我們堅持探索與實踐的全新領(lǐng)域。有了WebAssembly之后,我們就可以讓部門內(nèi)擅長視頻處理的專家級同事來配合實現(xiàn)更加出色的瀏覽器端視頻播放,相對以往的開發(fā)流程來說,無論是能力、成本控制還是效率與靈活程度都有十分顯著的提升。
上圖展現(xiàn)了瀏覽器端WebAssembly的支持情況,盡管個別低版本的瀏覽器有一些支持限制,但隨著標(biāo)準(zhǔn)化委員會對該標(biāo)準(zhǔn)的不斷推進(jìn),情況會變得越來越好。在包括一些混合式場景,例如APP內(nèi)嵌(比如聊天工具或通訊工具當(dāng)中打開一個鏈接)等情況,是否支持也取決于WebView本身提供的能力以及WebAssembly的支持情況,總體上來說趨于向好。
HEVC播放器的需求目標(biāo),就是基于 JavaScript 相關(guān)API,配合FFmpeg+WASM達(dá)成 HEVC 在瀏覽器端的解碼&解密、渲染播放的需求,接下來我們就開始研究如何落地這一目標(biāo)。
總體架構(gòu)設(shè)計思路如上圖所示,首先我們需要一個專門負(fù)責(zé)下載的下載器,該下載器也是基于瀏覽器的JS Fetch或XHR API,以實現(xiàn)文件獲取或直播拉流等操作。成功拉取的視頻流會被存儲在一個數(shù)據(jù)隊列當(dāng)中,隨后基于WebAssembly(WASM)+FFmpeg的解碼器會來消費處理隊列里這些流數(shù)據(jù),解碼出音視頻數(shù)據(jù),并放置在音視頻幀數(shù)據(jù)隊列當(dāng)中,等待隨后的渲染器對其進(jìn)行渲染處理。渲染器基于WebGL+Canvas與WebAudio調(diào)用硬件渲染出圖像與音頻。
最后則是控制層用于貫穿整體流程中下載、解碼、渲染等獨立模塊,同時實現(xiàn)底層一些基本功能:如之前我們提到JS為單線程,而瀏覽器提供的WebWork API可拉起一個子線程。該流程中每一個模塊都是獨立的,隊列中的生產(chǎn)與消費過程也是異步進(jìn)行的。(我們可基于JS本身一些比較好的特性實現(xiàn)諸多便捷的功能。例如基于Promise可以將異步過程進(jìn)行較為合理的封裝,并呈現(xiàn)一些異步處理邏輯流程的關(guān)鍵環(huán)節(jié)的控制到UI層。)
除此之外,還有控制層的一些基礎(chǔ)配置選項,包括播放器本身的一些事件或消息的管理,都可以基于控制層來實現(xiàn)。
下載器作為一個基本模塊獨立存在,具有初始配置、啟動、暫停、停止、隊列管理與Seek響應(yīng)(用于進(jìn)度條拖拽)等基本功能。上圖左側(cè)圖標(biāo)是在開發(fā)完成后,基于下載器的事件消息呈現(xiàn)的數(shù)據(jù)可視化結(jié)果。(柱狀圖表示單位時間下載量,這里我們可以看到的是,下載量并不均勻,其中的變化可能取決于推流端、服務(wù)端、用戶端,也可能取決于整個網(wǎng)絡(luò)環(huán)境。)
下載器方面需要留意五個關(guān)鍵問題點:
線性的數(shù)據(jù)流的合并與拆分
我們應(yīng)當(dāng)進(jìn)行線性數(shù)據(jù)流的合并與拆分。理論上瀏覽器從服務(wù)端下載一個視頻流的過程是線性的,但瀏覽器的表現(xiàn)實際上并非如此,二者的差異可能會很大。
例如當(dāng)一個瀏覽器啟動并基于JSFetch API抓取流,其過程也是通過API監(jiān)聽數(shù)據(jù)回調(diào)來實現(xiàn),每次回調(diào)可能間隔會很短、數(shù)據(jù)量也只是一個很小的一千字節(jié)左右的數(shù)據(jù)包。但有些瀏覽器的表現(xiàn)并非如此,它們會等抓取到一個1M或2M的數(shù)據(jù)包之后才反饋給API回調(diào)。
而那些過于零碎的數(shù)據(jù)直接丟給隊列或之后的流程來處理,這樣勢必導(dǎo)致更頻繁的數(shù)據(jù)處理;數(shù)據(jù)包體積大的直接隊列和后續(xù)流程勢必增加單次處理成本。
因此對線性數(shù)據(jù)流的合理合并與拆分十分必要,整個過程也是結(jié)合初始配置來實現(xiàn)閾值控制。
通過閾值調(diào)節(jié)控制,我們希望能夠做好用戶端瀏覽器硬件資源消耗,與該業(yè)務(wù)場景下媒體播放產(chǎn)品服務(wù)體驗之間的取舍與平衡。
內(nèi)部維護(hù)管理 range 狀態(tài)
除此之外,下載器實際上也需要內(nèi)部維護(hù)管理range 狀態(tài)。例如當(dāng)用戶選擇點播時,我們需要明確是從哪一個字節(jié)位置到另一個字節(jié)位置下載傳輸中間這一片數(shù)據(jù)。而在直播過程中,則可能出現(xiàn)由網(wǎng)絡(luò)環(huán)境造成卡頓或用戶端主動暫停的現(xiàn)象,此時下載器需要明確知道播放或當(dāng)前下載的位置。
不同媒體類型數(shù)據(jù)獲取的差異
第三點是不同媒體類型數(shù)據(jù)獲取的差異,也就是下載器針對不同的媒體類型開發(fā)不同的下載功能。例如一個FLV直播流可以理解為是一個連續(xù)的線性的數(shù)據(jù)獲取,而點播則以包為單位獲取。對于HLS流需要獲取m3u8列表,完成分析之后再從中選取數(shù)據(jù)包的地址并單獨下載,隨后進(jìn)行流的合并或拆分。總地來說,我們需要保證數(shù)據(jù)的最終產(chǎn)出盡量均勻存儲到隊列中,以便于后續(xù)的一系列處理。
MOOV 前置或后置
在媒體處理中像MOOV等的索引數(shù)據(jù)有前置與后置兩種情況,這里需要注意的是,我們的播放器基于Web端。
若索引文件為后置,如果播放器直接下載了一部分?jǐn)?shù)據(jù)就直接丟給FFmpeg解碼器進(jìn)行解碼,由于FFmpeg解碼器無法獲取索引,當(dāng)然也就無法解碼成功。除非解碼器等待整體媒體源下載完畢,實際上這樣是不現(xiàn)實的。
另外由于我們無法控制MOOV索引數(shù)據(jù)的體量,前置索引的大小無法確定,尤其對于一些特殊情況,這種邏輯會帶來很多問題。(但是這里有一個取巧的辦法,就是我們可以嘗試首先抓取前面幾個數(shù)據(jù)包,探測MOOV邊界,并基于此得到MOOV的長度,從而判斷取舍在什么時機啟動后續(xù)的解碼。)
慎重并折中的控制內(nèi)存消耗
最后,慎重并折中控制內(nèi)存消耗也至關(guān)重要。例如盡管較大的緩存能帶來流暢的播放,但在Seek時就會帶來很大的浪費,我們則需要根據(jù)服務(wù)所在的應(yīng)用場景、幀率碼率等來實現(xiàn)合理的折中與取舍。
下載器之后,整個流程的核心能力就是解碼器。解碼器的基本功能與下載器相比大同小異,需要特別關(guān)注的是解碼器并不是像下載器完全是去調(diào)用一個原生的JS Fetch API或XHR,而是在啟動WebWorker之后再啟動WebAssembly(這里的WebAssembly依賴中是引入了定制化的FFmpeg API,以解決解容器、解碼等需求),并實現(xiàn)一些API的交互。上圖左側(cè)展現(xiàn)了音頻與視頻幀解碼數(shù)據(jù)隊列的可視化結(jié)果。
解碼器方面,需要關(guān)注的關(guān)鍵問題主要有以下幾點:
啟動解碼前依賴數(shù)據(jù)量控制
剛才講到MOOV前置與后置時我們也提及這一點,也就是在啟動解碼前做好數(shù)據(jù)量控制,明確其數(shù)據(jù)量是否已經(jīng)達(dá)到FFmpeg的基本需求。如果索引文件的數(shù)據(jù)還沒有完全給到就直接使用命令行啟動FFmpeg,那么就會出現(xiàn)報錯的情況。我們應(yīng)當(dāng)結(jié)合數(shù)據(jù)量的精準(zhǔn)控制來對解碼器的啟動時機做合理的判斷。
主動向下載器獲取數(shù)據(jù)
解碼器需要主動獲取下載器生成的數(shù)據(jù)隊列,這樣系統(tǒng)便可根據(jù)數(shù)據(jù)消費效率獲知當(dāng)前解碼器是否處于繁忙的狀態(tài)。同時,主動向下載器獲取數(shù)據(jù)也能在一定程度上減輕CPU的負(fù)擔(dān),并可根據(jù)CPU的負(fù)載來決定當(dāng)前從下載端應(yīng)該獲取多少數(shù)據(jù)。例如如果CPU負(fù)載較大則數(shù)據(jù)隊列自然會出現(xiàn)累積,我們可以在下載器初始化時設(shè)置一個閾值,如果數(shù)據(jù)隊列積累達(dá)到該閾值則下載器暫停下載,這樣就可合理控制處理的整體流程并確保播放的正常。
動態(tài)解碼模式控制CPU消耗
整個解碼過程實際上還依賴CPU的性能,如果單幀解碼的時間較長,例如一個幀率是25的視頻,僅單幀解碼就需耗費半秒鐘甚至更長時間,此時如果我們依然按照這樣半秒鐘或更久的頻度解碼,則解碼數(shù)據(jù)生產(chǎn)效率完全跟不上渲染的自然時間進(jìn)度,效果肯定不符合預(yù)期,播放也會斷斷續(xù)續(xù)。因此我們需要針對不同的應(yīng)用場景,使用動態(tài)解碼模式(主動丟幀)控制好CPU的消耗。例如在直播或安防場景下,我們可以舍棄一些指標(biāo)以保證解碼與傳輸?shù)臅r效性。
獨立的音頻、畫面幀數(shù)據(jù)隊列
如上圖左側(cè)所示,獨立的音頻與畫面幀數(shù)據(jù)隊列分別管理;比如我們啟動丟幀策略的話,會看到畫面幀數(shù)據(jù)量變少,但聲音沒有變化。
音頻重新采樣
采集端編碼數(shù)據(jù)的音頻采樣率需要結(jié)合播放端的支持情況來留意兼容問題。
瀏覽器是一個比較特殊的應(yīng)用場景,各瀏覽器對音頻渲染中采樣率的支持程度也是不同的。
例如安防場景對聲音的要求并不是很高,通常16,000的采樣率即可,但是如果想在瀏覽器端播放視頻,則部分瀏覽器要求至少22,050的采樣率,否則瀏覽器端播放無法成功識別并渲染音頻數(shù)據(jù)。FFmpeg本身可以進(jìn)行音頻重新采樣,因此我們可以在解碼器端加入相應(yīng)的配置項,如果用戶有該需求那么就可以啟動音頻重新采樣,重新把16,000的音頻采樣率重采樣成符合瀏覽器所要求的22050采樣率。有了符合要求的獨立的音頻與視頻數(shù)據(jù)幀隊列,接下來也自然就能基于瀏覽器實現(xiàn)對音視頻的渲染與呈現(xiàn)。
渲染器的基本功能與下載器、解碼器相似,不同之處在于以下幾個關(guān)鍵點:
依賴解碼、UI提供畫布
渲染器需要瀏覽器提供一個獨立的畫布用于繪制相應(yīng)的視覺畫面內(nèi)容。在UI模塊初始化時呈現(xiàn)出一個畫布的容器,渲染器渲染生成的畫面才能表現(xiàn)在網(wǎng)頁上。
除此之外,渲染器依賴解碼器解碼生產(chǎn)出的音視頻幀數(shù)據(jù)才能進(jìn)行音畫渲染。
主動向解碼器獲取幀數(shù)據(jù)
這一點與解碼器向下載器主動拿數(shù)據(jù)相似。
分緩存隊列、渲染隊列
渲染器會消費處理等待渲染的幀數(shù)據(jù)隊列,只不過幀數(shù)據(jù)會被分為緩存隊列與渲染隊列。
而之前我們介紹的下載器與解碼器,本身只有一組數(shù)據(jù)隊列。為什么要這樣呢?渲染器調(diào)用WebAudio API將音頻數(shù)據(jù)傳輸給瀏覽器進(jìn)行PCM渲染時,無法將已經(jīng)通過該API傳輸給瀏覽器的數(shù)據(jù)做取回控制,因此就需要記錄當(dāng)前已經(jīng)給了多少數(shù)據(jù)到瀏覽器,這就是“渲染隊列”。而“緩存隊列”則是從進(jìn)程中獲取一部分?jǐn)?shù)據(jù)先存儲在一個臨時隊列當(dāng)中,從而避免頻繁地向處于另一個獨立WebWorker中的解碼器索取其音畫幀隊列數(shù)據(jù),而帶來不必要的時間消耗。
音畫同步、倍速播放、Waiting
音畫同步、倍速播放以及判定是否處于等待狀態(tài)至關(guān)重要。比如要追求直播的低延時,網(wǎng)絡(luò)抖動導(dǎo)致數(shù)據(jù)堆積發(fā)生的時候,倍速追幀是個有效的辦法。
動態(tài)碼率變化
一個視頻在播放的過程中,可能隨網(wǎng)絡(luò)狀態(tài)的波動出現(xiàn)碼率的動態(tài)變化,例如為適應(yīng)較差的網(wǎng)絡(luò)狀況,播放器可以主動將媒體流獲取從一個較為清晰的高分辨率變化到一個比較模糊的低分辨率源。
而再渲染中,基于WebGLCanavas的渲染器,我們首先需要對YUV著色器進(jìn)行初始化操作,而YUV著色器的初始化,依賴于其所繪制的數(shù)據(jù)對應(yīng)的分辨率、比例與尺寸。如果最開始的分辨率、比例和尺寸與之后要渲染的數(shù)據(jù)不一樣,而我們又未對此做相應(yīng)的響應(yīng)適配,那么就會出現(xiàn)畫面繪制花屏的情況。而動態(tài)碼率變化就是要隨時響應(yīng)每一畫面幀所對應(yīng)的分辨率變化,對YUV著色器作動態(tài)調(diào)整,從而保證畫面的實時性與穩(wěn)定性。
從下載、解碼到渲染,視頻播放器的基本流程就此建立,播放器便有了獲取媒體數(shù)據(jù)、完成解碼、呈現(xiàn)音畫效果的基本能力。
基本的UI如上圖左側(cè)所示,上半部分是整個播放器在實例化之前我們可以去做的一系列初始化配置。圖中所示的僅是一小部分參數(shù),例如媒體源的地址、是否啟用了加密Key、對應(yīng)的解密算法,包括渲染時為滿足某些特定場景下的需求,音視頻是同時進(jìn)行渲染還是在主動控制下僅渲染音頻或視頻——例如在安防監(jiān)控業(yè)務(wù)場景,會有一些設(shè)備需要音頻采集、另一些不需要,或者干脆播放時就不想播放源流音頻等等。若在這里播放器不做判定支持,則存在由于音畫同步控制依賴音頻幀視頻幀時間戳比對,但沒有音頻幀數(shù)據(jù)的原因?qū)е聼o法正常播放,而播放器使用者能進(jìn)行主動控制則可以避免該問題。
UI的基本功能包括實例化、用戶操作觸發(fā)后續(xù)流程涉及的各模塊接下來要做什么,還有狀態(tài)信息響應(yīng)展現(xiàn),也就是根據(jù)用戶交互行為和播放器工作狀態(tài)作出反饋與信息傳遞。
另外,UI也需要對相應(yīng)的狀態(tài)變化作出響應(yīng),例如用戶控制當(dāng)前播放器從正在播放切換到暫停,那么UI層面則需要針對用戶操作進(jìn)行相應(yīng)的變化。還有快進(jìn)、拖拽進(jìn)度條等等。
最后的控制層至關(guān)重要,首先控制層隔離校驗對外暴露的參數(shù)及方法。播放器可實現(xiàn)或具備的特性有很多,不可能全部暴露給用戶。在播放視頻時,下載與解碼的數(shù)據(jù)實際上存在一個前后呼應(yīng)的關(guān)系,如果我們不考慮用戶行為與需求,在網(wǎng)頁上呈現(xiàn)播放器的所有特性。而用戶也不對其進(jìn)行科學(xué)性選擇與判斷,而是隨意調(diào)用API,勢必會帶來矛盾、沖突與混亂。因此我們需要隔離配置信息、校驗對外暴露的控制參數(shù)及方法,以避免可能存在的沖突。
另外根據(jù)之前的介紹我們可以看到,不同模塊的基本功能大致相同。因此在控制層我們需要統(tǒng)一各模塊的生命周期,并完成用于調(diào)度各模塊工作的基礎(chǔ)類的實現(xiàn)。
每個獨立的模塊什么時刻可以實例裝載?什么時刻銷毀?該模塊是否支持熱插拔?各模塊生命周期狀態(tài)的管控與事件消息的監(jiān)聽與調(diào)度…… 這些都由控制層進(jìn)行管理。
有時我們需要做一些取舍,例如編碼器并不是基于FFmpeg,而是基于我們自己的解碼解決方案,那么就可以嘗試在播放器實例化時候,更換對應(yīng)模塊當(dāng)中相對應(yīng)的部分依賴為自己的解碼方案;如果我們需要調(diào)整播放器UI層界面樣式,那么就可能需要定制自己的UI模塊……
在這個播放器實現(xiàn)中,為了規(guī)避單線程一些弊端,我們基于WebWorker API對重點模塊開啟子線程。
而WebWorker本身的設(shè)計存在各種不便:
首先,要求我們必須單獨打包一個JS文件,基于 new Worker(“*.js”)引入到項目中。
但我們整個播放器作為SDK項目的構(gòu)建來說,通常只產(chǎn)生一個JS文件發(fā)布出去,才是合理的。如果同時產(chǎn)生多個JS文件,這對我們的調(diào)試、開發(fā)或后續(xù)應(yīng)用等來說都不方便。
針對這個問題我們結(jié)合Promise 實現(xiàn)了PromiseWebWorker,PromiseWebWorker 相對于原生Worker,參數(shù)不再必須是傳入一個JS引用路徑,而是可以傳入一個函數(shù)。
這樣以來我們就可以在項目編譯時生成一個獨立的JS文件,在播放器的執(zhí)行過程中將其中worker依賴的那部分函數(shù)內(nèi)容生成一個虛擬的文件依賴地址,作為WebWorker執(zhí)行的資源。
其次,WebWorker原生能力實現(xiàn)父子線程之間數(shù)據(jù)傳遞通訊,只能通過postMessage傳送數(shù)據(jù)、通過onMessage獲取傳送過來的數(shù)據(jù),這對于頻繁的數(shù)據(jù)交互中想保證上下文關(guān)聯(lián)對應(yīng)關(guān)系是比較麻煩的。PromiseWebWorker則借助了Promise的優(yōu)勢,對以上整個數(shù)據(jù)交換過程做嚴(yán)格的應(yīng)答封裝處理,從而實現(xiàn)播放器功能的健壯可靠。
上圖鏈接http://lab.pyzy.net/qhww中是...
若對此感興趣可以前往試用研究
調(diào)度控制層控制下載器、解碼器、渲染器與UI&交互四大模塊,如果要做某功能模塊的業(yè)務(wù)定制化開發(fā)、功能增強補充,對對應(yīng)獨立的模塊內(nèi)部進(jìn)行優(yōu)化并做出相應(yīng)的功能擴(kuò)展或者調(diào)整即可。
開發(fā)過程所遇到的難點總體可以用以上三點來概括:首先基于WebAssembly 工具鏈(emscripten.org),借助EMSC編譯器我們可以直接將一個C和C++編譯成JS可用。這一過程本身存在諸多不便之處,主要是因為其本身對系統(tǒng)一些底層庫的依賴或?qū)τ陂_發(fā)環(huán)境的要求,導(dǎo)致可移植性并沒有那么好,當(dāng)需要跨機器協(xié)作時容易出現(xiàn)諸多問題?,F(xiàn)在我們內(nèi)部的解決方案是自己找一臺專用機器來配置做為編譯發(fā)布使用。
第二點是隊列管理與狀態(tài)控制,只有精確實現(xiàn)隊列管理與狀態(tài)控制,我們才能保證整個程序能合理穩(wěn)定的執(zhí)行。
第三點就是項目構(gòu)建打包,我們要解決前端一些構(gòu)建打包的習(xí)慣以及其在邏輯需求上存在的一些沖突。
展望未來,我希望未來瀏覽器能對HEVC有更加出色的支持。本次分享雖然是一個播放器,但我們知道FFmpeg的能力不只是解碼播放,還可以做更多實用工具的發(fā)掘?qū)崿F(xiàn)。同時我也希望未來媒體類型百花齊放,甚至私有編解碼也能夠形成Web端場景更規(guī)范靈活的解決方案。WASM成熟、標(biāo)準(zhǔn)化完善、各業(yè)務(wù)領(lǐng)域?qū)?yīng)解決能力的細(xì)分,也是很值得期待的一件事情;而回到播放器本身,字幕、AI、互動交互等都是能進(jìn)一步提升音視頻播放服務(wù)的可玩性與用戶體驗方向值得研究的方向。