關鍵詞: 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從哪個版本開始,對AudioContext
的createScriptProcessor
方法呼叫時會在控制檯中列印方法過時提醒:[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
狀態; - 方式二:直接呼叫
AudioContext
的resume
方法,等到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】