Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ psrpc: # optional gzip compression of psrpc bus payloads, see the compatibility
quality: gzip level 1-9. 0, the default, disables compression
threshold: payload bytes below which compression is skipped (default 1024)
max_decompressed_size: cap on an inbound payload after decompression, 0 for unlimited
enable_opus: offer the Opus codec for SIP media (default false, experimental)
dtls_srtp:
enabled: accept WebRTC-style DTLS-SRTP offers (default false, experimental)
handshake_timeout: maximum time for ICE/DTLS setup (default 10s)
```

The config file can be added to a mounted volume with its location passed in the SIP_CONFIG_FILE env var, or its body can be passed in the SIP_CONFIG_BODY env var.
Expand All @@ -84,6 +88,16 @@ The config file can be added to a mounted volume with its location passed in the
> The remaining `psrpc` keys (`max_attempts`, `timeout`, `backoff`, `buffer_size`) are accepted for config
> parity with LiveKit server, but SIP does not read them.

#### Codecs

PCMU, PCMA, G722, and DTMF are negotiated by default. Opus is **disabled by default** - set `enable_opus: true` to offer it. Validate interoperability with your SIP infrastructure before enabling in production.

When enabled, Opus (`opus/48000/2`, 48 kHz mono) is preferred over G722 and G711. Peers that do not support Opus fall back to G722, then G711 transparently.

#### DTLS-SRTP

Set `dtls_srtp.enabled: true` to accept inbound `UDP/TLS/RTP/SAVPF` offers with DTLS fingerprints. ICE-lite offers, including those used by Meta WhatsApp Business Calling, are supported over IPv4. Existing RTP and SDES-SRTP calls continue to use their original media paths.

### Using the SIP service

#### Creating Bridge and Dispatch Rule
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ require (
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12
github.com/ory/dockertest/v3 v3.12.0
github.com/pion/rtp v1.10.5
github.com/pion/dtls/v3 v3.1.5
github.com/pion/sdp/v3 v3.0.19
github.com/pion/srtp/v3 v3.0.12
github.com/pion/webrtc/v4 v4.2.18
github.com/prometheus/client_golang v1.24.1
github.com/sirupsen/logrus v1.9.4
Expand Down Expand Up @@ -96,15 +98,13 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runc v1.3.3 // indirect
github.com/pion/datachannel v1.6.2 // indirect
github.com/pion/dtls/v3 v3.1.5 // indirect
github.com/pion/ice/v4 v4.4.0 // indirect
github.com/pion/interceptor v0.1.47 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/mdns/v2 v2.1.0 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.17 // indirect
github.com/pion/sctp v1.11.1 // indirect
github.com/pion/srtp/v3 v3.0.12 // indirect
github.com/pion/stun/v3 v3.1.6 // indirect
github.com/pion/transport/v4 v4.0.2 // indirect
github.com/pion/turn/v5 v5.0.12 // indirect
Expand Down
26 changes: 26 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ var (
DefaultRTPPortRange = rtcconfig.PortRange{Start: 10000, End: 20000}
)

// OpusConfig reserves encoder tuning fields for a media-sdk version that can
// expose them. Non-zero values are rejected rather than silently ignored.
type OpusConfig struct {
Bitrate int `yaml:"bitrate"` // target bitrate in bits/sec (e.g. 24000); 0 = auto
Complexity int `yaml:"complexity"` // encoder complexity 1-10; 0 = default
FEC bool `yaml:"fec"` // enable in-band Forward Error Correction
PacketLossPercent int `yaml:"packet_loss_percent"` // expected packet loss 0-100, tunes FEC
}

// DTLSSRTPConfig enables WebRTC-style DTLS-SRTP media for SIP peers such as
// Meta Business Calling. It is deliberately independent of SIP signaling TLS
// and of the legacy SDES media-encryption setting.
type DTLSSRTPConfig struct {
Enabled bool `yaml:"enabled"`
HandshakeTimeout time.Duration `yaml:"handshake_timeout"`
}

const (
// After a call closes we keep its RTP port bound and draining so a freshly
// allocated call can't inherit a port a peer is still sending stale media to.
Expand Down Expand Up @@ -126,6 +143,9 @@ type Config struct {
RTPDrainingDuration time.Duration `yaml:"rtp_draining_duration"`
IgnoreLocalAddrInSDP bool `yaml:"ignore_local_addr_in_sdp"` // enable symmetric RTP if local IP is specified in SDP
Codecs map[string]bool `yaml:"codecs"`
EnableOpus bool `yaml:"enable_opus"`
Opus OpusConfig `yaml:"opus"`
DTLSSRTP DTLSSRTPConfig `yaml:"dtls_srtp"`

// HideInboundPort controls how SIP endpoint responds to unverified inbound requests.
// Setting it to true makes SIP server silently drop INVITE requests if it gets a negative Auth or Dispatch response.
Expand Down Expand Up @@ -212,6 +232,12 @@ func (c *Config) Init() error {
if c.MaxCpuUtilization <= 0 || c.MaxCpuUtilization > 1 {
c.MaxCpuUtilization = 0.9
}
if c.DTLSSRTP.HandshakeTimeout <= 0 {
c.DTLSSRTP.HandshakeTimeout = 10 * time.Second
}
if c.Opus.Bitrate != 0 || c.Opus.Complexity != 0 || c.Opus.FEC || c.Opus.PacketLossPercent != 0 {
return fmt.Errorf("opus bitrate, complexity, fec, and packet_loss_percent are not supported by the current media-sdk")
}

if err := c.InitLogger(); err != nil {
return err
Expand Down
37 changes: 37 additions & 0 deletions pkg/config/config_opus_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2026 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 config

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestUnsupportedOpusSettingsAreRejected(t *testing.T) {
tests := map[string]OpusConfig{
"bitrate": {Bitrate: 24000},
"complexity": {Complexity: 5},
"fec": {FEC: true},
"packet loss": {PacketLossPercent: 10},
}
for name, opus := range tests {
t.Run(name, func(t *testing.T) {
conf := Config{Opus: opus}
err := conf.Init()
require.ErrorContains(t, err, "not supported by the current media-sdk")
})
}
}
Loading
Loading