WebRTC远程端音频无法播放问题排查求助
解决WebRTC P2P音频无法互通的问题
我帮你排查了代码里的几个关键问题,这些应该就是导致双方听不到对方音频的核心原因,下面是具体的修复方案和完整修正代码:
核心问题分析
- ICE服务器配置未生效:你定义了
pcConfig但创建RTCPeerConnection时传入了null,这会导致无法获取公网ICE候选,跨网络情况下根本无法建立连接。 - ICE候选处理不规范:发送候选时传递了
sdpMid,但接收时没正确对应,而且缺少错误捕获,候选添加失败也没办法排查。 - Answer方法缺少错误处理:
createAnswer没有catch块,出错了完全没日志,很难定位问题。 - Socket房间事件逻辑有漏洞:初始用户和加入用户的
isChannelReady设置时机不对,可能导致媒体流还没准备好就尝试建立连接。 - 服务器端代码不完整:你的
index.js没写完,Socket.io的房间转发逻辑缺失,消息根本传不到对方那里。
分步修复方案
1. 启用ICE服务器配置
修改createPeerConnection函数,把定义好的pcConfig传进去:
function createPeerConnection() { pc = new RTCPeerConnection(pcConfig); // 替换原来的null pc.onicecandidate = function (e) { if (e.candidate) { sendMessage({ type: 'candidate', label: e.candidate.sdpMLineIndex, id: e.candidate.sdpMid, candidate: e.candidate.candidate }); } } pc.ontrack = function (e) { console.log('Remote track added', e); remoteStream = e.streams[0]; remoteVideo.srcObject = remoteStream; } }
2. 规范ICE候选的接收逻辑
接收候选时要正确对应sdpMid,并且添加错误捕获:
socket.on('message', function (message) { // ... 其他逻辑不变 else if (message.type === 'candidate' && isStarted) { const candidate = new RTCIceCandidate({ sdpMLineIndex: message.label, sdpMid: message.id, // 对应发送时的id字段 candidate: message.candidate }); pc.addIceCandidate(candidate).catch(err => console.error('添加ICE候选失败:', err)); console.log('Candidate added successfully'); } });
3. 为Answer方法添加错误捕获
function answer() { pc.createAnswer(null) .then((sessionDescription) => { pc.setLocalDescription(sessionDescription); sendMessage(sessionDescription); }) .catch(e => console.error('创建Answer失败:', e)); }
4. 修正Socket房间事件逻辑
确保双方的isChannelReady都能正确触发,并且加入用户后主动通知对方:
socket.on('created', function (room) { isInitializer = true; console.log('Created room ' + room); }); socket.on('join', function (room) { console.log('Another peer joined room ' + room); isChannelReady = true; // 初始用户标记通道就绪 }); socket.on('joined', function (room) { console.log('Successfully joined room ' + room); isChannelReady = true; // 加入用户标记通道就绪 sendMessage('got user media'); // 通知对方自己已准备好媒体流 });
5. 补全服务器端Socket.io逻辑
你的index.js没写完,下面是完整的服务器代码,确保消息能正确转发到房间内的其他用户:
const express = require('express'); const path = require('path'); const app = express(); const server = require('http').createServer(app); const io = require('socket.io')(server); app.use(express.static(path.resolve(__dirname, '../public'))); app.get('*', (req, res) => { res.sendFile(path.resolve(__dirname, '../public/index.html')); }); const port = process.env.PORT || 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); }); // 处理Socket.io房间逻辑 io.on('connection', (socket) => { socket.on('create or join', (room) => { const clientsInRoom = io.sockets.adapter.rooms.get(room); const numClients = clientsInRoom ? clientsInRoom.size : 0; if (numClients === 0) { socket.join(room); socket.emit('created', room); } else if (numClients === 1) { socket.join(room); socket.emit('joined', room); // 通知房间内已有用户新用户加入 socket.to(room).emit('join', room); } else { socket.emit('full', room); } }); // 转发消息到房间内其他用户 socket.on('message', (message) => { // 获取当前socket所在的房间(这里假设用户只在一个房间) const room = [...socket.rooms][1]; // 第一个是socket自身的id,第二个是房间名 socket.to(room).emit('message', message); }); });
验证步骤
- 测试前确保浏览器允许麦克风权限,查看浏览器标签栏的麦克风图标是否亮起,确认本地媒体流已正确获取。
- 打开浏览器控制台,检查是否有
Offer、Answer、Candidate added successfully的日志,没有报错信息。 - 如果是跨网络测试,确保
stun:stun.l.google.com:19302可以正常访问(这个是公开STUN服务器,一般都可用)。
内容的提问来源于stack exchange,提问作者user8868343
相关产品推荐
相关产品推荐

