Como definir a descrição remota de um chamador WebRTC no Chrome sem erros?

Espero que não haja falha na lógica.

Passo 1 chamador cria oferta

Passo 2 chamador define localDescription

Etapa 3 chamador envia a descrição para o chamado

// ----------------------------------------------- ------- //

Passo 4 callee recebe a descrição remota dos conjuntos de ofertas

Step 5: callee cria resposta

Step 6: callee define a descrição local

Step 7: callee envie a descrição para o chamador

// ----------------------------------------------- ------- //

Step 8: chamador recebe a resposta e define a descrição remota

E aqui está o código para o acima

const socket = io();
const constraints = {
  audio: true,
  video: true
};
const configuration = {
  iceServers: [{
    "url": "stun:23.21.150.121"
  }, {
    "url": "stun:stun.l.google.com:19302"
  }]
};

const selfView = $('#selfView')[0];
const remoteView = $('#remoteView')[0];

var pc = new RTCPeerConnection(configuration);

pc.onicecandidate = ({
  candidate
}) => {
  socket.emit('message', {
    to: $('#remote').val(),
    candidate: candidate
  });
};

pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    socket.emit('message', {
      to: $('#remote').val(),
      desc: pc.localDescription
    });
  } catch (err) {
    console.error(err);
  }
};

pc.ontrack = (event) => {
  // don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

socket.on('message', async ({
  from,
  desc,
  candidate
}) => {
  $('#remote').val(from);
  try {
    if (desc) {
      // if we get an offer, we need to reply with an answer
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) => pc.addTrack(track, stream));
        selfView.srcObject = stream;
        await pc.setLocalDescription(await pc.createAnswer());
        console.log(pc.localDescription);
        socket.emit({
          to: from,
          desc: pc.localDescription
        });
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc).catch(err => console.log(err));
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(new RTCIceCandidate(candidate)).catch(err => console.log(err));
    }
  } catch (err) {
    console.error(err);
  }
});


async function start() {
  try {
    // get local stream, show it in self-view and add it to be sent
    const stream = await requestUserMedia(constraints);
    stream.getTracks().forEach((track) => pc.addTrack(track, stream));
    attachMediaStream(selfView, stream);
  } catch (err) {
    console.error(err);
  }
}

socket.on('id', (data) => {
  $('#myid').text(data.id);
});


// this function is called once the caller hits connect after inserting the unique id of the callee
async function connect() {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    socket.emit('message', {
      to: $('#remote').val(),
      desc: pc.localDescription
    });
  } catch (err) {
    console.error(err);
  }
}

socket.on('error', data => {
  console.log(data);
});

Agora, este código gera um erro ao executarStep 8

DOMException: falha ao executar 'setRemoteDescription' em 'RTCPeerConnection': falha ao definir a oferta remota sdp: chamado no estado incorreto: kHaveLocalOffer

DOMException: falha ao executar 'addIceCandidate' em 'RTCPeerConnection': erro ao processar candidato ICE

Tentei depurar, mas não encontrou nenhuma falha na lógica ou no código. Percebeu uma coisa estranha que opc objeto temlocalDescription ecurrentLocalDescription e acho que o destinatário que cria a resposta deve ter o tipo de descrição para seranswer mas mostra olocalDescription ser estaroffer ecurrentLocalDescription tipo éanswer.

Eu não tenho idéia se ele deve se comportar assim ou não, como eu sou iniciant

Desde já, obrigado

questionAnswers(1)

yourAnswerToTheQuestion