Skip to content

feat: add TCP ICE candidate support (RFC 6544)#2085

Draft
getroot wants to merge 1 commit intomasterfrom
feat/tcp-ice-candidate
Draft

feat: add TCP ICE candidate support (RFC 6544)#2085
getroot wants to merge 1 commit intomasterfrom
feat/tcp-ice-candidate

Conversation

@getroot
Copy link
Copy Markdown
Member

@getroot getroot commented Apr 24, 2026

Summary

Add TCP ICE candidate (RFC 6544) support so that WebRTC clients can connect over TCP when UDP is unavailable or restricted.

Motivation

Some network environments block UDP traffic. TCP ICE candidates allow WebRTC sessions to fall back to TCP without requiring a TURN relay, reducing latency and infrastructure cost compared to relay.

Backward Compatibility

The existing OvenPlayer has IceTransportPolicy: 'relay' configured. This means the moment iceServers are delivered to the player, the browser exclusively uses TURN relay and ignores all direct candidates. This behavior is the foundation of the compatibility design.

Default behavior (TcpRelayForce=false, DefaultTransport=udptcp):

  • Only direct candidates (UDP/TCP) are sent. No iceServers.
  • Existing player: IceTransportPolicy=relay has no effect since no iceServers are provided. Connects via direct candidates normally.
  • New player: same result.

TcpRelayForce=true:

  • Only relay (iceServers) is sent. No direct candidates.
  • Existing player: receives iceServers, IceTransportPolicy=relay kicks in, uses relay only.
  • New player: same result.

?transport=all:

  • UDP direct + TCP direct + relay (iceServers) are all sent.
  • Existing player: receives iceServers, IceTransportPolicy=relay kicks in, uses relay only. Same behavior as before.
  • New player (IceTransportPolicy=all): all three candidates available, browser picks by priority (UDP > TCP > relay). Maximum compatibility.

Configuration Changes

New <IceCandidates> fields:

<IceCandidates>
    <IceCandidate>*:10000/udp</IceCandidate>
    <IceWorkerCount>1</IceWorkerCount>            <!-- worker threads for UDP ICE candidate sockets (default: 1) -->
    <IceCandidate>*:10000/tcp</IceCandidate>      <!-- new: TCP direct ICE port (RFC 6544) -->
    <TcpIceWorkerCount>1</TcpIceWorkerCount>      <!-- new: worker threads for TCP ICE candidate sockets (default: 1) -->
    <TcpRelay>*:13478</TcpRelay>
    <TcpRelayWorkerCount>1</TcpRelayWorkerCount>  <!-- worker threads for TURN relay sockets (default: 1) -->
    
    <TcpRelayForce>false</TcpRelayForce>          <!-- replaces deprecated TcpForce (default: false) -->
    <DefaultTransport>udptcp</DefaultTransport>   <!-- new: transport policy when ?transport is absent (default: udptcp) -->
</IceCandidates>

<TcpForce> is preserved as a deprecated alias for <TcpRelayForce>.

Transport Policy (?transport)

Value Direct candidates Relay (iceServers) Old player result New player result
(none) follows <DefaultTransport> (default: udptcp) follows TcpRelayForce (default: false) direct direct
udp UDP only no UDP direct UDP direct
tcp TCP only no TCP direct TCP direct
relay none yes relay relay
udptcp UDP + TCP no direct direct
all UDP + TCP yes relay only (IceTransportPolicy=relay) UDP > TCP > relay

Known limitations

Work in progress. Testing ongoing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant