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:
@@ -31,8 +31,17 @@ impl UdpTransport {
|
||||
|
||||
impl Transport for UdpTransport {
|
||||
fn send(&self, packet: &[u8]) -> std::io::Result<()> {
|
||||
self.socket.send(packet)?;
|
||||
Ok(())
|
||||
match self.socket.send(packet) {
|
||||
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>>> {
|
||||
|
||||
Reference in New Issue
Block a user