WebRTC视频流传输问题:如何实现视频流发送?
WebRTC视频流传输问题修复
核心错误点
你当前代码的最大问题是误用DataChannel传输媒体流:WebRTC的DataChannel仅用于传输文本、二进制小数据,无法直接发送MediaStream对象。媒体流必须通过PeerConnection的媒体轨道机制来传输,这是WebRTC专门设计的媒体传输通道。
此外代码还有以下关键问题:
- 未将本地媒体流的轨道添加到PeerConnection
- 未监听远端媒体流的
ontrack事件来接收视频 - PeerConnection实例重复创建,导致连接状态混乱
- 用
setTimeout替代setLocalDescription的Promise回调,逻辑不可靠
修复方案与代码调整
1. 正确处理媒体流传输逻辑
获取到本地媒体流后,需要将流中的音视频轨道添加到PeerConnection,而非通过DataChannel发送。远端通过监听peerConnection.ontrack事件接收媒体流。
2. 修复PeerConnection的SDP处理逻辑
使用setLocalDescription的Promise回调替代setTimeout,确保SDP生成完成后再发送给信令服务器。
3. 完整修复代码
<button id='start'>1</button><br> <button id='connect'>2</button><br> <form onsubmit="return false;"> <input id='iprts' type="text" placeholder="Msg" size="40"> <input id='enter' type="submit" value="Send"> </form> <video autoplay id="vid1" width="400" height="300" controls></video> <video autoplay id="vid2" width="400" height="300" controls></video> <script src="client/socket.js"></script><!--v4.5.2 --> <script> var socket = io(); var isStarter = false var peerConnection = null var dataChannel = null var localStream = null // 初始化媒体流 navigator.mediaDevices.getDisplayMedia({ video: true, audio: true, }).then(stream => { localStream = stream vid1.srcObject = stream }) // 发起方逻辑 start.onclick = async function() { isStarter = true // 创建PeerConnection peerConnection = new RTCPeerConnection(); // 添加本地媒体轨道到PeerConnection localStream.getTracks().forEach(track => { peerConnection.addTrack(track, localStream) }) // 创建DataChannel用于文本通信 dataChannel = peerConnection.createDataChannel('test'); setupDataChannel(dataChannel) // 监听ICE候选 peerConnection.onicecandidate = (event) => { if (event.candidate) return; // 候选收集完成后发送SDP socket.emit('toserv', peerConnection.localDescription) } // 监听远端媒体流 peerConnection.ontrack = (event) => { vid2.srcObject = event.streams[0] } // 创建并设置本地Offer const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); } // 接收方逻辑 connect.onclick = async function() { peerConnection = new RTCPeerConnection(); // 添加本地媒体轨道到PeerConnection localStream.getTracks().forEach(track => { peerConnection.addTrack(track, localStream) }) // 监听远端DataChannel peerConnection.ondatachannel = (event) => { dataChannel = event.channel setupDataChannel(dataChannel) } // 监听ICE候选 peerConnection.onicecandidate = (event) => { if (event.candidate) return; socket.emit('toserv2', peerConnection.localDescription) } // 监听远端媒体流 peerConnection.ontrack = (event) => { vid2.srcObject = event.streams[0] } // 设置远端Offer并创建Answer await peerConnection.setRemoteDescription(window.offer2); const answer = await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); } // 信令消息监听 socket.on('toclnt', (inf) => { window.offer2 = inf }) socket.on('toclnt2', (ing) => { if (isStarter && peerConnection) { peerConnection.setRemoteDescription(ing) } }) // 文本消息发送 enter.onclick = function() { if (dataChannel && dataChannel.readyState === 'open') { dataChannel.send(iprts.value) } } // 通用DataChannel配置 function setupDataChannel(channel) { channel.onopen = () => console.log('channel_open!'); channel.onmessage = (event) => console.log('收到消息:', event.data); } </script>
关键修复说明
- 媒体流传输:通过
peerConnection.addTrack将本地轨道添加到连接,远端通过ontrack事件获取流并赋值给video元素 - SDP处理:使用
async/await处理createOffer、setLocalDescription等异步操作,替代不可靠的setTimeout - DataChannel职责:仅用于文本消息传输,不再处理媒体流
- PeerConnection实例管理:避免重复创建,确保连接状态一致
内容的提问来源于stack exchange,提问作者Renvo
相关产品推荐
相关产品推荐

