Skip to content

Feature: Add SettingEngine API for DSCP (QoS) marking on RTP/RTCP/SCTP packets #3392

@Awuqing

Description

@Awuqing

Summary

Add SettingEngine API to configure DSCP (Differentiated Services Code Point) marking on RTP/RTCP and SCTP packets, enabling QoS-aware network treatment for WebRTC media and data channels.

Motivation

Real-time video streaming applications (e.g., remote desktop, cloud gaming) require low-latency, low-jitter delivery. DSCP marking allows intermediate routers and switches to prioritize WebRTC traffic over bulk data, significantly improving quality on managed networks.

Use cases:

  • Mark video RTP packets with AF41 (DSCP 34) for assured forwarding with low drop probability
  • Mark audio RTP packets with EF (DSCP 46) for expedited forwarding (lowest latency)
  • Mark SCTP data channel traffic with AF21 (DSCP 18) for control messages (touch/gesture)

Expected outcome: Applications can set per-track or per-connection DSCP values through SettingEngine, and pion automatically applies the corresponding IP TOS byte on outgoing UDP sockets.

Other implementations:

  • libwebrtc (Chrome/Chromium): Supports DSCP via RtpParameters.encodings[].networkPriority and RTCPeerConnection.setConfiguration({ dscp: true }). See W3C WebRTC Priority API.
  • GStreamer webrtcbin: Supports dscp-qos property on the pipeline.
  • Otracker/Janus: Configurable via dscp_tos parameter.

Why primitives are insufficient: Currently the only workaround is to:

  1. Create a net.UDPConn manually
  2. Call syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS, dscpValue<<2) (Linux/macOS)
  3. Pass it via SetICEUDPMux

This approach is platform-specific (Linux IP_TOS vs Windows SIO_SET_QOS), error-prone (requires raw FD extraction via SyscallConn()), and cannot differentiate between audio/video/data channels sharing the same UDP mux socket.

Describe alternatives you've considered

  1. OS-level syscall.SetsockoptInt on UDPMux connections — Works on Linux/macOS but not Windows without SIO_SET_QOS. Cannot distinguish audio vs video vs data channel traffic on a bundled connection since all share one UDP socket.

  2. iptables/nftables rules matching on port ranges — Fragile, requires root access, and doesn't work when ICEUDPMux binds all PeerConnections to a single port.

  3. net.Dialer.Control hook — Can set TOS at socket creation time but pion's internal UDP listeners don't expose a Control function in SettingEngine.

Additional context

Proposed API sketch:

se := webrtc.SettingEngine{}

// Option A: Global DSCP for all media
se.SetDSCP(0x22) // AF41 = 34 decimal

// Option B: Per-track-type DSCP (preferred)
se.SetDSCPForMedia(webrtc.RTPCodecTypeVideo, 0x22) // AF41
se.SetDSCPForMedia(webrtc.RTPCodecTypeAudio, 0x2E) // EF
se.SetDSCPForDataChannel(0x12)                       // AF21

This came up while optimizing a large-scale remote-control WebRTC deployment (~10k concurrent sessions). Without DSCP, video/control packets compete equally with background traffic on enterprise networks, causing jitter spikes of 50-200ms. With DSCP marking at the application layer, managed switches can properly queue media packets, reducing P99 jitter by ~60% in our testing with tc/netem simulations.

Platform support matrix:

OS Mechanism Notes
Linux IP_TOS / IPV6_TCLASS Works with setsockopt
macOS IP_TOS / IPV6_TCLASS Same as Linux
Windows SIO_SET_QOS or IP_TOS Requires admin or Group Policy
Android IP_TOS (via JNI or Go syscall) Works on rooted / system apps

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions