Я пытаюсь создать комнату видеочата, и я застрял в этой ошибке: DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote answer sdp: Called in wrong state: kStable
Согласно моим поискам, кажется, что сигнализация находится в неправильном порядке, но я не могу на всю жизнь найти причину.
Насколько я знаю, для каждого клиента на сайте им требуется свое собственное соединение webrt c со всеми остальными людьми на сайте, а также то, что webrt c может поддерживать несколько соединений с несколькими компьютерами. , основываясь на том, что я генерирую экземпляры webrt c динамически, когда пользователи подключаются, и присваиваю каждому имя каждого идентификатора клиента, установленного комнатой, а затем использую этот идентификатор, чтобы отслеживать, кому какое соединение принадлежит.
я пытаюсь сделать так, чтобы каждый клиент подключался, динамически создавал новый видеоэлемент на всех других машинах и настраивал видео-соединение webrt c между ними. генерация / удаление элементов работает нормально, и я смог отправить сообщения между ними (успешно создан чат), но соединение видео потоков вместе оказывается очень трудным.
другие вопросы здесь по стеку переполнение, кажется, использует одно соединение между двумя машинами, и я не смог масштабировать ответы на множественные соединения между многими машинами.
, если бы кто-то мог указать этому новичку в правильном направлении, это было бы очень признательно .
ошибка возникает в строке 89 скрипта.
// Generate random room name if needed
if (!location.hash) {
location.hash = Math.floor(Math.random() * 0xFFFFFF).toString(16);
}
const roomHash = location.hash.substring(1);
const drone = new ScaleDrone('2xmbUiTsqTzukyf7');
const roomName = 'observable-' + roomHash;
const configuration = {
iceServers: [{
urls: 'stun:stun.l.google.com:19302'
}]
};
let room;
let pc;
let isNegotiating = false;
var list = [];
function onSuccess() {};
function onError(error) {
console.error(error);
}
drone.on('open', error => {
if (error) {
return console.error(error);
}
room = drone.subscribe(roomName);
room.on('open', error => {
if (error) {
onError(error);
}
});
room.on('members', members => {
members.forEach(function(element){
if(element.id != drone.clientId){
videoCreate(element);
}
});
});
room.on('member_join', function(member) {
videoCreate(member);
});
room.on('member_leave', function(member) {
videoLeave(member);
});
room.on('data', function(stream){
console.log(stream);
recieve(stream);
});
begin();
});
function videoCreate(element){
list.push(element.id);
var video = document.createElement('video');
video.id = element.id;
video.autoplay = true;
window[element.id] = new RTCPeerConnection(configuration);
window[element.id+"lock"] = false;
[element.id].onnegotiationneeded = () => {
if (window[element.id+"lock"]) {
console.log("SKIP nested negotiations");
return;
}
window[element.id+"lock"] = true;
}
[element.id].onsignalingstatechange = (e) => { // Workaround for Chrome: skip nested negotiations
window[element.id+"lock"] = ([element.id].signalingState != "stable");
}
document.getElementById("videos").appendChild(video);
console.log(element);
}
function videoLeave(element){
var el = document.getElementById(element.id);
el.parentNode.removeChild(el);
list.splice(list.indexOf(element.id), 1);
}
async function recieve(element){
if(element.device != drone.clientId){
if (element.sdp) {
console.log(element);
// This is called after receiving an offer or answer from another peer
await window[element.device].setRemoteDescription(element.sdp).then(function(){
// When receiving an offer lets answer it
if (window[element.device].remoteDescription.type === 'offer') {
window[element.device].createAnswer().then(
desc => {
localDescCreated(window[element.device],desc,element.device);
}
).catch(
error => {
console.log(2);
onError(error);
}
);
}
}, error => {
console.log(3);
onError(error);
});
// } else if (element.candidate) {
// // Add the new ICE candidate to our connections remote description
// window[element.device].addIceCandidate(
// new RTCIceCandidate(element.candidate),
// onSuccess,
// error => {
// console.log(4);
// onError(error);
// }
// );
}
[element.device].ontrack = event => {
document.getElementById(element.device).srcObject = event.streams[0];
event.track.onended = e => document.getElementById(element.device).srcObject = document.getElementById(element.device).srcObject;
};
document.getElementById(element.device).srcObject = element.value;
}
}
function begin(){
pc = new RTCPeerConnection(configuration);
pc.onicecandidate = event => {
if (event.candidate) {
sendMessage({'candidate': event.candidate});
}
};
navigator.mediaDevices.getUserMedia({
audio: true,
video: true,
}).then(stream => {
document.getElementById("local").srcObject = stream;
// Add your stream to be sent to the conneting peer
stream.getTracks().forEach(track => pc.addTrack(track, stream));
}, error => {
console.log(5);
onError(error);
});
pc.onnegotiationneeded = () => {
if (isNegotiating) {
console.log("SKIP nested negotiations");
return;
}
isNegotiating = true;
pc.createOffer().then(
desc => {
localDescCreated(pc,desc);
}
).catch(
error => {
console.log(6);
onError(error);
}
);
}
}
function sendMessage(message) {
drone.publish({
room: roomName,
message
});
}
function localDescCreated(rtc,desc,id=drone.clientId) {
console.log('local description: ', rtc.localDescription);
rtc.setLocalDescription(
desc,
() => sendMessage({device: id,'sdp': rtc.localDescription}),
error => {
console.log(7);
onError(error);
}
);
}
, а вот html
<html>
<head>
<script src='https://cdn.scaledrone.com/scaledrone.min.js'></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<style>
body {
background: #0098ff;
display: flex;
height: 100vh;
margin: 0;
align-items: center;
justify-content: center;
padding: 0 50px;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
video {
max-width: calc(50% - 100px);
margin: 0 50px;
box-sizing: border-box;
border-radius: 2px;
padding: 0;
background: white;
}
.copy {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
font-size: 16px;
color: white;
}
</style>
</head>
<body>
<div class="copy">Send your URL to a friend to start a video call</div>
<div id="videos">
<video id="local" autoplay muted></video>
</div>
<button onClick="console.log(window)">debug</button>
<script src="script.js?t=<?php echo time()?>"></script>
</body>
</html>
- это jsfiddle системы как есть: jsfiddle