Turns negotiated Session Description Protocol (SDP) text into a transport
config map that XGW.Peer can use.
What SDP is (for readers new to WebRTC)
Before two endpoints send audio or video, they exchange short text documents called SDP offers and answers. Those documents list:
- which codecs and media lines are in use
- ICE usernames/passwords used to authenticate connectivity probes
- the DTLS certificate fingerprint used to trust the encryption handshake
- optional candidates (IP/port addresses to try)
- whether a data channel (SCTP) is negotiated
Signaling (HTTP, WebSocket, SIP) carries those documents. XGW does not invent SDP; it consumes the pair your app already negotiated (for example via WebSocket/JSEP or SIP + xsdp) and extracts only the fields needed to run ICE/DTLS/SRTP.
Behavior notes
Uses XMediaLib.SDP only. ICE-lite v1 picks the first bundled DTLS-SRTP
m-line for fingerprint and ICE credentials. Session-level a=fingerprint /
ICE / setup attributes (common in Firefox) are inherited onto media lines
when those fields are absent at media level.
RFCs
- RFC 8866 -- Session Description Protocol (SDP)
- RFC 8829 -- JSEP (offer/answer usage in WebRTC)
- RFC 8445 -- ICE credentials and candidates in SDP
- RFC 8122 --
a=fingerprint - RFC 8841 -- SDP for SCTP data channels (when an application m-line is present)
Summary
Types
Parsed transport and media config for one peer.
Per-m-line media summary from the remote offer.
Negotiated SCTP data-channel ports when an application m-line is present.
SDP offer or answer as binary or parsed struct.
Functions
Parses offer and answer into peer config.
Compares two SDP fingerprints (case-insensitive value).
Normalizes an SDP fingerprint value to uppercase trimmed hex.
Parses SDP input into %XMediaLib.SDP{}.
Parses a restart offer for new remote ICE credentials and candidates.
Types
@type config() :: %{ remote_fingerprint: XMediaLib.SDP.Fingerprint.t(), local_fingerprint: XMediaLib.SDP.Fingerprint.t(), setup: XMediaLib.SDP.Media.setup(), bundle_mids: [String.t()], media_by_mid: %{optional(String.t()) => media_info()}, remote_candidates: [XMediaLib.SDP.Candidate.t()], local_candidates: [XMediaLib.SDP.Candidate.t()], ssrc_to_mid: %{optional(non_neg_integer()) => String.t()}, sctp: sctp_info() | nil, ice: %{ local_ufrag: String.t(), local_pwd: String.t(), remote_ufrag: String.t(), remote_pwd: String.t(), local_setup: XMediaLib.SDP.Media.setup(), remote_setup: XMediaLib.SDP.Media.setup() } }
Parsed transport and media config for one peer.
Fields
:remote_fingerprint/:local_fingerprint- DTLS cert digests from SDP:setup- local DTLS setup (:passive/:active/ ...):bundle_mids- BUNDLE group mids from the offer:media_by_mid- per-mid codec/SSRC summary from the offer:remote_candidates/:local_candidates- ICE candidates from offer/answer:ssrc_to_mid- offer SSRC map used to tag inbound RTP meta:sctp- negotiated data-channel ports, ornil:ice- local/remote ufrag, pwd, and setup atoms
@type media_info() :: %{ mid: String.t() | nil, type: XMediaLib.SDP.Media.type() | nil, protocol: XMediaLib.SDP.Media.protocol() | nil, rtcp_mux: boolean(), codecs: [XMediaLib.SDP.Codec.t()], ssrcs: [XMediaLib.SDP.Ssrc.t()] }
Per-m-line media summary from the remote offer.
Fields
:mid- media identification tag:type/:protocol- SDP media type and proto:rtcp_mux- whether RTCP shares the RTP port:codecs/:ssrcs- negotiated codecs and SSRCs
@type sctp_info() :: %{ remote_port: non_neg_integer(), local_port: non_neg_integer(), max_message_size: non_neg_integer() | nil }
Negotiated SCTP data-channel ports when an application m-line is present.
Fields
:remote_port/:local_port- SCTP port numbers from offer/answer:max_message_size- optional max message size from SDP
@type sdp_input() :: binary() | XMediaLib.SDP.t()
SDP offer or answer as binary or parsed struct.
Functions
Parses offer and answer into peer config.
Returns {:error, :invalid_sdp}, {:error, :missing_fingerprint},
{:error, :missing_ice_credentials}, or {:error, :invalid_setup} when
ice_lite: true and local setup is not :passive.
@spec fingerprints_match?( XMediaLib.SDP.Fingerprint.t(), XMediaLib.SDP.Fingerprint.t() ) :: boolean()
Compares two SDP fingerprints (case-insensitive value).
Examples
iex> a = %XMediaLib.SDP.Fingerprint{algorithm: "sha-256", value: "aa:bb:cc"}
iex> b = %XMediaLib.SDP.Fingerprint{algorithm: "SHA-256", value: "AA:BB:CC"}
iex> XGW.Descriptions.fingerprints_match?(a, b)
true
iex> a = %XMediaLib.SDP.Fingerprint{algorithm: "sha-256", value: "aa:bb:cc"}
iex> b = %XMediaLib.SDP.Fingerprint{algorithm: "sha-256", value: "dd:ee:ff"}
iex> XGW.Descriptions.fingerprints_match?(a, b)
false
Normalizes an SDP fingerprint value to uppercase trimmed hex.
Examples
iex> XGW.Descriptions.normalize_fingerprint("aa:bb:cc")
"AA:BB:CC"
iex> XGW.Descriptions.normalize_fingerprint(" aa:bb ")
"AA:BB"
@spec parse_sdp(sdp_input()) :: {:ok, XMediaLib.SDP.t()} | {:error, :invalid_sdp}
Parses SDP input into %XMediaLib.SDP{}.
Accepts a parsed struct or binary. Returns {:error, :invalid_sdp} for
non-string, non-struct input.
@spec restart_remote(sdp_input()) :: {:ok, %{ remote_ice_ufrag: String.t(), remote_ice_pwd: String.t(), remote_candidates: [XMediaLib.SDP.Candidate.t()] }} | {:error, term()}
Parses a restart offer for new remote ICE credentials and candidates.
Local ufrag/pwd are unchanged in v1; only remote side updates.