leg

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrRTTNotNegotiated = errors.New("RTT not negotiated for this leg")

ErrRTTNotNegotiated is returned by SendText when RTT was not agreed in the SDP offer/answer exchange (or the leg type does not support RTT).

Functions

func DiscoverPublicIPs added in v0.9.0

func DiscoverPublicIPs(stunURLs []string, log *slog.Logger) []string

DiscoverPublicIPs queries each STUN server in turn and returns the first IPv4 and IPv6 reflexive addresses learned. The result is suitable for PCMediaConfig.ExternalIPs (and pion's SetNAT1To1IPs). Returns nil when no STUN URL is reachable.

Each lookup is bounded by stunDiscoverTimeout. Failures are logged at warn level and the next server is tried; if every server fails the caller should fall back to gathering host candidates as-is.

Types

type Leg

type Leg interface {
	ID() string
	Type() LegType
	State() LegState
	SampleRate() int
	AudioReader() io.Reader
	AudioWriter() io.Writer
	OnDTMF(func(digit rune))
	SendDTMF(ctx context.Context, digits string) error
	AcceptDTMF() bool
	SetAcceptDTMF(accept bool)
	OnTextReceived(func(text string, lossMarker bool))
	SendText(ctx context.Context, text string) error
	AcceptText() bool
	SetAcceptText(accept bool)
	RTTNegotiated() bool
	Hangup(ctx context.Context) error
	Answer(ctx context.Context) error
	Context() context.Context
	RoomID() string
	SetRoomID(id string)
	AppID() string
	SetAppID(id string)
	IsMuted() bool
	SetMuted(muted bool)
	IsDeaf() bool
	SetDeaf(deaf bool)
	// Role returns the routing role used by the room's audio routing matrix.
	// Empty string means unroled (full mesh).
	Role() string
	SetRole(role string)
	SetSpeakingTap(w io.Writer)
	ClearSpeakingTap()
	IsHeld() bool
	CreatedAt() time.Time
	AnsweredAt() time.Time
	SIPHeaders() map[string]string

	// Headers returns custom protocol headers exposed by the leg's
	// transport (X-/P- headers from a SIP INVITE or from a WebSocket HTTP
	// upgrade, caller-supplied headers on an outbound WebSocket dial,
	// etc.). Returns nil for leg types with no header concept.
	Headers() map[string]string
	RTPStats() RTPStats

	// ClaimDisconnect returns true exactly once per leg. Termination paths
	// (API hangup, remote BYE, RTP timeout, session expiry, etc.) call this
	// before publishing leg.disconnected so concurrent paths cannot emit
	// duplicate events.
	ClaimDisconnect() bool
}

type LegState

type LegState string
const (
	StateRinging    LegState = "ringing"
	StateEarlyMedia LegState = "early_media"
	StateConnected  LegState = "connected"
	StateHeld       LegState = "held"
	StateHungUp     LegState = "hung_up"
)

type LegType

type LegType string
const (
	TypeSIPInbound         LegType = "sip_inbound"
	TypeSIPOutbound        LegType = "sip_outbound"
	TypeWebRTC             LegType = "webrtc"
	TypeWhatsAppInbound    LegType = "whatsapp_in"
	TypeWhatsAppOutbound   LegType = "whatsapp_out"
	TypeWebSocketInbound   LegType = "websocket_in"
	TypeWebSocketOutbound  LegType = "websocket_out"
	TypeMoQInbound         LegType = "moq_in"
	TypeLiveKitPublish     LegType = "livekit_publish"
	TypeLiveKitParticipant LegType = "livekit_participant"
)

type LiveKitParticipantLeg added in v0.8.0

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

LiveKitParticipantLeg represents a single remote LiveKit participant — auto-created when the umbrella LiveKitPublishLeg's transport receives an inbound audio track for that participant. From VoiceBlender's room perspective the participant is a normal leg whose AudioReader is the decoded PCM of the LK participant's published audio. The AudioWriter is io.Discard — the LK side already mixes outbound audio for the participant, so we have nothing meaningful to send back to them at the leg level.

State machine: created in StateConnected. No ringing, no early-media. DTMF and RTT are not negotiated over LiveKit, so SendDTMF/SendText return errors. Hangup tears down the leg locally; the API layer is responsible for asking the LK server to unsubscribe via the umbrella transport (so we stop receiving the participant's RTP).

func NewLiveKitParticipantLeg added in v0.8.0

func NewLiveKitParticipantLeg(identity, trackSID string, pcmReader io.Reader, sampleRate int, log *slog.Logger) *LiveKitParticipantLeg

NewLiveKitParticipantLeg builds a participant leg already bound to its decoded-PCM source. The reader is owned by the caller (typically the umbrella transport's per-track decoder goroutine); the leg only reads from it.

`identity` is the LK participant identity from JoinResponse / ParticipantUpdate. `trackSID` is the LK track ID of the audio track. Both are surfaced via Headers().

The leg starts in StateConnected; the API layer is responsible for adding it to the LegMgr, emitting `leg.connected`, and adding it to the VoiceBlender room.

func (*LiveKitParticipantLeg) AcceptDTMF added in v0.8.0

func (l *LiveKitParticipantLeg) AcceptDTMF() bool

func (*LiveKitParticipantLeg) AcceptText added in v0.8.0

func (l *LiveKitParticipantLeg) AcceptText() bool

func (*LiveKitParticipantLeg) Answer added in v0.8.0

Answer is a no-op for LK participant legs — connection completes when the leg is constructed.

func (*LiveKitParticipantLeg) AnsweredAt added in v0.8.0

func (l *LiveKitParticipantLeg) AnsweredAt() time.Time

func (*LiveKitParticipantLeg) AppID added in v0.8.0

func (l *LiveKitParticipantLeg) AppID() string

func (*LiveKitParticipantLeg) AudioReader added in v0.8.0

func (l *LiveKitParticipantLeg) AudioReader() io.Reader

AudioReader yields the decoded PCM of the LK participant's audio track. Driven by the umbrella transport's per-track decoder goroutine.

func (*LiveKitParticipantLeg) AudioWriter added in v0.8.0

func (l *LiveKitParticipantLeg) AudioWriter() io.Writer

AudioWriter is io.Discard: the LK side already mixes outbound audio for this participant, so anything VB tried to write would not reach them anyway. Reads from the VB room mixer's mixed-minus-self output land here and are dropped.

func (*LiveKitParticipantLeg) ClaimDisconnect added in v0.8.0

func (l *LiveKitParticipantLeg) ClaimDisconnect() bool

func (*LiveKitParticipantLeg) ClearSpeakingTap added in v0.8.0

func (l *LiveKitParticipantLeg) ClearSpeakingTap()

func (*LiveKitParticipantLeg) Context added in v0.8.0

func (l *LiveKitParticipantLeg) Context() context.Context

func (*LiveKitParticipantLeg) CreatedAt added in v0.8.0

func (l *LiveKitParticipantLeg) CreatedAt() time.Time

func (*LiveKitParticipantLeg) Hangup added in v0.8.0

Hangup transitions to StateHungUp and cancels the leg's context. The API layer is responsible for telling the umbrella transport to drop the LK subscription (so the server stops sending us this participant's RTP). Idempotent.

func (*LiveKitParticipantLeg) Headers added in v0.8.0

func (l *LiveKitParticipantLeg) Headers() map[string]string

func (*LiveKitParticipantLeg) ID added in v0.8.0

func (l *LiveKitParticipantLeg) ID() string

func (*LiveKitParticipantLeg) Identity added in v0.8.0

func (l *LiveKitParticipantLeg) Identity() string

Identity returns the LK participant identity from JoinResponse.

func (*LiveKitParticipantLeg) IsDeaf added in v0.8.0

func (l *LiveKitParticipantLeg) IsDeaf() bool

func (*LiveKitParticipantLeg) IsHeld added in v0.8.0

func (l *LiveKitParticipantLeg) IsHeld() bool

func (*LiveKitParticipantLeg) IsMuted added in v0.8.0

func (l *LiveKitParticipantLeg) IsMuted() bool

func (*LiveKitParticipantLeg) OnDTMF added in v0.8.0

func (l *LiveKitParticipantLeg) OnDTMF(_ func(rune))

func (*LiveKitParticipantLeg) OnTextReceived added in v0.8.0

func (l *LiveKitParticipantLeg) OnTextReceived(_ func(text string, lossMarker bool))

func (*LiveKitParticipantLeg) RTPStats added in v0.8.0

func (l *LiveKitParticipantLeg) RTPStats() RTPStats

func (*LiveKitParticipantLeg) RTTNegotiated added in v0.8.0

func (l *LiveKitParticipantLeg) RTTNegotiated() bool

func (*LiveKitParticipantLeg) Role added in v0.8.0

func (l *LiveKitParticipantLeg) Role() string

func (*LiveKitParticipantLeg) RoomID added in v0.8.0

func (l *LiveKitParticipantLeg) RoomID() string

func (*LiveKitParticipantLeg) SIPHeaders added in v0.8.0

func (l *LiveKitParticipantLeg) SIPHeaders() map[string]string

func (*LiveKitParticipantLeg) SampleRate added in v0.8.0

func (l *LiveKitParticipantLeg) SampleRate() int

func (*LiveKitParticipantLeg) SendDTMF added in v0.8.0

func (l *LiveKitParticipantLeg) SendDTMF(_ context.Context, _ string) error

func (*LiveKitParticipantLeg) SendText added in v0.8.0

func (l *LiveKitParticipantLeg) SendText(_ context.Context, _ string) error

func (*LiveKitParticipantLeg) SetAcceptDTMF added in v0.8.0

func (l *LiveKitParticipantLeg) SetAcceptDTMF(a bool)

func (*LiveKitParticipantLeg) SetAcceptText added in v0.8.0

func (l *LiveKitParticipantLeg) SetAcceptText(a bool)

func (*LiveKitParticipantLeg) SetAppID added in v0.8.0

func (l *LiveKitParticipantLeg) SetAppID(id string)

func (*LiveKitParticipantLeg) SetDeaf added in v0.8.0

func (l *LiveKitParticipantLeg) SetDeaf(d bool)

func (*LiveKitParticipantLeg) SetMuted added in v0.8.0

func (l *LiveKitParticipantLeg) SetMuted(m bool)

func (*LiveKitParticipantLeg) SetRole added in v0.8.0

func (l *LiveKitParticipantLeg) SetRole(r string)

func (*LiveKitParticipantLeg) SetRoomID added in v0.8.0

func (l *LiveKitParticipantLeg) SetRoomID(id string)

func (*LiveKitParticipantLeg) SetSpeakingTap added in v0.8.0

func (l *LiveKitParticipantLeg) SetSpeakingTap(io.Writer)

func (*LiveKitParticipantLeg) State added in v0.8.0

func (l *LiveKitParticipantLeg) State() LegState

func (*LiveKitParticipantLeg) TrackSID added in v0.8.0

func (l *LiveKitParticipantLeg) TrackSID() string

TrackSID returns the LK audio track ID this leg is bound to.

func (*LiveKitParticipantLeg) Type added in v0.8.0

func (l *LiveKitParticipantLeg) Type() LegType

type LiveKitPublishLeg added in v0.8.0

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

LiveKitPublishLeg represents the outbound (publish) audio direction of a LiveKit-room connection. One per umbrella `POST /v1/legs type=livekit_room` call; it owns the lkmedia.Transport (signaling + publisher PC + subscriber PC).

Audio model: the publish leg's AudioWriter is fed by the VB room mixer with mixed-minus-self PCM; that PCM is Opus-encoded and pushed onto the local LiveKit track. The leg has no upstream audio of its own — AudioReader returns emptyReader{}. Remote LK participants surface as separate LiveKitParticipantLeg entries in the same VB room (auto-managed by the API layer).

State machine: created in StateConnected (LK signaling already completed during the transport's Connect). No ringing, no early-media. DTMF and RTT are not negotiated over LiveKit (browser SDKs don't carry them), so SendDTMF/SendText return errors.

func NewLiveKitPublishLeg added in v0.8.0

func NewLiveKitPublishLeg(t *lkmedia.Transport, headers map[string]string, sampleRate int, log *slog.Logger) *LiveKitPublishLeg

NewLiveKitPublishLeg constructs the umbrella publish leg already bound to a connected transport. The leg is StateConnected immediately — the LK signaling JOIN completed during Connect, so there is no ringing phase to wait on.

The headers map is expected to carry observability fields surfaced via Leg.Headers (livekit_identity, livekit_room). The leg merges these with the transport's identity/room values so callers can pass nil or partial maps.

func (*LiveKitPublishLeg) AcceptDTMF added in v0.8.0

func (l *LiveKitPublishLeg) AcceptDTMF() bool

func (*LiveKitPublishLeg) AcceptText added in v0.8.0

func (l *LiveKitPublishLeg) AcceptText() bool

func (*LiveKitPublishLeg) Answer added in v0.8.0

func (l *LiveKitPublishLeg) Answer(_ context.Context) error

Answer is a no-op for LK legs — connection completes during NewTransport.

func (*LiveKitPublishLeg) AnsweredAt added in v0.8.0

func (l *LiveKitPublishLeg) AnsweredAt() time.Time

func (*LiveKitPublishLeg) AppID added in v0.8.0

func (l *LiveKitPublishLeg) AppID() string

func (*LiveKitPublishLeg) AudioReader added in v0.8.0

func (l *LiveKitPublishLeg) AudioReader() io.Reader

AudioReader returns emptyReader{} — the publish leg has no upstream audio of its own. Remote LK participants' audio arrives via separate LiveKitParticipantLeg entries managed by the API layer.

func (*LiveKitPublishLeg) AudioWriter added in v0.8.0

func (l *LiveKitPublishLeg) AudioWriter() io.Writer

func (*LiveKitPublishLeg) ClaimDisconnect added in v0.8.0

func (l *LiveKitPublishLeg) ClaimDisconnect() bool

func (*LiveKitPublishLeg) ClearSpeakingTap added in v0.8.0

func (l *LiveKitPublishLeg) ClearSpeakingTap()

func (*LiveKitPublishLeg) Context added in v0.8.0

func (l *LiveKitPublishLeg) Context() context.Context

func (*LiveKitPublishLeg) CreatedAt added in v0.8.0

func (l *LiveKitPublishLeg) CreatedAt() time.Time

func (*LiveKitPublishLeg) Hangup added in v0.8.0

func (l *LiveKitPublishLeg) Hangup(_ context.Context) error

Hangup tears down the transport (sends Leave + closes peer connections). Idempotent.

func (*LiveKitPublishLeg) Headers added in v0.8.0

func (l *LiveKitPublishLeg) Headers() map[string]string

func (*LiveKitPublishLeg) ID added in v0.8.0

func (l *LiveKitPublishLeg) ID() string

func (*LiveKitPublishLeg) IsDeaf added in v0.8.0

func (l *LiveKitPublishLeg) IsDeaf() bool

func (*LiveKitPublishLeg) IsHeld added in v0.8.0

func (l *LiveKitPublishLeg) IsHeld() bool

func (*LiveKitPublishLeg) IsMuted added in v0.8.0

func (l *LiveKitPublishLeg) IsMuted() bool

func (*LiveKitPublishLeg) OnDTMF added in v0.8.0

func (l *LiveKitPublishLeg) OnDTMF(_ func(rune))

func (*LiveKitPublishLeg) OnTextReceived added in v0.8.0

func (l *LiveKitPublishLeg) OnTextReceived(_ func(text string, lossMarker bool))

func (*LiveKitPublishLeg) RTPStats added in v0.8.0

func (l *LiveKitPublishLeg) RTPStats() RTPStats

func (*LiveKitPublishLeg) RTTNegotiated added in v0.8.0

func (l *LiveKitPublishLeg) RTTNegotiated() bool

func (*LiveKitPublishLeg) Role added in v0.8.0

func (l *LiveKitPublishLeg) Role() string

func (*LiveKitPublishLeg) RoomID added in v0.8.0

func (l *LiveKitPublishLeg) RoomID() string

func (*LiveKitPublishLeg) SIPHeaders added in v0.8.0

func (l *LiveKitPublishLeg) SIPHeaders() map[string]string

func (*LiveKitPublishLeg) SampleRate added in v0.8.0

func (l *LiveKitPublishLeg) SampleRate() int

func (*LiveKitPublishLeg) SendDTMF added in v0.8.0

func (l *LiveKitPublishLeg) SendDTMF(_ context.Context, _ string) error

func (*LiveKitPublishLeg) SendText added in v0.8.0

func (l *LiveKitPublishLeg) SendText(_ context.Context, _ string) error

func (*LiveKitPublishLeg) SetAcceptDTMF added in v0.8.0

func (l *LiveKitPublishLeg) SetAcceptDTMF(a bool)

func (*LiveKitPublishLeg) SetAcceptText added in v0.8.0

func (l *LiveKitPublishLeg) SetAcceptText(a bool)

func (*LiveKitPublishLeg) SetAppID added in v0.8.0

func (l *LiveKitPublishLeg) SetAppID(id string)

func (*LiveKitPublishLeg) SetDeaf added in v0.8.0

func (l *LiveKitPublishLeg) SetDeaf(d bool)

func (*LiveKitPublishLeg) SetMuted added in v0.8.0

func (l *LiveKitPublishLeg) SetMuted(m bool)

func (*LiveKitPublishLeg) SetRole added in v0.8.0

func (l *LiveKitPublishLeg) SetRole(r string)

func (*LiveKitPublishLeg) SetRoomID added in v0.8.0

func (l *LiveKitPublishLeg) SetRoomID(id string)

func (*LiveKitPublishLeg) SetSpeakingTap added in v0.8.0

func (l *LiveKitPublishLeg) SetSpeakingTap(io.Writer)

func (*LiveKitPublishLeg) State added in v0.8.0

func (l *LiveKitPublishLeg) State() LegState

func (*LiveKitPublishLeg) Transport added in v0.8.0

func (l *LiveKitPublishLeg) Transport() *lkmedia.Transport

Transport returns the underlying lkmedia.Transport. Used by the API handler to wait on Done() and surface CloseReason on disconnect.

func (*LiveKitPublishLeg) Type added in v0.8.0

func (l *LiveKitPublishLeg) Type() LegType

type Manager

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

func NewManager

func NewManager() *Manager

func (*Manager) Add

func (m *Manager) Add(l Leg)

func (*Manager) All

func (m *Manager) All() map[string]Leg

func (*Manager) FindSIPByCallID added in v0.3.0

func (m *Manager) FindSIPByCallID(callID string) *SIPLeg

FindSIPByCallID returns the SIPLeg whose dialog Call-ID matches the given value, or nil if none. Used to route in-dialog requests (e.g. REFER, NOTIFY) back to the leg that owns the dialog.

func (*Manager) Get

func (m *Manager) Get(id string) (Leg, bool)

func (*Manager) List

func (m *Manager) List() []Leg

func (*Manager) Remove

func (m *Manager) Remove(id string) (Leg, bool)

Remove deletes the leg from the manager and returns it together with a boolean indicating whether this call performed the removal. The boolean is the single-flight signal: termination paths use it to ensure only one of several racing callers (API DELETE × N, remote BYE, RTP timeout, session expiry) actually drives leg shutdown. Callers that get false should treat the leg as already being torn down by someone else.

type MoQLeg added in v0.6.0

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

MoQLeg wraps a moqmedia.Transport as a Leg. PoC scope: inbound only, straight to StateConnected, no DTMF, no RTT/text, no mute parity beyond the atomic bit. Mute/deaf are honored by the room's mixer adapter because the leg exposes IsMuted/IsDeaf.

func NewMoQInboundLeg added in v0.6.0

func NewMoQInboundLeg(t *moqmedia.Transport, headers map[string]string, sampleRate int, log *slog.Logger) *MoQLeg

NewMoQInboundLeg constructs an inbound MoQ leg already bound to a transport and starts the transport's send loop reading from the egress pipe. The leg goes straight to StateConnected (no ringing flow).

func (*MoQLeg) AcceptDTMF added in v0.6.0

func (l *MoQLeg) AcceptDTMF() bool

func (*MoQLeg) AcceptText added in v0.6.0

func (l *MoQLeg) AcceptText() bool

func (*MoQLeg) Answer added in v0.6.0

func (l *MoQLeg) Answer(_ context.Context) error

func (*MoQLeg) AnsweredAt added in v0.6.0

func (l *MoQLeg) AnsweredAt() time.Time

func (*MoQLeg) AppID added in v0.6.0

func (l *MoQLeg) AppID() string

func (*MoQLeg) AudioReader added in v0.6.0

func (l *MoQLeg) AudioReader() io.Reader

func (*MoQLeg) AudioWriter added in v0.6.0

func (l *MoQLeg) AudioWriter() io.Writer

func (*MoQLeg) ClaimDisconnect added in v0.6.0

func (l *MoQLeg) ClaimDisconnect() bool

func (*MoQLeg) ClearSpeakingTap added in v0.6.0

func (l *MoQLeg) ClearSpeakingTap()

func (*MoQLeg) Context added in v0.6.0

func (l *MoQLeg) Context() context.Context

func (*MoQLeg) CreatedAt added in v0.6.0

func (l *MoQLeg) CreatedAt() time.Time

func (*MoQLeg) Hangup added in v0.6.0

func (l *MoQLeg) Hangup(_ context.Context) error

func (*MoQLeg) Headers added in v0.6.0

func (l *MoQLeg) Headers() map[string]string

func (*MoQLeg) ID added in v0.6.0

func (l *MoQLeg) ID() string

func (*MoQLeg) IsDeaf added in v0.6.0

func (l *MoQLeg) IsDeaf() bool

func (*MoQLeg) IsHeld added in v0.6.0

func (l *MoQLeg) IsHeld() bool

func (*MoQLeg) IsMuted added in v0.6.0

func (l *MoQLeg) IsMuted() bool

func (*MoQLeg) OnDTMF added in v0.6.0

func (l *MoQLeg) OnDTMF(_ func(rune))

func (*MoQLeg) OnTextReceived added in v0.6.0

func (l *MoQLeg) OnTextReceived(_ func(text string, lossMarker bool))

func (*MoQLeg) RTPStats added in v0.6.0

func (l *MoQLeg) RTPStats() RTPStats

func (*MoQLeg) RTTNegotiated added in v0.6.0

func (l *MoQLeg) RTTNegotiated() bool

func (*MoQLeg) Role added in v0.7.0

func (l *MoQLeg) Role() string

func (*MoQLeg) RoomID added in v0.6.0

func (l *MoQLeg) RoomID() string

func (*MoQLeg) SIPHeaders added in v0.6.0

func (l *MoQLeg) SIPHeaders() map[string]string

func (*MoQLeg) SampleRate added in v0.6.0

func (l *MoQLeg) SampleRate() int

func (*MoQLeg) SendDTMF added in v0.6.0

func (l *MoQLeg) SendDTMF(_ context.Context, _ string) error

func (*MoQLeg) SendText added in v0.6.0

func (l *MoQLeg) SendText(_ context.Context, _ string) error

func (*MoQLeg) SetAcceptDTMF added in v0.6.0

func (l *MoQLeg) SetAcceptDTMF(a bool)

func (*MoQLeg) SetAcceptText added in v0.6.0

func (l *MoQLeg) SetAcceptText(a bool)

func (*MoQLeg) SetAppID added in v0.6.0

func (l *MoQLeg) SetAppID(id string)

func (*MoQLeg) SetDeaf added in v0.6.0

func (l *MoQLeg) SetDeaf(d bool)

func (*MoQLeg) SetMuted added in v0.6.0

func (l *MoQLeg) SetMuted(m bool)

func (*MoQLeg) SetRole added in v0.7.0

func (l *MoQLeg) SetRole(r string)

func (*MoQLeg) SetRoomID added in v0.6.0

func (l *MoQLeg) SetRoomID(id string)

func (*MoQLeg) SetSpeakingTap added in v0.6.0

func (l *MoQLeg) SetSpeakingTap(io.Writer)

func (*MoQLeg) State added in v0.6.0

func (l *MoQLeg) State() LegState

func (*MoQLeg) Transport added in v0.6.0

func (l *MoQLeg) Transport() *moqmedia.Transport

Transport returns the underlying moqmedia.Transport.

func (*MoQLeg) Type added in v0.6.0

func (l *MoQLeg) Type() LegType

type PCMedia added in v0.5.0

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

PCMedia wraps a pion PeerConnection and exposes PCM16 io.Reader/io.Writer at the codec's native sample rate. Inbound RTP is decoded to PCM on a per-packet goroutine; outbound PCM is chunked into 20 ms frames, encoded, and written to the local RTP track.

func NewPCMedia added in v0.5.0

func NewPCMedia(cfg PCMediaConfig) (*PCMedia, error)

func (*PCMedia) AddICECandidate added in v0.5.0

func (m *PCMedia) AddICECandidate(c webrtc.ICECandidateInit) error

func (*PCMedia) AudioReader added in v0.5.0

func (m *PCMedia) AudioReader() io.Reader

AudioReader yields decoded PCM (16-bit LE) at the codec's native rate.

func (*PCMedia) AudioWriter added in v0.5.0

func (m *PCMedia) AudioWriter() io.Writer

AudioWriter accepts PCM (16-bit LE) at the codec's native rate.

func (*PCMedia) ClearSpeakingTap added in v0.5.0

func (m *PCMedia) ClearSpeakingTap()

ClearSpeakingTap removes the installed tap.

func (*PCMedia) Close added in v0.5.0

func (m *PCMedia) Close() error

func (*PCMedia) Codec added in v0.5.0

func (m *PCMedia) Codec() codec.CodecType

func (*PCMedia) Context added in v0.5.0

func (m *PCMedia) Context() context.Context

func (*PCMedia) DrainLocalCandidates added in v0.5.0

func (m *PCMedia) DrainLocalCandidates() ([]webrtc.ICECandidateInit, bool)

DrainLocalCandidates returns buffered local ICE candidates and the gathering-complete flag, clearing the buffer.

func (*PCMedia) PC added in v0.5.0

func (m *PCMedia) PC() *webrtc.PeerConnection

func (*PCMedia) SampleRate added in v0.5.0

func (m *PCMedia) SampleRate() int

func (*PCMedia) SetOnDTMF added in v0.5.0

func (m *PCMedia) SetOnDTMF(fn func(digit rune))

func (*PCMedia) SetSpeakingTap added in v0.5.0

func (m *PCMedia) SetSpeakingTap(w io.Writer)

func (*PCMedia) Start added in v0.5.0

func (m *PCMedia) Start()

Start begins the outbound write loop. Idempotent.

type PCMediaConfig added in v0.5.0

type PCMediaConfig struct {
	Codec      codec.CodecType
	ICEServers []string
	RTPPortMin uint16
	RTPPortMax uint16
	Log        *slog.Logger

	// ExternalIPs are public addresses substituted into local host ICE
	// candidates via pion's SetNAT1To1IPs. Required when VB runs behind
	// NAT/Docker and the gathered host interface IPs aren't routable
	// from the remote peer.
	ExternalIPs []string

	OnDisconnect func(reason string)
	// OnConnected fires once when the peer connection reaches the
	// Connected state. Subsequent state transitions don't re-fire.
	OnConnected func()

	// AnsweringDTLSRole forces the DTLS role on actpass offers. Use
	// DTLSRoleClient against ice-lite peers (e.g. WhatsApp).
	AnsweringDTLSRole webrtc.DTLSRole

	// EnableTelephoneEvent advertises RFC 4733 DTMF (PT 126) in the
	// answer and routes inbound telephone-event packets to OnDTMF.
	EnableTelephoneEvent bool
}

PCMediaConfig configures a pion PeerConnection + audio pipeline shared by WebRTC and WhatsApp legs. The peer connection is created by NewPCMedia; the caller drives SDP negotiation via PC().

type RTPStats

type RTPStats struct {
	PacketsReceived uint32
	PacketsLost     uint32
	JitterMs        float64
	MOSScore        float64 // 1.0–5.0; 0 if insufficient data (< 2 packets)
}

RTPStats holds inbound RTP stream quality metrics.

type SIPLeg

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

SIPLeg wraps a SIP dialog (inbound or outbound) with RTP media handling.

func NewSIPInboundLeg

func NewSIPInboundLeg(call *sipmod.InboundCall, engine *sipmod.Engine, log *slog.Logger) *SIPLeg

func NewSIPOutboundLeg

func NewSIPOutboundLeg(call *sipmod.OutboundCall, engine *sipmod.Engine, log *slog.Logger) *SIPLeg

func NewSIPOutboundPendingLeg

func NewSIPOutboundPendingLeg(engine *sipmod.Engine, codecs []codec.CodecType, log *slog.Logger) *SIPLeg

NewSIPOutboundPendingLeg creates an outbound leg in ringing state with its own context. Call ConnectOutbound after the INVITE succeeds. If codecs is non-empty it overrides the engine's default codec list.

func (*SIPLeg) AcceptDTMF added in v0.3.0

func (l *SIPLeg) AcceptDTMF() bool

func (*SIPLeg) AcceptText added in v0.6.0

func (l *SIPLeg) AcceptText() bool

AcceptText reports whether this leg currently accepts inbound RTT events.

func (*SIPLeg) Answer

func (l *SIPLeg) Answer(ctx context.Context) error

Answer sends 200 OK to the inbound INVITE. Codec selection respects any hint set via SignalAnswer; a CodecUnknown hint falls back to the default offer-order preference. The hint is ignored when the leg is already in StateEarlyMedia (the codec was locked in at 183).

func (*SIPLeg) AnswerCh

func (l *SIPLeg) AnswerCh() <-chan struct{}

AnswerCh returns the channel that is closed when the REST answer endpoint is called.

func (*SIPLeg) AnsweredAt

func (l *SIPLeg) AnsweredAt() time.Time

func (*SIPLeg) AppID added in v0.4.0

func (l *SIPLeg) AppID() string

func (*SIPLeg) AudioReader

func (l *SIPLeg) AudioReader() io.Reader

func (*SIPLeg) AudioWriter

func (l *SIPLeg) AudioWriter() io.Writer

func (*SIPLeg) CallID

func (l *SIPLeg) CallID() string

CallID returns the SIP Call-ID for this leg's dialog.

func (*SIPLeg) ClaimDisconnect added in v0.6.0

func (l *SIPLeg) ClaimDisconnect() bool

ClaimDisconnect returns true on the first caller and false on every subsequent caller. Termination paths use this gate so only one publishes leg.disconnected, even when DELETE racing with remote BYE or RTP timeout.

func (*SIPLeg) ClearAMDTap added in v0.2.0

func (l *SIPLeg) ClearAMDTap()

ClearAMDTap removes the AMD tap.

func (*SIPLeg) ClearInTap

func (l *SIPLeg) ClearInTap()

ClearInTap removes the incoming tap.

func (*SIPLeg) ClearOutTap

func (l *SIPLeg) ClearOutTap()

ClearOutTap removes the outgoing tap.

func (*SIPLeg) ClearSpeakingTap added in v0.2.0

func (l *SIPLeg) ClearSpeakingTap()

ClearSpeakingTap removes the speaking detection tap.

func (*SIPLeg) ConnectOutbound

func (l *SIPLeg) ConnectOutbound(call *sipmod.OutboundCall) error

ConnectOutbound sets the answered outbound call on the leg, negotiates the codec, starts media, and transitions to connected.

func (*SIPLeg) Context

func (l *SIPLeg) Context() context.Context

func (*SIPLeg) CreatedAt

func (l *SIPLeg) CreatedAt() time.Time

func (*SIPLeg) DialogIdentity added in v0.3.0

func (l *SIPLeg) DialogIdentity() (callID, localTag, remoteTag string, ok bool)

DialogIdentity returns (callID, localTag, remoteTag) from this leg's view.

func (*SIPLeg) DisconnectReason added in v0.6.0

func (l *SIPLeg) DisconnectReason() string

DisconnectReason returns the override set by SetDisconnectReason, or "" if none. Consumed by the inbound-call goroutine when publishing leg.disconnected.

func (*SIPLeg) EnableEarlyMedia

func (l *SIPLeg) EnableEarlyMedia(ctx context.Context, preferred codec.CodecType) error

EnableEarlyMedia sends 183 Session Progress with SDP and sets up the media pipeline so audio can flow before the call is answered. Only valid for inbound legs in StateRinging.

preferred biases codec selection: when non-zero and present in both the remote offer and the supported list, it wins; otherwise selection falls back to the default offer-order preference.

func (*SIPLeg) HandleRemoteHold

func (l *SIPLeg) HandleRemoteHold(direction string)

HandleRemoteHold processes a remote re-INVITE's SDP direction for hold/unhold.

func (*SIPLeg) Hangup

func (l *SIPLeg) Hangup(ctx context.Context) error

Hangup transitions the leg to StateHungUp and (exactly once) sends a BYE on the underlying SIP dialog. State transition, context cancel, RTP-session close, and timer cleanup happen on every call (idempotent operations); the SIP BYE is gated by sync.Once so concurrent termination paths cannot emit duplicate BYEs.

func (*SIPLeg) Headers added in v0.6.0

func (l *SIPLeg) Headers() map[string]string

func (*SIPLeg) Hold

func (l *SIPLeg) Hold(ctx context.Context) error

Hold initiates hold by sending a re-INVITE with sendonly SDP.

func (*SIPLeg) ID

func (l *SIPLeg) ID() string

func (*SIPLeg) IsDeaf

func (l *SIPLeg) IsDeaf() bool

func (*SIPLeg) IsHeld

func (l *SIPLeg) IsHeld() bool

IsHeld returns true if the call is currently on hold.

func (*SIPLeg) IsMuted

func (l *SIPLeg) IsMuted() bool

func (*SIPLeg) JitterBufferMs added in v0.3.0

func (l *SIPLeg) JitterBufferMs() int

JitterBufferMs returns the configured target delay in milliseconds (0 = disabled).

func (*SIPLeg) OnDTMF

func (l *SIPLeg) OnDTMF(f func(digit rune))

func (*SIPLeg) OnHold

func (l *SIPLeg) OnHold(f func())

OnHold registers a callback for when the leg is put on hold.

func (*SIPLeg) OnRTPTimeout

func (l *SIPLeg) OnRTPTimeout(f func())

OnRTPTimeout sets a callback that fires when no RTP packets are received within the timeout period (30s). Called at most once.

func (*SIPLeg) OnSessionExpired

func (l *SIPLeg) OnSessionExpired(f func())

OnSessionExpired registers a callback for when the session timer expires without a refresh. Typically used to hang up the call.

func (*SIPLeg) OnTextReceived added in v0.6.0

func (l *SIPLeg) OnTextReceived(f func(text string, lossMarker bool))

OnTextReceived registers a callback for inbound T.140 text. The callback runs on the dispatcher goroutine so the caller can do moderately expensive work (e.g. publish webhook events) without blocking the RTP read loop.

func (*SIPLeg) OnUnhold

func (l *SIPLeg) OnUnhold(f func())

OnUnhold registers a callback for when the leg is taken off hold.

func (*SIPLeg) RTPStats

func (l *SIPLeg) RTPStats() RTPStats

RTPStats returns inbound stream quality metrics and a MOS estimate.

func (*SIPLeg) RTTNegotiated added in v0.6.0

func (l *SIPLeg) RTTNegotiated() bool

RTTNegotiated reports whether SDP agreed on an m=text section for this leg.

func (*SIPLeg) ReInviteAnswerSDP

func (l *SIPLeg) ReInviteAnswerSDP(remoteDirection string) []byte

ReInviteAnswerSDP builds an SDP body for responding to a remote re-INVITE. The direction is mirrored: if the remote sent "sendonly" (hold), we respond with "recvonly"; if "sendrecv" we respond with "sendrecv".

func (*SIPLeg) Reject added in v0.6.0

func (l *SIPLeg) Reject(ctx context.Context, statusCode int, reasonPhrase string) error

Reject sends a final non-2xx response on an unanswered inbound leg, terminating the dialog without ever creating a session. statusCode is the SIP status code (e.g. 486, 603); reasonPhrase is the SIP reason phrase shown after the status code on the response line.

Only valid on inbound legs in StateRinging or StateEarlyMedia. After Reject succeeds the dialog context is cancelled by sipgo, so the inbound-call goroutine wakes up and publishes leg.disconnected — if the caller wants the disconnect event to carry a specific reason, it should call SetDisconnectReason first.

func (*SIPLeg) RemoteOfferCodecs added in v0.6.0

func (l *SIPLeg) RemoteOfferCodecs() []codec.CodecType

RemoteOfferCodecs returns the codecs offered by the remote in the inbound INVITE SDP, in offer order. Returns nil for outbound legs or when no offer has been parsed yet.

func (*SIPLeg) ResetSessionTimer

func (l *SIPLeg) ResetSessionTimer()

ResetSessionTimer resets the session timer, called when a refresh re-INVITE is received from the remote UA.

func (*SIPLeg) Role added in v0.7.0

func (l *SIPLeg) Role() string

func (*SIPLeg) RoomID

func (l *SIPLeg) RoomID() string

func (*SIPLeg) SIPHeaders

func (l *SIPLeg) SIPHeaders() map[string]string

func (*SIPLeg) SampleRate

func (l *SIPLeg) SampleRate() int

func (*SIPLeg) SendDTMF

func (l *SIPLeg) SendDTMF(ctx context.Context, digits string) error

func (*SIPLeg) SendNotifySipfrag added in v0.3.0

func (l *SIPLeg) SendNotifySipfrag(ctx context.Context, statusCode int, reason string, terminated bool) error

SendNotifySipfrag emits a NOTIFY sipfrag for the implicit refer subscription.

func (*SIPLeg) SendRinging added in v0.6.0

func (l *SIPLeg) SendRinging(ctx context.Context) error

SendRinging sends a 180 Ringing provisional response with no SDP. Only valid for inbound legs in StateRinging. May be called multiple times — each call emits another 180 (RFC-allowed; receivers tolerate re-sends).

func (*SIPLeg) SendText added in v0.6.0

func (l *SIPLeg) SendText(ctx context.Context, text string) error

SendText queues UTF-8 text for transmission as RFC 4103 RTP. Returns ErrRTTNotNegotiated when the SDP exchange did not agree on m=text.

func (*SIPLeg) SessionTimerParams

func (l *SIPLeg) SessionTimerParams() (interval uint32, refresher string)

SessionTimerParams returns the negotiated session timer interval and refresher. Returns (0, "") if session timers are not active.

func (*SIPLeg) SetAMDTap added in v0.2.0

func (l *SIPLeg) SetAMDTap(w io.Writer)

SetAMDTap sets a writer that receives decoded incoming PCM for answering machine detection. Separate from inTap so AMD and recording can coexist.

func (*SIPLeg) SetAcceptDTMF added in v0.3.0

func (l *SIPLeg) SetAcceptDTMF(accept bool)

func (*SIPLeg) SetAcceptText added in v0.6.0

func (l *SIPLeg) SetAcceptText(accept bool)

SetAcceptText toggles inbound RTT event acceptance on this leg.

func (*SIPLeg) SetAppID added in v0.4.0

func (l *SIPLeg) SetAppID(id string)

func (*SIPLeg) SetDeaf

func (l *SIPLeg) SetDeaf(d bool)

func (*SIPLeg) SetDisconnectReason added in v0.6.0

func (l *SIPLeg) SetDisconnectReason(reason string)

SetDisconnectReason stores a reason that will be used in the next leg.disconnected event published for this leg, in place of the goroutine's default. Set by the API DELETE handler before calling Reject or Hangup so the user-provided cause flows through to the event.

func (*SIPLeg) SetHeld

func (l *SIPLeg) SetHeld(held bool)

SetHeld updates hold state and manages the 2-hour auto-hangup timer.

func (*SIPLeg) SetInTap

func (l *SIPLeg) SetInTap(w io.Writer)

SetInTap sets a writer that receives a copy of every decoded incoming PCM frame (before it enters inFrames). Used for standalone leg recording.

func (*SIPLeg) SetJitterBuffer added in v0.3.0

func (l *SIPLeg) SetJitterBuffer(targetMs, maxMs int)

SetJitterBuffer configures the SIP ingress jitter buffer. targetMs is the target play-out delay (e.g. 60 for 60 ms); 0 disables the buffer entirely (passthrough). maxMs caps the queue depth; 0 means "use a sensible default" (300 ms). Call before the leg's media pipeline is established.

func (*SIPLeg) SetMuted

func (l *SIPLeg) SetMuted(m bool)

func (*SIPLeg) SetOutTap

func (l *SIPLeg) SetOutTap(w io.Writer)

SetOutTap sets a writer that receives a copy of every outgoing PCM frame (including silence) from the writeLoop. Used for standalone leg recording.

func (*SIPLeg) SetRole added in v0.7.0

func (l *SIPLeg) SetRole(r string)

func (*SIPLeg) SetRoomID

func (l *SIPLeg) SetRoomID(id string)

func (*SIPLeg) SetSpeakingTap added in v0.2.0

func (l *SIPLeg) SetSpeakingTap(w io.Writer)

SetSpeakingTap sets a writer that receives decoded incoming PCM for voice activity detection.

func (*SIPLeg) SetupEarlyMediaOutbound

func (l *SIPLeg) SetupEarlyMediaOutbound(remoteSDP *sipmod.SDPMedia, rtpSess *sipmod.RTPSession) error

SetupEarlyMediaOutbound configures the media pipeline from a 183 response's SDP before the call is answered. Only valid in StateRinging.

func (*SIPLeg) SignalAnswer

func (l *SIPLeg) SignalAnswer(preferred codec.CodecType)

SignalAnswer signals the leg to answer (called from REST API). preferred is an optional codec hint passed to Answer; pass CodecUnknown for none.

func (*SIPLeg) State

func (l *SIPLeg) State() LegState

func (*SIPLeg) SupportedCodecs added in v0.9.0

func (l *SIPLeg) SupportedCodecs() []codec.CodecType

SupportedCodecs returns the engine's supported codecs for this leg, in preference order. Used by the REST layer to reject answer/early-media requests that name a codec the engine wasn't configured for.

func (*SIPLeg) Transfer added in v0.3.0

func (l *SIPLeg) Transfer(ctx context.Context, target string, replaces *sipmod.ReplacesParams) error

Transfer sends an in-dialog REFER. replaces non-nil = attended (RFC 3891).

func (*SIPLeg) Type

func (l *SIPLeg) Type() LegType

func (*SIPLeg) Unhold

func (l *SIPLeg) Unhold(ctx context.Context) error

Unhold resumes the call by sending a re-INVITE with sendrecv SDP.

func (*SIPLeg) WaitConnected

func (l *SIPLeg) WaitConnected(ctx context.Context) error

WaitConnected blocks until the leg reaches connected state or the context is cancelled. Returns nil if connected, or the context error otherwise.

type SIPResponseLogger added in v0.5.0

type SIPResponseLogger = WhatsAppSIPController

SIPResponseLogger is an alias retained for older call sites.

type WebRTCLeg

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

WebRTCLeg wraps a pion PeerConnection (via PCMedia) as a Leg.

func NewWebRTCLeg

func NewWebRTCLeg(media *PCMedia, log *slog.Logger) *WebRTCLeg

NewWebRTCLeg wraps a PCMedia and starts its outbound write loop.

func (*WebRTCLeg) AcceptDTMF added in v0.3.0

func (l *WebRTCLeg) AcceptDTMF() bool

func (*WebRTCLeg) AcceptText added in v0.6.0

func (l *WebRTCLeg) AcceptText() bool

func (*WebRTCLeg) AddICECandidate

func (l *WebRTCLeg) AddICECandidate(c webrtc.ICECandidateInit) error

AddICECandidate adds a remote ICE candidate.

func (*WebRTCLeg) Answer

func (l *WebRTCLeg) Answer(_ context.Context) error

func (*WebRTCLeg) AnsweredAt

func (l *WebRTCLeg) AnsweredAt() time.Time

func (*WebRTCLeg) AppID added in v0.4.0

func (l *WebRTCLeg) AppID() string

func (*WebRTCLeg) AudioReader

func (l *WebRTCLeg) AudioReader() io.Reader

func (*WebRTCLeg) AudioWriter

func (l *WebRTCLeg) AudioWriter() io.Writer

func (*WebRTCLeg) ClaimDisconnect added in v0.6.0

func (l *WebRTCLeg) ClaimDisconnect() bool

ClaimDisconnect returns true on the first caller and false on every subsequent caller. Termination paths use this gate so only one publishes leg.disconnected.

func (*WebRTCLeg) ClearSpeakingTap added in v0.2.0

func (l *WebRTCLeg) ClearSpeakingTap()

func (*WebRTCLeg) Context

func (l *WebRTCLeg) Context() context.Context

func (*WebRTCLeg) CreatedAt

func (l *WebRTCLeg) CreatedAt() time.Time

func (*WebRTCLeg) DrainCandidates

func (l *WebRTCLeg) DrainCandidates() ([]webrtc.ICECandidateInit, bool)

DrainCandidates returns buffered local ICE candidates and whether gathering is done.

func (*WebRTCLeg) Hangup

func (l *WebRTCLeg) Hangup(_ context.Context) error

func (*WebRTCLeg) Headers added in v0.6.0

func (l *WebRTCLeg) Headers() map[string]string

func (*WebRTCLeg) ID

func (l *WebRTCLeg) ID() string

func (*WebRTCLeg) IsDeaf

func (l *WebRTCLeg) IsDeaf() bool

func (*WebRTCLeg) IsHeld

func (l *WebRTCLeg) IsHeld() bool

func (*WebRTCLeg) IsMuted

func (l *WebRTCLeg) IsMuted() bool

func (*WebRTCLeg) Media added in v0.5.0

func (l *WebRTCLeg) Media() *PCMedia

Media returns the underlying PCMedia (for SDP negotiation + ICE trickle).

func (*WebRTCLeg) OnDTMF

func (l *WebRTCLeg) OnDTMF(f func(digit rune))

func (*WebRTCLeg) OnTextReceived added in v0.6.0

func (l *WebRTCLeg) OnTextReceived(_ func(text string, lossMarker bool))

func (*WebRTCLeg) RTPStats

func (l *WebRTCLeg) RTPStats() RTPStats

func (*WebRTCLeg) RTTNegotiated added in v0.6.0

func (l *WebRTCLeg) RTTNegotiated() bool

func (*WebRTCLeg) Role added in v0.7.0

func (l *WebRTCLeg) Role() string

func (*WebRTCLeg) RoomID

func (l *WebRTCLeg) RoomID() string

func (*WebRTCLeg) SIPHeaders

func (l *WebRTCLeg) SIPHeaders() map[string]string

func (*WebRTCLeg) SampleRate

func (l *WebRTCLeg) SampleRate() int

func (*WebRTCLeg) SendDTMF

func (l *WebRTCLeg) SendDTMF(_ context.Context, _ string) error

func (*WebRTCLeg) SendText added in v0.6.0

func (l *WebRTCLeg) SendText(_ context.Context, _ string) error

func (*WebRTCLeg) SetAcceptDTMF added in v0.3.0

func (l *WebRTCLeg) SetAcceptDTMF(a bool)

func (*WebRTCLeg) SetAcceptText added in v0.6.0

func (l *WebRTCLeg) SetAcceptText(_ bool)

func (*WebRTCLeg) SetAppID added in v0.4.0

func (l *WebRTCLeg) SetAppID(id string)

func (*WebRTCLeg) SetDeaf

func (l *WebRTCLeg) SetDeaf(d bool)

func (*WebRTCLeg) SetMuted

func (l *WebRTCLeg) SetMuted(m bool)

func (*WebRTCLeg) SetRole added in v0.7.0

func (l *WebRTCLeg) SetRole(r string)

func (*WebRTCLeg) SetRoomID

func (l *WebRTCLeg) SetRoomID(id string)

func (*WebRTCLeg) SetSpeakingTap added in v0.2.0

func (l *WebRTCLeg) SetSpeakingTap(w io.Writer)

func (*WebRTCLeg) State

func (l *WebRTCLeg) State() LegState

func (*WebRTCLeg) Type

func (l *WebRTCLeg) Type() LegType

type WebSocketLeg added in v0.6.0

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

WebSocketLeg wraps a wsmedia.Transport as a Leg, supporting both inbound (server-side upgrade) and outbound (client-side dial) directions. Both share state machinery; only Type() differs.

func NewWebSocketInboundLeg added in v0.6.0

func NewWebSocketInboundLeg(t *wsmedia.Transport, headers map[string]string, sampleRate int, rtt bool, log *slog.Logger) *WebSocketLeg

NewWebSocketInboundLeg constructs an inbound WS leg already bound to a transport. The leg goes straight to StateConnected (no ringing).

func NewWebSocketOutboundPendingLeg added in v0.6.0

func NewWebSocketOutboundPendingLeg(sampleRate int, rtt bool, log *slog.Logger) *WebSocketLeg

NewWebSocketOutboundPendingLeg constructs an outbound WS leg in StateRinging. Call AttachTransport once DialClient completes to transition to StateConnected.

func (*WebSocketLeg) AcceptDTMF added in v0.6.0

func (l *WebSocketLeg) AcceptDTMF() bool

func (*WebSocketLeg) AcceptText added in v0.6.0

func (l *WebSocketLeg) AcceptText() bool

func (*WebSocketLeg) Answer added in v0.6.0

func (l *WebSocketLeg) Answer(_ context.Context) error

func (*WebSocketLeg) AnsweredAt added in v0.6.0

func (l *WebSocketLeg) AnsweredAt() time.Time

func (*WebSocketLeg) AppID added in v0.6.0

func (l *WebSocketLeg) AppID() string

func (*WebSocketLeg) AttachTransport added in v0.6.0

func (l *WebSocketLeg) AttachTransport(t *wsmedia.Transport, headers map[string]string)

AttachTransport binds the wsmedia.Transport to the leg and starts the send loop reading from the egress pipe. headers may be nil for outbound dials where the server returned no relevant response headers.

func (*WebSocketLeg) AudioReader added in v0.6.0

func (l *WebSocketLeg) AudioReader() io.Reader

AudioReader returns inbound PCM as it arrives off the WebSocket.

func (*WebSocketLeg) AudioWriter added in v0.6.0

func (l *WebSocketLeg) AudioWriter() io.Writer

AudioWriter returns the write side of the egress pipe; the room writes mixed-minus-self PCM here, the transport's send loop reads it.

func (*WebSocketLeg) ClaimDisconnect added in v0.6.0

func (l *WebSocketLeg) ClaimDisconnect() bool

ClaimDisconnect single-flights the disconnect publication.

func (*WebSocketLeg) ClearSpeakingTap added in v0.6.0

func (l *WebSocketLeg) ClearSpeakingTap()

func (*WebSocketLeg) Context added in v0.6.0

func (l *WebSocketLeg) Context() context.Context

func (*WebSocketLeg) CreatedAt added in v0.6.0

func (l *WebSocketLeg) CreatedAt() time.Time

func (*WebSocketLeg) Hangup added in v0.6.0

func (l *WebSocketLeg) Hangup(_ context.Context) error

func (*WebSocketLeg) Headers added in v0.6.0

func (l *WebSocketLeg) Headers() map[string]string

func (*WebSocketLeg) ID added in v0.6.0

func (l *WebSocketLeg) ID() string

func (*WebSocketLeg) IsDeaf added in v0.6.0

func (l *WebSocketLeg) IsDeaf() bool

func (*WebSocketLeg) IsHeld added in v0.6.0

func (l *WebSocketLeg) IsHeld() bool

func (*WebSocketLeg) IsMuted added in v0.6.0

func (l *WebSocketLeg) IsMuted() bool

func (*WebSocketLeg) OnDTMF added in v0.6.0

func (l *WebSocketLeg) OnDTMF(_ func(rune))

func (*WebSocketLeg) OnTextReceived added in v0.6.0

func (l *WebSocketLeg) OnTextReceived(fn func(text string, lossMarker bool))

func (*WebSocketLeg) RTPStats added in v0.6.0

func (l *WebSocketLeg) RTPStats() RTPStats

func (*WebSocketLeg) RTTNegotiated added in v0.6.0

func (l *WebSocketLeg) RTTNegotiated() bool

func (*WebSocketLeg) Role added in v0.7.0

func (l *WebSocketLeg) Role() string

func (*WebSocketLeg) RoomID added in v0.6.0

func (l *WebSocketLeg) RoomID() string

func (*WebSocketLeg) SIPHeaders added in v0.6.0

func (l *WebSocketLeg) SIPHeaders() map[string]string

func (*WebSocketLeg) SampleRate added in v0.6.0

func (l *WebSocketLeg) SampleRate() int

func (*WebSocketLeg) SendDTMF added in v0.6.0

func (l *WebSocketLeg) SendDTMF(_ context.Context, _ string) error

func (*WebSocketLeg) SendText added in v0.6.0

func (l *WebSocketLeg) SendText(ctx context.Context, text string) error

func (*WebSocketLeg) SetAcceptDTMF added in v0.6.0

func (l *WebSocketLeg) SetAcceptDTMF(a bool)

func (*WebSocketLeg) SetAcceptText added in v0.6.0

func (l *WebSocketLeg) SetAcceptText(a bool)

func (*WebSocketLeg) SetAppID added in v0.6.0

func (l *WebSocketLeg) SetAppID(id string)

func (*WebSocketLeg) SetDeaf added in v0.6.0

func (l *WebSocketLeg) SetDeaf(d bool)

func (*WebSocketLeg) SetMuted added in v0.6.0

func (l *WebSocketLeg) SetMuted(m bool)

func (*WebSocketLeg) SetRole added in v0.7.0

func (l *WebSocketLeg) SetRole(r string)

func (*WebSocketLeg) SetRoomID added in v0.6.0

func (l *WebSocketLeg) SetRoomID(id string)

func (*WebSocketLeg) SetSpeakingTap added in v0.6.0

func (l *WebSocketLeg) SetSpeakingTap(io.Writer)

func (*WebSocketLeg) State added in v0.6.0

func (l *WebSocketLeg) State() LegState

func (*WebSocketLeg) Transport added in v0.6.0

func (l *WebSocketLeg) Transport() *wsmedia.Transport

Transport returns the underlying wsmedia.Transport.

func (*WebSocketLeg) Type added in v0.6.0

func (l *WebSocketLeg) Type() LegType

type WhatsAppLeg added in v0.5.0

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

WhatsAppLeg is a SIP-over-TLS leg with Opus media via ICE+DTLS-SRTP. Hold/unhold/transfer are unsupported (Meta rejects re-INVITE).

func NewWhatsAppInboundLeg added in v0.5.0

func NewWhatsAppInboundLeg(dialog *sipgo.DialogServerSession, media *PCMedia, from, to string, headers map[string]string, answerSDP []byte, log *slog.Logger) *WhatsAppLeg

NewWhatsAppInboundLeg wraps an accepted UAS dialog. The 200 OK is sent by Answer() once REST issues POST /v1/legs/{id}/answer.

func NewWhatsAppOutboundPendingLeg added in v0.5.0

func NewWhatsAppOutboundPendingLeg(media *PCMedia, from, to string, log *slog.Logger) *WhatsAppLeg

NewWhatsAppOutboundPendingLeg creates a ringing-state leg without a dialog. Caller drives the INVITE asynchronously and upgrades via ConnectOutbound on 200 OK.

func (*WhatsAppLeg) AcceptDTMF added in v0.5.0

func (l *WhatsAppLeg) AcceptDTMF() bool

func (*WhatsAppLeg) AcceptText added in v0.6.0

func (l *WhatsAppLeg) AcceptText() bool

func (*WhatsAppLeg) Answer added in v0.5.0

func (l *WhatsAppLeg) Answer(_ context.Context) error

func (*WhatsAppLeg) AnswerCh added in v0.5.0

func (l *WhatsAppLeg) AnswerCh() <-chan struct{}

func (*WhatsAppLeg) AnsweredAt added in v0.5.0

func (l *WhatsAppLeg) AnsweredAt() time.Time

func (*WhatsAppLeg) AppID added in v0.5.0

func (l *WhatsAppLeg) AppID() string

func (*WhatsAppLeg) AudioReader added in v0.5.0

func (l *WhatsAppLeg) AudioReader() io.Reader

func (*WhatsAppLeg) AudioWriter added in v0.5.0

func (l *WhatsAppLeg) AudioWriter() io.Writer

func (*WhatsAppLeg) ClaimDisconnect added in v0.6.0

func (l *WhatsAppLeg) ClaimDisconnect() bool

ClaimDisconnect returns true on the first caller and false on every subsequent caller. Termination paths use this gate so only one publishes leg.disconnected.

func (*WhatsAppLeg) ClearSpeakingTap added in v0.5.0

func (l *WhatsAppLeg) ClearSpeakingTap()

func (*WhatsAppLeg) ClientDialog added in v0.5.0

func (l *WhatsAppLeg) ClientDialog() *sipgo.DialogClientSession

func (*WhatsAppLeg) ConnectOutbound added in v0.5.0

func (l *WhatsAppLeg) ConnectOutbound(dialog *sipgo.DialogClientSession) error

func (*WhatsAppLeg) Context added in v0.5.0

func (l *WhatsAppLeg) Context() context.Context

func (*WhatsAppLeg) CreatedAt added in v0.5.0

func (l *WhatsAppLeg) CreatedAt() time.Time

func (*WhatsAppLeg) From added in v0.5.0

func (l *WhatsAppLeg) From() string

func (*WhatsAppLeg) Hangup added in v0.5.0

func (l *WhatsAppLeg) Hangup(ctx context.Context) error

func (*WhatsAppLeg) Headers added in v0.6.0

func (l *WhatsAppLeg) Headers() map[string]string

func (*WhatsAppLeg) ID added in v0.5.0

func (l *WhatsAppLeg) ID() string

func (*WhatsAppLeg) IsDeaf added in v0.5.0

func (l *WhatsAppLeg) IsDeaf() bool

func (*WhatsAppLeg) IsHeld added in v0.5.0

func (l *WhatsAppLeg) IsHeld() bool

func (*WhatsAppLeg) IsMuted added in v0.5.0

func (l *WhatsAppLeg) IsMuted() bool

func (*WhatsAppLeg) Media added in v0.5.0

func (l *WhatsAppLeg) Media() *PCMedia

func (*WhatsAppLeg) OnDTMF added in v0.5.0

func (l *WhatsAppLeg) OnDTMF(f func(digit rune))

func (*WhatsAppLeg) OnTextReceived added in v0.6.0

func (l *WhatsAppLeg) OnTextReceived(_ func(text string, lossMarker bool))

func (*WhatsAppLeg) RTPStats added in v0.5.0

func (l *WhatsAppLeg) RTPStats() RTPStats

func (*WhatsAppLeg) RTTNegotiated added in v0.6.0

func (l *WhatsAppLeg) RTTNegotiated() bool

func (*WhatsAppLeg) RequestAnswer added in v0.5.0

func (l *WhatsAppLeg) RequestAnswer() error

func (*WhatsAppLeg) Role added in v0.7.0

func (l *WhatsAppLeg) Role() string

func (*WhatsAppLeg) RoomID added in v0.5.0

func (l *WhatsAppLeg) RoomID() string

func (*WhatsAppLeg) SIPHeaders added in v0.5.0

func (l *WhatsAppLeg) SIPHeaders() map[string]string

func (*WhatsAppLeg) SampleRate added in v0.5.0

func (l *WhatsAppLeg) SampleRate() int

func (*WhatsAppLeg) SendDTMF added in v0.5.0

func (l *WhatsAppLeg) SendDTMF(_ context.Context, _ string) error

func (*WhatsAppLeg) SendText added in v0.6.0

func (l *WhatsAppLeg) SendText(_ context.Context, _ string) error

func (*WhatsAppLeg) ServerDialog added in v0.5.0

func (l *WhatsAppLeg) ServerDialog() *sipgo.DialogServerSession

func (*WhatsAppLeg) SetAcceptDTMF added in v0.5.0

func (l *WhatsAppLeg) SetAcceptDTMF(a bool)

func (*WhatsAppLeg) SetAcceptText added in v0.6.0

func (l *WhatsAppLeg) SetAcceptText(_ bool)

func (*WhatsAppLeg) SetAppID added in v0.5.0

func (l *WhatsAppLeg) SetAppID(id string)

func (*WhatsAppLeg) SetDeaf added in v0.5.0

func (l *WhatsAppLeg) SetDeaf(d bool)

func (*WhatsAppLeg) SetMuted added in v0.5.0

func (l *WhatsAppLeg) SetMuted(m bool)

func (*WhatsAppLeg) SetRole added in v0.7.0

func (l *WhatsAppLeg) SetRole(r string)

func (*WhatsAppLeg) SetRoomID added in v0.5.0

func (l *WhatsAppLeg) SetRoomID(id string)

func (*WhatsAppLeg) SetSIPController added in v0.5.0

func (l *WhatsAppLeg) SetSIPController(c WhatsAppSIPController)

func (*WhatsAppLeg) SetSIPResponseLogger added in v0.5.0

func (l *WhatsAppLeg) SetSIPResponseLogger(c SIPResponseLogger)

func (*WhatsAppLeg) SetSpeakingTap added in v0.5.0

func (l *WhatsAppLeg) SetSpeakingTap(w io.Writer)

func (*WhatsAppLeg) State added in v0.5.0

func (l *WhatsAppLeg) State() LegState

func (*WhatsAppLeg) To added in v0.5.0

func (l *WhatsAppLeg) To() string

func (*WhatsAppLeg) Type added in v0.5.0

func (l *WhatsAppLeg) Type() LegType

type WhatsAppSIPController added in v0.5.0

type WhatsAppSIPController interface {
	LogSyntheticResponse(req *sipproto.Request, statusCode int, reason string, body []byte, headers ...sipproto.Header)
	RespondInviteSDP(dialog *sipgo.DialogServerSession, sdp []byte) error
}

WhatsAppSIPController is satisfied by *sip.Engine. The interface lives here to avoid a cyclic import on internal/sip.

Jump to

Keyboard shortcuts

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