e490564316
Root-caused live on a phone at 100 Mbps (stream stuck seconds behind, then oscillating): a stack of transport defects, each amplifying the next. - MTU-safe shards: shard_payload 1452 overshot the IPv4/1500 budget (the old math forgot the 40 B header + 24 B crypto ride inside the UDP payload and counted IP+UDP as 8 B) — the kernel silently split EVERY video datagram into two IP fragments, doubling per-datagram loss on Wi-Fi. New config::mtu1500_shard_payload() = 1408 (1472 sealed = the exact ceiling), negotiated in the Welcome, pinned by a unit test. - Android batched I/O: recv/send batching was cfg(linux); Android is target_os="android" and silently fell back to a syscall per datagram. The libc crate binds neither recvmmsg/sendmmsg nor mmsghdr for Android, so a local bionic extern binding provides them (API 21+, floor is 28); cbindgen excludes them from the C header. The pump/runtime threads also get the Apple-QoS analogue on Android: nice −8 (below the decode thread's −10). - Latency-bounded receive: packets are consumed strictly in order at exactly the arrival rate, so a standing queue (Wi-Fi stall, power-save clumping) NEVER drains — observed as a stream permanently 6-7 s behind with both 32 MB socket buffers full. The pump now flushes the entire backlog (Session::flush_backlog: discard ring + kernel queue at memcpy speed, reset the reassembler) and requests a keyframe when frames keep completing > 400 ms behind the skew-corrected capture clock (30 consecutive, 2 s cooldown, logged). - Time-based loss window: the reassembler declared an incomplete frame lost a fixed 4 INDICES behind the newest — 33 ms at 120 fps, inside normal Wi-Fi retry/reorder timescales, so merely-late frames were pruned every few seconds, each costing a recovery-IDR burst + an inflated loss report. Now 120 ms of capture time (LOSS_WINDOW_NS), same fuse at every refresh rate, with a 64-index hard cap bounding memory against hostile pts. - Adaptive-FEC hysteresis: the controller was memoryless — one clean 750 ms report dropped FEC from 8 % straight back to the 1 % floor, so periodic burst loss (Wi-Fi scan / BT coexistence beats) always hit an unprotected stream and ping-ponged 1↔8 % with a frozen frame per cycle (observed in the host log as alternating loss_ppm=0/50000). Attack stays instant; decay is now one point per clean report. Verified: full core suite (incl. new flush + time-window tests) on macOS + Linux, host release build, arm64 cargo-ndk build, and a 30 s wired probe run at 2800x1260@120 — 3559/3559 frames, zero loss, capture→received p50 5.3 ms (host 5.1 + network 0.3). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
65 lines
2.2 KiB
TOML
65 lines
2.2 KiB
TOML
language = "C"
|
|
pragma_once = true
|
|
include_guard = "PUNKTFUNK_CORE_H"
|
|
autogen_warning = "/* Generated by cbindgen from punktfunk-core. Do not edit by hand. */"
|
|
header = "/* punktfunk-core C ABI — see crates/punktfunk-core/src/abi.rs */"
|
|
style = "type"
|
|
cpp_compat = true
|
|
tab_width = 4
|
|
documentation = true
|
|
documentation_style = "c99"
|
|
|
|
[parse]
|
|
parse_deps = false
|
|
|
|
[export]
|
|
# Internal platform-only FFI — NOT part of the C ABI. cbindgen otherwise sweeps the foreign
|
|
# imports and their #[repr(C)] structs into the header, where socklen_t/ssize_t/iovec/msghdr are
|
|
# undefined and the C harness fails to compile: the Apple batched recv (transport/udp.rs
|
|
# `recvmsg_x` + `MsghdrX`) and the Android bionic mmsg bindings (`android_mmsg` module).
|
|
exclude = ["MsghdrX", "recvmsg_x", "mmsghdr", "sendmmsg", "recvmmsg"]
|
|
|
|
[export.rename]
|
|
"InputEvent" = "PunktfunkInputEvent"
|
|
"InputKind" = "PunktfunkInputKind"
|
|
# Gamepad wire constants: bare BTN_* names collide with <linux/input-event-codes.h> (at
|
|
# DIFFERENT values — last definition silently wins); prefix everything we export.
|
|
"BTN_DPAD_UP" = "PUNKTFUNK_BTN_DPAD_UP"
|
|
"BTN_DPAD_DOWN" = "PUNKTFUNK_BTN_DPAD_DOWN"
|
|
"BTN_DPAD_LEFT" = "PUNKTFUNK_BTN_DPAD_LEFT"
|
|
"BTN_DPAD_RIGHT" = "PUNKTFUNK_BTN_DPAD_RIGHT"
|
|
"BTN_START" = "PUNKTFUNK_BTN_START"
|
|
"BTN_BACK" = "PUNKTFUNK_BTN_BACK"
|
|
"BTN_LS_CLICK" = "PUNKTFUNK_BTN_LS_CLICK"
|
|
"BTN_RS_CLICK" = "PUNKTFUNK_BTN_RS_CLICK"
|
|
"BTN_LB" = "PUNKTFUNK_BTN_LB"
|
|
"BTN_RB" = "PUNKTFUNK_BTN_RB"
|
|
"BTN_GUIDE" = "PUNKTFUNK_BTN_GUIDE"
|
|
"BTN_A" = "PUNKTFUNK_BTN_A"
|
|
"BTN_B" = "PUNKTFUNK_BTN_B"
|
|
"BTN_X" = "PUNKTFUNK_BTN_X"
|
|
"BTN_Y" = "PUNKTFUNK_BTN_Y"
|
|
"BTN_TOUCHPAD" = "PUNKTFUNK_BTN_TOUCHPAD"
|
|
"AXIS_LS_X" = "PUNKTFUNK_AXIS_LS_X"
|
|
"AXIS_LS_Y" = "PUNKTFUNK_AXIS_LS_Y"
|
|
"AXIS_RS_X" = "PUNKTFUNK_AXIS_RS_X"
|
|
"AXIS_RS_Y" = "PUNKTFUNK_AXIS_RS_Y"
|
|
"AXIS_LT" = "PUNKTFUNK_AXIS_LT"
|
|
"AXIS_RT" = "PUNKTFUNK_AXIS_RT"
|
|
"AUDIO_MAGIC" = "PUNKTFUNK_AUDIO_MAGIC"
|
|
"RUMBLE_MAGIC" = "PUNKTFUNK_RUMBLE_MAGIC"
|
|
|
|
# QualifiedScreamingSnakeCase already qualifies each variant with the enum name
|
|
# (PunktfunkStatus::Ok -> PUNKTFUNK_STATUS_OK); do NOT also set prefix_with_name or it doubles.
|
|
[enum]
|
|
rename_variants = "QualifiedScreamingSnakeCase"
|
|
|
|
[fn]
|
|
sort_by = "None"
|
|
|
|
[struct]
|
|
derive_eq = false
|
|
|
|
[defines]
|
|
"feature = quic" = "PUNKTFUNK_FEATURE_QUIC"
|