From 4a98808021cd767ebc203b6c4ffa7dca6fbcd1d8 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Fri, 26 Jun 2026 21:34:38 -0700 Subject: [PATCH] feat: add WithSettingEngineFunc ConnectOption to customize the SettingEngine The SDK builds its webrtc.SettingEngine internally in NewPCTransport with no extension point, so callers cannot influence ICE behavior beyond the existing WithICETransportPolicy / WithDisableTURN options. When a client is co-located with the SFU on a host that has many network interfaces (docker bridges, veth, IPv6), pion gathers ICE host candidates from all of them and can select an unreachable one, silently breaking the media path. The fix is SettingEngine.SetInterfaceFilter / SetIPFilter, which the SDK does not currently expose. WithSettingEngineFunc passes the SettingEngine to a caller-supplied func after the SDK finishes configuring it and before the API is built, for both the publisher and subscriber PeerConnections. It is opt-in and changes nothing when unset. Co-Authored-By: Claude Opus 4.8 (1M context) --- engine.go | 2 ++ room.go | 11 +++++++++++ setting_engine_test.go | 39 +++++++++++++++++++++++++++++++++++++++ signalling/interfaces.go | 7 +++++++ transport.go | 6 ++++++ 5 files changed, 65 insertions(+) create mode 100644 setting_engine_test.go diff --git a/engine.go b/engine.go index d3c1bcbb..2f6658c1 100644 --- a/engine.go +++ b/engine.go @@ -409,6 +409,7 @@ func (e *RTCEngine) createPublisherPCLocked(configuration webrtc.Configuration) OnRTTUpdate: e.setRTT, IsSender: true, DTLSEllipticCurves: e.connParams.DTLSEllipticCurves, + SettingEngineFunc: e.connParams.SettingEngineFunc, }); err != nil { return err } @@ -519,6 +520,7 @@ func (e *RTCEngine) createSubscriberPCLocked(configuration webrtc.Configuration) Interceptors: e.connParams.Interceptors, IncludeDefaultInterceptors: e.connParams.IncludeDefaultInterceptors, DTLSEllipticCurves: e.connParams.DTLSEllipticCurves, + SettingEngineFunc: e.connParams.SettingEngineFunc, }); err != nil { return err } diff --git a/room.go b/room.go index 35e8d57c..5416b754 100644 --- a/room.go +++ b/room.go @@ -175,6 +175,17 @@ func WithICETransportPolicy(iceTransportPolicy webrtc.ICETransportPolicy) Connec } } +// WithSettingEngineFunc customizes the pion SettingEngine (ICE interface/IP +// filters, NAT1To1 mappings, timeouts) before the WebRTC API is constructed. +// fn is invoked for every PeerConnection the SDK creates (publisher and +// subscriber). Use it when the host has interfaces or IPs that must be excluded +// from ICE candidate gathering and the existing options are insufficient. +func WithSettingEngineFunc(fn func(*webrtc.SettingEngine)) ConnectOption { + return func(p *connParams) { + p.SettingEngineFunc = fn + } +} + // WithDisableTURN removes TURN/TURNS URLs from the ICE server configuration // provided by the SFU. Use this when the client is co-located with the SFU // and does not need relay candidates. diff --git a/setting_engine_test.go b/setting_engine_test.go new file mode 100644 index 00000000..31a0193b --- /dev/null +++ b/setting_engine_test.go @@ -0,0 +1,39 @@ +// Copyright 2024 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package lksdk + +import ( + "testing" + + "github.com/pion/webrtc/v4" + + "github.com/livekit/server-sdk-go/v2/signalling" +) + +func TestWithSettingEngineFunc(t *testing.T) { + p := &connParams{ConnectParams: &signalling.ConnectParams{}} + + called := false + WithSettingEngineFunc(func(se *webrtc.SettingEngine) { called = true })(p) + + if p.SettingEngineFunc == nil { + t.Fatal("WithSettingEngineFunc must set ConnectParams.SettingEngineFunc") + } + + p.SettingEngineFunc(&webrtc.SettingEngine{}) + if !called { + t.Fatal("the stored func must be the one supplied to WithSettingEngineFunc") + } +} diff --git a/signalling/interfaces.go b/signalling/interfaces.go index 9153fb7d..3391ce72 100644 --- a/signalling/interfaces.go +++ b/signalling/interfaces.go @@ -91,6 +91,13 @@ type ConnectParams struct { ICETransportPolicy webrtc.ICETransportPolicy + // SettingEngineFunc, if set, is invoked with the pion SettingEngine after the + // SDK builds it (per PeerConnection) and before the WebRTC API is created. It + // lets callers customize ICE behavior the SDK does not otherwise expose — for + // example SetInterfaceFilter / SetIPFilter to exclude interfaces or IPs from + // candidate gathering. See WithSettingEngineFunc. + SettingEngineFunc func(*webrtc.SettingEngine) + // DisableTURN removes TURN/TURNS URLs from the ICE server list provided by the SFU. // Use this when the client is co-located with the SFU and does not need relay candidates. DisableTURN bool diff --git a/transport.go b/transport.go index 95fd9a72..2c526ad6 100644 --- a/transport.go +++ b/transport.go @@ -81,6 +81,7 @@ type PCTransportParams struct { OnRTTUpdate func(rtt uint32) IsSender bool DTLSEllipticCurves []dtlsElliptic.Curve + SettingEngineFunc func(*webrtc.SettingEngine) } func (t *PCTransport) registerDefaultInterceptors(params PCTransportParams, i *interceptor.Registry) error { @@ -219,6 +220,11 @@ func NewPCTransport(params PCTransportParams) (*PCTransport, error) { if lf != nil { se.LoggerFactory = lf } + // Allow callers to customize the SettingEngine (ICE interface/IP filters, + // NAT1To1, etc.) before the API is built. + if params.SettingEngineFunc != nil { + params.SettingEngineFunc(&se) + } api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithSettingEngine(se), webrtc.WithInterceptorRegistry(i)) pc, err := api.NewPeerConnection(params.Configuration)