Documentation
¶
Overview ¶
Package xphone provides an event-driven SIP user agent for embedding VoIP telephony into Go applications. It manages SIP registration, call state machines, RTP media pipelines, codec encode/decode, and DTMF — exposing a concurrency-safe API for handling multiple concurrent calls.
Index ¶
- Constants
- Variables
- func DTMFCodeDigit(code int) string
- func DTMFDigitCode(digit string) int
- func EncodeDTMF(digit string, ts uint32, seq uint16, ssrc uint32) ([]*rtp.Packet, error)
- func EncodeInfoDTMF(digit string, durationMs int) (string, error)
- func ParseInfoDTMF(body string) string
- type AcceptOption
- type Call
- type CallState
- type Codec
- type Config
- type DTMFEvent
- type DialOption
- func WithAuth(username, password string) DialOption
- func WithCallerID(id string) DialOption
- func WithCodecOverride(codecs ...Codec) DialOption
- func WithDialTimeout(d time.Duration) DialOption
- func WithEarlyMedia() DialOption
- func WithHeader(name, value string) DialOption
- func WithVideo(codecs ...VideoCodec) DialOption
- type DialOptions
- type Direction
- type DtmfMode
- type EndReason
- type ExtensionState
- type MockCall
- func (c *MockCall) Accept(opts ...AcceptOption) error
- func (c *MockCall) AddVideo(codecs ...VideoCodec) error
- func (c *MockCall) AttendedTransfer(other Call) error
- func (c *MockCall) BlindTransfer(target string) error
- func (c *MockCall) CallID() string
- func (c *MockCall) Codec() Codec
- func (c *MockCall) Direction() Direction
- func (c *MockCall) Duration() time.Duration
- func (c *MockCall) End() error
- func (c *MockCall) From() string
- func (c *MockCall) FromName() string
- func (c *MockCall) HasVideo() bool
- func (c *MockCall) Header(name string) []string
- func (c *MockCall) Headers() map[string][]string
- func (c *MockCall) Hold() error
- func (c *MockCall) ID() string
- func (c *MockCall) InjectRTP(pkt *rtp.Packet)
- func (c *MockCall) LastTransferTarget() string
- func (c *MockCall) LocalSDP() string
- func (c *MockCall) MediaSessionActive() bool
- func (c *MockCall) Mute() error
- func (c *MockCall) MuteAudio() error
- func (c *MockCall) MuteVideo() error
- func (c *MockCall) Muted() bool
- func (c *MockCall) OnDTMF(fn func(string))
- func (c *MockCall) OnEnded(fn func(EndReason))
- func (c *MockCall) OnHold(fn func())
- func (c *MockCall) OnMedia(fn func())
- func (c *MockCall) OnMute(fn func())
- func (c *MockCall) OnResume(fn func())
- func (c *MockCall) OnState(fn func(CallState))
- func (c *MockCall) OnUnmute(fn func())
- func (c *MockCall) OnVideo(fn func())
- func (c *MockCall) OnVideoRequest(fn func(*VideoUpgradeRequest))
- func (c *MockCall) PCMReader() <-chan []int16
- func (c *MockCall) PCMWriter() chan<- []int16
- func (c *MockCall) PacedPCMWriter() chan<- []int16
- func (c *MockCall) RTPRawReader() <-chan *rtp.Packet
- func (c *MockCall) RTPReader() <-chan *rtp.Packet
- func (c *MockCall) RTPWriter() chan<- *rtp.Packet
- func (c *MockCall) Reject(code int, reason string) error
- func (c *MockCall) RemoteDID() string
- func (c *MockCall) RemoteIP() string
- func (c *MockCall) RemotePort() int
- func (c *MockCall) RemoteSDP() string
- func (c *MockCall) RemoteURI() string
- func (c *MockCall) ReplaceAudioWriter(newSrc <-chan []int16) error
- func (c *MockCall) RequestKeyframe() error
- func (c *MockCall) Resume() error
- func (c *MockCall) SendDTMF(digit string) error
- func (c *MockCall) SentDTMF() []string
- func (c *MockCall) SetCodec(codec Codec)
- func (c *MockCall) SetDirection(d Direction)
- func (c *MockCall) SetHasVideo(v bool)
- func (c *MockCall) SetHeader(name, value string)
- func (c *MockCall) SetLocalSDP(sdp string)
- func (c *MockCall) SetMediaSessionActive(active bool)
- func (c *MockCall) SetRemoteIP(ip string)
- func (c *MockCall) SetRemotePort(port int)
- func (c *MockCall) SetRemoteSDP(sdp string)
- func (c *MockCall) SetRemoteURI(uri string)
- func (c *MockCall) SetStartTime(t time.Time)
- func (c *MockCall) SetState(s CallState)
- func (c *MockCall) SetVideoCodec(vc VideoCodec)
- func (c *MockCall) SimulateDTMF(digit string)
- func (c *MockCall) StartTime() time.Time
- func (c *MockCall) State() CallState
- func (c *MockCall) To() string
- func (c *MockCall) Unmute() error
- func (c *MockCall) UnmuteAudio() error
- func (c *MockCall) UnmuteVideo() error
- func (c *MockCall) VideoCodec() VideoCodec
- func (c *MockCall) VideoRTPReader() <-chan *rtp.Packet
- func (c *MockCall) VideoRTPWriter() chan<- *rtp.Packet
- func (c *MockCall) VideoReader() <-chan VideoFrame
- func (c *MockCall) VideoWriter() chan<- VideoFrame
- type MockPhone
- func (p *MockPhone) AttendedTransfer(callA Call, callB Call) error
- func (p *MockPhone) Calls() []Call
- func (p *MockPhone) Connect(_ context.Context) error
- func (p *MockPhone) Dial(_ context.Context, target string, opts ...DialOption) (Call, error)
- func (p *MockPhone) Disconnect() error
- func (p *MockPhone) FindCall(callID string) Call
- func (p *MockPhone) LastCall() Call
- func (p *MockPhone) OnCallDTMF(fn func(Call, string))
- func (p *MockPhone) OnCallEnded(fn func(Call, EndReason))
- func (p *MockPhone) OnCallState(fn func(Call, CallState))
- func (p *MockPhone) OnError(fn func(error))
- func (p *MockPhone) OnIncoming(fn func(Call))
- func (p *MockPhone) OnMessage(fn func(SipMessage))
- func (p *MockPhone) OnRegistered(fn func())
- func (p *MockPhone) OnUnregistered(fn func())
- func (p *MockPhone) OnVoicemail(fn func(VoicemailStatus))
- func (p *MockPhone) SendMessage(_ context.Context, _ string, _ string) error
- func (p *MockPhone) SendMessageWithType(_ context.Context, _ string, _ string, _ string) error
- func (p *MockPhone) SimulateError(err error)
- func (p *MockPhone) SimulateIncoming(from string)
- func (p *MockPhone) SimulateMWI(status VoicemailStatus)
- func (p *MockPhone) SimulateMessage(msg SipMessage)
- func (p *MockPhone) State() PhoneState
- func (p *MockPhone) SubscribeEvent(_ context.Context, _ string, _ string, _ int, _ func(NotifyEvent)) (string, error)
- func (p *MockPhone) UnsubscribeEvent(_ string) error
- func (p *MockPhone) Unwatch(_ string) error
- func (p *MockPhone) Watch(_ context.Context, _ string, _ func(string, ExtensionState, ExtensionState)) (string, error)
- type NotifyEvent
- type PeerAuthConfig
- type PeerConfig
- type Phone
- type PhoneOption
- func WithCodecs(codecs ...Codec) PhoneOption
- func WithCredentials(username, password, host string) PhoneOption
- func WithDisplayName(name string) PhoneOption
- func WithDtmfMode(mode DtmfMode) PhoneOption
- func WithICE(enabled bool) PhoneOption
- func WithJitterBuffer(d time.Duration) PhoneOption
- func WithLogger(l *slog.Logger) PhoneOption
- func WithMediaTimeout(d time.Duration) PhoneOption
- func WithNATKeepalive(d time.Duration) PhoneOption
- func WithOutboundCredentials(username, password string) PhoneOption
- func WithOutboundProxy(uri string) PhoneOption
- func WithPCMRate(rate int) PhoneOption
- func WithRTPPorts(min, max int) PhoneOption
- func WithSRTP() PhoneOption
- func WithStunServer(server string) PhoneOption
- func WithTransport(protocol string, tlsCfg *tls.Config) PhoneOption
- func WithTurnCredentials(username, password string) PhoneOption
- func WithTurnServer(server string) PhoneOption
- func WithVoicemailURI(uri string) PhoneOption
- type PhoneState
- type Server
- type ServerConfig
- type ServerState
- type SipMessage
- type SubState
- type VideoCodec
- type VideoFrame
- type VideoUpgradeRequest
- type VoicemailStatus
Constants ¶
const DTMFPayloadType = 101
DTMFPayloadType is the RTP payload type for DTMF events (RFC 4733).
Variables ¶
var ( // ErrNotRegistered is returned when an operation requires an active SIP registration. ErrNotRegistered = errors.New("xphone: not registered") // ErrCallNotFound is returned when a call ID does not match any active call. ErrCallNotFound = errors.New("xphone: call not found") // ErrInvalidState is returned when a call operation is invalid for the current state. ErrInvalidState = errors.New("xphone: invalid state for operation") // ErrMediaTimeout is returned when no RTP packets are received within the configured timeout. ErrMediaTimeout = errors.New("xphone: RTP media timeout") // ErrDialTimeout is returned when an outbound call is not answered before the dial timeout. ErrDialTimeout = errors.New("xphone: dial timeout exceeded before answer") // ErrNoRTPPortAvailable is returned when the RTP port range is exhausted. ErrNoRTPPortAvailable = errors.New("xphone: RTP port range exhausted") // ErrRegistrationFailed is returned when SIP registration is rejected by the server. ErrRegistrationFailed = errors.New("xphone: registration failed") // ErrTransferFailed is returned when a blind transfer (REFER) is rejected. ErrTransferFailed = errors.New("xphone: transfer failed") // ErrTLSConfigRequired is returned when TLS transport is selected without providing a TLS config. ErrTLSConfigRequired = errors.New("xphone: TLS transport requires TLSConfig") // ErrInvalidDTMFDigit is returned when SendDTMF receives a digit outside 0-9, *, #, A-D. ErrInvalidDTMFDigit = errors.New("xphone: invalid DTMF digit") // ErrAlreadyMuted is returned when Mute is called on an already-muted call. ErrAlreadyMuted = errors.New("xphone: already muted") // ErrNotMuted is returned when Unmute is called on a call that is not muted. ErrNotMuted = errors.New("xphone: not muted") // ErrNoVideo is returned when a video operation is called on an audio-only call. ErrNoVideo = errors.New("xphone: no video stream") // ErrAlreadyConnected is returned when Connect is called on a phone that is already connected. ErrAlreadyConnected = errors.New("xphone: already connected") // ErrNotConnected is returned when Disconnect is called on a phone that is not connected. ErrNotConnected = errors.New("xphone: not connected") // ErrHostRequired is returned when Connect is called without a Host configured. ErrHostRequired = errors.New("xphone: Host is required") // ErrSubscriptionRejected is returned when the server permanently rejects a SUBSCRIBE. ErrSubscriptionRejected = errors.New("xphone: subscription rejected") // ErrSubscriptionNotFound is returned when UnsubscribeEvent is called with an unknown ID. ErrSubscriptionNotFound = errors.New("xphone: subscription not found") // ErrVideoAlreadyActive is returned when AddVideo is called on a call that already has video. ErrVideoAlreadyActive = errors.New("xphone: video already active") // ErrAlreadyListening is returned when Listen is called on a server that is already listening. ErrAlreadyListening = errors.New("xphone: server already listening") // ErrNotListening is returned when an operation requires the server to be listening. ErrNotListening = errors.New("xphone: server not listening") // ErrPeerNotFound is returned when a dial target references an unknown peer name. ErrPeerNotFound = errors.New("xphone: peer not found") // ErrPeerRejected is returned when an incoming request fails peer authentication. ErrPeerRejected = errors.New("xphone: peer authentication failed") )
Sentinel errors returned by Phone and Call methods.
Functions ¶
func DTMFCodeDigit ¶
DTMFCodeDigit returns the digit string for an RFC 4733 event code. Returns "" if the code is unknown.
func DTMFDigitCode ¶
DTMFDigitCode returns the RFC 4733 event code for a digit string. Returns -1 if the digit is invalid.
func EncodeDTMF ¶
EncodeDTMF encodes a DTMF digit into a sequence of RTP packets (RFC 4733).
func EncodeInfoDTMF ¶ added in v0.3.0
EncodeInfoDTMF creates a SIP INFO application/dtmf-relay body. Format: "Signal=<digit>\r\nDuration=<ms>\r\n"
func ParseInfoDTMF ¶ added in v0.3.0
ParseInfoDTMF parses a SIP INFO application/dtmf-relay body and returns the digit. Parsing is case-insensitive for the "Signal" key and tolerates whitespace. Returns "" if no valid digit is found.
Types ¶
type AcceptOption ¶
type AcceptOption func(*acceptOptions)
AcceptOption is a functional option for Accept().
type Call ¶
type Call interface {
ID() string
CallID() string
Direction() Direction
From() string
To() string
FromName() string
RemoteURI() string
RemoteDID() string
RemoteIP() string
RemotePort() int
State() CallState
Codec() Codec
LocalSDP() string
RemoteSDP() string
StartTime() time.Time
Duration() time.Duration
Header(name string) []string
Headers() map[string][]string
Accept(opts ...AcceptOption) error
Reject(code int, reason string) error
End() error
MediaSessionActive() bool
Hold() error
Resume() error
Mute() error
Unmute() error
MuteAudio() error
UnmuteAudio() error
SendDTMF(digit string) error
BlindTransfer(target string) error
AttendedTransfer(other Call) error
RTPRawReader() <-chan *rtp.Packet
RTPReader() <-chan *rtp.Packet
RTPWriter() chan<- *rtp.Packet
PCMReader() <-chan []int16
PCMWriter() chan<- []int16
PacedPCMWriter() chan<- []int16
ReplaceAudioWriter(newSrc <-chan []int16) error
OnDTMF(func(digit string))
OnHold(func())
OnResume(func())
OnMute(func())
OnUnmute(func())
HasVideo() bool
VideoCodec() VideoCodec
VideoReader() <-chan VideoFrame
VideoWriter() chan<- VideoFrame
VideoRTPReader() <-chan *rtp.Packet
VideoRTPWriter() chan<- *rtp.Packet
MuteVideo() error
UnmuteVideo() error
RequestKeyframe() error
AddVideo(codecs ...VideoCodec) error
OnVideoRequest(func(*VideoUpgradeRequest))
OnVideo(func())
OnMedia(func())
OnState(func(state CallState))
OnEnded(func(reason EndReason))
}
Call is the public interface for an active call.
type CallState ¶
type CallState int
CallState represents the current state of a call.
const ( StateIdle CallState = iota StateRinging // inbound: INVITE received, not yet accepted StateDialing // outbound: INVITE sent, no response yet StateRemoteRinging // outbound: 180 received StateEarlyMedia // outbound: 183 received + WithEarlyMedia set StateActive // call established, RTP flowing StateOnHold // re-INVITE with a=sendonly/inactive StateEnded // terminal )
type Config ¶
type Config struct {
Username string
Password string
Host string
Port int
Transport string
TLSConfig *tls.Config
RegisterExpiry time.Duration
RegisterRetry time.Duration
RegisterMaxRetry int
// OutboundProxy is the SIP URI to route outbound INVITEs through (e.g. "sip:proxy.example.com:5060").
// When set, the initial INVITE is sent to this address instead of Host.
// Registration still goes to Host. In-dialog requests use the route set from Record-Route.
OutboundProxy string
// OutboundUsername is the SIP digest username for outbound INVITE auth (401/407).
// Falls back to Username if empty.
OutboundUsername string
// OutboundPassword is the SIP digest password for outbound INVITE auth (401/407).
// Falls back to Password if empty.
OutboundPassword string
NATKeepaliveInterval time.Duration
StunServer string
SRTP bool
// TurnServer is the TURN server address (host:port) for relay allocation.
// When set with TurnUsername/TurnPassword, the phone allocates a TURN relay
// during call setup for symmetric NAT traversal.
TurnServer string
TurnUsername string
TurnPassword string
// ICE enables ICE-Lite candidate gathering and STUN responder on the media socket.
ICE bool
RTPPortMin int
RTPPortMax int
CodecPrefs []Codec
JitterBuffer time.Duration
MediaTimeout time.Duration
PCMFrameSize int
PCMRate int
DtmfMode DtmfMode
// VoicemailURI is the SIP URI to subscribe for MWI (RFC 3842).
// Example: "sip:*97@pbx.local". When set, the phone auto-subscribes on connect.
VoicemailURI string
// DisplayName is included in SIP Contact headers.
// Helps PBXes identify the device (e.g., "VoiceApp/1.0").
DisplayName string
Logger *slog.Logger
}
Config holds all configuration for a Phone instance.
type DTMFEvent ¶
DTMFEvent represents a decoded DTMF event from an RTP packet.
func DecodeDTMF ¶
DecodeDTMF decodes a DTMF event from an RTP payload. Returns nil if the payload is less than 4 bytes.
type DialOption ¶
type DialOption func(*DialOptions)
DialOption is a functional option for Dial().
func WithAuth ¶ added in v0.5.11
func WithAuth(username, password string) DialOption
WithAuth sets per-call SIP digest credentials for 401/407 proxy authentication challenges. Overrides phone-level WithOutboundCredentials and WithCredentials for this single dial attempt.
func WithCallerID ¶
func WithCallerID(id string) DialOption
WithCallerID sets the caller ID for an outbound call.
func WithCodecOverride ¶
func WithCodecOverride(codecs ...Codec) DialOption
WithCodecOverride overrides the codec preference for an outbound call.
func WithDialTimeout ¶
func WithDialTimeout(d time.Duration) DialOption
WithDialTimeout sets the dial timeout for an outbound call.
func WithEarlyMedia ¶
func WithEarlyMedia() DialOption
WithEarlyMedia enables early media (183 Session Progress).
func WithHeader ¶
func WithHeader(name, value string) DialOption
WithHeader adds a custom SIP header to an outbound call.
func WithVideo ¶ added in v0.3.0
func WithVideo(codecs ...VideoCodec) DialOption
WithVideo enables video in the SDP offer. If no codecs are specified, defaults to [H264, VP8] preference order.
type DialOptions ¶
type DialOptions struct {
CallerID string
CustomHeaders map[string]string
EarlyMedia bool
Timeout time.Duration
CodecOverride []Codec
Video bool // enable video in SDP offer
VideoCodecs []VideoCodec // video codec preferences (default: [H264, VP8])
AuthUsername string // per-call SIP digest username (overrides config)
AuthPassword string // per-call SIP digest password (overrides config)
}
DialOptions holds configuration for an outbound call.
type DtmfMode ¶ added in v0.3.0
type DtmfMode int
DtmfMode controls how DTMF digits are sent and received.
const ( // DtmfRfc4733 sends and receives DTMF via RFC 4733 RTP telephone-event packets (default). DtmfRfc4733 DtmfMode = iota // DtmfSipInfo sends and receives DTMF via SIP INFO with application/dtmf-relay body (RFC 2976). DtmfSipInfo // DtmfBoth sends DTMF via RFC 4733 RTP; also accepts incoming SIP INFO DTMF. DtmfBoth )
type EndReason ¶
type EndReason int
EndReason describes why a call ended.
const ( EndedByLocal EndReason = iota // End() while Active/OnHold EndedByRemote // BYE received EndedByTimeout // MediaTimeout exceeded EndedByError // internal or transport error EndedByTransfer // REFER completed (200 OK) EndedByRejected // Reject() called EndedByCancelled // End() before 200 OK (outbound) EndedByTransferFailed // REFER rejected by remote party )
type ExtensionState ¶ added in v0.3.0
type ExtensionState int
ExtensionState represents the monitoring state of a remote extension (BLF).
const ( ExtensionAvailable ExtensionState = iota // idle, no active dialogs ExtensionRinging // early/proceeding dialog ExtensionOnThePhone // confirmed dialog ExtensionOffline // not registered or subscription failed ExtensionUnknown // subscription pending or parse error )
type MockCall ¶
type MockCall struct {
// contains filtered or unexported fields
}
MockCall is a mock implementation of the Call interface for testing. It provides setters for all state and simulation methods for callbacks.
func NewMockCall ¶
func NewMockCall() *MockCall
NewMockCall creates a new MockCall with default state (Ringing, Inbound).
func (*MockCall) Accept ¶
func (c *MockCall) Accept(opts ...AcceptOption) error
func (*MockCall) AddVideo ¶ added in v0.3.0
func (c *MockCall) AddVideo(codecs ...VideoCodec) error
func (*MockCall) AttendedTransfer ¶ added in v0.5.3
func (*MockCall) BlindTransfer ¶
func (*MockCall) LastTransferTarget ¶
func (*MockCall) MediaSessionActive ¶ added in v0.2.0
func (*MockCall) OnVideoRequest ¶ added in v0.3.0
func (c *MockCall) OnVideoRequest(fn func(*VideoUpgradeRequest))
func (*MockCall) PacedPCMWriter ¶ added in v0.3.2
func (*MockCall) RTPRawReader ¶
func (*MockCall) RemotePort ¶
func (*MockCall) ReplaceAudioWriter ¶ added in v0.5.0
func (*MockCall) RequestKeyframe ¶ added in v0.3.0
func (*MockCall) SetDirection ¶
func (*MockCall) SetHasVideo ¶ added in v0.3.0
SetHasVideo enables or disables video on the mock call.
func (*MockCall) SetLocalSDP ¶
func (*MockCall) SetMediaSessionActive ¶ added in v0.2.0
func (*MockCall) SetRemoteIP ¶
func (*MockCall) SetRemotePort ¶
func (*MockCall) SetRemoteSDP ¶
func (*MockCall) SetRemoteURI ¶
func (*MockCall) SetStartTime ¶
func (*MockCall) SetVideoCodec ¶ added in v0.3.0
func (c *MockCall) SetVideoCodec(vc VideoCodec)
SetVideoCodec sets the negotiated video codec.
func (*MockCall) SimulateDTMF ¶
SimulateDTMF fires both the phone-level and per-call OnDTMF callbacks.
func (*MockCall) UnmuteAudio ¶ added in v0.3.0
func (*MockCall) UnmuteVideo ¶ added in v0.3.0
func (*MockCall) VideoCodec ¶ added in v0.3.0
func (c *MockCall) VideoCodec() VideoCodec
func (*MockCall) VideoRTPReader ¶ added in v0.3.0
func (*MockCall) VideoRTPWriter ¶ added in v0.3.0
func (*MockCall) VideoReader ¶ added in v0.3.0
func (c *MockCall) VideoReader() <-chan VideoFrame
func (*MockCall) VideoWriter ¶ added in v0.3.0
func (c *MockCall) VideoWriter() chan<- VideoFrame
type MockPhone ¶
type MockPhone struct {
// contains filtered or unexported fields
}
MockPhone is a mock implementation of the Phone interface for testing. It simulates registration, dialing, and incoming calls without SIP transport.
func NewMockPhone ¶
func NewMockPhone() *MockPhone
NewMockPhone creates a new MockPhone in the Disconnected state.
func (*MockPhone) AttendedTransfer ¶ added in v0.3.0
func (*MockPhone) Disconnect ¶
func (*MockPhone) OnCallDTMF ¶ added in v0.2.0
func (*MockPhone) OnCallEnded ¶ added in v0.2.0
func (*MockPhone) OnCallState ¶ added in v0.2.0
func (*MockPhone) OnIncoming ¶
func (*MockPhone) OnMessage ¶ added in v0.3.0
func (p *MockPhone) OnMessage(fn func(SipMessage))
func (*MockPhone) OnRegistered ¶
func (p *MockPhone) OnRegistered(fn func())
func (*MockPhone) OnUnregistered ¶
func (p *MockPhone) OnUnregistered(fn func())
func (*MockPhone) OnVoicemail ¶ added in v0.3.0
func (p *MockPhone) OnVoicemail(fn func(VoicemailStatus))
func (*MockPhone) SendMessage ¶ added in v0.3.0
func (*MockPhone) SendMessageWithType ¶ added in v0.3.0
func (*MockPhone) SimulateError ¶
SimulateError fires the OnError callbacks with the given error.
func (*MockPhone) SimulateIncoming ¶
SimulateIncoming creates an incoming MockCall and fires the OnIncoming callback.
func (*MockPhone) SimulateMWI ¶ added in v0.3.0
func (p *MockPhone) SimulateMWI(status VoicemailStatus)
SimulateMWI fires the OnVoicemail callbacks with the given status.
func (*MockPhone) SimulateMessage ¶ added in v0.3.0
func (p *MockPhone) SimulateMessage(msg SipMessage)
SimulateMessage fires the OnMessage callbacks with the given message.
func (*MockPhone) State ¶
func (p *MockPhone) State() PhoneState
func (*MockPhone) SubscribeEvent ¶ added in v0.3.0
func (*MockPhone) UnsubscribeEvent ¶ added in v0.3.0
type NotifyEvent ¶ added in v0.3.0
type NotifyEvent struct {
Event string // Event header (e.g. "dialog", "presence")
ContentType string // Content-Type of the NOTIFY body
Body string // raw NOTIFY body
SubscriptionState SubState // parsed Subscription-State
Expires int // expires parameter from Subscription-State header
Reason string // reason parameter (e.g. "deactivated", "rejected")
}
NotifyEvent is delivered by SubscribeEvent callbacks for each incoming NOTIFY.
type PeerAuthConfig ¶ added in v0.4.0
PeerAuthConfig holds SIP digest authentication credentials for a peer.
type PeerConfig ¶ added in v0.4.0
type PeerConfig struct {
// Name is a human-readable identifier for this peer (e.g. "office-pbx", "twilio").
Name string
// Host is a single IP address for this peer. Checked during IP-based auth.
Host string
// Hosts is a list of IP addresses and/or CIDR ranges (e.g. "54.172.60.0/30").
Hosts []string
// Port is the SIP port for this peer. Default: 5060.
Port int
// Auth enables SIP digest authentication for this peer.
Auth *PeerAuthConfig
// RTPAddress overrides the server-level RTPAddress for this peer.
// Use when this peer needs to see a different IP in SDP (e.g. different
// network interface). Empty means use the server-level setting.
RTPAddress string
// Codecs restricts the codecs offered/accepted for this peer.
// Empty means accept any codec from the server-level CodecPrefs.
Codecs []Codec
}
PeerConfig defines a trusted SIP peer for Server mode.
type Phone ¶
type Phone interface {
Connect(ctx context.Context) error
Disconnect() error
Dial(ctx context.Context, target string, opts ...DialOption) (Call, error)
OnIncoming(func(call Call))
OnRegistered(func())
OnUnregistered(func())
OnError(func(err error))
OnCallState(func(call Call, state CallState))
OnCallEnded(func(call Call, reason EndReason))
OnCallDTMF(func(call Call, digit string))
OnVoicemail(func(status VoicemailStatus))
OnMessage(func(msg SipMessage))
SendMessage(ctx context.Context, target string, body string) error
SendMessageWithType(ctx context.Context, target string, contentType string, body string) error
Watch(ctx context.Context, extension string, fn func(ext string, state ExtensionState, prev ExtensionState)) (string, error)
Unwatch(subscriptionID string) error
SubscribeEvent(ctx context.Context, uri string, event string, expires int, fn func(NotifyEvent)) (string, error)
UnsubscribeEvent(subscriptionID string) error
AttendedTransfer(callA Call, callB Call) error
FindCall(callID string) Call
Calls() []Call
State() PhoneState
}
Phone is the public interface for the xphone library.
func New ¶
func New(opts ...PhoneOption) Phone
New creates a new Phone with the given options. Unset fields receive sensible defaults.
type PhoneOption ¶
type PhoneOption func(*Config)
PhoneOption is a functional option for New().
func WithCodecs ¶
func WithCodecs(codecs ...Codec) PhoneOption
WithCodecs sets the codec preference order.
func WithCredentials ¶
func WithCredentials(username, password, host string) PhoneOption
WithCredentials sets the SIP username, password, and host.
func WithDisplayName ¶ added in v0.5.6
func WithDisplayName(name string) PhoneOption
WithDisplayName sets the display name for SIP Contact headers. Helps PBXes identify the device (e.g., "VoiceApp/1.0").
func WithDtmfMode ¶ added in v0.3.0
func WithDtmfMode(mode DtmfMode) PhoneOption
WithDtmfMode sets the DTMF signaling mode. Default is DtmfRfc4733 (RTP telephone-event packets).
func WithICE ¶ added in v0.3.0
func WithICE(enabled bool) PhoneOption
WithICE enables ICE-Lite candidate gathering and STUN connectivity check responder on the media socket (RFC 8445 section 2.2).
func WithJitterBuffer ¶
func WithJitterBuffer(d time.Duration) PhoneOption
WithJitterBuffer sets the jitter buffer depth.
func WithLogger ¶
func WithLogger(l *slog.Logger) PhoneOption
WithLogger sets the structured logger for the phone. If nil or not called, slog.Default() is used.
func WithMediaTimeout ¶
func WithMediaTimeout(d time.Duration) PhoneOption
WithMediaTimeout sets the RTP media timeout.
func WithNATKeepalive ¶
func WithNATKeepalive(d time.Duration) PhoneOption
WithNATKeepalive sets the NAT keepalive interval (CRLF ping over UDP).
func WithOutboundCredentials ¶ added in v0.4.1
func WithOutboundCredentials(username, password string) PhoneOption
WithOutboundCredentials sets separate SIP digest credentials for outbound INVITE authentication (401/407 challenges). When unset, the registration credentials (Username/Password) are used for all requests.
func WithOutboundProxy ¶ added in v0.4.1
func WithOutboundProxy(uri string) PhoneOption
WithOutboundProxy sets the SIP URI to route outbound INVITEs through. Registration still goes to the configured Host. In-dialog requests (re-INVITE, BYE) use the route set established via Record-Route. Example: "sip:proxy.example.com:5060"
func WithPCMRate ¶
func WithPCMRate(rate int) PhoneOption
WithPCMRate sets the output sample rate for PCMReader.
func WithRTPPorts ¶
func WithRTPPorts(min, max int) PhoneOption
WithRTPPorts sets the RTP port range.
func WithSRTP ¶ added in v0.2.0
func WithSRTP() PhoneOption
WithSRTP enables SRTP (Secure RTP) for media encryption. When enabled, SDP offers use RTP/SAVP with AES_CM_128_HMAC_SHA1_80.
func WithStunServer ¶ added in v0.2.0
func WithStunServer(server string) PhoneOption
WithStunServer sets the STUN server for NAT-mapped address discovery. Format: "host:port" (e.g. "stun.l.google.com:19302"). When set, the phone queries the STUN server during Connect() to discover its public IP, which is then used in SIP Contact headers and SDP.
func WithTransport ¶
func WithTransport(protocol string, tlsCfg *tls.Config) PhoneOption
WithTransport sets the SIP transport protocol and optional TLS config.
func WithTurnCredentials ¶ added in v0.3.0
func WithTurnCredentials(username, password string) PhoneOption
WithTurnCredentials sets the TURN long-term credentials.
func WithTurnServer ¶ added in v0.3.0
func WithTurnServer(server string) PhoneOption
WithTurnServer sets the TURN server for relay allocation (RFC 5766). Format: "host:port" (e.g. "turn.example.com:3478"). Must be used with WithTurnCredentials.
func WithVoicemailURI ¶ added in v0.3.0
func WithVoicemailURI(uri string) PhoneOption
WithVoicemailURI sets the voicemail server URI for MWI SUBSCRIBE (RFC 3842). When set, the phone subscribes to voicemail notifications on Connect() and fires the OnVoicemail callback when the mailbox status changes. Example: "sip:*97@pbx.local"
type PhoneState ¶
type PhoneState int
PhoneState represents the registration state of the phone.
const ( PhoneStateDisconnected PhoneState = iota PhoneStateRegistering PhoneStateRegistered PhoneStateUnregistering PhoneStateRegistrationFailed )
type Server ¶ added in v0.4.0
type Server interface {
Listen(ctx context.Context) error
Shutdown() error
Dial(ctx context.Context, peer string, to string, from string, opts ...DialOption) (Call, error)
DialURI(ctx context.Context, uri string, from string, opts ...DialOption) (Call, error)
OnIncoming(func(call Call))
OnCallState(func(call Call, state CallState))
OnCallEnded(func(call Call, reason EndReason))
OnCallDTMF(func(call Call, digit string))
OnError(func(err error))
OnOptions(func() int)
FindCall(callID string) Call
Calls() []Call
State() ServerState
}
Server is the public interface for SIP trunk/server mode. It accepts and places calls directly with trusted SIP peers (PBXes, trunk providers) without SIP registration. Both Server and Phone produce the same Call interface — the downstream API is identical.
func NewServer ¶ added in v0.4.0
func NewServer(cfg ServerConfig) Server
NewServer creates a new Server with the given configuration.
type ServerConfig ¶ added in v0.4.0
type ServerConfig struct {
// Listen is the address to bind the SIP listener on (e.g. "0.0.0.0:5080").
Listen string
// Listener is an optional pre-created UDP socket for the SIP listener.
// When set, the server uses this connection instead of creating its own,
// giving the caller full control over socket creation (e.g. SO_REUSEPORT,
// buffer sizes, fd passing for zero-downtime deploys). The server takes
// ownership: it will close the connection when Listen returns. Listen is
// ignored when Listener is set.
Listener net.PacketConn
// RTPPortMin is the minimum RTP port to allocate. 0 means OS-assigned.
RTPPortMin int
// RTPPortMax is the maximum RTP port to allocate. 0 means OS-assigned.
RTPPortMax int
// RTPAddress is the public IP to advertise in SDP when listening on 0.0.0.0.
// If empty, the listen address or auto-detected local IP is used.
RTPAddress string
// SRTP enables SRTP media encryption (RTP/SAVP with AES_CM_128_HMAC_SHA1_80).
SRTP bool
// CodecPrefs sets the codec preference order. Default: [PCMU].
CodecPrefs []Codec
// JitterBuffer sets the jitter buffer depth. Default: 50ms.
JitterBuffer time.Duration
// MediaTimeout sets the RTP inactivity timeout. Default: 30s.
MediaTimeout time.Duration
// PCMRate sets the output sample rate for PCMReader. Default: 8000.
PCMRate int
// DtmfMode controls DTMF signaling mode. Default: DtmfRfc4733.
DtmfMode DtmfMode
// Peers is the list of trusted SIP peers that can send/receive calls.
Peers []PeerConfig
// Logger sets the structured logger. Default: slog.Default().
Logger *slog.Logger
}
ServerConfig holds all configuration for a Server instance.
type ServerState ¶ added in v0.4.0
type ServerState int
ServerState represents the state of a Server instance.
const ( ServerStateStopped ServerState = iota ServerStateListening // Listen() active, accepting SIP )
type SipMessage ¶ added in v0.3.0
SipMessage represents an incoming or outgoing SIP MESSAGE (RFC 3428).
type SubState ¶ added in v0.3.0
type SubState int
SubState represents the state of a SIP event subscription (RFC 6665).
type VideoCodec ¶ added in v0.3.0
type VideoCodec int
VideoCodec represents a video codec.
const ( VideoCodecH264 VideoCodec = 96 VideoCodecVP8 VideoCodec = 97 )
type VideoFrame ¶ added in v0.3.0
type VideoFrame struct {
Codec VideoCodec
Timestamp uint32
IsKeyframe bool
Data []byte // H.264: Annex-B NAL units; VP8: raw frame
}
VideoFrame represents a single assembled video frame from the RTP pipeline. The library does NOT encode/decode video — consumers use platform APIs (VideoToolbox, VA-API, etc.) and feed raw encoded frames in/out.
type VideoUpgradeRequest ¶ added in v0.3.0
type VideoUpgradeRequest struct {
// contains filtered or unexported fields
}
VideoUpgradeRequest is created when the remote sends a re-INVITE adding video. The app must call Accept() or Reject(). If neither is called within 30 seconds, the request is automatically rejected. Calling Accept or Reject more than once is a no-op.
func (*VideoUpgradeRequest) Accept ¶ added in v0.3.0
func (r *VideoUpgradeRequest) Accept()
Accept accepts the video upgrade, starts the video pipeline, and responds 200 OK with a video SDP answer to the remote party.
func (*VideoUpgradeRequest) Reject ¶ added in v0.3.0
func (r *VideoUpgradeRequest) Reject()
Reject rejects the video upgrade and responds 200 OK with m=video 0 (RFC 3264 media rejection). Audio is unchanged.
func (*VideoUpgradeRequest) RemoteCodec ¶ added in v0.3.0
func (r *VideoUpgradeRequest) RemoteCodec() VideoCodec
RemoteCodec returns the video codec offered by the remote party.
type VoicemailStatus ¶ added in v0.3.0
type VoicemailStatus struct {
// MessagesWaiting indicates whether new messages are waiting.
MessagesWaiting bool
// Account is the optional message account URI (e.g. "sip:*97@pbx.local").
Account string
// NewMessages is the count of new (unheard) voice messages.
NewMessages int
// OldMessages is the count of old (heard) voice messages.
OldMessages int
}
VoicemailStatus represents the state of a voicemail mailbox (RFC 3842 MWI).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ice implements ICE-Lite (RFC 8445 §2.2) for SIP media NAT traversal.
|
Package ice implements ICE-Lite (RFC 8445 §2.2) for SIP media NAT traversal. |
|
internal
|
|
|
rtcp
Package rtcp implements basic RTCP (RFC 3550) Sender/Receiver Reports for trunk compatibility.
|
Package rtcp implements basic RTCP (RFC 3550) Sender/Receiver Reports for trunk compatibility. |
|
sip
Package sip implements a minimal SIP stack for xphone: message parsing/building, digest authentication, UDP transport, and client transactions.
|
Package sip implements a minimal SIP stack for xphone: message parsing/building, digest authentication, UDP transport, and client transactions. |
|
srtp
Package srtp implements SRTP/SRTCP (RFC 3711) with AES_CM_128_HMAC_SHA1_80.
|
Package srtp implements SRTP/SRTCP (RFC 3711) with AES_CM_128_HMAC_SHA1_80. |
|
stun
Package stun implements a STUN Binding client (RFC 5389) and provides shared STUN message primitives used by the TURN and ICE packages.
|
Package stun implements a STUN Binding client (RFC 5389) and provides shared STUN message primitives used by the TURN and ICE packages. |
|
Package turn implements a TURN client (RFC 5766) for NAT relay allocation.
|
Package turn implements a TURN client (RFC 5766) for NAT relay allocation. |