基于Socket.io的实时音频流跨设备失效问题求助
解决跨设备Socket.io实时音频流的404 Blob错误
嘿,这个问题我太熟了!你现在遇到的核心问题是:你把浏览器本地生成的Blob URL直接通过Socket.io发给了其他设备,但这个URL只在当前浏览器的上下文里有效,其他设备根本找不到对应的资源,所以才会报404。
为什么本地同窗口能行?
同一Chrome窗口里的标签页/iframe共享同一个浏览器进程,Blob URL对应的资源存在本地内存里,所以能正常访问。但跨设备(甚至不同浏览器窗口)就完全是独立的上下文了,别的浏览器根本不知道你发的blob:https://server/xxx是什么东西。
正确的解决思路
别传Blob URL,要传音频的原始数据!我们可以用MediaRecorder把音频流拆成小片段,转成Base64字符串(或者ArrayBuffer)发送给服务器,服务器再转发给其他设备,接收端拿到数据后自己生成Blob和播放URL。
修改后的代码实现
前端main.js调整
$(document).ready(function(){ // 修正Socket.io连接参数,verify=false不是正确写法 var socket = io("https://server:4000", { rejectUnauthorized: false }); var mediaRecorder = null; // 接收音频数据并播放 socket.on('audio stream', function(name, audioData){ var video = document.querySelector('video'); // 把Base64字符串转回Blob var byteCharacters = atob(audioData); var byteNumbers = new Array(byteCharacters.length); for (var i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } var byteArray = new Uint8Array(byteNumbers); var blob = new Blob([byteArray], { type: 'audio/webm' }); // 更新播放源并尝试播放 video.src = URL.createObjectURL(blob); video.play().catch(err => console.log('自动播放被阻止,请先触发用户交互:', err)); }); $('#ptt').click(function(){ micOn(); document.getElementById("ptt").disabled = true; document.getElementById("ptt-off").disabled = false; }); $('#ptt-off').click(function(){ // 停止录制和麦克风 if (mediaRecorder) mediaRecorder.stop(); if (window.stream) window.stream.getAudioTracks()[0].stop(); document.getElementById("ptt").disabled = false; document.getElementById("ptt-off").disabled = true; }); function micOn(){ navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; var constraints = { audio: true, video: false }; function successCallback(stream) { window.stream = stream; // 创建MediaRecorder录制音频,指定格式为webm(Chrome支持良好) mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm' }); // 每录制一段数据就发送给服务器 mediaRecorder.ondataavailable = function(e) { var reader = new FileReader(); reader.onload = function(event) { // 去掉data URL前缀,只传Base64部分 var base64Audio = event.target.result.split(',')[1]; socket.emit('audio stream', myName, base64Audio); }; reader.readAsDataURL(e.data); }; // 每1秒发送一次音频片段,可根据需求调整间隔 mediaRecorder.start(1000); } function errorCallback(error) { console.log('麦克风获取失败: ', error); } navigator.getUserMedia(constraints, successCallback, errorCallback); } });
服务端index.js调整
服务端只需要原样转发数据就行,因为现在传的是通用的Base64字符串,不是本地Blob URL了:
socket.on('audio stream', function(name, base64Audio){ console.log(name + ' 正在广播音频'); socket.broadcast.emit('audio stream', name, base64Audio); });
额外注意事项
- 如果你的服务器用的是自签SSL证书,
rejectUnauthorized: false可以跳过验证,但生产环境建议用合法证书。 MediaRecorder的mimeType可以根据浏览器兼容性调整,比如audio/ogg或audio/wav,但audio/webm在Chrome里表现最好。- 音频片段的间隔(
start(1000)里的1000毫秒):值越小延迟越低,但网络传输量越大,按需调整。 - 浏览器的自动播放策略:大部分现代浏览器会阻止无用户交互的音频播放,所以最好在页面上加个"开始播放"按钮,让用户主动触发后再播放音频。
内容的提问来源于stack exchange,提问作者JCAguilera
相关产品推荐
相关产品推荐

