如何通过ReadableStream流式播放服务器返回的音频文件?
流式音频边接收边播放的实现方案
方案一:使用MediaSource API(最常用)
MediaSource是专门处理流式媒体的API,能让你把分片的音频数据逐步喂给<audio>元素,实现边下载边播放。
具体步骤:
- 创建
<audio>元素,开启控制栏(也可设置自动播放,但需注意浏览器的自动播放限制,通常需要用户交互触发) - 实例化MediaSource,将其生成的URL赋值给audio的
src属性 - 监听MediaSource的
sourceopen事件,在事件回调中创建对应音频格式的SourceBuffer(格式需和服务器返回的编码匹配,比如MP3对应audio/mpeg; codecs="mp3") - 通过
fetch获取流式响应,用body.getReader()读取分片数据 - 等待上一个分片处理完成(监听SourceBuffer的
updateend事件)后,将新分片的ArrayBuffer数据append到SourceBuffer中 - 所有分片读取完成后,调用
mediaSource.endOfStream()结束流
代码示例:
const audio = document.createElement('audio'); audio.controls = true; document.body.appendChild(audio); const mediaSource = new MediaSource(); audio.src = URL.createObjectURL(mediaSource); mediaSource.addEventListener('sourceopen', async () => { // 替换为实际的音频编码格式,需和服务器返回一致 const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg; codecs="mp3"'); const response = await fetch('/your-audio-stream-api'); const reader = response.body.getReader(); async function processChunk() { const { done, value } = await reader.read(); if (done) { mediaSource.endOfStream(); return; } // 等待SourceBuffer处理完上一个分片 while (sourceBuffer.updating) { await new Promise(resolve => sourceBuffer.addEventListener('updateend', resolve, { once: true })); } sourceBuffer.appendBuffer(value); processChunk(); } processChunk(); });
注意事项:
- 服务器返回的音频必须是支持流式的编码格式,比如MP3、AAC(封装在MP4容器中),无压缩的WAV格式无法分片解码,不适用
- 服务器需要按完整的编码帧切割数据流,否则SourceBuffer无法解析分片数据
- 浏览器自动播放限制:直接加载可能被拦截,建议通过用户点击等交互触发播放
方案二:使用Web Audio的AudioWorklet(适合自定义处理场景)
如果需要对音频做实时自定义处理,可使用AudioWorklet,它能在独立音频线程中处理分片数据,避免阻塞主线程。
核心思路:
- 创建AudioContext并加载自定义的AudioWorklet处理器
- 使用支持流式解码的WebAssembly库(比如libmp3lame的Wasm版本),将读取到的音频分片解码为PCM格式
- 将PCM数据通过AudioWorklet的端口传递给处理器,由处理器输出到扬声器
这个方案复杂度较高,需要额外引入解码库,仅在需要自定义音频处理时使用,单纯播放推荐用MediaSource。
原方案失效原因
你之前用AudioContext.decodeAudioData失败,是因为该API仅能解码完整的音频文件或完整的编码帧序列,不支持增量式的流式解码。单独的音频分片不是可独立解码的完整单元,因此无法直接播放。
内容的提问来源于stack exchange,提问作者bittere
相关产品推荐
相关产品推荐

