WebRTC SDP (Session Description Protocol) is the text-based description format that two peers exchange to negotiate how a media session will run: which codecs to use, which network candidates to try, and how to secure the media with DTLS. Every WebRTC connection begins with an SDP offer and answer under the JSEP model. Understanding these fields is what separates developers who can debug failed calls from those who stare at connection errors. If you'd rather skip raw SDP entirely, VideoSDK's video calling SDK handles the entire negotiation for you across ten-plus platforms.

Introduction

A WebRTC connection that fails silently is one of the most frustrating bugs you can chase. The media never arrives, the connection state flickers, and the logs give you nothing but a wall of text. That wall of text is usually SDP, and it holds the answer. Session Description Protocol is the negotiation layer of every WebRTC session, and nearly every failed call traces back to something inside it: a codec the remote peer doesn't support, an ICE candidate that never connected, or a DTLS fingerprint that didn't match.
The problem is that most developers treat SDP as opaque. They copy the offer, paste the answer, and hope. That works until it doesn't, and when it doesn't, you need to read the protocol.
This guide walks through the full anatomy of a WebRTC SDP message, the offer/answer workflow under JSEP, field-by-field explanations, real debugging strategies for the most common failures, and the libraries you can use to parse and manipulate SDP safely in JavaScript, Rust, and Go. By the end, you'll be able to look at an SDP dump and diagnose why a call failed in minutes instead of hours.

What is WebRTC SDP?

Session Description Protocol is defined as a text-based format, standardized in RFC 4566, for describing multimedia sessions: what media is involved, which codecs encode it, which transport addresses carry it, and which security parameters protect it. In WebRTC, SDP is the negotiation contract between two peers, and the JavaScript Session Establishment Protocol (JSEP) model defines how offers and answers are created and exchanged without forcing the browser API to expose raw signaling.
SDP works by letting one peer describe everything it wants to send and receive, then letting the other peer accept, reject, or modify each part in its answer. VideoSDK builds on this same WebRTC foundation, wrapping the SDP and ICE machinery in a rooms-based architecture so your application code deals with participants and streams instead of session descriptions. You can explore that architecture in the VideoSDK concepts and architecture guide.

Core Components of an SDP Message

Every SDP message follows a strict line-based structure where each line begins with a single letter and an equals sign. The message has one session-level section at the top and one or more media-level sections below it. Reading it top to bottom tells you the session's origin, its timing, and then each media stream's codecs, transport candidates, and security attributes.

Session Header

The session header lines establish the session's identity and timing. The version line marks the SDP protocol version, which is always zero in practice. The origin line contains a username, a session identifier, a version number, a network type, an address type, and a unicast address, and together these fields give the session a globally unique identity that lets peers detect re-invites or session updates. The session name line is a human-readable label, usually a dash in WebRTC. The timing line specifies when the session is active, and for WebRTC it is set to zero and zero, meaning the session is permanent and unbounded rather than scheduled.

Media Descriptions

Each media description line declares one media stream: the media type (audio, video, or application for data channels), the transport port, the transport protocol (UDP-TLS-RTP-SAVPF in WebRTC, meaning SRTP over DTLS over UDP), and a list of payload type numbers. Each payload type in the media description is then elaborated by attribute lines below it that map the number to a concrete codec and its parameters. In modern browsers using Unified Plan, each media description corresponds to one track, which is why a video call with screen share produces multiple video media descriptions.

ICE Attributes

Interactive Connectivity Establishment (ICE) attributes handle NAT traversal. The ice-ufrag and ice-pwd lines carry a short username and password used to authenticate STUN binding requests and TURN allocations between the peers. The candidate lines each describe one potential transport path: the component type, transport protocol, priority, address, port, and candidate type (host, server-reflexive, or relay). The more candidates exchanged, the higher the odds one path punches through or relays around whatever network restriction sits between the peers.

DTLS Fingerprint

The fingerprint attribute carries a hash of the certificate the peer will present during the DTLS handshake, and the setup attribute declares the DTLS role, either active or passive, so both peers know who initiates the handshake. These two lines are what make WebRTC media encrypted by default: once DTLS completes, the negotiated keys protect every SRTP packet. If the fingerprint in the SDP doesn't match the certificate presented on the wire, the connection is rejected.

Codec and RTP Mapping

The rtpmap attributes map each payload type number to a codec name and clock rate, and the fmtp attributes carry codec-specific parameters such as profile levels for H.264 or min and max bitrates for Opus.
The hierarchy of these components looks like this:
Architecture Diagram

SDP Offer/Answer Workflow

The offer/answer model, formalized in RFC 3264 and adapted for WebRTC by JSEP in RFC 8829, is the heart of every connection. One peer creates a complete description of the session it wants, the other peer responds with a matching description, and the two descriptions together define the agreed session.

Offer Creation

When your application asks the browser to create an offer, the browser first inspects what local media exists: which tracks have been added, which codecs the platform supports, which resolutions the sender can produce. It then assembles an SDP message describing all of it, including the codec lists, the DTLS fingerprint from a freshly generated certificate, and the initial ICE credentials. At the same time, ICE gathering begins: the browser enumerates local network interfaces for host candidates, queries STUN servers for server-reflexive candidates that reveal its public-facing address, and requests relay candidates from TURN servers as a last resort. Each discovered candidate can be embedded directly in the offer or sent separately, which is where trickle ICE comes in.

Answer Generation

When the remote peer receives the offer, it evaluates every media description against its own capabilities. For each media description it selects the codecs it supports from the offered list, decides whether to send, receive, or both, and generates its own ICE credentials and DTLS fingerprint. The answer must mirror the offer's structure: it cannot introduce codecs that weren't offered, and it cannot add media sections the offer didn't contain. Once the answer is applied on the offering side, both peers know exactly which codecs, candidates, and security parameters to use, and connectivity checks begin.

Role of ICE Gathering and Trickle ICE

Gathering all ICE candidates can take seconds, especially when TURN servers are slow to respond. Trickle ICE solves this by letting peers exchange candidates incrementally as they're discovered, rather than waiting for the full set before sending the SDP. The SDP offer and answer go out immediately with empty or partial candidate lists, and candidates flow through the signaling channel afterward. This cuts connection setup time significantly, which is why most production WebRTC stacks, including VideoSDK's, use trickle ICE under the hood.
Architecture Diagram

Common SDP Fields Explained

These are the fields you'll encounter in nearly every WebRTC SDP dump, and each one has a practical impact on whether your session connects.

v= (Version)

The version line is always zero and simply identifies the SDP format version. If it's missing, the message isn't valid SDP and parsers will reject it outright.

o= (Origin)

The origin line carries the username, a numeric session identifier, a session version counter, and the originator's address. The session ID and version matter for renegotiation: when a peer adds a track mid-call, the version increments so the remote side knows this is an update to an existing session rather than a new one.

s= (Session Name)

The session name is a free-text label. Browsers emit a dash here, and it has no functional role in WebRTC, but it must be present for the message to parse.

t= (Timing)

The timing line specifies session start and stop times. WebRTC always uses zeros for both, marking the session as permanent, because WebRTC sessions are ad hoc and controlled by the application rather than scheduled.

ICE Username and Password Attributes

These are the ICE short-term credentials. Every STUN connectivity check between the peers must carry the ufrag and hash the pwd correctly, or the check fails silently. If you see ICE checks failing with no obvious network cause, mismatched or corrupted credentials during SDP munging are a prime suspect.

DTLS Fingerprint and Setup Attributes

The fingerprint is the SHA-256 hash of the DTLS certificate, and setup assigns the DTLS client and server roles. Both peers must agree on roles, exactly one active and one passive, or the handshake deadlocks. These lines are the backbone of WebRTC's mandatory encryption.

Media Identification Attribute

The mid attribute tags each media section with an identifier so RTP and RTCP packets can be routed to the right section using the header extension that carries it. It's essential for bundling, where multiple media sections share one transport.

Codec Mapping and Format Parameters

The rtpmap attribute binds a payload type number to a codec and clock rate, for example mapping payload type 111 to Opus at 48 kilohertz. The fmtp attribute then carries that codec's tuning parameters, such as Opus's minimum and maximum audio bandwidth or H.264's profile-level-id.

Candidate Attribute

Each candidate line describes one reachable transport address with a priority and type. Host candidates are local interfaces, server-reflexive candidates are public addresses discovered via STUN, and relay candidates are TURN-allocated addresses. The priority ordering determines which pair ICE tries first, and relay candidates, while the most expensive, are the only ones guaranteed to work behind strict firewalls.

Debugging SDP Issues

When a call fails, the SDP dumps from both sides are your primary evidence. Comparing them systematically catches most problems in minutes.

Mismatched Codecs

A codec mismatch happens when the offer lists payload types the answerer can't decode, and the answerer marks the media section as rejected by setting its port to zero. The symptom is a connected session with black video or silent audio on one track. To diagnose, compare the codec lists in the offer's m= lines against the answer's: if the answer rejected a section, check whether the offered codecs match what the remote platform actually supports. This appears most often in cross-browser calls involving H.264 profile variations, or when a hardware-limited device like an IoT camera offers a codec the browser doesn't ship. The fix is to constrain the sender's codec preferences before creating the offer so only mutually supported codecs are proposed.

ICE Failures

ICE failure means no candidate pair passed connectivity checks, and the session dies in the checking state. Compare the candidate lists on both sides: if one peer only has host candidates on a private network and the other is behind a symmetric NAT, no direct path exists and a TURN relay is required. Verify your TURN server credentials and allocation in the SDP, confirm relay candidates actually appear, and check that your signaling channel delivered every trickled candidate. Missing candidates due to signaling races are a classic cause: the answer arrives before all candidates, and if your code doesn't apply late candidates, the pair never forms.

Fingerprint Mismatches

A fingerprint mismatch means the certificate presented during the DTLS handshake doesn't hash to the value in the SDP. The browser aborts the connection and logs a fingerprint validation error. This almost always indicates SDP tampering: if any intermediary or your own code rewrites the SDP and corrupts the fingerprint line, or if a renegotiation reuses stale SDP from a previous session, validation fails. The rule is simple: never modify fingerprint or setup lines, and always regenerate offers rather than caching them.

Using SDP Dump Tools

Chrome's internal WebRTC debugging page (chrome://webrtc-internals) captures full SDP offers, answers, and ICE state transitions for every session, and is the fastest way to grab both sides of a negotiation. Firefox offers similar capture via its about:webrtc page. Export the dumps, paste them into any diff tool, and compare the m= lines, candidate lists, and attributes side by side. Most failures become obvious the moment you see what each peer actually sent versus what it received.

SDP Manipulation and Libraries

Sometimes you need to read or modify SDP programmatically: to inspect codec priorities, to log negotiation state, or to adapt SDP when bridging WebRTC to a non-browser system. Hand-editing SDP strings with text replacement is fragile, so use a parser that understands the format's structure.

JavaScript: sdp-transform

The sdp-transform package is the standard JavaScript library for parsing SDP into a JSON object tree and serializing it back. You parse a session into a structured object, walk the media sections, inspect or reorder codec entries, and write it back out. It's the right choice when you're building a Node.js signaling server that needs to inspect or adjust offers in transit, or when you're writing browser-side diagnostics that pretty-print SDP for logging. Because it round-trips through a structured representation rather than string surgery, you're far less likely to corrupt a line you didn't intend to touch.

Rust: webrtc-sdp

The webrtc-sdp crate, maintained under the Mozilla umbrella and used in Firefox's Rust components, parses SDP into strongly typed structures and validates it against the WebRTC profile of the specification. Its strictness is the feature: it will reject malformed SDP that a lenient parser would silently accept, which makes it ideal for security-sensitive gateways and for any Rust service that terminates or rewrites WebRTC signaling. Use it when correctness matters more than convenience, such as in a media server front end that must never pass through a malformed session description.

Go: pion/sdp

The pion/sdp package is part of the Pion WebRTC ecosystem, the most widely used pure-Go WebRTC stack. It provides full SDP marshaling and unmarshaling with an API that fits naturally into Go services, and it powers Pion's own offer/answer handling. If you're building signaling infrastructure, SFU components, or server-side media tooling in Go, pion/sdp is the natural choice, and its integration with the wider Pion stack means your SDP handling stays consistent with the rest of the pipeline.

Best Practices for Safe Modification

Treat SDP as a negotiated contract, not a free-form document. Only modify fields you fully understand, prefer removing or reordering offered codecs over rewriting transport attributes, never touch ICE credentials or DTLS fingerprints, and always test modifications across all browsers and devices you support, because each has slightly different tolerance for munged SDP.

Security Considerations in SDP

SDP carries the security parameters of the session, which makes it a security-critical surface even though the protocol itself is just text.

DTLS Fingerprint Validation

The DTLS fingerprint in SDP is what prevents man-in-the-middle attacks on the media path. Because the fingerprint binds the peer's certificate to the negotiated description, an attacker who intercepts media can't present their own certificate without failing validation against the SDP both peers agreed on. The critical caveat is that this binding only holds if the signaling channel carrying the SDP is itself protected. If your signaling runs over unencrypted WebSocket or an unauthenticated REST endpoint, an attacker can rewrite the fingerprints in transit and defeat the whole mechanism. Always run signaling over TLS with proper authentication, which is exactly what VideoSDK does: token-based authentication with server-side generation, as described in the VideoSDK authentication and token guide, ensures no peer joins a room or intercepts its negotiation unauthenticated.

Protecting ICE Credentials

The ice-ufrag and ice-pwd values authenticate STUN messages between the peers. They're short-lived and scoped to a single session, which limits the blast radius if leaked, but leaking them still lets an attacker inject fake candidates or spoof connectivity checks, redirecting media through a path they control. Don't log full SDP dumps with credentials into long-lived production logs, rotate credentials by design since every new session generates fresh ones, and keep SDP exchange inside your authenticated signaling channel rather than exposing it through URLs or client-side storage.
SDP handling in WebRTC has consolidated around a few clear directions, and aligning with them now saves migration pain later.

Unified Plan vs Plan B

Plan B, the older Chrome convention that stuffed all tracks of the same type into a single m= line, is dead. Unified Plan, where each track gets its own media section, is the universal standard across all browsers and is required for advanced features like simulcast and layered codecs. If you maintain legacy code that assumes Plan B semantics, such as parsing a single video m= line and expecting multiple ssrc entries inside it, migrate now: every modern stack, including VideoSDK's SDKs, is built on Unified Plan.

SDP Bundling and Media Direction

Bundling, negotiated through the bundle-only and group attributes, lets all media sections share a single transport, which cuts ICE and DTLS setup cost dramatically and is now the default everywhere. Media direction attributes (sendrecv, sendonly, recvonly, inactive) are increasingly used for dynamic control, letting applications mute or pause streams through renegotiation instead of tearing tracks down. Prefer direction changes over track removal when you need reversible muting behavior.

Automated SDP Generation

The strongest best practice is to avoid touching SDP at all. Managed SDKs generate, exchange, and validate SDP for you, handle trickle ICE, renegotiation, and TURN fallback automatically, and expose a higher-level model of rooms, participants, and streams. VideoSDK's video calling SDKs do exactly this across React, Flutter, React Native, Android, iOS, JavaScript, Unity, and Python, and its Prebuilt UI Kit gets a working call interface running with zero custom negotiation code. Reach for raw SDP manipulation only when you have a reason no SDK covers.

Definitions Glossary

SDP (Session Description Protocol): A text-based format, standardized in RFC 4566, that describes the media, codecs, transports, and security parameters of a multimedia session. In WebRTC it is the negotiation contract exchanged between peers before media flows.
JSEP (JavaScript Session Establishment Protocol): The architecture, defined in RFC 8829, that governs how WebRTC creates and exchanges SDP offers and answers while keeping signaling mechanics out of the browser API.
ICE Candidate: A potential transport path, described in an SDP candidate attribute line, that a peer offers for connectivity. Types include host, server-reflexive, and relay, with relay candidates provided by TURN servers for firewall traversal.
DTLS Fingerprint: The hash of a peer's DTLS certificate, carried in SDP, that the remote peer verifies during the handshake to guarantee the media path isn't intercepted.
Unified Plan: The modern SDP convention where each media track gets its own m= line, replacing the deprecated Plan B format and enabling simulcast and per-track control.
Trickle ICE: The practice of sending ICE candidates incrementally as they're discovered, rather than waiting for the full set, which significantly reduces connection setup time.

Key Takeaways

  • WebRTC SDP is the negotiation contract between peers, covering codecs, ICE candidates, and DTLS security, and nearly every failed connection traces back to something inside it.
  • The offer/answer workflow under JSEP defines the session: the offer describes capabilities, the answer selects from them, and neither side can invent terms the other didn't propose.
  • Debug failed calls by diffing both SDP dumps, focusing on rejected m= lines for codec mismatches, candidate lists for ICE failures, and fingerprint lines for tampering.
  • Use structured parsers (sdp-transform in JavaScript, webrtc-sdp in Rust, pion/sdp in Go) instead of string surgery, and never modify ICE credentials or DTLS fingerprints.
  • SDP security depends on a protected signaling channel: fingerprints and ICE credentials are only trustworthy if the transport carrying them is authenticated and encrypted.
  • For most applications, a managed SDK like VideoSDK eliminates raw SDP handling entirely, offering multi-platform video calling with trickle ICE, adaptive streaming, and server-side token authentication built in.

Conclusion

WebRTC SDP is dense, but it's not mysterious: a session header, media descriptions, ICE attributes, and DTLS security lines, all negotiated through a strict offer and answer exchange. Once you can read those fields, connection failures stop being black boxes and become diagnosable in minutes, whether the cause is a rejected codec, a missing TURN candidate, or a corrupted fingerprint. And when you'd rather ship features than parse session descriptions, VideoSDK handles the entire negotiation layer for you, from ICE and DTLS to adaptive streaming, across ten-plus platforms. Start free at app.videosdk.live/login, explore the code samples, and join the VideoSDK Discord community to compare notes with thousands of developers. What are you building with WebRTC? Drop a comment, I'd love to hear what kind of real-time use case you're working on.

Free $20 Balance for AI Voice Agents & Video Calls

FAQ