Shared UDP (and optional TCP) socket that receives all WebRTC traffic for
many peers and routes each packet to the right XGW.Peer.
Why a shared listener
Opening one UDP port per browser would not scale and would break demux
assumptions. One Listener binds the media port; inbound bytes are classified
(XGW.Demux) and dispatched:
- STUN Binding -- look up the Peer by local ICE ufrag in USERNAME
(
local:remoteat this host) - RTP / RTCP / DTLS -- look up by remote
{ip, port}afterbind_remote/3 - TURN ChannelData -- look up by channel number from
bind_turn_channel/3 - TURN Data Indication -- unwrap, then re-demux the inner packet
With a single registered Peer and no bind keys, all classified packets go to that Peer (Echo / one-Peer-per-Listener back-compat).
Inbound media meta includes :arrival_us (monotonic microseconds) stamped in
route_packet/4 before Peer.deliver/4, so TWCC arrivals exclude Peer
mailbox delay.
Gather options (on start_link/1)
- Host:
:advertised_ip,:advertised_ips,:include_loopback(defaulttrue) - srflx:
:stun_servers(default[]),:gather_timeout_ms(default3000) - relay:
:turn_serversplus TURN credential opts :relay_only-- skip host/srflx SDP validation when the answer is relay-only- ICE-TCP:
:ice_tcp_enabled/:ice_tcp_portfor passive TCP candidates
Call gather_candidates/2 before publishing answer SDP when you need host +
srflx + relay lines embedded up front.
RFCs
- RFC 7983 -- demux of STUN / DTLS / TURN ChannelData / RTP/RTCP on one socket
- RFC 8445 -- ICE candidate gather and STUN Binding dispatch by ufrag
- RFC 8656 -- TURN Data Indication unwrap and ChannelData
- RFC 6544 -- optional ICE-TCP passive candidates and accept path
Summary
Functions
Allocates the next TURN channel number unique on this Listener socket.
Maps remote {ip, port} to peer_pid for RTP/RTCP/DTLS demux.
Maps TURN ChannelData channel to peer_pid.
Maps local ICE ufrag to peer_pid for STUN Binding demux.
Returns {certfile, keyfile} paths for DTLS handshakes.
Returns a specification to start this module under a supervisor.
Gathers host, srflx, and relay candidates within timeout_ms.
Host gather options configured at listener start.
Pre-answer srflx gather timeout in milliseconds.
Host ICE candidate with highest priority for the listener UDP socket.
SDP a=candidate: attribute value (without prefix) for the highest-priority host.
SDP a=candidate: lines for all gathered host candidates (without prefix).
All host ICE candidates for the listener UDP socket (multi-interface).
Returns true when ICE-TCP passive candidates are enabled.
Returns the SSL listen socket for DTLS accept (shared UDP via Transport).
SHA-256 fingerprint of the listener DTLS certificate (SDP format).
Bound port of the shared UDP socket.
Registers peer_pid for demux (monitor only until bind_* keys are set).
SDP a=candidate: lines for relay candidates gathered within the timeout.
When true, answer SDP may omit host/srflx locals (relay-only / zero-public-port).
Sends a UDP datagram on the listener socket (STUN replies, etc.).
Sends a UDP datagram and waits for :gen_udp.send/4 result (outbound media).
Sends a framed STUN message over an established ICE-TCP connection to peer_addr.
Bound socket {ip, port}.
SDP a=candidate: lines for srflx candidates gathered within the timeout.
Starts a UDP listener on ip / port (use port: 0 for an ephemeral bind).
Sends a STUN Binding request on the listener socket and waits for a response.
Configured STUN server URIs for srflx gather.
SDP a=candidate: lines for passive TCP host candidates.
Passive ICE-TCP host candidates (empty when ICE-TCP is disabled).
Connects to peer_addr over TCP if needed and sends a framed STUN message.
Returns the bound ICE-TCP listen port when enabled, else nil.
TURN credential options for relay gather.
Sends a STUN/TURN request on the listener socket and waits for a response.
Configured TURN server URIs for relay gather.
Returns the shared UDP socket used for STUN/RTP and DTLS cb_info.
Removes peer_pid and all demux keys that pointed at it.
Functions
@spec allocate_turn_channel(pid(), pid()) :: {:ok, non_neg_integer()}
Allocates the next TURN channel number unique on this Listener socket.
@spec bind_remote(pid(), pid(), {:inet.ip_address(), :inet.port_number()}) :: :ok
Maps remote {ip, port} to peer_pid for RTP/RTCP/DTLS demux.
@spec bind_turn_channel(pid(), pid(), non_neg_integer()) :: :ok
Maps TURN ChannelData channel to peer_pid.
Maps local ICE ufrag to peer_pid for STUN Binding demux.
Returns {certfile, keyfile} paths for DTLS handshakes.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec gather_candidates( pid(), keyword() ) :: %{ host: [XMediaLib.SDP.Candidate.t()], srflx: [XMediaLib.SDP.Candidate.t()], relay: [XMediaLib.SDP.Candidate.t()] }
Gathers host, srflx, and relay candidates within timeout_ms.
Returns %{host: [...], srflx: [...], relay: [...]}.
Host gather options configured at listener start.
@spec gather_timeout_ms(pid()) :: non_neg_integer()
Pre-answer srflx gather timeout in milliseconds.
@spec host_candidate(pid()) :: XMediaLib.SDP.Candidate.t()
Host ICE candidate with highest priority for the listener UDP socket.
SDP a=candidate: attribute value (without prefix) for the highest-priority host.
SDP a=candidate: lines for all gathered host candidates (without prefix).
@spec host_candidates(pid()) :: [XMediaLib.SDP.Candidate.t()]
All host ICE candidates for the listener UDP socket (multi-interface).
Returns true when ICE-TCP passive candidates are enabled.
@spec listen_sock(pid()) :: :ssl.sslsocket()
Returns the SSL listen socket for DTLS accept (shared UDP via Transport).
SHA-256 fingerprint of the listener DTLS certificate (SDP format).
@spec port(pid()) :: :inet.port_number()
Bound port of the shared UDP socket.
Registers peer_pid for demux (monitor only until bind_* keys are set).
SDP a=candidate: lines for relay candidates gathered within the timeout.
When true, answer SDP may omit host/srflx locals (relay-only / zero-public-port).
@spec send(pid(), :inet.ip_address(), :inet.port_number(), binary()) :: :ok
Sends a UDP datagram on the listener socket (STUN replies, etc.).
@spec send_datagram(pid(), :inet.ip_address(), :inet.port_number(), binary()) :: :ok | {:error, term()}
Sends a UDP datagram and waits for :gen_udp.send/4 result (outbound media).
@spec send_tcp_stun(pid(), XGW.ICE.endpoint(), binary()) :: :ok | {:error, term()}
Sends a framed STUN message over an established ICE-TCP connection to peer_addr.
@spec sockname(pid()) :: {:inet.ip_address(), :inet.port_number()}
Bound socket {ip, port}.
SDP a=candidate: lines for srflx candidates gathered within the timeout.
Starts a UDP listener on ip / port (use port: 0 for an ephemeral bind).
Optional gather opts: :advertised_ip, :advertised_ips, :include_loopback,
:stun_servers, :gather_timeout_ms, :turn_servers, :relay_only, TURN
credential opts, :ice_tcp_enabled, :ice_tcp_port.
@spec stun_binding( pid(), :inet.ip_address(), :inet.port_number(), non_neg_integer(), binary() | nil ) :: {:ok, binary()} | {:error, term()}
Sends a STUN Binding request on the listener socket and waits for a response.
Optional fifth argument is pre-encoded request wire (defaults to plain Binding).
Configured STUN server URIs for srflx gather.
SDP a=candidate: lines for passive TCP host candidates.
@spec tcp_candidates(pid()) :: [XMediaLib.SDP.Candidate.t()]
Passive ICE-TCP host candidates (empty when ICE-TCP is disabled).
@spec tcp_connect_stun(pid(), XGW.ICE.endpoint(), binary()) :: :ok | {:error, term()}
Connects to peer_addr over TCP if needed and sends a framed STUN message.
@spec tcp_port(pid()) :: :inet.port_number() | nil
Returns the bound ICE-TCP listen port when enabled, else nil.
TURN credential options for relay gather.
@spec turn_request( pid(), :inet.ip_address(), :inet.port_number(), non_neg_integer(), binary() ) :: {:ok, binary()} | {:error, term()}
Sends a STUN/TURN request on the listener socket and waits for a response.
Configured TURN server URIs for relay gather.
Returns the shared UDP socket used for STUN/RTP and DTLS cb_info.
Removes peer_pid and all demux keys that pointed at it.