fix(audio+net): revert dual-stack [::]:0, add Oboe playout stall auto-restart
Two fixes: ## Revert [::]:0 dual-stack sockets → back to 0.0.0.0:0 Android's IPV6_V6ONLY=1 default on some kernels (confirmed on Nothing Phone) makes [::]:0 IPv6-only, silently killing ALL IPv4 traffic. This broke P2P direct calls: IPv4 LAN candidates (172.16.81.x) couldn't complete QUIC handshakes through the IPv6-only socket, causing local_direct_ok=false and relay fallback on every call after the first. Reverted all bind sites to 0.0.0.0:0 (reliable IPv4). IPv6 host candidates are disabled in local_host_candidates() until a proper dual-socket approach (one IPv4 + one IPv6 endpoint, Phase 7) is implemented. ## Fix A (task #35): Oboe playout callback stall auto-restart The Nothing Phone's Oboe playout callback fires once (cb#0) and then stops draining the ring on ~50% of cold-launch calls. Fix D+C (stop+prime from previous commit) didn't help because audio_stop is a no-op on cold launch. New approach: self-healing watchdog in audio_write_playout. Tracks the playout ring's read_idx across writes. If read_idx hasn't advanced in 50 consecutive writes (~1 second), the Oboe playout callback has stopped: 1. Log "playout STALL detected" 2. Call wzp_oboe_stop() to tear down the stuck streams 3. Clear both ring buffers (prevent stale data reads) 4. Call wzp_oboe_start() to rebuild fresh streams 5. Log success/failure 6. Return 0 (caller retries on next frame) This is the same teardown+rebuild that "rejoin" does — but triggered automatically from the first stalled call instead of requiring the user to hang up and redial. The watchdog runs on every write so it fires within 1s of the stall starting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,14 +170,14 @@ pub async fn race(
|
||||
}
|
||||
None => {
|
||||
let (sc, _cert_der) = wzp_transport::server_config();
|
||||
// [::]:0 = dual-stack socket — handles both IPv4 (via
|
||||
// v4-mapped addrs) and IPv6 natively. Pre-Phase-5.5
|
||||
// used 0.0.0.0:0 (IPv4-only) which silently made
|
||||
// all IPv6 host candidates non-functional: dials
|
||||
// to [2a0d:...] failed or hung, accepts from IPv6
|
||||
// peers never arrived, and the JoinSet wasted time
|
||||
// on dead candidates before the IPv4 one won.
|
||||
let bind: SocketAddr = "[::]:0".parse().unwrap();
|
||||
// 0.0.0.0:0 = IPv4 socket. [::]:0 dual-stack was
|
||||
// tried but breaks on Android devices where
|
||||
// IPV6_V6ONLY=1 (default on some kernels) —
|
||||
// IPv4 candidates silently fail. IPv6 host
|
||||
// candidates are skipped for now; they need a
|
||||
// dedicated IPv6 socket alongside the v4 one
|
||||
// (like WebRTC's dual-socket approach).
|
||||
let bind: SocketAddr = "0.0.0.0:0".parse().unwrap();
|
||||
let fresh = wzp_transport::create_endpoint(bind, Some(sc))?;
|
||||
tracing::info!(
|
||||
local_addr = ?fresh.local_addr().ok(),
|
||||
@@ -213,14 +213,14 @@ pub async fn race(
|
||||
ep
|
||||
}
|
||||
None => {
|
||||
// [::]:0 = dual-stack socket — handles both IPv4 (via
|
||||
// v4-mapped addrs) and IPv6 natively. Pre-Phase-5.5
|
||||
// used 0.0.0.0:0 (IPv4-only) which silently made
|
||||
// all IPv6 host candidates non-functional: dials
|
||||
// to [2a0d:...] failed or hung, accepts from IPv6
|
||||
// peers never arrived, and the JoinSet wasted time
|
||||
// on dead candidates before the IPv4 one won.
|
||||
let bind: SocketAddr = "[::]:0".parse().unwrap();
|
||||
// 0.0.0.0:0 = IPv4 socket. [::]:0 dual-stack was
|
||||
// tried but breaks on Android devices where
|
||||
// IPV6_V6ONLY=1 (default on some kernels) —
|
||||
// IPv4 candidates silently fail. IPv6 host
|
||||
// candidates are skipped for now; they need a
|
||||
// dedicated IPv6 socket alongside the v4 one
|
||||
// (like WebRTC's dual-socket approach).
|
||||
let bind: SocketAddr = "0.0.0.0:0".parse().unwrap();
|
||||
let fresh = wzp_transport::create_endpoint(bind, None)?;
|
||||
tracing::info!(
|
||||
local_addr = ?fresh.local_addr().ok(),
|
||||
|
||||
Reference in New Issue
Block a user