背景
最近在负责游戏项目的开发,不免需要接触到音频相关能力的开发。在移动端实践后才了解到,iOS下的AudioDOM并没有被完全实现,比如iOS下就无法直接通过JS控制AudioDOM的音量大小,只能控制其是否被静音,另外静音拨杆的优先级比音量键的优先级要高。如果用户通过拨杆打开了静音模式,AudioDOM默认是没办法控制音量的。
尝试解决问题
由于需要实现通用能力中规划的音频的响度控制,搜索一圈后发现可以引入WebAudio来解决问题。(https://stackoverflow.com/questions/27296391/is-there-any-possibility-to-control-html5-audio-volume-on-ios)
改造前音频架构
初始化阶段的时候,通用组件会检查DOM树是否存在AudioDOM,如果不存在就会尝试创建一个新的AudioDOM并挂载到DOM树上。状态操作也是直接对DOM树上的AudioDOM操作。
改造后音频架构
WebAudio支持多种方式创建音频输入,即可以直接通过AudioBuffer获得音频输入,也可以通过createMediaElementSource从已有的AudioDOM上创建音频输入。此处我使用的是后者,通过在AudioDOM与扬声器之间创建了一个“代理层”,将音频输出通过GainNode(增益器节点)处理了一下。通过GainNode我们可以设置音频的响度,间接的实现了iOS的音量控制。
错误上报
不幸的是,测试的Testcase还不够完善,业务被发布后,错误量激增,告警拉满😂。
经过梳理后主要是以下三种问题:
- Failed to construct ‘AudioContext’: The number of hardware contexts provided (6) is greater than or equal to the maximum bound (6).
- The provided value is non-finite
- undefined is not a constructor (evaluating ‘new(window.AudioContext||window.webkitAudioContext)’)
1.AudioContext只能创建6个实例
Webview版本小于66中只能创建小于等于6个的AudioContext,会导致页面无法继续播放音频。(https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)
解决办法:
1. 增加全局AudioContext池,通过一个实例来管理最大音频对象数量。
2. 不使用的AudioContext需要被及时释放,避免占用音频对象资源。
2.音频节点提供的不是有限值
在某些环境中,JS的除运算计算出来的值不是一个有限值,直接赋值给WebAudio的节点会报该错误。
解决办法:
赋值前通过isFinite函数检查是否是有限值,如果不是则使用一个固定的值作为兜底即可。
3.window.AudioContext不存在
按道理来说AudioContext在执行JS代码之前就应该被挂载到window上了。但遗憾的是,iOS15之后的WKWebview会经常出现,执行JS代码的时候AudioContext仍然未被挂载到window上,导致执行构造函数的时候报错。
解决办法:
捕获错误并且定时重试,一般第一次失败的2-3秒后就能创建成功了。