几行代码把Chrome搞崩溃之:HTML5 MP3录音由ScriptProcessorNode升级成AudioWorkletNode采坑记
<blockquote><p><strong>关键词: STATUS_ACCESS_VIOLATION AudioContext AudioWorkletNode audioWorklet addModule resume suspended createScriptProcessor</strong><br>
搞崩Chrome测试页:测试页地址</p>
</blockquote>
<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>事件起因</li><li>现象复现</li><li>填坑处理</li><li>最终结果</li></ul></div><p></p>
<h1 id="事件起因">事件起因</h1>
<p>我前些年GitHub开源的前端H5录音库:https://github.com/xiangyuecn/Recorder,提供了 mp3 wav ogg webm amr 格式支持,拥有丰富的音频可视化、变速变调处理、音频流播放、ASR语音识别等配套功能;搭配上强大的实时处理支持,可用于各种网页应用;最近打算尝试使用新浏览器特性升级一下,跟随上时代的发展。</p>
<p>不记得Chrome从哪个版本开始,对<code>AudioContext</code>的<code>createScriptProcessor</code>方法调用时会在控制台中打印方法过时提醒:<code> The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead.</code>。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/23ca700e086662ab90ee7509d7cdebce.png"></p>
<p><code>ScriptProcessor</code>虽然被标记为过时了,但当前所有现代浏览器包括早期点的浏览器均得到了很好的支持,还没有在哪个浏览器上被正式的移除,Chrome的过这个时提醒就导致了经常有人问是不是要改用<code>AudioWorklet</code>了,目前的结论是还没有这个必要,因为单就获得录音数据这个场景而言,PC端两者并在性能上并没有区别,反而<code>ScriptProcessor</code>在移动端更具性能优势,这当然是后话了。</p>
<p>其实很早就想去升级提供<code>AudioWorklet</code>的支持,简单的过了一遍文档,预计的需要增加的代码量也不会很大(最后实际压缩后增加了2KB 这里查看这100来行的代码),不过拖到了前段时间才把代码给写了。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/030df1eaa8d5f863c7e2b619b240e870.png"></p>
<h1 id="现象复现">现象复现</h1>
<p>在刚开始编写好测试的过程中,发现只要交互操作足够快,Chrome (版本:97)浏览器经常莫名其妙的崩溃(从来没有见过的现象),老版本Chrome80也会崩溃,错误代码:<code>STATUS_ACCESS_VIOLATION</code>,更老的66、70反而没有出现崩溃(得益于之前写的《自己制作Chrome便携版实现多版本共存》很方便测试),FireFox也不会崩溃。</p>
<p>经过反复测试,定位到问题:<code>suspended</code>状态下的<code>AudioContext</code>,在<code>audioWorklet.addModule</code>+<code>构造AudioWorkletNode</code>未完成时,同时进行<code>resume</code>调用,在恢复到<code>running</code>状态那一刻,浏览器崩溃了。其实根源还是在页面还没有用户交互过,就创建了<code>AudioContext</code>,这时的状态大概率是<code>suspended</code>(目的是浏览器禁止绕过没有用户操作自动声音播放)。</p>
<p>这些测试代码我已整合成了一个测试文件,点此进行测试,Chrome里面非常容易复现。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/7d4266434b0c9bd641b3b95693f650a1.png"></p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/5ed68683459f97db8e620a5f8041d731.png"></p>
<h1 id="填坑处理">填坑处理</h1>
<p>知道问题后,那解决就很简单了:</p>
<ul>
<li>方式一:等到有用户操作后再进行<code>AudioContext</code>的创建,此时能保证它一定是<code>running</code>状态;</li>
<li>方式二:直接调用<code>AudioContext</code>的<code>resume</code>方法,等到<code>running</code>后就没有崩溃的问题了。</li>
</ul>
<p>由于我这个是开源库,没法决定开发者是否会等用户操作,所以方式二根据普遍适用性。</p>
<p>调用<code>AudioContext.resume</code>后,它返回的是一个<code>Promise</code>,在<code>finally</code>块(不管<code>reject</code>,小概率中的小概率崩溃可以忽略)中进行<code>AudioWorklet</code>的初始化,就是把原有的初始化代码套一层即可。</p>
<p><img src="https://img-blog.csdnimg.cn/img_convert/5f4f6f2aa3cbc8d88815dd110cde169d.png"></p>
<p>另外<code>AudioContext.audioWorklet.addModule</code>在本地<code>file://</code>协议下,Chrome竟然不支持加载Blob Url(FireFox没有这个问题),同样是Worker,<code>WebWorker</code>就没有这个毛病;最后改用<code>data url</code>来兼容Chrome:<code>data:text/javascript;base64,......</code></p>
<h1 id="最终结果">最终结果</h1>
<p>坑也填了,该测试的也测试了,满怀欣喜的发布了采用<code>AudioWorklet</code>录音的新版本。发布后,在手机上把玩把玩,咦,怎么好像短了几秒录音。。。</p>
<p>PC端、手机端反复的定时测试,最后发布声明:由于audioWorklet内部1秒375次回调,在移动端可能会有性能问题导致回调丢失录音变短,PC端无影响,暂不建议开启audioWorklet。</p>
<p>所以,又更新了一个版本,简单恢复了一下,库里面默认<code>ScriptProcessor</code>依旧是主力,可通过设置<code>Recorder.ConnectEnableWorklet=true</code>强制开启使用<code>AudioWorklet</code>。</p>
<p>面向未来,如果以后哪个浏览器正式移除了<code>ScriptProcessor</code>,Recorder将会自动启用<code>AudioWorklet</code>,所以现在写的代码对将来会有很大意义,虽然目前有点鸡肋(感觉对<code>AudioWorklet</code>支持还是写早了,有点白写了的感觉),但意义还是很大的。</p>
<p>完整<code>AudioWorklet</code>实现代码,请移步阅读:recorder-core.js 第L157-L281行。</p>
<p>【End】</p><br><br>
来源:https://www.cnblogs.com/xiangyuecn/p/15988061.html
頁:
[1]