如何实现WebSocket音频数据流式传入audio元素无中断播放
音频流式无缝播放实现方案
直接使用浏览器原生的 MediaSource Extensions (MSE) API 即可实现你要的「管道」能力:你只需要在一端持续写入收到的音频数据包,浏览器自动将数据流向audio播放端,全程不需要自己编写复杂的底层播放管控代码,也不会打断正在进行的播放。
核心实现逻辑
- 全局仅初始化一次audio元素和MediaSource实例,将两者绑定,不需要每次收到数据包都新建音频实例
- 利用MediaSource的SourceBuffer作为数据缓冲区,每次收到新的音频数据直接追加到缓冲区中
- 新增简单的队列机制避免并发写入缓冲区报错
完整实现代码
初始化代码(仅执行一次,建议在WebSocket连接前初始化即可
// 初始化全局唯一的audio元素 const audio = document.createElement('audio'); audio.controls = true; document.body.appendChild(audio); const mediaSource = new MediaSource(); audio.src = URL.createObjectURL(mediaSource); let sourceBuffer = null; // 数据队列,解决SourceBuffer写入过程中追加数据的冲突问题 const bufferQueue = []; let isAppending = false; // MediaSource实例加载完成后初始化缓冲区 mediaSource.addEventListener('sourceopen', () => { // 此处MIME类型需要和你实际的音频编码完全匹配,MP3格式统一用'audio/mpeg'即可 sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg'); // 缓冲区写入完成后自动消费队列里的下一段数据 sourceBuffer.addEventListener('updateend', () => { isAppending = false; if (bufferQueue.length > 0) { const nextBuffer = bufferQueue.shift(); isAppending = true; sourceBuffer.appendBuffer(nextBuffer); } }); });
WebSocket消息处理逻辑
ws.onmessage = e => { const soundDataBase64 = JSON.parse(e.data); // base64转Uint8Array优化实现 const bytes = window.atob(soundDataBase64); const uint8Array = Uint8Array.from(bytes, c => c.charCodeAt(0)); // 缓冲区未就绪或正在写入时先将数据推入队列 if (!sourceBuffer || isAppending) { bufferQueue.push(uint8Array); return; } // 直接追加数据到缓冲区 isAppending = true; sourceBuffer.appendBuffer(uint8Array); // 首次收到数据触发播放,注意浏览器自动播放限制需要用户此前有过页面交互 if (audio.paused) { audio.play().catch(err => { console.log('请先点击页面任意位置触发音频播放权限', err); }); } };
注意事项
- MIME类型必须和实际音频编码完全匹配,如果你用的音频不是MP3而是AAC等其他格式,修改为对应MIME值即可
- 若音频流时长很长的场景下,可以定期调用
sourceBuffer.remove(0, audio.currentTime - 5)方法删除已经播放过的缓冲数据,释放内存 - 浏览器默认禁止未交互页面自动播放,需要用户至少和页面有过一次交互(比如点击页面任意位置),可以在页面上加个简单引导提示用户点击一次即可正常触发自动播放
内容的提问来源于stack exchange,提问作者Green 绿色
相关产品推荐
相关产品推荐

