Этот вопрос задавали несколько раз раньше, но мне не удалось заставить какое-либо решение работать. Я создаю многопользовательский видеочат. Однако всякий раз, когда одноранговый узел пытается подключиться, я получаю эту ошибку:
DOMException: не удалось выполнить 'setRemoteDescription' в 'RTCPeerConnection': не удалось установить удаленный ответ sdp: вызывается в неправильном состоянии: kStable
Как ни странно, если пользователь перезагружает страницу, я не получаю эту ошибку и видео отображается. Оба клиента должны выполнить перезагрузку. Я предполагаю, что браузер кэшировал что-то и повторно использует его со второй попытки.
// When a remote user joins, an object of this class is created.
// Its job is to create am RTCPeerConnection between the local user
// and the remote user.
class VideoChat
{
constructor(name, remoteView)
{
this.remoteName = name; // ID of remote peer used by signal server
this.remoteView = remoteView; // html video object to display remote video
var configuration = {"iceServers": [
{urls: "stun:stun.l.google.com:19302"}
{urls: "turn:numb.viagenie.ca", username: "xxx", credential: "xxx"}
]};
this.pc = new RTCPeerConnection(configuration);
// 'onicecandidate' notifies us whenever an ICE agent needs to deliver a
// message to the other peer through the signaling server
this.pc.onicecandidate = event => {
if (event.candidate) {
ChatRoom.relay("signal", this.remoteName, event.candidate);
console.log(`onicecadidate (${this.remoteName}): ${event.candidate}`);
}
};
// let the 'negotiationneeded' event create the offer
this.pc.onnegotiationneeded = async () => {
try {
await this.pc.setLocalDescription();
ChatRoom.relay("signal", this.remoteName, {desc: this.pc.localDescription})
} catch (err) {
console.error(err);
}
}
// When a remote stream arrives display it in the #remoteView element
this.pc.ontrack = (track, streams) => {
log("adding remote TRACK to video element");
// don't set srcObject again if it is already set.
if (this.remoteView.srcObject) return;
this.remoteView.srcObject = event.streams[0];
};
}
// This is called by main program when a remote user has signed on
// This initiates everything....
// localVideo is html video element connected to local camera
// stream is the main (local) user's mediaStream
async start(localVideo, stream) {
try {
for (const track of stream.getTracks()) {
this.pc.addTrack(track, stream);
}
localVideo.srcObject = stream;
} catch (err) {
console.error(err);
}
}
// A message from the signal server
async onmessage(message) {
try {
if (message.desc) {
await this.pc.setRemoteDescription(message.desc);
if (message.desc.type == "offer") {
await this.pc.setLocalDescription();
ChatRoom.relay("signal", this.remoteName, {desc: this.pc.localDescription});
}
}
else if (message.candidate) {
await this.pc.addIceCandidate(message);
}
} catch (err) {
console.error(err);
}
}
}
Обратите внимание на функцию: ChatRoom.relay("signal", this.remoteName, something)
отправляет сообщение на сервер сигналов, которое передается только на удаленный узел с идентификатором this.remoteName
.
Кроме того, я использую свой собственный сервер сигналов, созданный в Java.