为何Blob数据无法在audio元素播放?基于SSE的音频流问题
SSE流式音频播放无声音问题解决方案
问题根源
- SSE是文本协议,直接传输二进制音频会导致数据损坏,即便用base64编码,单次片段也无法通过
audio.src直接播放(仅完整音频文件可行)。 - 原代码仅处理首次SSE消息,后续更新的音频片段未被整合到播放流程中。
- 此前尝试
MediaSource时未正确初始化SourceBuffer,也没处理片段拼接逻辑。
1. 修复服务端PHP代码(实现增量base64传输)
必须将二进制音频编码为base64避免数据损坏,同时只传输文件新增内容(而非整个文件),减少带宽占用并保证实时性:
<?php header("Content-Type: text/event-stream"); header("Cache-Control: no-cache"); header("Connection: keep-alive"); $livefile = "audio.ogg"; $lastPos = 0; // 记录上次读取位置,实现增量传输 while (true) { clearstatcache(true, $livefile); $currentSize = filesize($livefile); // 仅当文件有新内容时发送 if ($currentSize > $lastPos) { $handle = fopen($livefile, 'rb'); fseek($handle, $lastPos); $newAudioData = fread($handle, $currentSize - $lastPos); fclose($handle); // 编码为base64后通过SSE发送 echo "data: " . base64_encode($newAudioData) . "\n\n"; $lastPos = $currentSize; } flush(); ob_flush(); if (connection_aborted()) break; sleep(1); // 缩短轮询间隔,提升实时性 } exit(); ?>
2. 客户端JavaScript实现MediaSource流式拼接播放
使用MediaSource正确拼接音频片段,支持实时增量播放,解决片段无法直接播放的问题:
console.log("getLiveStream"); const sseSource = new EventSource("sse.php"); const mediaSource = new MediaSource(); const audioElement = document.createElement("audio"); // 设置音频控件并添加到页面 audioElement.controls = true; document.body.appendChild(audioElement); // 关联MediaSource与音频元素 audioElement.src = URL.createObjectURL(mediaSource); let sourceBuffer; // 初始化MediaSource mediaSource.addEventListener('sourceopen', () => { // 匹配你的音频编码格式,必须与服务端文件一致 sourceBuffer = mediaSource.addSourceBuffer('audio/ogg; codecs=opus'); sourceBuffer.mode = 'sequence'; // 按顺序拼接音频片段 }); // 处理SSE消息 sseSource.onmessage = function(event) { // 将base64解码为Uint8Array二进制数据 const binaryStr = atob(event.data); const audioData = new Uint8Array(binaryStr.length); for (let i = 0; i < binaryStr.length; i++) { audioData[i] = binaryStr.charCodeAt(i); } // 等待SourceBuffer可写入时添加数据 const appendAudio = () => { if (sourceBuffer.updating) { setTimeout(appendAudio, 100); return; } sourceBuffer.appendBuffer(audioData); // 处理自动播放(受浏览器策略限制,建议添加用户触发按钮) if (audioElement.paused) { audioElement.play().catch(err => console.log('自动播放失败:', err)); } }; appendAudio(); }; // 处理SSE连接异常 sseSource.onerror = function(err) { console.error('SSE连接出错:', err); sseSource.close(); };
重要注意事项
- 浏览器自动播放限制:现代浏览器要求用户主动交互(比如点击按钮)后才能播放音频,建议添加一个播放按钮,由用户触发
audioElement.play()。 - 格式一致性:确保服务端音频文件的编码是
opus,与客户端addSourceBuffer中的参数完全匹配。 - SourceBuffer状态:必须等待
sourceBuffer.updating为false时再调用appendBuffer,否则会抛出错误。 - 增量传输:服务端只发送新增的音频片段,避免重复传输完整文件导致播放混乱。
内容的提问来源于stack exchange,提问作者Xavier
相关产品推荐
相关产品推荐

