WebRTC

Features

WebRTC extension capabilities

Topologies

The WebRTC extension supports two connection topologies, chosen per call room:

Mesh Topology

Every participant connects directly to every other participant via peer-to-peer connections. Best for small groups (2--4 people) with minimal server load.

ext, _ := webrtc.New(streamingExt, config, webrtc.WithTopology(webrtc.TopologyMesh))
room, _ := ext.CreateCallRoom(ctx, "standup", streaming.RoomOptions{
    ID:         "standup",
    Name:       "Daily Standup",
    MaxMembers: 4,
})

In mesh mode, the server only handles signaling (SDP and ICE exchange). All media flows directly between peers.

SFU Topology

A Selective Forwarding Unit routes all media through the server. Participants send their streams once to the server, which forwards them to other participants. Scales to larger groups:

ext, _ := webrtc.New(streamingExt, config, webrtc.WithTopology(webrtc.TopologySFU))
room, _ := ext.CreateCallRoom(ctx, "all-hands", streaming.RoomOptions{
    ID:         "all-hands",
    Name:       "All Hands Meeting",
    MaxMembers: 50,
})

The SFU router manages publisher/subscriber relationships:

router := ext.GetSFURouter()

// Subscribe a viewer to a specific track
router.SubscribeToTrack(ctx, subscriberID, publisherID, trackID)

// Unsubscribe
router.UnsubscribeFromTrack(ctx, subscriberID, trackID)

// Set quality layer for a receiver
router.SetQuality(ctx, trackID, receiverID, "high")

// List available tracks
tracks := router.GetAvailableTracks()

SDP Signaling

Automated SDP (Session Description Protocol) offer/answer exchange through the streaming extension's room broadcasting system:

signalingManager := ext.GetSignalingManager()

// Send SDP offer to a specific peer
signalingManager.SendOffer(ctx, roomID, peerID, offer)

// Send SDP answer back
signalingManager.SendAnswer(ctx, roomID, peerID, answer)

// Handle incoming offers
signalingManager.OnOffer(func(roomID, peerID string, offer *webrtc.SessionDescription) {
    // Process offer, create answer
})

// Handle incoming answers
signalingManager.OnAnswer(func(roomID, peerID string, answer *webrtc.SessionDescription) {
    // Set remote description
})

ICE Candidate Exchange

ICE (Interactive Connectivity Establishment) candidates are relayed via streaming rooms for NAT traversal:

// Send ICE candidate to a peer
signalingManager.SendICECandidate(ctx, roomID, peerID, candidate)

// Receive ICE candidates
signalingManager.OnICECandidate(func(roomID, peerID string, candidate *webrtc.ICECandidate) {
    // Add to peer connection
})

Configure STUN/TURN servers for reliable connectivity:

webrtc.WithSTUNServers([]string{
    "stun:stun.l.google.com:19302",
    "stun:stun1.l.google.com:19302",
})

webrtc.WithTURNServers([]webrtc.TURNServer{
    {
        URL:      "turn:turn.example.com:3478",
        Username: "user",
        Password: "pass",
    },
})

Call Rooms

Isolated rooms manage participants, tracks, and lifecycle:

// Create a room
room, _ := ext.CreateCallRoom(ctx, "meeting-123", streaming.RoomOptions{
    ID:         "meeting-123",
    Name:       "Team Sync",
    MaxMembers: 10,
})

// Join a call
peer, _ := room.JoinCall(ctx, "user-1", &webrtc.JoinOptions{
    AudioEnabled: true,
    VideoEnabled: true,
    DisplayName:  "Alice",
    Metadata:     map[string]any{"role": "presenter"},
})

// List participants
participants := room.GetParticipants()
for _, p := range participants {
    fmt.Printf("%s: audio=%v video=%v screen=%v\n",
        p.DisplayName, p.AudioEnabled, p.VideoEnabled, p.ScreenSharing)
}

// Mute/unmute, enable/disable video
room.MuteUser(ctx, "user-2")
room.UnmuteUser(ctx, "user-2")
room.DisableVideo(ctx, "user-2")
room.EnableVideo(ctx, "user-2")

// Screen sharing
room.StartScreenShare(ctx, "user-1", screenTrack)
room.StopScreenShare(ctx, "user-1")

// Leave
room.Leave(ctx, "user-1")

// Clean up
ext.DeleteCallRoom(ctx, "meeting-123")

Peer Connections

Each PeerConnection represents a single user's WebRTC connection:

// Create an SDP offer
offer, _ := peer.CreateOffer(ctx)

// Set local/remote descriptions
peer.SetLocalDescription(ctx, offer)
peer.SetRemoteDescription(ctx, answer)

// Add ICE candidates
peer.AddICECandidate(ctx, candidate)

// Add/remove media tracks
peer.AddTrack(ctx, audioTrack)
peer.RemoveTrack(ctx, trackID)

// Get all tracks
tracks := peer.GetTracks()

// Connection event handlers
peer.OnICECandidate(func(candidate *webrtc.ICECandidate) {
    // Send to remote peer via signaling
})

peer.OnTrack(func(track webrtc.MediaTrack, receiver *webrtc.TrackReceiver) {
    // Handle incoming remote track (audio/video)
})

peer.OnConnectionStateChange(func(state webrtc.ConnectionState) {
    log.Printf("connection state: %s", state)
})

Data Channels

Support for WebRTC data channels for arbitrary data exchange between peers:

peer.OnDataChannel(func(dc webrtc.DataChannel) {
    fmt.Printf("data channel opened: %s\n", dc.Label())

    dc.OnMessage(func(msg []byte) {
        fmt.Printf("received: %s\n", string(msg))
    })

    dc.Send([]byte("hello from peer"))
    dc.SendText("text message")

    dc.OnClose(func() {
        fmt.Println("data channel closed")
    })
})

Data channels support both binary (Send) and text (SendText) messages.

Quality Monitoring

Per-peer monitoring of connection quality at configurable intervals (default: 5 seconds):

monitor := ext.GetQualityMonitor()

// Start monitoring a peer
monitor.Monitor(ctx, peer)

// Get current quality
quality, _ := monitor.GetQuality(peerID)
// quality.Score       -- 0-100 quality score
// quality.PacketLoss  -- percentage
// quality.Jitter      -- milliseconds
// quality.Latency     -- round-trip time
// quality.BitrateKbps -- current bitrate
// quality.Warnings    -- quality degradation warnings

// Subscribe to quality changes
monitor.OnQualityChange(func(peerID string, quality *webrtc.ConnectionQuality) {
    if quality.PacketLoss > 5.0 {
        log.Printf("peer %s: high packet loss (%.1f%%)", peerID, quality.PacketLoss)
    }
})

// Stop monitoring
monitor.Stop(peerID)

Adaptive Quality

When AdaptiveQuality is enabled, the extension automatically adjusts media quality based on connection metrics. Bitrate and resolution are reduced when packet loss or jitter exceed thresholds.

Audio and Video Configuration

Fine-grained control over media settings:

webrtc.WithAudioConfig(webrtc.AudioConfig{
    Enabled:    true,
    Codecs:     []string{"opus"},
    MaxBitrate: 128_000, // 128 kbps
})

webrtc.WithVideoConfig(webrtc.VideoConfig{
    Enabled:       true,
    Codecs:        []string{"VP8", "H264"},
    MinBitrate:    100_000,    // 100 kbps
    MaxBitrate:    2_500_000,  // 2.5 Mbps
    MaxWidth:      1920,
    MaxHeight:     1080,
    MaxFrameRate:  30,
})

Recording

Optional call recording with configurable output path (stub implementation -- recording infrastructure is not yet wired):

recorder := ext.GetRecorder()

Authentication

Require authentication for joining calls (default: enabled). When enabled, join requests must pass through the configured auth middleware:

webrtc.WithAuth(true)
webrtc.WithAllowGuests(false)

Sentinel Errors

ErrorMeaning
ErrPeerNotFoundPeer connection not found
ErrPeerAlreadyExistsPeer already in the room
ErrConnectionFailedWebRTC connection failed
ErrSignalingFailedSDP or ICE exchange failed
ErrSignalingTimeoutSignaling timed out
ErrInvalidSDPMalformed session description
ErrRoomNotFoundCall room does not exist
ErrRoomFullRoom at max capacity
ErrNotInRoomUser not in the room
ErrTrackNotFoundMedia track not found
ErrCodecNotSupportedRequested codec not supported
ErrSFUNotEnabledSFU operation on mesh room
ErrUnauthorizedAuthentication required

How is this guide?

On this page