Fixed SmallWebRTC data channel silently stalling on networks with a 1280-byte MTU.

This commit is contained in:
filipi87
2026-04-23 12:18:33 -03:00
parent 21f6c2afa5
commit 94304ec74e
2 changed files with 53 additions and 1 deletions

View File

@@ -215,3 +215,25 @@ WHATSAPP_APP_SECRET=...
# xAI / Grok # xAI / Grok
XAI_API_KEY=... XAI_API_KEY=...
# SmallWebRTC / aiortc
#
# PIPECAT_SCTP_MAX_CHUNK_SIZE controls the maximum SCTP DATA-chunk payload
# size (bytes) used by aiortc's data channel. The default is 1100.
#
# When to change this:
# Lower it if the data channel silently stalls on your network. This happens
# when aiortc's default (1200) produces UDP datagrams larger than your path
# MTU: each chunk adds ~105 bytes of SCTP + DTLS + UDP + IP headers, so
# 1200 → ~1305-byte datagrams, which exceeds the 1280-byte MTU common on
# IPv6 paths, Tailscale overlays, and many consumer VPNs. The kernel drops
# the packet with EMSGSIZE; aiortc retransmits at the same size and the
# channel stalls indefinitely. A value of 1100 (~1205-byte datagrams) fits
# any path with MTU ≥ 1210.
# Raise it only if you are on a controlled LAN (MTU 1500) and are sending
# large data-channel messages where extra fragmentation matters. Values
# above 1200 are unsafe on most internet paths.
#
# Note: this is a process-global setting (aiortc limitation) — all
# SmallWebRTC connections in the same process share the same value.
#PIPECAT_SCTP_MAX_CHUNK_SIZE=1100

View File

@@ -13,6 +13,7 @@ for real-time communication applications.
import asyncio import asyncio
import json import json
import os
import time import time
import uuid import uuid
from typing import Any, Literal from typing import Any, Literal
@@ -23,6 +24,7 @@ from pydantic import BaseModel, TypeAdapter
from pipecat.utils.base_object import BaseObject from pipecat.utils.base_object import BaseObject
try: try:
import aiortc.rtcsctptransport as _sctp_transport
from aiortc import ( from aiortc import (
RTCConfiguration, RTCConfiguration,
RTCIceServer, RTCIceServer,
@@ -36,6 +38,34 @@ except ModuleNotFoundError as e:
logger.error("In order to use the SmallWebRTC, you need to `pip install pipecat-ai[webrtc]`.") logger.error("In order to use the SmallWebRTC, you need to `pip install pipecat-ai[webrtc]`.")
raise Exception(f"Missing module: {e}") raise Exception(f"Missing module: {e}")
# Clamp aiortc's SCTP DATA-chunk payload size so the on-wire UDP packet fits
# inside the smallest-MTU path we're likely to see (IPv6 minimum 1280,
# Tailscale overlays default to 1280, some consumer VPNs are lower).
#
# aiortc hardcodes USERDATA_MAX_LENGTH = 1200. After adding SCTP (28) +
# DTLS/GCM (~29) + UDP (8) + IPv6 (40) headers that produces a ~1305-byte
# UDP datagram — over the 1280 MTU. The kernel rejects it with EMSGSIZE,
# SCTP retransmits at the same size, and the data channel silently stalls.
# aiortc has no PMTU discovery (RFC 8831 §6), so there is no auto-recovery.
#
# 1100 brings the worst-case datagram to ~1205 bytes (~75 bytes of slack).
# Throughput cost is negligible: RTVI control frames fragment across one
# extra chunk at most, and audio uses RTP (a separate path).
#
# There is no public API to set this — RTCConfiguration exposes no MTU knob
# and all method references are bare module-global lookups, so patching the
# module attribute before any RTCSctpTransport is instantiated is the only
# option short of forking aiortc.
#
# Remove once aiortc ships DPLPMTUD (RFC 8899) or exposes this as a
# configurable parameter.
_SCTP_MAX_CHUNK_SIZE_DEFAULT = 1100
_sctp_max_chunk_size = int(
os.environ.get("PIPECAT_SCTP_MAX_CHUNK_SIZE", _SCTP_MAX_CHUNK_SIZE_DEFAULT)
)
_sctp_transport.USERDATA_MAX_LENGTH = _sctp_max_chunk_size
logger.debug(f"[SCTP] USERDATA_MAX_LENGTH set to {_sctp_max_chunk_size}")
SIGNALLING_TYPE = "signalling" SIGNALLING_TYPE = "signalling"
AUDIO_TRANSCEIVER_INDEX = 0 AUDIO_TRANSCEIVER_INDEX = 0
VIDEO_TRANSCEIVER_INDEX = 1 VIDEO_TRANSCEIVER_INDEX = 1