Real-Time AI Voice Pipelines over WebRTC SFU

• min read
Real-Time AI Voice Pipelines over WebRTC SFU

Integrating conversational AI voice models such as Gemini Live or OpenAI Realtime API into live multi-party WebRTC calls typically forces engineering teams into a fragile architecture: spinning up headless browsers or heavy C++ GStreamer workers on the AI backend just to speak Opus over SRTP. When designing the AI gateway inside Slyber SFU Server, I eliminated server-side WebRTC duplication by introducing a lightweight, full-duplex binary WebSocket pipe (/ai/ws).

Combined with the zero-allocation Go routing engine, this gateway allows any Python, Node.js, or Go orchestrator to stream raw Opus frames into a WebRTC room while preserving hardware-accelerated browser playback and end-to-end encryption.

1. Decoupling WebRTC Transport from AI Inference (/ai/ws)

Instead of managing ICE candidates, DTLS handshakes, and RTCP feedback inside your AI microservice, the SFU exposes an authenticated WebSocket endpoint at /ai/ws?room=room-id&mode=mux (gateway documentation):

  1. Human-to-AI audio extraction. As participants speak in the room, the SFU strips SRTP/RTP headers and forwards raw Opus frames over the WebSocket prefixed with [len(speaker_id), rfc6464_audio_level, speaker_id...] so your AI backend knows exactly which participant is talking.
  2. Multi-bot multiplexing (mode=mux). A single WebSocket connection can dynamically spawn up to 6 independent AI agents in the same room via JSON control commands (spawn_agent and remove_agent), each assigned its own SSRC and track ID so clients display individual active-speaker indicators per bot.
  3. Single-frame HTTP injection. For one-off voice alerts or announcements, backend services can push a raw Opus payload via POST /ai?room=room-id without maintaining a persistent socket.

2. Smoothing Bursty Neural Audio with a 20ms RTP Pacer

Multimodal LLMs generate speech chunks unevenly—frequently delivering 400–800 ms of audio in a single burst followed by network jitter. Injecting bursty packets directly into a WebRTC session causes browser jitter buffer underruns and audible clicks.

Inside Slyber SFU Server, every spawned AI slot is backed by an isolated ring buffer (up to 600 frames / 12 seconds of speech) driven by a strict 20ms ticker that stamps outbound Opus packets with monotonic sequence numbers and exact +960 RTP timestamp increments at 48 kHz.

3. Sub-60ms VAD Barge-In Without Decrypting Audio

Natural human-AI dialogue requires immediate interruption: the moment a user starts speaking, the bot must stop talking. Decrypting Opus payloads on the SFU to run neural voice activity detection would burn CPU cores and break RFC 9605 SFrame end-to-end encryption.

Instead, the SFU inspects the unencrypted RFC 6464 (urn:ietf:params:rtp-hdrext:ssrc-audio-level) RTP header extension on incoming human audio packets:

Code
// When human voice energy crosses threshold for 3 consecutive frames (~60ms):
1. Auto-flush immediately clears all queued unspoken frames in the bot's 20ms pacer
2. SFU emits {"type": "ai_vad_barge_in", "participant_id": "user-1"} over /ai/ws
3. After 360ms of trailing silence, SFU emits {"type": "ai_vad_speech_end"}
Pipeline StageMechanismLatency & Scalability Benefit
AI Ingress / EgressMultiplexed binary WebSocket (/ai/ws?mode=mux)Up to 6 distinct AI voices over 1 TCP socket
Jitter Elimination20ms server-side RTP pacer (+960 timestamp step)Smooth browser playback from bursty LLM output
Interruption DetectionRFC 6464 header inspection (3-frame onset)<60ms barge-in flush with zero audio decryption
Zero-Trust CompatibilityWorks alongside SFrame E2EE + MLS key rotationCompatible with REQUIRE_E2EE=true rooms

Because voice energy is read from the RTP header extension, barge-in detection operates at line rate with zero cryptographic overhead—even when REQUIRE_E2EE=true is enforced across the room. Explore the full deployment benchmarks on the Slyber SFU Server case study or get started on slyber.com.

Need a high-load architecture or custom engineering solution?

I design and build fault tolerant Go backends, real time WebRTC streaming systems, custom protocols, and fullstack applications. Let’s discuss your technical challenges and architecture.

Related Articles

Zero-Allocation WebRTC SFU Architecture in Go

Building a basic WebRTC Selective Forwarding Unit (SFU) in Go using pion/webrtc/v4 takes a couple of days, but scaling it to thousands of concurrent rooms exposes severe bottlenecks: opening tens of thousands of ephemeral UDP ports breaks container networking, per-packet heap allocations trigger garbage collection pauses, and standard SDP renegotiation introduces multi-second black screens during peer switches.

How an SFU Server Works

How a high-load SFU server works under the hood. Core isolation, SPSC queues, TWCC and GCC bandwidth estimation, Simulcast and SVC layers, WHIP gateways, and the MoQ stack.

Slyber TURN Server Architecture

You can write a basic STUN and TURN server in Go using the pion/turn/v4 library in a single evening with just a hundred lines of code. However, as soon as such a prototype hits a production server with hundreds of concurrent media streams, systemic issues surface: a single UDP socket queue hits Linux kernel lock contention, mobile clients switching networks clog the port pool with dead allocations, and an open relay becomes a target for scanning the internal cloud network.

Why I Replaced Legacy Coturn for My TURN Server

When you launch a WebRTC project on a local network or test calls between two home routers, a direct connection via STUN works flawlessly. The problems start in production. Around 15 to 20 percent of users are behind NAT, carrier-grade CGNAT on mobile networks, or strict corporate firewalls. Without a reliable relay server (TURN), these clients stare at an endless connecting screen and leave.