使用JavaScript捕获音频并立即播放时持续出现NotSupportedError错误求助
使用JavaScript捕获音频并立即播放时持续出现NotSupportedError错误求助
我现在遇到一个头疼的问题:用JavaScript捕获音频后尝试播放,第一个音频片段可能能正常工作,但后续所有的片段都无法播放,浏览器一直抛出NotSupportedError: Failed to load because no supported source was found.。我换了Chrome、Safari,还换了两台笔记本,问题都一模一样。
控制台输出的错误信息如下:
untitled.html:40 Blob {size: 81199, type: 'audio/webm'} untitled.html:46 Playback failed: NotSupportedError: Failed to load because no supported source was found. (anonymous) @ untitled.html:46 Promise.catch playAudio @ untitled.html:46 mediaRecorder.ondataavailable @ untitled.html:27 untitled.html:40 Blob {size: 81536, type: 'audio/webm'} untitled.html:46 Playback failed: NotSupportedError: Failed to load because no supported source was found.
我用来测试的HTML代码如下(保存为.html文件即可直接运行):
<html> <head> <title>Example</title> </head> <script> let audioStream; let mediaRecorder; navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { audioStream = new MediaStream(stream.getAudioTracks()); mediaRecorder = new MediaRecorder(audioStream, { mimeType: "audio/webm" }); mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { playAudio(event.data); } }; mediaRecorder.start(5000); }) .catch(err => { console.error(err); }); const playAudio = (audioChunk) => { const audioBlob = new Blob([audioChunk], { type: "audio/webm" }); // Ensure correct MIME type const audioUrl = URL.createObjectURL(audioBlob); console.log(audioBlob); const audioElement = document.createElement("audio"); audioElement.src = audioUrl; audioElement.controls = true; document.body.appendChild(audioElement); audioElement.play().catch((e) => console.error("Playback failed:", e)); }; </script> </html>
问题根源
你遇到的问题核心在于MediaRecorder的时间切片产生的片段不是完整的WebM文件:
- 当你调用
mediaRecorder.start(5000)时,浏览器会每隔5秒触发一次ondataavailable事件,返回一个音频片段(chunk)。 - WebM格式需要完整的文件头(包含编码信息、轨道定义等元数据)才能被浏览器识别,而这个文件头只存在于第一个返回的chunk中。后续的chunk都是没有文件头的纯媒体数据块,浏览器无法解析这种不完整的结构,所以会抛出“不支持的源”错误。
解决方案
根据你的需求,这里提供两种可行的解决思路:
方案1:收集所有片段,录制结束后播放完整音频
如果你的需求是录制完成后再播放完整内容,那么可以先把所有ondataavailable返回的chunk收集起来,直到调用mediaRecorder.stop()后,再将所有chunk拼接成一个完整的Blob进行播放:
<html> <head> <title>Example - 录制完成后播放完整音频</title> </head> <script> let audioStream; let mediaRecorder; let audioChunks = []; // 存储所有音频片段 navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { audioStream = new MediaStream(stream.getAudioTracks()); mediaRecorder = new MediaRecorder(audioStream, { mimeType: "audio/webm" }); mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0) { audioChunks.push(event.data); // 收集片段 } }; mediaRecorder.onstop = () => { // 拼接所有片段为完整的Blob const fullAudioBlob = new Blob(audioChunks, { type: "audio/webm" }); playAudio(fullAudioBlob); audioChunks = []; // 重置数组,方便下次录制 }; mediaRecorder.start(5000); // 示例:录制15秒后自动停止 setTimeout(() => mediaRecorder.stop(), 15000); }) .catch(err => { console.error(err); }); const playAudio = (audioBlob) => { const audioUrl = URL.createObjectURL(audioBlob); console.log(audioBlob); const audioElement = document.createElement("audio"); audioElement.src = audioUrl; audioElement.controls = true; document.body.appendChild(audioElement); audioElement.play().catch((e) => console.error("Playback failed:", e)); }; </script> </html>
方案2:使用MediaSource Extensions实现实时片段播放
如果你的需求是实时播放每个5秒的切片,那么需要使用MediaSource Extensions API,它允许你动态向媒体元素添加无文件头的媒体片段:
<html> <head> <title>Example - 实时播放音频切片</title> </head> <script> let audioStream; let mediaRecorder; let mediaSource; let sourceBuffer; // 初始化MediaSource,用于动态加载媒体片段 function initMediaSource() { mediaSource = new MediaSource(); const audioElement = document.createElement("audio"); audioElement.controls = true; audioElement.src = URL.createObjectURL(mediaSource); document.body.appendChild(audioElement); mediaSource.addEventListener('sourceopen', () => { // 注意指定编码格式,确保和MediaRecorder一致 sourceBuffer = mediaSource.addSourceBuffer('audio/webm; codecs="opus"'); sourceBuffer.mode = 'sequence'; // 设置为序列模式,允许连续添加片段 }); } navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { initMediaSource(); audioStream = new MediaStream(stream.getAudioTracks()); mediaRecorder = new MediaRecorder(audioStream, { mimeType: "audio/webm; codecs=opus" }); mediaRecorder.ondataavailable = (event) => { if (event.data.size > 0 && sourceBuffer && !sourceBuffer.updating) { // 将Blob转换为ArrayBuffer后添加到SourceBuffer event.data.arrayBuffer().then(buffer => { sourceBuffer.appendBuffer(buffer); }).catch(e => console.error("添加片段失败:", e)); } }; mediaRecorder.start(5000); }) .catch(err => { console.error(err); }); </script> </html>
额外注意事项
- 编码格式一致性:确保MediaRecorder和MediaSource使用的MIME类型完全一致(比如
audio/webm; codecs=opus),Opus是WebM音频兼容性最好的编码。 - Safari兼容性:Safari对WebM的MediaRecorder支持有限,如果需要兼容Safari,可以尝试使用
audio/mp4作为MIME类型(需要Safari 14.1及以上版本)。
备注:内容来源于stack exchange,提问作者Donny van V
相关产品推荐
相关产品推荐

