SDP Message Overview
After creating a peer connection, you should exchange SDP (Session Description Protocol), which is a standard format for describing multimedia communication sessions for a peer-to-peer connection.
SDP serves as the foundational negotiation protocol that enables WebRTC peers to understand each other's capabilities and establish compatible communication channels. This text-based protocol acts as a contract between peers, defining not only what media types and codecs are supported, but also specifying network information, security parameters, and session timing details. The protocol follows a structured format where each line begins with a single character identifier followed by an equals sign and the associated value.
The SDP negotiation process is critical for ensuring interoperability between different browsers, devices, and WebRTC implementations. Each peer generates its own SDP based on its local capabilities, available codecs, supported media formats, and network configuration. When these SDPs are exchanged, both peers can determine the intersection of their capabilities and establish the optimal configuration for their communication session.
The SDP includes essential information for making a peer connection, such as Codec, source address, media types of audio and video, and other associated properties, as you can see in the SDP message below:
v=0
o=- 5168502270746789779 2 IN IP4 127.0.0.1
s=
c=IN IP4 0.0.0.0
t=0 0
m=audio 49170 RTP/AVP 0
a=rtpmap:0 PCMU/8000
m=video 51372 RTP/AVP 31
a=rtpmap:31 H261/90000
m=video 53000 RTP/AVP 32
a=rtpmap:32 MPV/90000
This SDP example demonstrates the hierarchical structure where session-level information is defined first, followed by media-level descriptions. The version line (v=0) indicates SDP version, while the origin line (o=) contains session identification and versioning information. Media lines (m=) define individual media streams with their respective transport ports and payload types, while attribute lines (a=) provide additional codec-specific information such as payload type mappings and codec parameters.
Modern WebRTC implementations generate much more complex SDPs that include additional attributes for advanced features like simulcast, scalable video coding, redundancy mechanisms, and security parameters. These SDPs also contain bandwidth information, preferred codec orderings, and various WebRTC-specific extensions that enable features like data channels and advanced media routing.
Each peer can figure out what types of Codec or media will be delivered by exchanging SDP messages. Let's see a scenario that Alice and Bob want to connect on a video call:
- Alice suggests a peer connection with Bob by creating an SDP Offer that the signaling server will deliver.
- Bob accepts the SDP Offer and responds by creating an SDP Answer that the signaling server will deliver.
- Alice accepts the SDP Answer, and they will prepare to establish a connection between them.
This offer-answer model follows the Session Initiation Protocol (SIP) paradigm, ensuring a structured negotiation process. The offer represents Alice's proposal for the session, including her preferred codecs, media types, and capabilities. Bob's answer serves as both an acceptance of the session and a counter-proposal that specifies which of Alice's offered capabilities he can support and his own preferences within those constraints.
The negotiation process is inherently asymmetric, with the offerer (Alice) taking the initiative and the answerer (Bob) responding within the constraints of the offer. This asymmetry helps resolve conflicts and ensures deterministic outcomes when both peers have different preferences or capabilities. The process also allows for graceful fallbacks when certain features or codecs aren't mutually supported.
The steps above can be drawn like the figure below:
The local peer (Alice) sends a SDP Offer and the remote peer (Bob) return a SDP Answer, and then it's ready for establishing a peer connection.
Create an SDP Offer
The SDP offer contains everything peers need to connect, such as MediaStreamTrack, Codec, and other associated properties, as we've discussed before. You can create an SDP offer with the RTCPeerConnection.createOffer() method, which initiates the creation of an SDP offer to start a new WebRTC connection to a remote peer:
const offerOptions = {
iceRestart: true,
offerToReceiveAudio: true,
offerToReceiveVideo: true
};
const offer = await localPeerConnection.createOffer(offerOptions);
The createOffer method performs a comprehensive analysis of the peer connection's current state, including all added media tracks, configured data channels, and ICE gathering settings. It generates an SDP that represents the peer's maximum capabilities and preferences, creating a comprehensive proposal for the communication session. The method considers factors like codec preferences, bitrate constraints, media track directions, and any previously negotiated parameters.
The offer generation process involves codec enumeration where the browser lists all supported audio and video codecs in order of preference. For video codecs, this might include H.264, VP8, VP9, and AV1, each with their specific profile levels and capabilities. Audio codecs typically include Opus, G.722, PCMU, and PCMA, along with their supported sample rates and channel configurations.
As you can see in the code above, you can give an offerOptions
parameter to the createOffer
method. The offerOptions
provides offerToReceiveAudio
and offerToReceiveVideo
options, providing additional control over audio and video's directionality.
The iceRestart
option triggers a complete ICE renegotiation, which is particularly useful when network conditions have changed or when connectivity issues are detected. This process generates new ICE credentials and initiates fresh candidate gathering, potentially discovering new network paths that weren't available during the initial connection establishment.
The directional options (offerToReceiveAudio
and offerToReceiveVideo
) influence the media direction attributes in the generated SDP. These options help establish whether the connection should be sendonly, recvonly, sendrecv, or inactive for each media type. This granular control is essential for applications that need specific media flow patterns, such as broadcasting scenarios or receive-only viewers.
After creating the SDP offer, you should set the message to the peer connection as a local description and make it clear that your local peer connection initiates the WebRTC connection to a remote peer:
await localPeerConnection.setLocalDescription(offer);
Setting the local description serves multiple critical functions beyond just storing the SDP. It transitions the peer connection's signaling state to "have-local-offer" and triggers the ICE gathering process if it hasn't started already. The browser begins collecting ICE candidates from all available network interfaces, including local addresses, STUN-derived server reflexive addresses, and TURN-allocated relayed addresses.
This step also validates the SDP structure and ensures all referenced codecs and media formats are actually supported by the local browser. If any incompatibilities are detected, the setLocalDescription call will fail with a descriptive error, allowing applications to handle unsupported configurations gracefully.
Let's assume the remote peer received an SDP offer from a signaling server. Then the remote peer connection should set the SDP offer message as a remote description and make it clear that the remote peer connection received the SDP offer message:
await remotePeerConnection.setRemoteDescription(offer);
When the remote peer sets the received offer as its remote description, several important processes are initiated. The peer connection analyzes the offered codecs and media types, determining which ones it can support and in what configurations. This analysis includes checking codec compatibility, verifying supported media formats, and assessing any bandwidth or resolution constraints specified in the offer.
The browser also begins preparing its media pipeline based on the offered session description. This includes initializing decoders for the offered video and audio codecs, setting up RTP processing chains, and preparing any necessary media format conversions. The peer connection transitions to the "have-remote-offer" state, indicating it's ready to generate a compatible answer.
Create an SDP Answer
After receiving the SDP offer message from the local peer, the remote peer should return an SDP answer message to the local peer. You can create an SDP answer with the RTCPeerConnection.createAnswer() method, which contains information about media sessions, codecs and options supported by browser to reply to the local peer:
const answer = await remotePeerConnection.createAnswer();
The createAnswer method generates an SDP that represents the intersection of the remote peer's capabilities with what was offered by the local peer. Unlike createOffer, which represents maximum capabilities, createAnswer is constrained by the received offer and can only include media types, codecs, and configurations that were explicitly offered or are compatible with the offered session.
The answer generation process involves careful codec negotiation where the answering peer selects from the offered codecs based on its own preferences and capabilities. For each media line in the offer, the answerer must either accept it (potentially with modified parameters), reject it by setting the port to zero, or propose alternative configurations within the constraints of the original offer.
The answerer also has the opportunity to specify additional parameters that weren't fully defined in the offer, such as specific codec configurations, bandwidth limitations, or media format preferences. However, these additions must remain within the bounds of what the offerer proposed, ensuring the final negotiated session is acceptable to both peers.
Now let's assume the local peer received an SDP answer from a signaling server. Then the local peer connection should set the SDP answer message as a remote description and make it clear the peer connection has established:
await localPeerConnection.setRemoteDescription(desc);
When the offerer receives and sets the SDP answer as its remote description, the negotiation process reaches completion. The peer connection can now finalize all session parameters, knowing exactly which codecs will be used, what media formats will be exchanged, and how the RTP streams will be configured. Both peers now have a complete and compatible view of the session.
At this point, the peer connection transitions to a "stable" signaling state, indicating that the SDP negotiation is complete and the connection is ready for media transport. However, the actual media flow won't begin until the ICE connectivity establishment process is also completed, which happens in parallel with the SDP exchange.
Exchange SDP Messages
Now let's combine all the concepts above and establish a peer connection by exchanging SDP messages between a local peer (p1
) and a remote peer (p2
), and each peer wants to establish a peer-to-peer connection by exchanging SDP messages:
let localStream, pc1, pc2;
async function attachLocalMedia() {
callPc1Button.disabled = true;
try {
const stream = await navigator.mediaDevices.getUserMedia(videoConstraints);
localVideo.srcObject = stream;
localStream = stream;
callPc2Button.disabled = false;
} catch (e) {
onCatch(e)
}
}
async function peerConnection() {
callPc2Button.disabled = true;
disconnectButton.disabled = false;
pc1 = new RTCPeerConnection(rtcConfig);
pc2 = new RTCPeerConnection(rtcConfig);
localStream.getTracks().forEach(track => pc1.addTrack(track, localStream));
try {
console.log('pc1 createOffer start');
const offer = await pc1.createOffer({
iceRestart: true,
offerToReceiveAudio: true,
offerToReceiveVideo: true
});
await onCreateOfferSuccess(offer);
} catch (e) {
onCatch(e);
}
}
async function onCreateOfferSuccess(desc) {
console.log(`Offer from pc1\nsdp: ${desc.sdp}`);
try {
await pc1.setLocalDescription(desc);
} catch (e) {
onCatch(e)
}
try {
await pc2.setRemoteDescription(desc);
} catch (e) {
onCatch(e)
}
try {
const answer = await pc2.createAnswer();
await onCreateAnswerSuccess(answer);
} catch (e) {
onCatch(e);
}
}
function gotRemoteStream(e) {
if (remoteVideo.srcObject !== e.streams[0]) {
remoteVideo.srcObject = e.streams[0];
}
}
async function onCreateAnswerSuccess(desc) {
try {
await pc2.setLocalDescription(desc);
} catch (e) {
onCatch(e)
}
try {
await pc1.setRemoteDescription(desc);
} catch (e) {
onCatch(e)
}
}
function getName(pc) {
return (pc === pc1) ? 'pc1' : 'pc2';
}
function getOtherPc(pc) {
return (pc === pc1) ? pc2 : pc1;
}
This comprehensive example demonstrates the complete SDP exchange workflow in a controlled environment. The code creates two peer connections on the same page, simulating what would normally happen between two different browsers or devices connected through a signaling server. The media tracks are added to the first peer connection before creating the offer, ensuring that the generated SDP includes proper media descriptions for the available audio and video streams.
The sequential nature of the SDP exchange is clearly visible in the code flow. Each step must complete successfully before proceeding to the next, as the signaling state machine enforces strict ordering requirements. Error handling at each step is crucial because SDP operations can fail due to various reasons including unsupported codecs, invalid SDP formats, or state machine violations.
The example also demonstrates the importance of proper media stream handling. The addTrack
method associates media tracks with the peer connection before offer creation, ensuring that the generated SDP accurately reflects the media that will be sent. The gotRemoteStream
function handles incoming media streams, connecting them to video elements for playback.
The local peer (p1
) initiates the creation of an SDP offer to start a new WebRTC connection to a remote peer (p2
), and the remote peer (p2
) returns an SDP answer to finalize a peer connection to the local peer (p1
).
Typically, this process should be done by a signaling server responsible for resolving connectivity problems and establishing a connection between peers by exposing minimized private information. But in this tutorial, we don't use a signaling server and set up a connection between two RTCPeerConnection
objects (known as peers) on the same page with each peer to help you better grasp how SDP messages are exchanged.
In real-world applications, the signaling server acts as a trusted intermediary that facilitates secure SDP exchange without exposing sensitive information like IP addresses or network topologies to application code. The server typically implements WebSocket connections, Server-Sent Events, or HTTP long-polling to provide real-time SDP delivery between peers. Modern signaling implementations often include additional features like presence management, room-based routing, and authentication mechanisms.
The signaling server also plays a crucial role in handling edge cases such as simultaneous offers (glare conditions), connection failures, and renegotiation scenarios. It can implement sophisticated routing logic to handle multi-party scenarios, cascade negotiations through multiple peers, and provide fallback mechanisms when direct peer-to-peer connections aren't possible.
Now, you should do one more setup for establishing peer connections ultimately. The code above will not work now because peers don't know how to reach each other behind a NAT/Firewall in their local network. For this, you need to set up ICE candidates.
The SDP exchange establishes what media will be sent and how it will be encoded, but it doesn't solve the fundamental networking challenge of connecting peers across different networks. NAT devices and firewalls create barriers that prevent direct communication between peers, requiring additional mechanisms to discover viable network paths. ICE candidates represent these potential network paths, and their exchange is essential for completing the connection establishment process.
The relationship between SDP and ICE is symbiotic: SDP negotiation determines the session parameters while ICE discovery determines the network connectivity. Both processes must complete successfully for a WebRTC connection to become fully operational. The timing of these processes can overlap, with ICE gathering often beginning during SDP creation and continuing throughout the negotiation phase.