WebRT C завис в состоянии подключения - PullRequest
1 голос
/ 26 мая 2020

Я успешно передал предложение, ответ и замороженных кандидатов для подключения WebRT C от A к B. На этом этапе соединение застряло в состоянии "connecting". Инициатор (A), кажется, истекает по таймауту или что-то в этом роде через некоторое время и переключается в состояние "failed", тогда как его пульт (B) постоянно находится в состоянии "connecting".

Любая помощь будет очень признательна .

Создание пира (A и B):

let peer = new RTCPeerConnection({
    iceServers: [
        {
            urls: [
                "stun:stun1.l.google.com:19302",
                "stun:stun2.l.google.com:19302",
            ],
        },
        {
            urls: [
                "stun:global.stun.twilio.com:3478?transport=udp",
            ],
        },
    ],
    iceCandidatePoolSize: 10,
});

Создание предложения (A):

peer.onnegotiationneeded = async () => {
    offer = await peer.createOffer();
    await peer.setLocalDescription(offer);
};

Сбор ледяных кандидатов (A):

peer.onicecandidate = (evt) => {
    if (evt.candidate) {
        iceCandidates.push(evt.candidate);
    } else {
        // send offer and iceCandidates to B through signaling server
        // this part is working perfectly
    }
};

Создание ответа и заполнение ледяных кандидатов (B):

await peer.setRemoteDescription(offer);

let answer = await this._peer.createAnswer();
await peer.setLocalDescription(answer);

// send answer back to A through signaling server

for (let candidate of sigData.iceCandidates) {
    await peer.addIceCandidate(candidate);
}

При ответе от B через сервер сигнализации (A):

await peer.setRemoteDescription(answer);

Обнаружение изменения состояния соединения ( A и B):

peer.onconnectionstatechange = () => {
    console.log("state changed")
    console.log(peer.connectionState);
}

Также обратите внимание, что было два случая, когда он был успешно подключен, но я еще не видел, чтобы он снова работал.

EDIT : Я забыл упомянуть, что я также создаю канал данных (событие onicecandidate, похоже, не вызывается без этого). Это вызывается сразу после создания RTCPeerConnection и присоединения любых обработчиков событий.

let channel = peer.createDataChannel("...", {
    id: ...,
    ordered: true,
});

EDIT 2 : Как предлагается @ jib , я теперь также собираем ледяных кандидатов в B и отправляем их обратно в A для добавления. Однако та же проблема сохраняется.

РЕДАКТИРОВАТЬ 3 : Кажется, подключается в первый раз, когда я сильно перезагружаю веб-страницу для A и веб-страницу для B. Соединение перестает работать снова, пока я не сделаю еще одна жесткая перезагрузка. Есть ли у кого-нибудь идеи, почему это так? По крайней мере, я смогу продолжить разработку до тех пор, пока не выясню эту проблему.

РЕДАКТИРОВАТЬ 4 : Я удалил iceServers, который использовал, и оставил RTCPeerConnection конструктор пустой. Как-то теперь намного надежнее. Но мне еще не удалось установить соединение на iOS Safari!

Ответы [ 2 ]

2 голосов
/ 26 мая 2020

Откройте этот в двух браузерах windows и нажмите кнопку Connect в одном из них. Это код:

const pc = new RTCPeerConnection();

call.onclick = async () => {
  const stream = await navigator.mediaDevices.getUserMedia({video:true,audio:true})
  video.srcObject = stream;
  for (const track of stream.getTracks()) {
    pc.addTrack(track, stream);
  }
};

pc.ontrack = ({streams}) => video.srcObject = streams[0];
pc.oniceconnectionstatechange = () => console.log(pc.iceConnectionState);
pc.onicecandidate = ({candidate}) => sc.send({candidate});
pc.onnegotiationneeded = async () => {
  await pc.setLocalDescription(await pc.createOffer());
  sc.send({sdp: pc.localDescription});
}

const sc = new localSocket(); // localStorage signaling hack
sc.onmessage = async ({data: {sdp, candidate}}) => {
  if (sdp) {
    await pc.setRemoteDescription(sdp);
    if (sdp.type == "offer") {
      await pc.setLocalDescription(await pc.createAnswer());
      sc.send({sdp: pc.localDescription});
    }
  } else if (candidate) await pc.addIceCandidate(candidate);
}

Это тот же источник для A и B. Замените хак localSocket на ваш предпочтительный канал сигнализации (например, веб-сокет).

Не кэшировать Кандидаты ICE, поскольку это побеждает цель Trickle ICE. Это может показаться быстрым локально, но в реальных сетях ICE может занять время.

Фактически, отправка кандидатов бессмысленна, если вы откладываете отправку предложения / ответа до тех пор, пока все местные кандидаты не будут собраны, поскольку кандидаты уже встроены в предложение / ответ (pc.localDescription) в этот момент.

0 голосов
/ 14 июня 2020

Наконец-то! Через несколько недель я выяснил проблему, которая не была очевидна в коде, который я включил в свой вопрос, но все же может быть полезна для всех, у кого есть подобные проблемы. был завершен после срабатывания события onnegotiationneeded и создания предложения / ответа.

Из-за этого неверного предположения я сигнализировал предложение / ответ вместе с ледяными кандидатами на этом этап, но очень часто (всегда в iOS Safari по моему опыту) предложение / ответ еще не было создано на этом этапе.

Я решил это, создав два обещания для а) завершения сбора ледяных кандидатов , и б) создание предложения / ответа. Я использовал Promise.all в двух обещаниях, и когда они оба были выполнены, я отправил ледяных кандидатов и предложение / ответ через сервер сигнализации одновременно.

Это работает, но, конечно, в будущем я должна «просачиваться» этой информацией, посылая кусочки по мере их поступления, вместо того, чтобы ждать, пока все будет полностью завершено. Но я буду беспокоиться об этом в будущем, поскольку в настоящий момент я использую HTTP-запросы, и это слишком хлопотно.

EDIT : мое соединение все еще всегда зависает, когда iceServers включены, поэтому я создал новый вопрос . Но локальные соединения, когда не включено iceServers, теперь полностью надежны на 100% :)

...