WTVPNM: tweak udp output rate a bit, make burst multiplier configurable

This commit is contained in:
zefie
2026-04-22 17:53:45 -04:00
parent 7246cc11f0
commit 0358f0ffd7

View File

@@ -202,13 +202,9 @@ class WTVPNM {
session.capabilitiesLogged = true; session.capabilitiesLogged = true;
const cap = this.extractCapabilities(data); const cap = this.extractCapabilities(data);
session.capabilities = cap; session.capabilities = cap;
// Detect WebTV via User-Agent string in raw request data. // Detect WebTV via User-Agent, since it prefers low server challenges
// The WebTV PNM client advertises cook/sipr caps like modern
// RealPlayer, so codec-sniffing is unreliable; the UA is the
// only dependable discriminator. Captured UA format:
// 'Mozilla/3.0 WebTV/2.5 (Compatible; MSIE 2.0)'
const raw = data.toString('latin1'); const raw = data.toString('latin1');
session.isWebTV = /WebTV\//i.test(raw); session.isWebTV = /WebTV\//i.test(session.pnaFields?.useragent) || false;
if (cap.length > 0) { if (cap.length > 0) {
this.debugLog('client capabilities', session.id, cap.join(', ')); this.debugLog('client capabilities', session.id, cap.join(', '));
} }
@@ -1489,11 +1485,13 @@ class WTVPNM {
// granularity. Compensation scales with interval: shorter intervals // granularity. Compensation scales with interval: shorter intervals
// need more compensation. Formula: 1 - (fixedOverhead / calculatedInterval) // need more compensation. Formula: 1 - (fixedOverhead / calculatedInterval)
let intervalMs; let intervalMs;
if (session.avgBitRate > 0) { if (session.avgBitRate > 0) {
intervalMs = (avgBytesPerPacket * 8000) / session.avgBitRate; intervalMs = (avgBytesPerPacket * 8000) / session.avgBitRate;
// Adaptive Windows timer compensation: 13ms is empirical fixed overhead // Adaptive Windows timer compensation: 13ms is empirical fixed overhead
const compensation = Math.max(0.90, 1 - (13 / intervalMs)); const compensation = Math.max(0.90, 1 - (13 / intervalMs));
intervalMs *= compensation; intervalMs *= compensation;
intervalMs -= 6;
} else { } else {
intervalMs = 220; intervalMs = 220;
} }
@@ -1508,6 +1506,8 @@ class WTVPNM {
const burstPrestartMs = typeof this.service_config.burst_prestart_ms === 'number' const burstPrestartMs = typeof this.service_config.burst_prestart_ms === 'number'
? this.service_config.burst_prestart_ms ? this.service_config.burst_prestart_ms
: 3000; : 3000;
const burstMultiplier = typeof this.service_config.burst_multiplier === 'number'
? this.service_config.burst_multiplier : 2;
const burstFrameCount = burstPrestartMs > 0 ? Math.ceil(burstPrestartMs / intervalMs) : 0; const burstFrameCount = burstPrestartMs > 0 ? Math.ceil(burstPrestartMs / intervalMs) : 0;
session.burstFramesSent = 0; session.burstFramesSent = 0;
@@ -1519,6 +1519,7 @@ class WTVPNM {
`bodyLen=${bodyLen}`, `bodyLen=${bodyLen}`,
`interval=${intervalMs.toFixed(2)}ms`, `interval=${intervalMs.toFixed(2)}ms`,
`burstFrames=${burstFrameCount}`, `burstFrames=${burstFrameCount}`,
`burstRate=${(session.avgBitRate * burstMultiplier) || 'unknown'}bps`,
`target=${targetIp}:${mediaTargetPort}`, `target=${targetIp}:${mediaTargetPort}`,
`sourcePort=${session.serverUdpPort || 'unknown'}`); `sourcePort=${session.serverUdpPort || 'unknown'}`);
@@ -1625,10 +1626,10 @@ class WTVPNM {
session.burstFramesSent++; session.burstFramesSent++;
// Use half the interval during the pre-start burst window, then // Use half the interval during the pre-start burst window, then
// drop to normal pacing once burstFrameCount frames have been sent. // drop to normal pacing once burstFrameCount frames have been sent.
const delay = session.burstFramesSent < burstFrameCount ? intervalMs / 2 : intervalMs; const delay = session.burstFramesSent < burstFrameCount ? intervalMs / burstMultiplier : intervalMs;
session.udpTimer = setTimeout(tick, delay); session.udpTimer = setTimeout(tick, delay);
}; };
const initialDelay = session.burstFramesSent < burstFrameCount ? intervalMs / 2 : intervalMs; const initialDelay = session.burstFramesSent < burstFrameCount ? intervalMs / burstMultiplier : intervalMs;
session.udpTimer = setTimeout(tick, initialDelay); session.udpTimer = setTimeout(tick, initialDelay);
}; };