audio标签仅支持服务器启用range请求的mp3/ogg/wav等静态文件“伪流式”播放,不支持hls、dash、webrtc或websocket实时流;真流需用web audio api或hls.js等库桥接。

audio 标签本身不支持直接播放“流式音频”(如 HTTP Live Streaming、WebRTC 音频流或 chunked-transfer 编码的实时音频),它只接受完整可寻址的媒体资源 URL。想播流,必须先转成浏览器能识别的格式+协议,或换用 Web Audio API。
audio 标签能播什么类型的“流”?
严格来说,audio 标签只支持“伪流式”:即服务器支持 Range 请求的普通音频文件(MP3/OGG/WAV),浏览器可按需加载片段、拖动进度条、边下边播。这不是真正的流协议,但体验接近。
- ✅ 支持:MP3 文件托管在支持字节范围请求(
Accept-Ranges: bytes)的 HTTP 服务器上(Nginx/Apache 默认开启) - ❌ 不支持:HLS(
.m3u8)、DASH(.mpd)、WebRTCMediaStream、WebSocket 推送的原始 PCM 数据 - ⚠️ 有陷阱:某些 CDN 或静态托管(如 GitHub Pages、Vercel 的静态导出)会关闭
Range响应,导致audio拖动失效、duration为NaN、甚至无法播放
为什么直接写 src="http://xxx/audio-stream" 常常失败?
常见错误现象是控件显示“加载中”后卡住,控制台报 net::ERR_CONNECTION_RESET 或 Failed to load resource。根本原因不是路径错,而是服务端没返回正确响应头或格式不被 audio 元素识别。
- 服务器必须返回匹配的
Content-Type:MP3 对应audio/mpeg,OGG 对应audio/ogg;写错或返回text/plain会被 Chrome/Safari 直接拒载 - 流式端点若返回
Transfer-Encoding: chunked但无Content-Length且不支持Range,audio会认为资源不可寻址,拒绝初始化 - 跨域流地址必须带
crossorigin="anonymous",否则play()调用会因 CORS 报错
真正需要播实时流时,该选 Web Audio API 还是第三方库?
如果你的“流”来自 WebSocket、WebRTC、或自定义分块推送,audio 标签完全无能为力——它不提供解码、缓冲、时序调度能力。此时必须用 AudioContext + AudioBufferSourceNode 或 MediaStreamAudioSourceNode。
- Web Audio API 适合:低延迟语音、实时变声、音频分析、多音轨混音;但开发成本高,需手动管理缓冲、时间戳、采样率对齐
- 轻量级替代:可用
hls.js(播 HLS)、dash.js(播 DASH),它们内部把流转成MediaSource,再喂给audio标签——这是目前最稳的“流 → audio 标签”桥接方案 - 别踩坑:不要试图用
fetch()+response.body.getReader()拿到 chunk 后直接赋值给audio.src,这会触发多次销毁重建,iOS Safari 会直接崩溃
移动端 iOS Safari 上 audio 播流最易忽略的细节
iOS 对任何非用户手势触发的音频初始化都极其敏感,哪怕你用 MediaSource 或 AudioContext,只要没在 click/touchstart 回调里启动,就大概率静音或失败。
- 必须在用户首次交互后才创建
AudioContext(否则状态为suspended) - 用
MediaSource播 HLS 时,sourceopen事件后需立即调用audio.play(),且不能延迟到setTimeout或 Promise.then - 不要复用同一个
audio元素反复切换流地址:iOS 会缓存旧解码器状态,新流可能黑屏无声;建议每次播新流都新建new Audio()实例











