WebRTC会议应用首个轨道无法播放及轨道ID变更问题求助
问题1:首个轨道无法播放,打开第二个标签页后恢复
根因
你当前的轨道拉取逻辑是仅在接收端初始化时主动发一次request-streams请求,服务器也只有收到该请求时才会把现有轨道批量加到对端peer连接里。
第一个用户进空房间推流时,房间内没有其他在线用户,不会触发任何轨道下发动作。后续新用户进房间时,初始化触发的request-streams会拉取到包括第一个用户在内的所有轨道,连带完成之前漏协商的首个轨道的建联,所以才会出现开第二个标签页后首个轨道恢复播放的现象。
还有个高频隐藏问题:你没有处理onnegotiationneeded的协商竞态,如果短时间内多次addTrack触发多次协商,前一次协商还没完成就触发下一次,会导致SDP协商失效,轨道无法正常解码播放。
修复方案
- 服务器侧新增逻辑:每当有新轨道推到服务器时,主动遍历房间内所有其他用户的peer连接,直接调用
addTrack把新轨道加进去,自动触发协商,不需要等用户主动发request-streams拉取。 - 加协商锁避免竞态,示例逻辑:
let isNegotiating = false peer.onnegotiationneeded = async () => { if (isNegotiating) return isNegotiating = true try { // 原有协商逻辑 const offer = await peer.createOffer(); await peer.setLocalDescription(offer); const sdp = peer.localDescription; socket.emit("negotiation", sdp); socket.off("negotiation-returned"); socket.on("negotiation-returned", async (remoteSdp) => { const desc = new RTCSessionDescription(remoteSdp); await peer.setRemoteDescription(desc).catch((e) => console.log(e)); }); } catch(e) { console.error(e) } finally { isNegotiating = false } }
- 接收端拿到轨道后,必须手动挂载到已经插入DOM的
<video>/<audio>元素上,不要只存在状态管理库中,否则浏览器不会自动解码播放。
问题2:轨道ID变化无法识别所属用户
根因
你存在核心认知错误:WebRTC规范里,发送端的MediaStreamTrack.id不会同步到接收端,接收端拿到的track.id是本地随机生成的,和发送端完全没有关联,你靠track.id做身份关联本身就是错误方案。
修复方案
不要用track.id做身份标识,选以下任意一种方案即可:
- 优先用流ID关联:调用
peer.addTrack(track, stream)的时候带上所属的MediaStream,接收端的RTCTrackEvent.streams[0].id和发送端的stream.id是一致的,你只要在信令层把stream.id和用户ID、轨道类型绑定同步即可。 - 用mid/ssrc关联:每次addTrack完成后,拿到对应RTCRtpSender的
mid或者ssrc,通过信令把mid/ssrc和用户ID、轨道类型同步给接收端,接收端从RTCTrackEvent.transceiver.mid或者RTCTrackEvent.receiver.getParameters().encodings[0].ssrc拿到对应值,就能匹配到所属用户。 - 自定义RTP扩展头:在SDP里协商自定义的RTP扩展头,发送端在每个RTP包里面带用户ID和轨道标识,接收端直接从轨道的RTP扩展头里拿身份信息。
内容的提问来源于stack exchange,提问作者NGK
相关产品推荐
相关产品推荐

