🌞

纯前端实现音频无缝切换

所谓无缝切换,目的是想在没有完整获取文件之前,先加载第一部分。从而能够保证音频内容可以提前漏出,或者实现一些直播的效果。背景区别于直接推流,这里的需求方式其实可以不是直播的方式。只要是几个片段歌曲在切

文链接在语雀:https://www.yuque.com/wumingshi/rkh1qq/

所谓无缝切换,目的是想在没有完整获取文件之前,先加载第一部分。从而能够保证音频内容可以提前漏出,或者实现一些直播的效果。

背景

区别于直接推流,这里的需求方式其实可以不是直播的方式。只要是几个片段歌曲在切换的时候可以做到无缝切换,同时歌曲拆分片段的时候能够按照节拍对齐,也是同样可以实现无缝切换的。客户端本身是具有无缝播放的能力,那前端是否可以基于能力本身做出无缝切换的效果呢?本篇文章就是通过几种维度进行尝试,目的是希望能够达到类似客户端一样的无缝切换的。

直接推流

这个方案是最彻底的,就是单纯直播/推流的方式。主要就是通过搭建后端服务,针对音频进行封装,通过某种协议进行实时推流的方式。

目前主流的协议主要包含

FLV

RTMP

基于flash方案,显然不适合当下

HLS

苹果推崇的一种协议标准,参考hls.js

以上是一些主流的推流方案,而本文将要探索的是不基于推流的方案,单纯基于音频的片段拼接,实现在前端层面的无缝衔接播放。

音频片段无缝播放

探索1:

要想达到无缝播放的效果,最容易想到的是通过数据提前准备,在合适的时机进行切换。方案也特别简单:

懂得都懂,这里推荐下Hoverjs可以比较方便的使用webAudio,然而实现的效果却特别不理想,会出现播放中断的现象。

原因其实也比较容易接受/理解,就是在切换播放的时候,涉及到上下文的销毁创建,以及在监听播放结束的时候,音频已经断开输出以及一些其他不可能完美衔接的原因。

效果演示:https://demo.onehacker.top/seamless-play/?type=demo2 点击【方案1】进行试听

【推荐】探索2:

基于“探索1”的理所当然的失败,就需要探索一种不需要切换播放上下文,那就需要一种直接进行数据拼接的方式。前端进行数据拼接,同时在这里我们还有一种需求就是需要进行动态数据拼接,也就是我们不能在拼接完成之后进行数据拼接,而是需要在播放的过程中进行数据拼接。


想法有了,大概要怎么做呢?

整体实现流程:

也就是将原有的数据转化为二进制数据,基于audioBuffer进行动态填充,从而实现了在不中断播放的前提下,实时推送数据的目的。

在实现过程中有两个坑点:

  • AudioContext.decodeAudioData在iOS下不支持Promise,需要通过回调的方式进行调用
  • AudioBuffer.copyToChannel的实现也在iOS下实现不完整,需要通过buffer.getChannelData(0).set(audioData.getChannelData(0), 0);进行代替。

同时需要注意的是,进行decodeAudioData的时候,会根据当前的音频进行音频重采样,这个一定程度会对音频的质量存在影响,同时即使使用高的采样率也没办法保证音频质量,反而会使音频变声。

总之,此种方案能够比较好的满足当前的场景,是一种比较推荐的方案。其实这里还没有用到 webAudio processNode,或许基于此种方案能够实现更令人意外的效果,这里暂时先不展开了,毕竟当前方案已经满足了😄

效果演示:https://demo.onehacker.top/seamless-play/?type=demo2 点击【方案2】进行试听

探索3:

媒体源扩展 API(MSE)提供了实现无插件且基于 Web 的流媒体的功能。使用 MSE,媒体串流能够通过 JavaScript 创建,并且能通过使用 <audio><video> 元素进行播放。

基于媒体源拓展API,同样可以实现类似“探索2”的方案,只不过实现的流程有点区别。

此种方案也能够达到数据拼接的效果,但是MSE在iOS尤其是iPhone上支持度很差

实际播放的效果没有方案2流程,猜想可能的原因:

  • 音频本身的原因,待验证
  • 通过sourceBuffer进行appendBuffer进行数据解码,导致前后没有一致性?但方案2也存在类似的问题才对。
  • 最终使用audio进行播放,或许改为webAudio会有所变化,待验证

效果演示:https://demo.onehacker.top/seamless-play/?type=demo2 点击【方案3】进行试听

总结

在 android 上可直接使用“方案3”作为首选,方案2作为降级方案;iOS直接使用方案2。当前整体方案还处于demo阶段,是否能够“in Production”还需要进一步推敲。

updatedupdated2023-10-312023-10-31