function createStatsSummary() { return { video: { packetsLost: 0, packetsReceived: 0, bytesReceived: 0, jitter: 0, roundTripTime: 0, fps: 0, bitrate: 0 }, audio: { packetsLost: 0, packetsReceived: 0, bytesReceived: 0, jitter: 0 }, network: { totalPacketsLost: 0, totalPacketsReceived: 0, inboundRtpCount: 0, jitter: 0, roundTripTime: 0 } }; } export function summarizeInboundStats(stats) { const summary = createStatsSummary(); stats.forEach((report) => { if (report.type === 'inbound-rtp' && report.mediaType === 'video') { summary.network.inboundRtpCount++; if (report.packetsLost !== undefined && report.packetsReceived !== undefined) { summary.network.totalPacketsLost += report.packetsLost; summary.network.totalPacketsReceived += report.packetsReceived; } if (report.jitter !== undefined) { summary.network.jitter = Math.max(summary.network.jitter, report.jitter); } if (report.roundTripTime !== undefined) { summary.network.roundTripTime = Math.max(summary.network.roundTripTime, report.roundTripTime); } summary.video.packetsLost = report.packetsLost || 0; summary.video.packetsReceived = report.packetsReceived || 0; summary.video.bytesReceived = report.bytesReceived || 0; summary.video.jitter = report.jitter || 0; summary.video.roundTripTime = report.roundTripTime || 0; summary.video.fps = report.framesPerSecond || 0; if (report.bytesReceived && report.timestamp) { const duration = report.timestamp / 1000; summary.video.bitrate = duration > 0 ? Math.round((report.bytesReceived * 8) / (duration * 1000)) : 0; } } else if (report.type === 'inbound-rtp' && report.mediaType === 'audio') { summary.audio.packetsLost = report.packetsLost || 0; summary.audio.packetsReceived = report.packetsReceived || 0; summary.audio.bytesReceived = report.bytesReceived || 0; summary.audio.jitter = report.jitter || 0; } }); return summary; } export function getNetworkQualityFromSummary(summary) { const { totalPacketsLost, totalPacketsReceived, inboundRtpCount, jitter, roundTripTime } = summary.network; if (inboundRtpCount === 0) { return 'no_signal'; } const packetLossRate = totalPacketsReceived > 0 ? totalPacketsLost / (totalPacketsLost + totalPacketsReceived) : 0; const jitterMs = jitter * 1000; const rttMs = roundTripTime * 1000; if (packetLossRate > 0.05 || jitterMs > 100 || rttMs > 300) { return 'poor'; } if (packetLossRate > 0.02 || jitterMs > 50 || rttMs > 150) { return 'fair'; } if (packetLossRate > 0.01 || jitterMs > 30 || rttMs > 100) { return 'good'; } return 'excellent'; }