无法设置远程应答 sdp:在错误状态下调用:稳定 [英] Failed to set remote answer sdp: Called in wrong state: stable
问题描述
我正在尝试使用 socket.io
编写一个 WebRTC
应用程序.
I am trying to write a WebRTC
application using socket.io
.
信令服务器是用python编写的,看起来像这样.
The signalling server is written in python and looks like this.
import socketio
import uvicorn
from starlette.applications import Starlette
ROOM = 'room'
sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)
@sio.event
async def connect(sid, environ):
await sio.emit('ready', room=ROOM, skip_sid=sid)
sio.enter_room(sid, ROOM)
@sio.event
async def data(sid, data):
await sio.emit('data', data, room=ROOM, skip_sid=sid)
@sio.event
async def disconnect(sid):
sio.leave_room(sid, ROOM)
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8003)
客户端看起来像这样
<script>
const SIGNALING_SERVER_URL = 'http://127.0.0.1:8003?session_id=1';
// WebRTC config: you don't have to change this for the example to work
// If you are testing on localhost, you can just use PC_CONFIG = {}
const PC_CONFIG = {};
// Signaling methods
let socket = io(SIGNALING_SERVER_URL, {autoConnect: false});
socket.on('data', (data) => {
console.log('Data received: ', data);
handleSignalingData(data);
});
socket.on('ready', () => {
console.log('Ready');
// Connection with signaling server is ready, and so is local stream
createPeerConnection();
sendOffer();
});
let sendData = (data) => {
socket.emit('data', data);
};
// WebRTC methods
let pc;
let localStream;
let remoteStreamElement = document.querySelector('#remoteStream');
let getLocalStream = () => {
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then((stream) => {
console.log('Stream found');
localStream = stream;
// Connect after making sure that local stream is availble
socket.connect();
})
.catch(error => {
console.error('Stream not found: ', error);
});
}
let createPeerConnection = () => {
try {
pc = new RTCPeerConnection(PC_CONFIG);
pc.onicecandidate = onIceCandidate;
pc.onaddstream = onAddStream;
pc.addStream(localStream);
console.log('PeerConnection created');
} catch (error) {
console.error('PeerConnection failed: ', error);
}
};
let sendOffer = () => {
console.log('Send offer');
pc.createOffer().then(
setAndSendLocalDescription,
(error) => {
console.error('Send offer failed: ', error);
}
);
};
let sendAnswer = () => {
console.log('Send answer');
pc.createAnswer().then(
setAndSendLocalDescription,
(error) => {
console.error('Send answer failed: ', error);
}
);
};
let setAndSendLocalDescription = (sessionDescription) => {
pc.setLocalDescription(sessionDescription);
console.log('Local description set');
sendData(sessionDescription);
};
let onIceCandidate = (event) => {
if (event.candidate) {
console.log('ICE candidate');
sendData({
type: 'candidate',
candidate: event.candidate
});
}
};
let onAddStream = (event) => {
console.log('Add stream');
remoteStreamElement.srcObject = event.stream;
};
let handleSignalingData = (data) => {
// let msg = JSON.parse(data);
switch (data.type) {
case 'offer':
createPeerConnection();
pc.setRemoteDescription(new RTCSessionDescription(data));
sendAnswer();
break;
case 'answer':
pc.setRemoteDescription(new RTCSessionDescription(data));
break;
case 'candidate':
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
break;
}
};
// Start connection
getLocalStream();
</script>
我也将此代码用于客户端作为 socket.io
Also i use this code for client as socket.io
https://github.com/socketio/socket.io/blob/master/client-dist/socket.io.js
当两个人联系在一起时,一切都很好.但是一旦第三个用户尝试连接到他们,流媒体就会停止并出现错误
When two people are in the connection, everything works great. But as soon as a third user tries to connect to them, the streaming stops with an error
未捕获(承诺)DOMException:无法执行RTCPeerConnection"上的setRemoteDescription":无法设置远程回答 sdp:在错误状态下调用:稳定
Uncaught (in promise) DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: stable
我对 javascript
了解不多,所以我需要你的帮助.谢谢.
I don't have much knowledge of javascript
, so I need your help. Thanks.
P.S.我在所有浏览器中都看到此错误.
查看这个仓库
https://github.com/pfertyk/webrtc-working-example
查看此说明
https://pfertyk.me/2020/03/webrtc-一个工作示例/
推荐答案
我已经在上面详细回答了这个问题,为什么您会遇到这个问题.但似乎您真正要寻找的是一些关于如何修复它的示例工作代码......所以你去:
I've answered this question above in details as to why you are having this issue. But seems like what you are really looking for is some sample working code on how to fix it... so here you go:
index.html:稍微更新 HTML 页面,所以现在我们有一个 div,我们将附加传入的远程视频.
index.html: Slightly update the HTML page, so now we have a div that we will append incoming remote videos.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebRTC working example</title>
</head>
<body>
<div id="remoteStreams"></div>
<script src="socket.io.js"></script>
<script src="main.js"></script>
</body>
</html>
app.py:更新了一些数据和就绪事件处理程序,以便我们正确地将套接字 ID 发送给其他对等方.
app.py: updated data and ready event handlers a bit so that we emit the socket id to other peers correctly.
import socketio
import uvicorn
from starlette.applications import Starlette
ROOM = 'room'
sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
star_app = Starlette(debug=True)
app = socketio.ASGIApp(sio, star_app)
@sio.event
async def connect(sid, environ):
await sio.emit('ready', {'sid': sid}, room=ROOM, skip_sid=sid)
sio.enter_room(sid, ROOM)
@sio.event
async def data(sid, data):
peerToSend = None
if 'sid' in data:
peerToSend = data['sid']
data['sid'] = sid
await sio.emit('data', data, room=peerToSend if peerToSend else ROOM, skip_sid=sid)
@sio.event
async def disconnect(sid):
sio.leave_room(sid, ROOM)
if __name__ == '__main__':
uvicorn.run(app, host='localhost', port=8003)
main.js:创建这个 peers 对象以将套接字 ID 映射到 RTCPeerConnections 并更新一些函数以使用它而不是 pc 变量.
main.js: Created this peers object to map socket ids to RTCPeerConnections and updated some of the functions to use that instead of the pc variable.
const SIGNALING_SERVER_URL = 'ws://127.0.0.1:8003';
// WebRTC config: you don't have to change this for the example to work
// If you are testing on localhost, you can just use PC_CONFIG = {}
const PC_CONFIG = {};
// Signaling methods
let socket = io(SIGNALING_SERVER_URL, {autoConnect: false});
socket.on('data', (data) => {
console.log('Data received: ', data);
handleSignalingData(data);
});
socket.on('ready', (msg) => {
console.log('Ready');
// Connection with signaling server is ready, and so is local stream
peers[msg.sid] = createPeerConnection();
sendOffer(msg.sid);
addPendingCandidates(msg.sid);
});
let sendData = (data) => {
socket.emit('data', data);
};
// WebRTC methods
let peers = {}
let pendingCandidates = {}
let localStream;
let getLocalStream = () => {
navigator.mediaDevices.getUserMedia({audio: true, video: true})
.then((stream) => {
console.log('Stream found');
localStream = stream;
// Connect after making sure thzat local stream is availble
socket.connect();
})
.catch(error => {
console.error('Stream not found: ', error);
});
}
let createPeerConnection = () => {
const pc = new RTCPeerConnection(PC_CONFIG);
pc.onicecandidate = onIceCandidate;
pc.onaddstream = onAddStream;
pc.addStream(localStream);
console.log('PeerConnection created');
return pc;
};
let sendOffer = (sid) => {
console.log('Send offer');
peers[sid].createOffer().then(
(sdp) => setAndSendLocalDescription(sid, sdp),
(error) => {
console.error('Send offer failed: ', error);
}
);
};
let sendAnswer = (sid) => {
console.log('Send answer');
peers[sid].createAnswer().then(
(sdp) => setAndSendLocalDescription(sid, sdp),
(error) => {
console.error('Send answer failed: ', error);
}
);
};
let setAndSendLocalDescription = (sid, sessionDescription) => {
peers[sid].setLocalDescription(sessionDescription);
console.log('Local description set');
sendData({sid, type: sessionDescription.type, sdp: sessionDescription.sdp});
};
let onIceCandidate = (event) => {
if (event.candidate) {
console.log('ICE candidate');
sendData({
type: 'candidate',
candidate: event.candidate
});
}
};
let onAddStream = (event) => {
console.log('Add stream');
const newRemoteStreamElem = document.createElement('video');
newRemoteStreamElem.autoplay = true;
newRemoteStreamElem.srcObject = event.stream;
document.querySelector('#remoteStreams').appendChild(newRemoteStreamElem);
};
let addPendingCandidates = (sid) => {
if (sid in pendingCandidates) {
pendingCandidates[sid].forEach(candidate => {
peers[sid].addIceCandidate(new RTCIceCandidate(candidate))
});
}
}
let handleSignalingData = (data) => {
// let msg = JSON.parse(data);
console.log(data)
const sid = data.sid;
delete data.sid;
switch (data.type) {
case 'offer':
peers[sid] = createPeerConnection();
peers[sid].setRemoteDescription(new RTCSessionDescription(data));
sendAnswer(sid);
addPendingCandidates(sid);
break;
case 'answer':
peers[sid].setRemoteDescription(new RTCSessionDescription(data));
break;
case 'candidate':
if (sid in peers) {
peers[sid].addIceCandidate(new RTCIceCandidate(data.candidate));
} else {
if (!(sid in pendingCandidates)) {
pendingCandidates[sid] = [];
}
pendingCandidates[sid].push(data.candidate)
}
break;
}
};
// Start connection
getLocalStream();
我尝试尽可能少地更改您的代码,因此您应该能够复制粘贴并使其正常工作.
I tried to change your code as little as possible, so you should be able to just copy-paste and have it working.
这是我的工作代码:https://github.com/lnogueir/webrtc-socketio
如果您在运行它时遇到任何问题,请告诉我或在那里打开一个问题,我会尽力提供帮助.
If you have any issues running it, let me know or open an issue there and I'll do my best to help.
这篇关于无法设置远程应答 sdp:在错误状态下调用:稳定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!