The app UHF doesn't appear to issue a clean socket teardown before opening a new TCP connection during reconnect. This causes it to inherit the collapsed receive window from the previous dead session and starts in a hole. The user suggests a clean close() and buffer reset (SO_RCVBUF) before the new TCP session is opened.
Long post but I wanted to give the UHF team something actionable if they're watching. I love the UI and want to stick with this app, so here's what I've dug into. \--- \*\*The symptoms\*\* On AHL channels (and likely others): streams freeze, jumpback glitches occur, and when the stream stops entirely, hitting restart inside the app does nothing. Only a full force-quit and relaunch restores playback. \--- \*\*What the packet captures show\*\* I ran Wireshark captures during a freeze event and compared them side-by-side with captures from Chillio (another IPTV app) playing the same stream. Key findings: \- When UHF's stream stalls, the \*\*TCP receive window collapses\*\*— the client stops advertising buffer space, the server backs off, and the stream dies \- When you hit restart \*without\* force-quitting, UHF opens a \*\*new TCP connection\*\* — but the receive window is already tiny from the previous stall. The socket state from the dead session appears to not be properly reset before the new handshake \- The server is sending data fine throughout. This is entirely a client-side socket/buffer issue \--- \*\*UHF vs Chillio — the architectural difference\*\* Both apps use KSPlayer, but the user-agent strings in the captures tell a different story: \- \*\*Chillio\*\* advertises \`Lavf/62.3.100\` → it's using \*\*FFmpeg for the network layer\*\*, with KSPlayer handling rendering only \- \*\*UHF\*\* advertises \`KSPlayer\` → KSPlayer is handling \*\*both\*\* networking and rendering end-to-end This matters a lot. FFmpeg's network layer runs socket reads on a \*\*dedicated isolated thread\*\* (\`AVIOContext\`). A stall downstream in the decode or render pipeline can't starve the socket reader — the buffer stays fed. With KSPlayer doing everything, the pipeline is more tightly coupled. A decode hiccup, a UI thread spike, or a render stall can propagate back upstream and starve the socket. Buffer fills → receive window shrinks → server backs off → freeze. \--- \*\*Why force-quit fixes it but in-app restart doesn't\*\* Force-quitting fully clears the socket and buffer state at the OS level. The in-app restart opens a new TCP connection but doesn't appear to issue a clean socket teardown first — so it inherits the collapsed receive window from the previous dead session and starts in a hole. \--- \*\*A note on MSS/clamping\*\* Earlier I suspected an MSS issue (AppleTV advertises 1460 due to not being VPN-aware, server responds with 1380 MSS — so PMTUD is actually working correctly). MSS clamping via router settings seemed to fix it at the time, but I now think that was coincidental — it forced a fresh TCP handshake that happened to work, rather than fixing any real MTU problem. \--- \*\*What I'd suggest the team look at\*\* 1. \*\*Socket teardown on reconnect\*\* — is there a clean \`close()\` and buffer reset (\`SO\_RCVBUF\`) before the new TCP session is opened? If not, that's likely the direct fix for the restart issue 2. \*\*Receive buffer sizing\*\* — is \`SO\_RCVBUF\` being set statically somewhere in KSPlayer init in a way that persists across reconnect attempts? 3. \*\*Network thread isolation\*\* — longer term, decoupling the socket read from the rest of the KSPlayer pipeline (even just a dedicated read thread with its own queue) would address the root cause and bring UHF in line with how Chillio handles it \--- Hope this helps. Happy to share the pcap details if a dev wants to reach out. Really do prefer UHF's UI — just want the playback reliability to match. 3/17/2026 UPDATE: UHF Support has responded with a thank you for the analysis and a potential fix aimed in an upcoming build!