幾行程式碼把Chrome搞崩潰之:HTML5 MP3錄音由ScriptProcessorNode升級成AudioWorkletNode採坑記

xiangyuecn發表於2022-03-10

關鍵詞: STATUS_ACCESS_VIOLATION AudioContext AudioWorkletNode audioWorklet addModule resume suspended createScriptProcessor
搞崩Chrome測試頁:測試頁地址

事件起因

我前些年GitHub開源的前端H5錄音庫:https://github.com/xiangyuecn/Recorder,提供了 mp3 wav ogg webm amr 格式支援,擁有豐富的音訊視覺化、變速變調處理、音訊流播放、ASR語音識別等配套功能;搭配上強大的實時處理支援,可用於各種網頁應用;最近打算嘗試使用新瀏覽器特性升級一下,跟隨上時代的發展。

不記得Chrome從哪個版本開始,對AudioContextcreateScriptProcessor方法呼叫時會在控制檯中列印方法過時提醒:[Deprecation] The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead.

日誌提醒

ScriptProcessor雖然被標記為過時了,但當前所有現代瀏覽器包括早期點的瀏覽器均得到了很好的支援,還沒有在哪個瀏覽器上被正式的移除,Chrome的過這個時提醒就導致了經常有人問是不是要改用AudioWorklet了,目前的結論是還沒有這個必要,因為單就獲得錄音資料這個場景而言,PC端兩者並在效能上並沒有區別,反而ScriptProcessor在移動端更具效能優勢,這當然是後話了。

其實很早就想去升級提供AudioWorklet的支援,簡單的過了一遍文件,預計的需要增加的程式碼量也不會很大(最後實際壓縮後增加了2KB 這裡檢視這100來行的程式碼),不過拖到了前段時間才把程式碼給寫了。

核心程式碼

現象復現

在剛開始編寫好測試的過程中,發現只要互動操作足夠快,Chrome (版本:97)瀏覽器經常莫名其妙的崩潰(從來沒有見過的現象),老版本Chrome80也會崩潰,錯誤程式碼:STATUS_ACCESS_VIOLATION,更老的66、70反而沒有出現崩潰(得益於之前寫的《自己製作Chrome便攜版實現多版本共存》很方便測試),FireFox也不會崩潰。

經過反覆測試,定位到問題:suspended狀態下的AudioContext,在audioWorklet.addModule+構造AudioWorkletNode未完成時,同時進行resume呼叫,在恢復到running狀態那一刻,瀏覽器崩潰了。其實根源還是在頁面還沒有使用者互動過,就建立了AudioContext,這時的狀態大概率是suspended(目的是瀏覽器禁止繞過沒有使用者操作自動聲音播放)。

這些測試程式碼我已整合成了一個測試檔案,點此進行測試,Chrome裡面非常容易復現。

復現測試頁

被搞崩潰了

填坑處理

知道問題後,那解決就很簡單了:

  • 方式一:等到有使用者操作後再進行AudioContext的建立,此時能保證它一定是running狀態;
  • 方式二:直接呼叫AudioContextresume方法,等到running後就沒有崩潰的問題了。

由於我這個是開源庫,沒法決定開發者是否會等使用者操作,所以方式二根據普遍適用性。

呼叫AudioContext.resume後,它返回的是一個Promise,在finally塊(不管reject,小概率中的小概率崩潰可以忽略)中進行AudioWorklet的初始化,就是把原有的初始化程式碼套一層即可。

填坑程式碼

另外AudioContext.audioWorklet.addModule在本地file://協議下,Chrome竟然不支援載入Blob Url(FireFox沒有這個問題),同樣是Worker,WebWorker就沒有這個毛病;最後改用data url來相容Chrome:data:text/javascript;base64,......

最終結果

坑也填了,該測試的也測試了,滿懷欣喜的釋出了採用AudioWorklet錄音的新版本。釋出後,在手機上把玩把玩,咦,怎麼好像短了幾秒錄音。。。

PC端、手機端反覆的定時測試,最後釋出宣告:由於audioWorklet內部1秒375次回撥,在移動端可能會有效能問題導致回撥丟失錄音變短,PC端無影響,暫不建議開啟audioWorklet。

所以,又更新了一個版本,簡單恢復了一下,庫裡面預設ScriptProcessor依舊是主力,可通過設定Recorder.ConnectEnableWorklet=true強制開啟使用AudioWorklet

面向未來,如果以後哪個瀏覽器正式移除了ScriptProcessor,Recorder將會自動啟用AudioWorklet,所以現在寫的程式碼對將來會有很大意義,雖然目前有點雞肋(感覺對AudioWorklet支援還是寫早了,有點白寫了的感覺),但意義還是很大的。

完整AudioWorklet實現程式碼,請移步閱讀:recorder-core.js 第L157-L281行

【End】

相關文章