fix(core): drop video packets on a full UDP send buffer, don't fail the session

UdpTransport sockets are non-blocking, so a momentarily-full kernel send buffer
makes socket.send return WouldBlock (EAGAIN). submit_frame propagated that as a
fatal error, tearing the whole punktfunk/1 session down — observed when attaching
to an already-running source (a headless Steam session) that emits frames at full
rate the instant capture connects: the first burst saturates the tx queue and the
session dies before a single frame reaches the client.

The data plane is lossy + Leopard-FEC-protected and runs infinite-GOP with RFI
keyframes, so the real-time-correct response to a full tx queue is to DROP the
packet (the next frame / FEC recovers) — exactly what the recv path already does
for WouldBlock. Blocking would queue stale frames and add latency. Loopback/M1
paths are unaffected (LoopbackTransport never blocks; M1 tests stay green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:55:12 +00:00
parent e414ec0895
commit b6f4164454
+11 -2
View File
@@ -31,8 +31,17 @@ impl UdpTransport {
impl Transport for UdpTransport { impl Transport for UdpTransport {
fn send(&self, packet: &[u8]) -> std::io::Result<()> { fn send(&self, packet: &[u8]) -> std::io::Result<()> {
self.socket.send(packet)?; match self.socket.send(packet) {
Ok(()) Ok(_) => Ok(()),
// The kernel UDP send buffer is momentarily full (a frame burst saturated the
// tx queue — common right after attaching to an already-running source that
// emits at full rate). Drop this packet rather than fail the whole stream: the
// data plane is lossy + FEC-protected and the next frame/RFI keyframe recovers,
// whereas blocking would queue stale frames and add latency, and erroring tears
// the session down. Mirrors the `recv` WouldBlock handling above.
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(()),
Err(e) => Err(e),
}
} }
fn recv(&self) -> std::io::Result<Option<Vec<u8>>> { fn recv(&self) -> std::io::Result<Option<Vec<u8>>> {