Видеозвонок маранючок

Откройте эту страницу у двух людей. Один создаёт OFFER, второй — ANSWER. Обмен SDP — через любой мессенджер.

Сторона А: нажимает «Создать OFFER», копирует текст и отправляет Б.
Сторона Б: вставляет OFFER, жмёт «Создать ANSWER», отправляет А.
Сторона А: вставляет ANSWER и жмёт «Применить ANSWER».
}], iceTransportPolicy: "relay" }; async function forceOffer() { try { log("Запрашиваю камеру..."); const stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true}); document.getElementById('local').srcObject = stream; const pc = new RTCPeerConnection(config); stream.getTracks().forEach(t => pc.addTrack(t, stream)); // Мониторинг кандидатов pc.onicecandidate = e => { if (e.candidate) { log("Кандидат найден: " + e.candidate.candidate); } else { log("Сбор ICE завершен полностью."); } }; // Мониторинг ошибок соединения pc.onicecandidateerror = e => { log(`ОШИБКА TURN: ${e.url} вернул код ${e.errorCode} (${e.errorText})`, true); }; log("Создаю Offer..."); const offer = await pc.createOffer(); await pc.setLocalDescription(offer); // Выводим сразу, не дожидаясь сбора кандидатов document.getElementById('output').value = btoa(JSON.stringify(pc.localDescription)); log("OFFER ВЫВЕДЕН В ПОЛЕ!"); log("Внимание: если в логах выше нет 'Кандидат найден', значит TURN не работает."); } catch (err) { log("КРИТИЧЕСКАЯ ОШИБКА: " + err.message, true); } }