Node.js与React客户端P2P连接无法触发iceCandidate及ontrack事件
问题排查与修复方案
核心问题:RTSP转WebRTC推流时,PeerConnection的onicecandidate和客户端ontrack事件未触发,根源在于媒体轨道未正确关联到PeerConnection,且SDP无媒体描述导致ICE流程未启动。
关键问题点及修复
1. 服务器端媒体轨道未加入MediaStream
你创建了videoTrack但未将其添加到videoStream中,导致videoStream.getTracks()为空,PeerConnection无媒体轨道可传输,自然不会触发ICE候选和媒体轨道事件。
修改服务器代码:
const videoStream = new MediaStream() const videoSource = new RTCVideoSource() const videoTrack = videoSource.createTrack() videoStream.addTrack(videoTrack) // 新增:将轨道加入流
2. SDP缺少媒体描述(m-line)
从你提供的Offer和Answer SDP可见,无任何媒体相关的m=行,这是因为PeerConnection没有媒体轨道,不会生成对应SDP内容,ICE候选也不会被收集。修复上述第1点后,生成的SDP会自动包含视频媒体的m-line,ICE流程将正常启动。
3. Socket广播方式错误
服务器端用socketIoServer.emit('toClient', ...)会向所有连接客户端发送消息,应改为向当前连接的客户端发送,避免多客户端冲突:
修改服务器fromClientCb函数及Socket逻辑:
// 保存当前连接的socket let currentSocket = null; socketIoServer.on('connect', (socket) => { console.log('connected') currentSocket = socket; socket.on('fromClient', (message) => fromClientCb(message, socket)) }) // 调整回调函数,传入当前socket const fromClientCb = async (message, socket) => { if (message.offer) { console.log(message.offer) await pc.setRemoteDescription(message.offer) const answer = await pc.createAnswer() console.log({answer}) await pc.setLocalDescription(answer) socket.emit('toClient', {answer}) // 用当前socket发送响应 } if (message.iceCandidate) { console.log({iceCandidate: message.iceCandidate}) await pc.addIceCandidate(message.iceCandidate) // 加上await确保异步操作完成 } } // 修改onicecandidate事件,仅向当前客户端发送候选 pc.onicecandidate = (event) => { console.log('onicecandidate', event) if (event.candidate && currentSocket) { currentSocket.emit('toClient', {iceCandidate: event.candidate}) } }
4. FFmpeg参数错误,无法正确解码为I420格式
当前FFmpeg参数用-c copy直接复制流,输出的是MPEG-TS封装视频,不是原始I420帧,videoSource.onFrame无法正确处理。需修改参数解码为原始YUV420P格式:
修改FFmpeg启动参数:
const ffmpeg = spawn('ffmpeg', [ '-i', rtspUrl, '-f', 'rawvideo', '-pix_fmt', 'yuv420p', // 指定输出格式为YUV420P(即I420) '-s', `${width}x${height}`, // 确保分辨率匹配 'pipe:1' ])
5. 客户端PeerConnection事件监听优化
客户端onicecandidate会在event.candidate为null时触发(表示ICE收集完成),可添加判断避免发送空候选:
peer.onicecandidate = (event) => { if (event.candidate) { // 仅发送非空候选 console.log('onicecandidate', {event}) socketRef.current.emit('fromClient', {iceCandidate: event.candidate}) } }
验证步骤
- 重启服务器,查看FFmpeg stderr输出确认RTSP流拉取正常
- 启动React客户端,检查控制台是否打印
onicecandidate日志 - 确认服务器端收到ICE候选,且自身
onicecandidate事件触发并发送给客户端 - 客户端
ontrack事件触发后,视频应能正常播放
内容的提问来源于stack exchange,提问作者OrganLover
相关产品推荐
相关产品推荐

