LiveKit SDK Version
v0.3.9
IDF Version
v6.0.1
Issue Description
Body
Summary
When connecting an ESP32-S3 device to a remote LiveKit server over the public internet, WebRTC media (ICE) fails with an RTC timeout. Signaling (WebSocket + protobuf) works correctly, and TURN allocation succeeds, but the ICE agent only performs connectivity checks using the local host candidate (cand:0, private LAN IP). It never uses the allocated relay candidate (cand:2) or srflx candidate (cand:1).
The same firmware connects successfully to a local LiveKit server on the same LAN (host-to-host ICE works).
This appears to be an esp_peer ICE candidate pairing / connectivity-check issue in the prebuilt libpeer_default.a, not a server-side TURN port configuration issue.
Environment
| Item |
Version |
| Chip |
ESP32-S3 |
| ESP-IDF |
v6.0.1 |
| esp_peer |
1.4.1 (from ESP Component Registry) |
| LiveKit ESP32 SDK |
0.3.9 |
| Integration |
LiveKit engine.c → esp_peer_open() with ESP_PEER_ICE_TRANS_POLICY_ALL |
| Remote server |
LiveKit SFU + embedded TURN (turn:36.131.215.25:3478?transport=udp) |
| Local network |
ESP32 behind NAT (e.g. 192.168.3.175), public IP via STUN ~182.200.13.118 |
Steps to Reproduce
- Flash ESP32-S3 firmware using LiveKit SDK 0.3.9 + esp_peer 1.4.1.
- Connect to a remote LiveKit room over the internet (
wss://...).
- Join succeeds; Answer + ICE trickle received.
- Observe ICE agent logs: repeated
Send binding request (cand:0) from private host IP to remote SFU ports on public IP.
- After ~10–15 s: peer state
CONNECTING → FAILED, failure reason: RTC.
Control test: Same firmware connecting to a local LiveKit server (192.168.3.x) succeeds; ICE checks use host candidates and complete quickly.
Expected Behavior
When connecting to a remote SFU over the internet:
- ICE should eventually use relay-to-relay or srflx-to-host candidate pairs, e.g.:
local relay (36.x.x.x:52827) ↔ remote SFU (36.x.x.x:56935)
- Or at minimum, after host checks fail, fall back to
cand:1 / cand:2.
With ESP_PEER_ICE_TRANS_POLICY_RELAY (force_relay), local relay should be able to reach remote host/srflx candidates via TURN (standard WebRTC iceTransportPolicy: relay semantics).
Actual Behavior
Case A: ESP_PEER_ICE_TRANS_POLICY_ALL (default)
- TURN Allocate succeeds; SDP includes relay candidate:
a=candidate:2 ... 36.131.215.25 52827 typ relay
- Remote trickle adds SFU candidates (
Add remote type:1 36.131.215.25:56935).
- ICE checks only use
cand:0 (host):
AGENT: 0 0 Send binding request (cand:0) local0:192.168.3.175:64367 remote0:36.131.215.25:56935
AGENT: 0 1 Send binding request (cand:0) local0:192.168.3.175:64367 remote0:36.131.215.25:58945
- No logs for
cand:1 or cand:2 binding requests.
- ~15 s later:
State changed: 1 -> 4, RTC timeout.
Case B: ESP_PEER_ICE_TRANS_POLICY_RELAY (force_relay = true)
- SDP contains only relay local candidate (
typ relay).
- Remote still sends type:1 (host/srflx on public IP) candidates.
- ICE fails immediately (~9 ms), no
Send binding request logs at all.
- Suggests no valid candidate pairs are formed when local is relay-only and remote is host/srflx.
Additional notes
- TCP TURN is skipped by esp_peer:
W AGENT: Skip TCP stun server turns:livekit-turn.shengshitong.com:443?transport=tcp
- Browser clients on the same network connect via TCP to SFU port 7881 (
182.x.x.x:xxxx ↔ 36.x.x.x:7881), which ESP32 cannot use.
Relevant Logs (abbreviated)
I livekit_signaling: Join: room=6666 ... ice_servers=1
I livekit_signaling: Join.ice[0][0]: turn:36.131.215.25:3478?transport=udp
I livekit_engine: ICE force_relay: server=0 kconfig=0 effective=0
I AGENT: 0 Get candidate success ...
W AGENT: Skip TCP stun server turns:livekit-turn.shengshitong.com:443?transport=tcp
Generated offer includes:
a=candidate:0 ... 192.168.3.175 64367 typ host
a=candidate:1 ... 182.200.13.118 5083 typ srflx
a=candidate:2 ... 36.131.215.25 52827 typ relay
I AGENT: 0 Add remote type:1 36.131.215.25:56935
I AGENT: 0 Add remote type:1 36.131.215.25:58945
I livekit_peer.pub: State changed: 0 -> 1
I AGENT: 0 0 Send binding request (cand:0) local0:192.168.3.175:64367 remote0:36.131.215.25:56935
... (repeated for ~15s, only cand:0, no response)
I livekit_peer.pub: State changed: 1 -> 4
E voice_session: failure reason: RTC
Server / TURN Verification (already ruled out)
We verified on the server side:
- TURN UDP 3478 reachable; Allocate succeeds on device.
- TURN relay ports configured in range 50000–55000; allocated relay (e.g.
52827) is in range.
- Firewall allows the relay port range.
Packet capture showed the issue is on the client ICE path selection, not missing server relay ports.
Questions
- Is it expected that with
ESP_PEER_ICE_TRANS_POLICY_ALL, esp_peer only checks cand:0 (host) against remote relay/host candidates, and never tries local relay (cand:2) within the ICE timeout window?
- With
ESP_PEER_ICE_TRANS_POLICY_RELAY, should local relay be pairable with remote host candidates (standard WebRTC relay policy)? If yes, is the instant failure (~9 ms, no binding requests) a known bug?
- Is there a recommended
esp_peer_default_cfg_t or other configuration to force relay candidate use for connectivity checks against a remote SFU?
- Is TCP TURN (
turns:443?transport=tcp) or SFU TCP media (e.g. port 7881) planned for ESP32?
Workaround
- Local LiveKit server on same LAN: works (host ICE).
- Remote cloud LiveKit: not working with current esp_peer 1.4.1.
Thank you for any guidance or fixes. Happy to provide full serial logs or pcaps if needed.
Additional context (optional, fill before submit)
- Project: esp32s3-voice-ai (ESP32-S3 voice AI + LiveKit)
- LiveKit server: self-hosted cloud instance
- Full serial log: attach
*.txt if needed
Hardware Configuration
- Project: esp32s3-voice-ai (ESP32-S3 voice AI + LiveKit)
- LiveKit server: self-hosted cloud instance
- Full serial log: attach
*.txt if needed
LiveKit SDK Version
v0.3.9
IDF Version
v6.0.1
Issue Description
Body
Summary
When connecting an ESP32-S3 device to a remote LiveKit server over the public internet, WebRTC media (ICE) fails with an RTC timeout. Signaling (WebSocket + protobuf) works correctly, and TURN allocation succeeds, but the ICE agent only performs connectivity checks using the local host candidate (
cand:0, private LAN IP). It never uses the allocated relay candidate (cand:2) or srflx candidate (cand:1).The same firmware connects successfully to a local LiveKit server on the same LAN (host-to-host ICE works).
This appears to be an
esp_peerICE candidate pairing / connectivity-check issue in the prebuiltlibpeer_default.a, not a server-side TURN port configuration issue.Environment
engine.c→esp_peer_open()withESP_PEER_ICE_TRANS_POLICY_ALLturn:36.131.215.25:3478?transport=udp)192.168.3.175), public IP via STUN ~182.200.13.118Steps to Reproduce
wss://...).Send binding request (cand:0)from private host IP to remote SFU ports on public IP.CONNECTING → FAILED,failure reason: RTC.Control test: Same firmware connecting to a local LiveKit server (
192.168.3.x) succeeds; ICE checks use host candidates and complete quickly.Expected Behavior
When connecting to a remote SFU over the internet:
local relay (36.x.x.x:52827) ↔ remote SFU (36.x.x.x:56935)cand:1/cand:2.With
ESP_PEER_ICE_TRANS_POLICY_RELAY(force_relay), local relay should be able to reach remote host/srflx candidates via TURN (standard WebRTCiceTransportPolicy: relaysemantics).Actual Behavior
Case A:
ESP_PEER_ICE_TRANS_POLICY_ALL(default)Add remote type:1 36.131.215.25:56935).cand:0(host):cand:1orcand:2binding requests.State changed: 1 -> 4, RTC timeout.Case B:
ESP_PEER_ICE_TRANS_POLICY_RELAY(force_relay = true)typ relay).Send binding requestlogs at all.Additional notes
182.x.x.x:xxxx ↔ 36.x.x.x:7881), which ESP32 cannot use.Relevant Logs (abbreviated)
Server / TURN Verification (already ruled out)
We verified on the server side:
52827) is in range.Packet capture showed the issue is on the client ICE path selection, not missing server relay ports.
Questions
ESP_PEER_ICE_TRANS_POLICY_ALL, esp_peer only checkscand:0(host) against remote relay/host candidates, and never tries local relay (cand:2) within the ICE timeout window?ESP_PEER_ICE_TRANS_POLICY_RELAY, should local relay be pairable with remote host candidates (standard WebRTC relay policy)? If yes, is the instant failure (~9 ms, no binding requests) a known bug?esp_peer_default_cfg_tor other configuration to force relay candidate use for connectivity checks against a remote SFU?turns:443?transport=tcp) or SFU TCP media (e.g. port 7881) planned for ESP32?Workaround
Thank you for any guidance or fixes. Happy to provide full serial logs or pcaps if needed.
Additional context (optional, fill before submit)
*.txtif neededHardware Configuration
*.txtif needed