如何修复“RTCPeerConnection”上无法执行“setRemoteDescription”的问题:无法设置远程应答 sdp:在错误状态下调用:kStable

The*_*NVB 6 javascript webrtc

我参考以下文档在我的 Peer Communicate Web 应用程序中使用完美的协商模式进行构建:

  1. 这个来自 MDN
  2. 这个来自github

我的网页内容如下:

<html>
	<head>
		<meta charset="UTF-8">
		<title>WebRTC Caller</title>
		<script src="/socket.io/socket.io.js"></script>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
		<script>
			
			var peer=null;
			var socket = io.connect();
			var dc,pc,polite=false;
			var localVideo,remoteVideo;
			var makingOffer = false, ignoreOffer = false;
			var configuration = {iceServers: 
				[{urls: "stun:stun.stunprotocol.org"},
				 {urls: "stun:stun.l.google.com:19302"},
				 {urls: "turn:numb.viagenie.ca", credential: "***", username: "***"}		
				]};
			$( document ).ready(function() {
				localVideo=document.getElementById("selfView");
				remoteVideo=document.getElementById("remoteView");
			});
			function call() {
				polite=true;
				createConnection();
			}
			function createConnection(){
				pc=new RTCPeerConnection(configuration);
				pc.onnegotiationneeded=negotiationEventHandler;
				pc.onicecandidate=iceCandidateEventHandler;
				dc= pc.createDataChannel('chat');
			}
			function hangUp() {
				dc.close();
				pc.close();
			}
			function iceCandidateEventHandler(event){
				if (event.candidate==null){
					writeLog("All ICE Candidates are sent");
				} else {
					writeLog("Send ICE Candidate");
					socket.emit('send',{candidate:event.candidate});
				}
			}
			async function negotiationEventHandler(){
				writeLog('Handle Negotitation');
				
				try {
					makingOffer = true;
					await pc.setLocalDescription();
					socket.emit("send",{ description: pc.localDescription });
				} catch(err) {
					writeLog(err);
				} finally {
					makingOffer = false;
				}
				
			}
			function clearLog() {
			  chatelement = document.getElementById('logger');
			  chatelement.innerHTML='';
			}		
			function writeLog(message) {
				var logger=document.getElementById("logger");
				logger.innerHTML=message+"<br>"+logger.innerHTML;
			}	
			socket.on("receive",async (req)=>{
				let ignoreOffer = false;
				clearLog();
				createConnection();
				if (req.description){
					const offerCollision = (req.description.type == "offer") &&
								 (makingOffer || pc.signalingState != "stable");

					ignoreOffer = !polite && offerCollision;
					if (ignoreOffer) {
						return;
					}
					await pc.setRemoteDescription(req.description);
					if (req.description.type =="offer") {
						await pc.setLocalDescription();
						socket.emit("send",{description: pc.localDescription});
					}
				} else {
					if (req.candidate){
						try {
							if (pc.currentRemoteDescription){
								await pc.addIceCandidate(req.candidate);
							}
						} catch(err) {
							if (!ignoreOffer) {
							  throw err;
							}
						}
					}
				}
			});			
		</script>
	</head>
	<body>
		<table border=1>
			<tr>
				<td>
					<h1> Self View </h1>
					<video id="selfView" width="320" height="240" autoplay muted></video>
				</td>
				<td>
					<h1> Remote View </h1>
					<video id="remoteView" width="320" height="240" autoplay muted></video>
				</td>
			</tr>
			<tr>
				<td><button onclick="call()">Make A Call</button><button onclick="addMedia()">Add Media</button></td>
				<td><button onclick="hangUp()">Hang Up</button></td>
			</tr>	
		</table>
		<div id="logger" style="border:1px solid black;width:100%;height:30%;overflow:scroll">
		</div>
	</body>
</html>	
Run Code Online (Sandbox Code Playgroud)

我的 node.js 服务器端代码如下:

var fs = require('fs');
var https = require('https');

var express = require('express');
var app = express();

var options = {
  key: fs.readFileSync('private.key'),
  ca: [fs.readFileSync('ca_bundle.crt')],
  cert: fs.readFileSync('certificate.crt')
};
var serverPort = 443;

var server = https.createServer(options, app);
var io = require('socket.io')(server);
var userList=[];

app.use(express.static('public'));
io.on('connection', function(socket) {
  console.log('new connection');
  userList.push(socket.id); 
  socket.emit('message', 'This is a message from the dark side.');
});
io.on('connection', (socket) => {
    console.log('a user connected@'+(new Date()));
    socket.on("send",(req)=>{
        console.log("Receive send request");
        socket.broadcast.emit("receive", req);
    });
    socket.on('disconnect', () => {
        console.log('user disconnected@'+(new Date()));
    });
});

server.listen(serverPort, function() {
  console.log('server up and running at %s port', serverPort);
});
Run Code Online (Sandbox Code Playgroud)

我用 2 个浏览器打开网页(1 个是 chrome,1 个是 firefox)。当我在chrome浏览器中点击“拨打电话”按钮时,chrome提示如下错误信息:

Uncaught (in promise) DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: kStable
Run Code Online (Sandbox Code Playgroud)

当我在firefox浏览器中点击“拨打电话”按钮时,firefox提示如下错误信息:

InvalidStateError: Cannot set remote answer in state stable
Run Code Online (Sandbox Code Playgroud)

有什么问题?我该如何解决?