voice

package
v0.0.0-...-818fa05 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2026 License: AGPL-3.0 Imports: 29 Imported by: 0

Documentation

Overview

Package voice is the Pion-based SFU (docs/architecture.md §6, ADRs 0012 and 0023, M26): rooms of connections, each admitted by the DTLS fingerprint in the offer the backend relays, sending its tracks and receiving others' over pre-allocated slots.

Nothing here starts on its own: norite-server starts the SFU at M33, which also builds the backend's half of admission over Control. Until then the tests and voicetest's harness are its only callers.

No bearer token reaches this package (ADR 0023). What admits a connection is the backend's Provision with the client's offer, and DTLS refusing any certificate that is not the offer's fingerprint.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrFull is a bound reached: rooms, participants, or one user's connections.
	ErrFull = errors.New("voice: the SFU is at a limit")
	// ErrEvicted is a provision issued before an eviction's cutoff.
	ErrEvicted = errors.New("voice: the connection was evicted before it was provisioned")
	// ErrStale is a provision issued longer ago than the provision timeout.
	ErrStale = errors.New("voice: the provision is too old")
	// ErrDuplicate is a connection id already provisioned.
	ErrDuplicate = errors.New("voice: the connection is already provisioned")
	// ErrNotFound is a receiver with no connection in the room.
	ErrNotFound = errors.New("voice: no such connection in the room")
	// ErrClosed is an SFU that has been closed.
	ErrClosed = errors.New("voice: the SFU is closed")
)

Errors Provision answers with. M33 maps each to what the client is told.

View Source
var ErrBadOffer = errors.New("voice: the offer is not one the SFU admits")

ErrBadOffer is an offer the SFU will not answer. Its message says which rule the offer broke, in words for whoever wrote the client; it carries nothing from the offer itself.

Functions

This section is empty.

Types

type Control

type Control interface {
	// Provision admits one connection and returns the SDP answer to the client's offer. issued is a
	// snowflake the backend took when it authorized the connection; Evict compares against it.
	Provision(ctx context.Context, room, user, conn, issued snowflake.ID, offer string) (string, error)
	// Evict closes the user's connections in the room issued at or before cutoff, and refuses any
	// provision issued at or before it that arrives afterwards.
	Evict(ctx context.Context, room, user, cutoff snowflake.ID) error
	// EvictConnection closes one connection, and refuses that connection's provision if it arrives
	// afterwards. A connection already gone is not an error.
	EvictConnection(ctx context.Context, conn snowflake.ID) error
	// SetExclusion sets the users whose tracks are never forwarded to receiver, over the client's own
	// subscription; a pin does not override it. Empty until M70 decides what fills it.
	SetExclusion(ctx context.Context, room, receiver snowflake.ID, excluded []snowflake.ID) error
	// CloseRoom closes every connection in the room, and refuses any provision for the room issued at or
	// before cutoff that arrives afterwards: a deleted channel is not reopened by a join already in flight.
	CloseRoom(ctx context.Context, room, cutoff snowflake.ID) error
}

Control is the SFU's half of ADR 0023's control channel: what the backend tells the SFU. On a self-hosted instance the SFU shares the backend's process and this is a Go call; on the flagship it is carried over mTLS to SFU pods (M116). It is settled whole at M26 because changing it after that changes a wire protocol.

A room is a voice channel's id. Admission is keyed by connection, which the backend mints, so one user may hold two connections in one room: a second machine, or M105's separate video connection.

type Limits

type Limits struct {
	// Rooms is the most rooms one SFU holds, ParticipantsPerRoom the most connections in one, and
	// ConnsPerUser the most connections one user holds in one room (a second machine, M105's video).
	Rooms               int
	ParticipantsPerRoom int
	ConnsPerUser        int

	// Tracks bounds, per kind, how many tracks a connection sends and how many slots it receives on.
	// Video is zero until M105–M107 raise it.
	Tracks map[string]TrackBounds

	// MaxCandidates bounds the ICE candidates an offer carries.
	MaxCandidates int

	// ProvisionTimeout is how long a provision waits for its connection. It also bounds how long an
	// eviction's cutoff is remembered: a provision older than this is refused for its age alone.
	ProvisionTimeout time.Duration

	// PacketsPerSecond and BitsPerSecond bound what one track may send. Above either, packets are dropped
	// unforwarded and do not count towards the active speaker; StrikesToEvict seconds in a row at more
	// than twice the bound close the connection.
	PacketsPerSecond int
	BitsPerSecond    int
	StrikesToEvict   int

	// RTCPPerSecond bounds the RTCP packets one connection sends, with the same strikes.
	RTCPPerSecond int

	// MessagesPerSecond bounds data-channel messages; voiceproto.MaxMessage bounds their size.
	MessagesPerSecond int

	// ExtraChannels is how many data channels beyond the first a peer may open, each closed at once,
	// before the connection is closed.
	ExtraChannels int
}

Limits bounds everything a stranger's connection makes the SFU hold or do. Every participant sends RTP, RTCP and SCTP the SFU parses, and in public matchmaking that is anybody, so each bound is set against a hostile peer rather than a correct client, the rule M19 set for the daemon (finding 5 of M26's plan).

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits are the bounds a server starts with.

The slot bound is a ceiling on what a client may ask for, not what it gets: the client chooses its slot count, so changing a default later changes no wire constant. The forwarding benchmark is what sets it (bench_test.go).

type Options

type Options struct {
	// Conn is the one UDP socket every connection's media arrives on, through ICE's UDP mux. The SFU owns
	// it from New and closes it on Close.
	Conn net.PacketConn
	// Loopback admits loopback candidates, for tests and the harness on one machine.
	Loopback bool
	// Public are the addresses the SFU announces in place of the ones its socket is bound to: a server
	// behind a 1:1 NAT or a port-forwarding router cannot be reached at an address it can see. Empty
	// announces what is bound.
	Public []netip.Addr
	// Relays are the addresses TURN's relay sockets are bound on, one a family, admitted as remote
	// candidates whatever range they are in. Empty when there is no relay.
	Relays []netip.Addr
	// Version is the server's version, against which a data channel's hello is checked.
	Version string
	Limits  Limits
	Logger  zerolog.Logger
	// Now is the clock; nil is time.Now.
	Now func() time.Time
}

Options configures an SFU.

type SFU

type SFU struct {
	// contains filtered or unexported fields
}

SFU is the selective forwarding unit: rooms of connections, each sending its tracks and receiving others' over pre-allocated slots.

func New

func New(opts Options) (*SFU, error)

New starts an SFU on opts.Conn.

func (*SFU) Close

func (s *SFU) Close() error

Close closes every connection and the socket.

func (*SFU) CloseRoom

func (s *SFU) CloseRoom(_ context.Context, roomID, cut snowflake.ID) error

CloseRoom implements Control.

func (*SFU) Evict

func (s *SFU) Evict(_ context.Context, roomID, user, cut snowflake.ID) error

Evict implements Control.

func (*SFU) EvictConnection

func (s *SFU) EvictConnection(_ context.Context, connID snowflake.ID) error

EvictConnection implements Control.

func (*SFU) Provision

func (s *SFU) Provision(ctx context.Context, roomID, user, connID, issued snowflake.ID, offer string) (string, error)

Provision implements Control.

func (*SFU) SetExclusion

func (s *SFU) SetExclusion(_ context.Context, roomID, receiver snowflake.ID, excluded []snowflake.ID) error

SetExclusion implements Control.

type Stack

type Stack struct {
	SFU    *SFU
	TURN   *turn.Server
	Secret []byte
}

Stack is a running SFU and the TURN server that relays to it.

func StartStack

func StartStack(opts StackOptions) (_ *Stack, err error)

StartStack binds every port and starts both, or starts nothing: a port already in use is reported here, at startup, and nothing is left listening.

func (*Stack) Close

func (s *Stack) Close() error

Close stops the SFU, then TURN: what the SFU sends a relayed client as it closes the connection goes through the relay, which must still be there to carry it.

type StackOptions

type StackOptions struct {
	// MediaListen is the SFU's UDP socket; Public what it announces instead of the bound address.
	MediaListen string
	Public      []netip.Addr

	// TURNListen is TURN's UDP and TCP address. TURNTLSListen, with a certificate's two files, is TURN
	// over TLS; empty for none.
	TURNListen    string
	TURNTLSListen string
	TLSCert       string
	TLSKey        string

	// Relay is where relay sockets are bound, SFU where they reach the SFU, and RelayPorts their range.
	// OtherRelay and OtherSFU are the same in IPv6 when the SFU is reached on loopback in both families,
	// used only if the media socket listens on both and this host can bind ::1.
	Relay      netip.Addr
	SFU        netip.AddrPort
	OtherRelay netip.Addr
	OtherSFU   netip.AddrPort
	RelayPorts [2]uint16

	// RelayPerAddress is how many allocations, and how many TCP and TLS connections, one address may
	// hold on TURN, an IPv6 /64 being one; zero for TURN's default.
	RelayPerAddress int

	// SigningKey is the instance's, which TURN's secret is derived from.
	SigningKey []byte

	// Loopback admits loopback candidates, for the harness on one machine.
	Loopback bool
	Version  string
	Logger   zerolog.Logger
}

StackOptions configure the SFU and TURN together, as norite-server and the harness both start them.

type TrackBounds

type TrackBounds struct {
	Send  int
	Slots int
}

TrackBounds is one kind's bounds per connection.

Directories

Path Synopsis
Package pionlog sends Pion's logs to the server's logger, for the SFU and for TURN alike.
Package pionlog sends Pion's logs to the server's logger, for the SFU and for TURN alike.
Package rate bounds what a stranger sends, over one-second windows, for the SFU and for TURN alike.
Package rate bounds what a stranger sends, over one-second windows, for the SFU and for TURN alike.
Package turn is the embedded TURN and STUN server: a relay for clients whose network will not let them reach the SFU directly, over UDP, TCP or TLS, that reaches the SFU and nothing else.
Package turn is the embedded TURN and STUN server: a relay for clients whose network will not let them reach the SFU directly, over UDP, TCP or TLS, that reaches the SFU and nothing else.
Package voicetest is a voice client for the SFU's tests and the harness: a Pion peer connection shaped as the voice-worker's will be (one track sent, N slots received, one data channel), sending real Opus from an Ogg file and recording what it receives.
Package voicetest is a voice client for the SFU's tests and the harness: a Pion peer connection shaped as the voice-worker's will be (one track sent, N slots received, one data channel), sending real Opus from an Ogg file and recording what it receives.
cmd/voice-client command
Command voice-client is one voice connection for a manual pass: it posts its offer to a cmd/voice-sfu, connects, sends the committed tone for as long as asked, and writes what each slot received as an Ogg file a person can play.
Command voice-client is one voice connection for a manual pass: it posts its offer to a cmd/voice-sfu, connects, sends the committed tone for as long as asked, and writes what each slot received as an Ogg file a person can play.
cmd/voice-sfu command
Command voice-sfu hosts the SFU and TURN for a manual pass, and for M29 before M33 exists: the stack norite-server starts, and a plain HTTP endpoint that hands out TURN credentials and provisions a connection for whoever posts an offer.
Command voice-sfu hosts the SFU and TURN for a manual pass, and for M29 before M33 exists: the stack norite-server starts, and a plain HTTP endpoint that hands out TURN credentials and provisions a connection for whoever posts an offer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址