function log(...args) { console.log.apply(null, ["[webrtc-internal-exporter:override]", ...args]); } log("Override RTCPeerConnection."); class WebrtcInternalExporter { peerConnections = new Map(); url = ""; enabled = false; updateInterval = 2000; enabledStats = []; constructor() { window.addEventListener("message", async (message) => { const { event, options } = message.data; if (event === "webrtc-internal-exporter:options") { log("options updated:", options); Object.assign(this, options); } }); window.postMessage({ event: "webrtc-internal-exporter:ready" }); this.collectAllStats(); } randomId() { return ( window.crypto?.randomUUID() || (2 ** 64 * Math.random()).toString(16) ); } add(pc) { const id = this.randomId(); this.peerConnections.set(id, pc); pc.addEventListener("connectionstatechange", () => { if (pc.connectionState === "closed") { this.peerConnections.delete(id); } }); //this.collectAndPostStats(id); } async collectAndPostSingleStat(id) { const stats = await this.collectStats(id, this.collectAndPostSingleStat); if (Object.keys(stats).length === 0 || !stats) return; window.postMessage( { event: "webrtc-internal-exporter:peer-connection-stats", ...stats, }, [stats], ); } async collectAllStats() { const stats = []; for (const [id, pc] of this.peerConnections) { if (this.url && this.enabled && pc.connectionState === "connected") { const pcStats = await this.collectStats(id, pc); stats.push(pcStats); } } //if (stats.length !== 0) { window.postMessage( { event: "webrtc-internal-exporter:peer-connections-stats", stats, }, [stats], ); log(`Stats collected:`, stats); //} setTimeout(this.collectAllStats.bind(this), this.updateInterval); return stats; } async collectStats(id, pc, binding) { var completeStats = {}; if (!pc) { pc = this.peerConnections.get(id); if (!pc) return; } if (this.url && this.enabled && pc.connectionState === "connected") { try { const stats = await pc.getStats(); const values = [...stats.values()].filter( (v) => ["peer-connection", ...this.enabledStats].indexOf(v.type) !== -1 ); completeStats = { url: window.location.href, id, state: pc.connectionState, values, }; } catch (error) { log(`collectStats error: ${error.message}`); } } if (pc.connectionState === "closed") { this.peerConnections.delete(id); } else { if (binding) { setTimeout(binding.bind(this), this.updateInterval, id); } } return completeStats; } } const webrtcInternalExporter = new WebrtcInternalExporter(); window.RTCPeerConnection = new Proxy(window.RTCPeerConnection, { construct(target, argumentsList) { log(`RTCPeerConnection`, argumentsList); const pc = new target(...argumentsList); webrtcInternalExporter.add(pc); return pc; }, });