SHA256
feat(call): серверная выдача ICE/TURN и подключение в WebRTC
This commit is contained in:
@@ -1412,6 +1412,13 @@ export class AuthService {
|
||||
if (response.status !== 200) throw opError('CallSignalToSession', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async getCallIceConfig() {
|
||||
const response = await this.ws.request('GetCallIceConfig', {});
|
||||
if (response.status !== 200) throw opError('GetCallIceConfig', response);
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async listContacts() {
|
||||
const response = await this.ws.request('ListContacts', {});
|
||||
if (response.status !== 200) throw opError('ListContacts', response);
|
||||
|
||||
@@ -23,6 +23,10 @@ let toneTimerId = null;
|
||||
let toneName = '';
|
||||
let toneFlip = false;
|
||||
|
||||
const DEFAULT_ICE_SERVERS = Object.freeze([
|
||||
{ urls: 'stun:stun.l.google.com:19302' },
|
||||
]);
|
||||
|
||||
function nowMs() {
|
||||
return Date.now();
|
||||
}
|
||||
@@ -55,6 +59,65 @@ function formatDuration(ms) {
|
||||
return `${sec}с`;
|
||||
}
|
||||
|
||||
function cloneDefaultIceServers() {
|
||||
return DEFAULT_ICE_SERVERS.map((row) => ({ ...row }));
|
||||
}
|
||||
|
||||
function parseIceUrls(raw) {
|
||||
if (Array.isArray(raw)) {
|
||||
return raw
|
||||
.map((item) => String(item || '').trim())
|
||||
.filter((item) => item.length > 0);
|
||||
}
|
||||
const single = String(raw || '').trim();
|
||||
if (!single) return [];
|
||||
return [single];
|
||||
}
|
||||
|
||||
function uniqueUrls(urls = []) {
|
||||
const out = [];
|
||||
const seen = new Set();
|
||||
urls.forEach((url) => {
|
||||
const clean = String(url || '').trim();
|
||||
if (!clean || seen.has(clean)) return;
|
||||
seen.add(clean);
|
||||
out.push(clean);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
async function resolveIceServers(call) {
|
||||
try {
|
||||
const payload = await authService.getCallIceConfig();
|
||||
const stunUrls = uniqueUrls(parseIceUrls(payload?.stunUrls));
|
||||
const turnUrls = uniqueUrls(parseIceUrls(payload?.turnUrls));
|
||||
const turnUsername = String(payload?.turnUsername || '').trim();
|
||||
const turnPassword = String(payload?.turnPassword || '').trim();
|
||||
|
||||
const iceServers = [];
|
||||
if (stunUrls.length > 0) {
|
||||
iceServers.push({ urls: stunUrls.length === 1 ? stunUrls[0] : stunUrls });
|
||||
}
|
||||
if (turnUrls.length > 0 && turnUsername && turnPassword) {
|
||||
iceServers.push({
|
||||
urls: turnUrls.length === 1 ? turnUrls[0] : turnUrls,
|
||||
username: turnUsername,
|
||||
credential: turnPassword,
|
||||
});
|
||||
}
|
||||
|
||||
if (iceServers.length === 0) {
|
||||
await emitDebug(call, 'warn', 'call_ice_empty_from_server', 'using_default_stun');
|
||||
return cloneDefaultIceServers();
|
||||
}
|
||||
await emitDebug(call, 'info', 'call_ice_loaded_from_server', `stun=${stunUrls.length}; turn=${turnUrls.length}`);
|
||||
return iceServers;
|
||||
} catch (error) {
|
||||
await emitDebug(call, 'warn', 'call_ice_load_failed', toErrorText(error));
|
||||
return cloneDefaultIceServers();
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAudioContext() {
|
||||
if (audioContext) return audioContext;
|
||||
const Ctx = window.AudioContext || window.webkitAudioContext;
|
||||
@@ -365,8 +428,9 @@ async function sendSignal(call, type, data = '') {
|
||||
async function ensurePeerConnection(call) {
|
||||
if (call.pc) return call.pc;
|
||||
|
||||
const iceServers = await resolveIceServers(call);
|
||||
const pc = new RTCPeerConnection({
|
||||
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
|
||||
iceServers,
|
||||
});
|
||||
|
||||
if (call.debugMode && call.debugRole === 'initiator') {
|
||||
|
||||
Reference in New Issue
Block a user