Переподключение нескольких peerConnections после перезагрузки страницы
Я создаю веб-приложение для мониторинга с помощью смартфонов с помощью 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
консольный вывод: 1
журнал показывает, что signalingState
связи "стабильно", но когда я разрабатываю объект, signalingState
равнозначен "иметь удаленное предложение"
1 ответ
Удалить TempDescriptions
глобальная переменная, и передать sdp getStream(offer.sdp)
непосредственно.
В противном случае вы socket.on('ask', function(offer){
звонил 4 раза, перезаписывал TempDescriptions
, Затем через 3 с лишним секунды ваши 4 setTimeout
s вращаются, все получают доступ к окончательному значению 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
Scoping: общая проблема
Здесь вы имеете дело с четырьмя связями, но у вас нет подхода к определению объема каждого экземпляра.
Большинство других языков сказали бы вам создать класс и сделать экземпляры объекта, и поместить все, включая connection
на this
, Это один хороший подход. В JS вместо этого вы можете использовать замыкания. Но как минимум вам все еще нужно 4 переменные, содержащие 4 соединения, или массив connections
, Затем вы смотрите вверх - например, из id
Я упомянул - с какой связью иметь дело.
Кроме того, ваш try
/catch
Они не собираются ловить асинхронные ошибки. Вместо того, чтобы определять все эти обратные вызовы, я настоятельно рекомендую использовать обещания или даже async/await при работе с высоко асинхронным API WebRTC. Это делает область видимости тривиальной. Например
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);
}
};
Таким образом, обработка ошибок является надежной.