> = 3 way video: не удалось установить удаленный ответ sdp: вызвано в неправильном состоянии: kStable - PullRequest
0 голосов
/ 28 апреля 2020

Я пытаюсь создать комнату видеочата, и я застрял в этой ошибке: 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

...