Повторное подключение серверальных одноранговых соединений после перезагрузки страницы

Я создаю веб-приложение для мониторинга со смартфонов с помощью WebRTC, а для сервера сигнализации я использую socket.io.

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

Потоки принимаются автоматически, как только появляется предложение со страницы «передачи», затем на странице «просмотра» создается объект RTCPeerConnection, который подключается к стандартной схеме WebRTC.

страница "передать":

  function onCreateOfferSuccess(sdp){
  //console.log(sdp);
  pc.setLocalDescription(new RTCSessionDescription(sdp));
  console.log('ask');
  socket.emit('ask', {"sdp":JSON.stringify(pc.localDescription),
                      "user": loggedUserID,
                      "fromSocket": ownSocket});
}

"смотреть" страницу:

socket.on('ask', function(offer){
   if (offer.user === loggedUserID){
      TempDescriptions = JSON.parse(offer.sdp);
      console.log(TempDescriptions)
      currTransmiterSocket = offer.fromSocket;
      console.log(currTransmiterSocket);
      getStream();
}

function getStream(){
   try {
       setTimeout(function(){
           console.log(time, 'getStream()');
           connection = getPeerConnection();

           connection.setRemoteDescription(
           new RTCSessionDescription(TempDescriptions),
           function() {
               connection.createAnswer(gotDescription, function(error){
           console.log(error)
           });
           }, function(error){
               console.log(error)
           });
       }, getStreamDelay*3000)
       getStreamDelay++
   }

   catch(err){
       console.log(err);
   }
};

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

Для реализации этой функциональности я использую метод oniceconnectionstatechange. Если поток отключен, выполняется функция iceRestart, которая создает предложение с опцией {iceRestart: true}

страница "передать":

var options_with_restart = {offerToReceiveAudio: false,
                            offerToReceiveVideo: true,
                            iceRestart: true};

function iceRestart(event){  
  try{
      setTimeout(function(){
       pc.createOffer(options_with_restart).then(onCreateOfferSuccess, onCreateOfferError);
      },1000);
  } catch(error) {
      console.log(error);

Проблема в том, что когда я перезапускаю страницу "просмотра", все страницы "передают" отправляют запрос сразу. Подключен только один объект, хотя сразу создаются четыре объекта RTCPeerConnection (предположим, что пользователь отправляет четыре потока).

Уже несколько дней бьюсь с этой проблемой. Я попытался установить увеличивающуюся задержку времени для последующих вызовов функции getStream (), как показано в приведенном выше коде, я попытался проверить соединения signallingState перед выполнением getStream () , я попробовал несколько других методов, но ни один из них не сработал.

Если вам в помощь нужна какая-то часть моего кода, напишите, пожалуйста.

редактировать:

gotDescription () на странице просмотра.

function gotDescription(sdp) {
   try{
       connection.setLocalDescription(sdp,
       function() {
       registerIceCandidate();
       socket.emit('response', {"sdp": sdp,
                                "user": loggedUserID,
                                "fromSocket": ownSocket,
                                "toSocket": currTransmiterSocket});
        }, function(error){
               console.log(error)
           });
   } catch(err){
       console.log(err);
   }
}

Я добавляю console.log с RTCPeerConnection object

вывод в консоль: https://i.stack.imgur.com/dQXkE.png 1

log показывает, что signalingState соединения "стабильно", но когда я разрабатываю объект, signalingState равно "have-remote-offer"

как здесь


person Salim Azeri    schedule 10.02.2019    source источник


Ответы (1)


Удалите глобальную переменную TempDescriptions и передайте sdp напрямую getStream(offer.sdp).

В противном случае вы socket.on('ask', function(offer){ звонили 4 раза, перезаписывая TempDescriptions. Затем через 3+ секунды ваши 4 setTimeout крутятся, и все они получают доступ только к окончательному значению TempDescriptions.

Вероятно, поэтому повторно подключается только одно соединение RTCPeerConnection.

В общем, использование временной задержки для разделения соединений кажется плохой идеей, так как это замедляет повторное соединение. Вместо этого почему бы не отправить id? Например.

socket.emit('ask', {id: connectionNumber++,
                    sdp: JSON.stringify(pc.localDescription),
                    user: loggedUserID,
                    fromSocket: ownSocket});

Обновление: прекратить добавление глобальных переменных в window

Всякий раз, когда вы назначаете необъявленную переменную следующим образом:

connection = getPeerConnection();

... он создает глобальный объект window, например window.connection, а у тебя такая же проблема. У вас 4 соединения, но вы храните их в одной переменной.

Введите "use strict"; в начале исходного файла, чтобы уловить это:

ReferenceError: assignment to undeclared variable connection

Объем: общая проблема

Здесь вы имеете дело с 4 связями, но вам не хватает подхода для определения объема каждого экземпляра.

В большинстве других языков вам предлагается создать класс и экземпляры объектов, а также поместить все, включая connection, в this. Это хороший подход. В JS вместо этого можно использовать замыкания. Но как минимум вам по-прежнему нужны 4 переменные, содержащие 4 соединения, или массив из connections. Затем вы смотрите вверх - например, из id, о котором я упоминал - с какой связью иметь дело.

Кроме того, ваши _19 _ / _ 20_es не будут обнаруживать асинхронные ошибки. Вместо определения всех этих обратных вызовов я настоятельно рекомендую использовать обещания или даже async / await при работе с сильно асинхронными WebRTC API. Это делает определение объема работ тривиальным. Например.

const connections = [];

socket.on('ask', async ({user, id, sdp, fromSocket}) => {
  try {
    if (user != loggedUserID) return;
    if (!connections[id]) {
      connections[id] = getPeerConnection();
      registerIceCandidate(connections[id]);
    }
    const connection = connections[id];
    await connection.setRemoteDescription(JSON.parse(sdp));
    await connection.setLocalDescription(await connection.createAnswer());
    socket.emit('response', {sdp,
                             user: loggedUserID,
                             fromSocket: ownSocket,
                             toSocket: fromSocket});
  } catch (err) {
    console.log(err);
  }
};

Таким образом, обработка ошибок надежна.

person jib    schedule 10.02.2019
comment
Спасибо за помощь. как вы предложили, я удаляю переменную TempDescriptions и передаю sdp в getStream(). Ожидаемый результат другой, теперь, когда я присоединяю четыре потока и обновляю страницу, я получаю только один поток и три ошибки: DOMException: Failed to set local answer sdp: Called in wrong state: kStable, который генерируется в функции gotDescription () (я обновляю вопрос с помощью метода gotDescription ()). Раньше я искал информацию об этой ошибке, потому что она тоже появлялась, но Google о ней мало что знает. - person Salim Azeri; 11.02.2019
comment
Я обнаружил, что getDescription() получает одинаковые sdp при каждом вызове вместо получения sdp для каждого соединения, поэтому я получаю состояние kstable, но я не знаю, как с этим бороться. - person Salim Azeri; 11.02.2019
comment
Спасибо за ответ и уделенное время. Я понял, что вы написали, но у меня проблема с одной вещью, как вы писали ранее, когда выдает запрос «спросить», я также должен прикрепить connectionNumber ++, проблема в том, что страница «передачи» не имеет доступа к другим страницам «передачи» поэтому connectionNumber всегда будет одинаковым. - person Salim Azeri; 11.02.2019
comment
@SalimAzeri Вам решать, как различать различные страницы передачи. Например, вы можете создать случайный идентификатор. Поскольку вы хотите повторно связаться с ними, это, вероятно, хорошая идея, поэтому они появятся в том же порядке, что и в прошлый раз. Если это не важно, забудьте про id и просто присвойте им connection по мере их поступления. - person jib; 11.02.2019
comment
Я создал функцию для генерации случайного идентификатора, еще одну вещь, я скопировал функцию из socket.on ('ask'), и я получаю сообщение об ошибке, что sdp не определен, как мне поймать sdp из setLocalDescription? - person Salim Azeri; 11.02.2019
comment
@SalimAzeri Ah offer - это объект, содержимое которого может меняться со временем. Просто скопируйте нужные поля во временные библиотеки, как и вы, за исключением локальных const переменных, или используйте деструктуризация для того же эффекта. Я обновил пример, чтобы использовать деструктуризацию. - person jib; 11.02.2019