Как установить удаленное описание для вызывающего абонента WebRTC в Chrome без ошибок? - PullRequest
0 голосов
/ 28 октября 2018

Я надеюсь, что в логике нет недостатка.

Шаг 1: вызывающий абонент создает предложение

Шаг 2: вызывающий абонент устанавливает localDescription

Шаг 3: вызывающий абонент отправляет описание вызываемому абоненту

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

Шаг 4: вызываемый абонент получает предложение удаленного описания

шаг 5: вызываемый создает ответ

шаг 6: вызываемый абонент устанавливает локальное описание

Шаг 7: вызываемый абонент отправляет описание звонящему

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

Шаг 8: абонент получает ответ и устанавливаетудаленное описание

А вот код для вышеупомянутого

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);
});

Теперь этот код выдает ошибку при выполнении Шаг 8

DOMException: не удалось выполнить setRemoteDescription для RTCPeerConnection: не удалось установить удаленное предложение sdp: вызванонеправильное состояние: kHaveLocalOffer

DOMException: не удалось выполнить addIceCandidate для RTCPeerConnection: ошибка при обработке кандидата ICE

Попытка отладки, но не обнаружена ошибка в логике иликод.Заметил одну странную вещь, что объект pc имеет localDescription и currentLocalDescription, и я думаю, что вызываемый объект, который создает ответ, должен иметь оба типа описания, чтобы быть answer, но вместо этого показывает localDescription, чтобы быть offer и currentLocalDescription тип answer.

enter image description here Я понятия не имею, должен ли он вести себя так или нет, как я начинающий.

Заранее спасибо.

1 Ответ

0 голосов
/ 28 октября 2018

Ваш код правильный.Это давняя ошибка в Chrome с negotiationneeded.

Я инструктировал ее в скрипке (щелкните правой кнопкой мыши и откройте в двух смежных окнах, затем нажмите кнопку вызова в одном).

В Firefox это работает.Оферент согласовывает один раз, потому что вы добавляете сразу две дорожки (видео / аудио):

negotiating in stable
onmessage answer

, а со стороны ответчика дорожки, которые вы добавляете за пределами состояния 'stable', добавляются к ответу:

onmessage offer
adding audio track
adding video track

Но в Chrome он не работает, дважды запускает negotiationneeded для источника, добавляется один раз для каждой дорожки:

negotiating in stable
negotiating in stable
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

и дважды запускается negotiationneeded на стороне отвечающего, чтодаже не в 'stable' состоянии:

onmessage offer
adding audio track
adding video track
negotiating in have-remote-offer
negotiating in have-remote-offer
onmessage offer
DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection':
  Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer

Эти дополнительные события приводят к хаосу взаимных ошибок состояния, наблюдаемых здесь на обоих концах.

Чтобы быть точным, Chrome нарушает две части spec здесь:

  1. «Поставить задачу в очередь» для запуска этого события. "постановка в очередь предотвращает преждевременное срабатывание согласования в общей ситуации, когда одновременно выполняется несколько модификаций соединения."

  2. Если состояние сигнализации соединенияне "stable", отмените эти шаги [для запуска события].

Обходной путь

Обход Обе Требуются ошибки Chrome (используя async / await для краткости):

let negotiating = false;
pc.onnegotiationneeded = async e => {
  try {
    if (negotiating || pc.signalingState != "stable") return;
    negotiating = true;
    /* Your async/await-using code goes here */
  } finally {
    negotiating = false;
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...