/** * Unified RTP port pool — replaces the three separate allocators * in sipproxy.ts, calloriginator.ts, and webrtcbridge.ts. * * Allocates even-numbered UDP ports from a configured range. * Each allocation binds a dgram socket and returns it ready to use. */ import dgram from 'node:dgram'; export interface IRtpAllocation { port: number; sock: dgram.Socket; } export class RtpPortPool { private min: number; private max: number; private allocated = new Map(); private log: (msg: string) => void; constructor(min: number, max: number, log: (msg: string) => void) { this.min = min % 2 === 0 ? min : min + 1; // ensure even start this.max = max; this.log = log; } /** * Allocate an even-numbered port and bind a UDP socket to it. * Returns null if the pool is exhausted. */ allocate(): IRtpAllocation | null { for (let port = this.min; port < this.max; port += 2) { if (this.allocated.has(port)) continue; const sock = dgram.createSocket('udp4'); try { sock.bind(port, '0.0.0.0'); } catch { try { sock.close(); } catch { /* ignore */ } continue; } this.allocated.set(port, sock); this.log(`[rtp-pool] allocated port ${port} (${this.allocated.size} in use)`); return { port, sock }; } this.log('[rtp-pool] WARN: port pool exhausted'); return null; } /** * Release a port back to the pool and close its socket. */ release(port: number): void { const sock = this.allocated.get(port); if (!sock) return; try { sock.close(); } catch { /* ignore */ } this.allocated.delete(port); this.log(`[rtp-pool] released port ${port} (${this.allocated.size} in use)`); } /** Number of currently allocated ports. */ get size(): number { return this.allocated.size; } /** Total capacity (number of even ports in range). */ get capacity(): number { return Math.floor((this.max - this.min) / 2); } }